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

+2 -1
View File
@@ -43,13 +43,14 @@ const pingModule: ServerModule = {
};
describe("ServerModule 骨架", () => {
it("清单里是四个 W2 模块 + 文件管理,名字唯一", () => {
it("清单里是四个 W2 模块 + 文件管理 + Pi 上游,名字唯一", () => {
expect(SERVER_MODULES.map((m) => m.name)).toEqual([
"auth-settings",
"sources-identity",
"acquisition-organize",
"agent",
"file-manager",
"pi",
]);
});