import { z } from "zod"; import * as cache from "../cache.ts"; import { WebSearchTestInput, WebSearchTestResult } from "../capabilities-io.ts"; import { CapabilityDescriptor, KnownCapabilityId } from "../capability.ts"; import { Id, IdParam } from "../common.ts"; import * as mcp from "../mcp.ts"; import * as pi from "../pi.ts"; import * as settingsNs from "../settings.ts"; import { GlobalSettings, GlobalSettingsUpdate } from "../settings.ts"; import { ep, OkResult } from "./shared.ts"; export const platformApi = { // ── 能力 ── capabilities: { list: ep({ method: "get", path: "/capabilities", scope: "read", summary: "能力注册中心描述", response: z.object({ items: z.array(CapabilityDescriptor) }), }), invoke: ep({ method: "post", path: "/capabilities/{capabilityId}/invoke", scope: "read", summary: "调用能力(实际所需 scope 见描述符;服务端按能力再次鉴权)", params: z.object({ capabilityId: KnownCapabilityId }), body: z.object({ input: z.unknown(), preview: z.boolean().default(false) }), response: z.object({ output: z.unknown(), taskId: Id.nullable(), planId: Id.nullable() }), }), stats: ep({ method: "get", path: "/capabilities/stats", scope: "read", summary: "能力调用统计:每能力调用数、失败数、平均耗时、最近调用、调用来源(内置智能体 / MCP / REST)", query: mcp.CapabilityStatsQuery, response: mcp.CapabilityStatsResponse, }), tools: ep({ method: "get", path: "/capabilities/tools", scope: "read", summary: "能力清单:描述符、工具名、输入输出 JSON Schema、当前授权状态", response: mcp.CapabilityToolsResponse, }), }, // ── 对外 MCP 服务端(Streamable HTTP 在 /mcp,不进 OpenAPI;这里是状态与配置)── mcp: { status: ep({ method: "get", path: "/mcp/status", scope: "admin", summary: "MCP 服务端状态:启用、允许的 API Key、当前会话、最近调用、限速", response: mcp.McpServerStatus, }), update: ep({ method: "patch", path: "/mcp/config", scope: "admin", summary: "修改 MCP 服务端配置(启用开关、允许的 API Key)", body: mcp.McpServerConfigUpdate, response: mcp.McpServerConfig, }), }, // ── 缓存(C2-A,docs/runbook/cache.md) ── cache: { tmdb: ep({ method: "get", path: "/cache/tmdb", scope: "admin", summary: "TMDB 缓存条目(请求响应与识别结果:键、过期时间、大小)与上限 / TTL", query: cache.TmdbCacheQuery, response: cache.TmdbCacheList, }), tmdbClear: ep({ method: "post", path: "/cache/tmdb/clear", scope: "admin", summary: "清空 TMDB 缓存(请求响应与识别结果)", response: cache.CacheClearResult, }), images: ep({ method: "get", path: "/cache/images", scope: "admin", summary: "图片代理缓存用量", response: cache.ImageCacheStatus, }), imagesClear: ep({ method: "post", path: "/cache/images/clear", scope: "admin", summary: "清空图片代理缓存", response: cache.CacheClearResult, }), regions: ep({ method: "get", path: "/cache/regions", scope: "admin", summary: "通用缓存(kv_cache)按区统计:条目数、已过期数、占用字节、最近写入", response: cache.CacheRegionList, }), regionClear: ep({ method: "post", path: "/cache/regions/{region}/clear", scope: "admin", summary: "清空通用缓存的一个区", params: cache.CacheRegionParam, response: cache.CacheClearResult, }), }, // ── 网页搜索(WEB-1,docs/runbook/websearch.md) ── websearch: { test: ep({ method: "post", path: "/websearch/test", scope: "admin", summary: "用已保存的搜索服务 Key 实际查询一次(不走缓存,计入每分钟上限)", body: WebSearchTestInput, response: WebSearchTestResult, }), }, // ── 设置 ── settings: { get: ep({ method: "get", path: "/settings", scope: "read", summary: "全局设置", response: GlobalSettings }), update: ep({ method: "patch", path: "/settings", scope: "admin", summary: "修改全局设置", body: GlobalSettingsUpdate, response: GlobalSettings, }), }, // ── 整理命名模板(W9-C,docs/design/naming-template.md):保存前服务端编译校验 ── naming: { variables: ep({ method: "get", path: "/naming/variables", scope: "read", summary: "命名变量表(名称对齐 MP,含说明/示例值/我们特有标记)、过滤器、预览内置样本", response: settingsNs.NamingVariablesResponse, }), preview: ep({ method: "post", path: "/naming/preview", scope: "read", summary: "预览:模板 + 样本(文件名 / 资源 / 资产 / 内置)→ 相对路径、段角色、错误(行列号)、提示;已有路径绑定时目录用绑定", body: settingsNs.NamingPreviewInput, response: settingsNs.NamingPreviewResult, }), templates: ep({ method: "get", path: "/naming/templates", scope: "read", summary: "全局 / 库根 / 作品三级模板与按类型的生效结果(作品 > 库根 > 全局)", query: settingsNs.NamingTemplatesQuery, response: settingsNs.NamingTemplatesView, }), saveTemplates: ep({ method: "put", path: "/naming/templates", scope: "admin", summary: "保存全局命名模板(逐条编译校验,有错 422 不保存)", body: settingsNs.NamingSettingsPut, response: settingsNs.NamingSettings, }), libraryRoot: ep({ method: "put", path: "/library-roots/{id}/naming", scope: "admin", summary: "库根级命名覆盖(按类型;null 清除)", params: IdParam, body: settingsNs.LibraryRootNamingPut, response: settingsNs.LibraryRootNamingPut, }), entity: ep({ method: "put", path: "/media-entities/{id}/naming-override", scope: "admin", summary: "作品级命名覆盖(按类型;null 清除)。已有路径绑定的目录不受影响,只影响之后的文件名与新季目录", params: IdParam, body: settingsNs.EntityNamingOverridePut, response: settingsNs.EntityNamingOverridePut, }), }, // ── 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: "模型服务列表(不含密钥)", response: pi.PiUpstreamList, }), get: ep({ method: "get", path: "/pi/upstreams/{id}", scope: "admin", summary: "模型服务详情", params: IdParam, response: pi.PiUpstream, }), create: ep({ method: "post", path: "/pi/upstreams", scope: "admin", auth: "session", summary: "新建模型服务(apiKey 只写,存 secret 表)", body: pi.PiUpstreamCreate, response: pi.PiUpstream, }), update: ep({ method: "patch", path: "/pi/upstreams/{id}", scope: "admin", auth: "session", summary: "修改模型服务(provider 不可改;makeDefault 设默认模型服务)", params: IdParam, body: pi.PiUpstreamUpdate, response: pi.PiUpstream, }), remove: ep({ method: "delete", path: "/pi/upstreams/{id}", scope: "admin", auth: "session", summary: "删除模型服务(连同凭据;是默认模型服务时清空默认)", 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: "模型列表:已获取过远程列表时返回远程与静态目录的合并(远程优先),否则静态目录", params: IdParam, response: pi.PiUpstreamModels, }), modelsRefresh: ep({ method: "post", path: "/pi/upstreams/{id}/models/refresh", scope: "admin", auth: "session", summary: "获取模型列表:用该模型服务的凭证调 provider 的模型列表接口并存库;失败或不支持时回静态目录并给出原因", params: IdParam, response: pi.PiUpstreamModels, }), modelsProbe: ep({ method: "post", path: "/pi/models/probe", scope: "admin", auth: "session", summary: "新建模型服务前试拉模型列表(凭证只用于这一次请求,不落库)", body: pi.PiModelsProbeRequest, 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, }), }, } as const;