feat(server): pi module with upstream CRUD, server-side OAuth and token refresh

- /pi/providers, /pi/upstreams CRUD, test (auth-only by default, no cost), models
- subscription login via pi models.login with a suspended interaction: browser mode
  waits for the pasted redirect URL, device mode polls server-side; per-provider
  login lock, readable callback-port-in-use errors
- credential store locks the pi_upstream row (select for update) so api and worker
  refreshes never double-write a rotated refresh token
- refreshPiTokens task: refresh before expiry; failure marks expired and opens a
  relogin decision, closed again after a successful login
- runAgent picks upstreamId > agent_run.upstream_id > default upstream > legacy
  settings; credentials injected by the server, never logged

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 02:23:22 +08:00
1 parent 743fe2486d
commit fd3ffe5a8d
13 files changed
+1617 -26

No files matched your search

+10 -1
View File
@@ -29,12 +29,21 @@ export function registerAgentTasks(registry: TaskRegistry, env: () => AgentEnv)
defineTask({
name: "runAgent",
version: 1,
inputSchema: z.object({ agentRunId: id }),
/** upstreamId:覆盖默认上游(W7-A);缺省用 agent_run.upstream_id,再缺省 settings.pi.defaultUpstreamId */
inputSchema: z.object({ agentRunId: id, upstreamId: id.optional() }),
retryPolicy: DB_RETRY,
handler: async (t) => {
const e = env();
const runId = t.input.agentRunId;
const upstreamId = t.input.upstreamId;
await t.step.run("start", async () => {
if (upstreamId)
await e.ctx.db
.updateTable("agent_run")
.set({ upstream_id: upstreamId })
.where("id", "=", runId)
.where("started_at", "is", null)
.execute();
await e.ctx.db
.updateTable("task")
.set({ status: "running", started_at: new Date(), attempts: sql`attempts + 1` })