feat(contracts,db): Pi upstream contracts and pi_upstream table

- contracts/pi.ts: 41 builtin provider ids + openai-compatible + fake, auth kinds,
  upstream row/view/create/update, provider capability table (oauth modes, termsNote),
  test/models/oauth start/complete schemas
- settings.pi gains defaultUpstreamId; legacy single-upstream fields kept for fallback
- api.ts: /pi/providers, /pi/upstreams CRUD, test, models, oauth start/complete/logout
  (writes and login are console-session only)
- migration 0027: pi_upstream, agent_run.upstream_id, one-time conversion of saved
  legacy settings.pi into a default upstream

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 02:23:09 +08:00
1 parent 39a001c099
commit b9dbd54e37
8 files changed
+665 -4

No files matched your search

+103
View File
@@ -12,6 +12,7 @@ import * as identity from "./identity.ts";
import * as instances from "./instances.ts";
import * as ops from "./ops.ts";
import * as organization from "./organization.ts";
import * as pi from "./pi.ts";
import * as policy from "./policy.ts";
import { GlobalSettings, GlobalSettingsUpdate } from "./settings.ts";
import * as sources from "./sources.ts";
@@ -1230,6 +1231,108 @@ export const Api = {
}),
},
// ── Pi 模型上游(W7-A):多上游共存 + 订阅登录;写操作与登录只接受控制台会话 ──
pi: {
providers: ep({
method: "get",
path: "/pi/providers",
scope: "admin",
summary: "Pi provider 能力表(认证方式、订阅登录方式与条款提示、baseUrl 覆盖、env 字段、静态模型表)",
query: pi.PiProvidersQuery,
response: pi.PiProvidersResponse,
}),
list: ep({
method: "get",
path: "/pi/upstreams",
scope: "admin",
summary: "Pi 上游列表(不含密钥)",
response: pi.PiUpstreamList,
}),
get: ep({
method: "get",
path: "/pi/upstreams/{id}",
scope: "admin",
summary: "Pi 上游详情",
params: IdParam,
response: pi.PiUpstream,
}),
create: ep({
method: "post",
path: "/pi/upstreams",
scope: "admin",
auth: "session",
summary: "新建 Pi 上游(apiKey 只写,存 secret 表)",
body: pi.PiUpstreamCreate,
response: pi.PiUpstream,
}),
update: ep({
method: "patch",
path: "/pi/upstreams/{id}",
scope: "admin",
auth: "session",
summary: "修改 Pi 上游(provider 不可改;makeDefault 设默认上游)",
params: IdParam,
body: pi.PiUpstreamUpdate,
response: pi.PiUpstream,
}),
remove: ep({
method: "delete",
path: "/pi/upstreams/{id}",
scope: "admin",
auth: "session",
summary: "删除 Pi 上游(连同凭据;是默认上游时清空默认)",
params: IdParam,
response: OkResult,
}),
test: ep({
method: "post",
path: "/pi/upstreams/{id}/test",
scope: "admin",
auth: "session",
summary: "测试上游:默认只解析凭据与校验模型(不产生费用);mode=completion 发一次最小补全",
params: IdParam,
body: pi.PiUpstreamTestRequest,
response: pi.PiUpstreamTestResult,
}),
models: ep({
method: "get",
path: "/pi/upstreams/{id}/models",
scope: "admin",
summary: "上游可用模型(静态目录;Radius 先动态刷新)",
params: IdParam,
response: pi.PiUpstreamModels,
}),
oauthStart: ep({
method: "post",
path: "/pi/upstreams/{id}/oauth/start",
scope: "admin",
auth: "session",
summary: "开始订阅登录:返回授权链接(browser)或验证码(device)",
params: IdParam,
body: pi.PiOAuthStartRequest,
response: pi.PiOAuthStartResult,
}),
oauthComplete: ep({
method: "post",
path: "/pi/upstreams/{id}/oauth/complete",
scope: "admin",
auth: "session",
summary: "完成订阅登录:browser 粘回地址栏 URL;device 服务端轮询(本次最多等 waitMs,未完成返回 pending)",
params: IdParam,
body: pi.PiOAuthCompleteRequest,
response: pi.PiOAuthCompleteResult,
}),
oauthLogout: ep({
method: "post",
path: "/pi/upstreams/{id}/oauth/logout",
scope: "admin",
auth: "session",
summary: "退出订阅登录:删除本地 token(不会撤销提供方侧授权)",
params: IdParam,
response: pi.PiUpstream,
}),
},
// ── 登录门禁 ──
/** /auth/setup、/auth/login、/auth/session 免鉴权;改密与 TOTP 只接受控制台 cookie 会话 */
auth: {