import { z } from "zod"; import * as browser from "../browser.ts"; import { ep } from "./shared.ts"; export const browserApi = { // ── 浏览器(C1-E):站点浏览器会话、原站视图、人工接管;写动作需人工持有控制权(接管) ── browser: { providers: ep({ method: "get", path: "/browser/providers", scope: "admin", summary: "浏览器服务列表(地址、凭证引用、启用、能力 supported/verified);probe=true 现场探测并写回", query: browser.BrowserProvidersQuery, response: z.object({ items: z.array(browser.BrowserProviderView) }), }), createProvider: ep({ method: "post", path: "/browser/providers", scope: "admin", summary: "登记浏览器服务(camofox 地址、访问凭据存凭证库、启用)", body: browser.BrowserProviderCreate, response: browser.BrowserProviderView, }), updateProvider: ep({ method: "patch", path: "/browser/providers/{id}", scope: "admin", summary: "修改浏览器服务地址、访问凭据或启用(环境变量登记的只能改启用)", params: browser.BrowserProviderParams, body: browser.BrowserProviderUpdate, response: browser.BrowserProviderView, }), deleteProvider: ep({ method: "delete", path: "/browser/providers/{id}", scope: "admin", summary: "删除浏览器服务(有未关闭会话或由环境变量登记时拒绝)", params: browser.BrowserProviderParams, response: z.object({ id: z.string() }), }), probeProvider: ep({ method: "post", path: "/browser/providers/{id}/probe", scope: "admin", summary: "测试连通:探测该浏览器服务的 /health 与 /openapi.json 并写回能力", params: browser.BrowserProviderParams, response: browser.BrowserProviderView, }), sessions: ep({ method: "get", path: "/browser/sessions", scope: "admin", summary: "浏览器会话列表(以数据库为准;默认只列未关闭的)", query: browser.BrowserSessionsQuery, response: z.object({ items: z.array(browser.BrowserSessionView) }), }), open: ep({ method: "post", path: "/browser/sessions", scope: "admin", summary: "打开站点浏览器会话(同一站点/账号已有未关闭会话时直接返回它)", body: browser.BrowserSessionOpen, response: browser.BrowserSessionView, }), get: ep({ method: "get", path: "/browser/sessions/{id}", scope: "admin", summary: "浏览器会话详情(控制者、标签页、租约、是否需重新读取页面)", params: browser.BrowserSessionParams, response: browser.BrowserSessionView, }), close: ep({ method: "delete", path: "/browser/sessions/{id}", scope: "admin", summary: "关闭浏览器会话(关闭上游标签页,撤销租约)", params: browser.BrowserSessionParams, response: browser.BrowserSessionView, }), openTab: ep({ method: "post", path: "/browser/sessions/{id}/tabs", scope: "admin", summary: "新建标签页并设为当前(需持有控制权)", params: browser.BrowserSessionParams, body: browser.BrowserTabOpen, response: browser.BrowserSessionView, }), closeTab: ep({ method: "delete", path: "/browser/sessions/{id}/tabs/{tabId}", scope: "admin", summary: "关闭标签页(需持有控制权)", params: browser.BrowserTabParams, response: browser.BrowserSessionView, }), navigate: ep({ method: "post", path: "/browser/sessions/{id}/navigate", scope: "admin", summary: "地址栏导航或后退/前进/刷新(需持有控制权;限站点域名)", params: browser.BrowserSessionParams, body: browser.BrowserNavigate, response: browser.BrowserNavigation, }), snapshot: ep({ method: "get", path: "/browser/sessions/{id}/snapshot", scope: "admin", summary: "结构化页面快照(凭证已遮蔽)", params: browser.BrowserSessionParams, query: browser.BrowserSnapshotQuery, response: browser.BrowserSnapshot, }), screenshot: ep({ method: "get", path: "/browser/sessions/{id}/screenshot", scope: "admin", summary: "当前画面截图(base64,只给原站视图,不进模型)", params: browser.BrowserSessionParams, query: browser.BrowserScreenshotQuery, response: browser.BrowserScreenshotView, }), click: ep({ method: "post", path: "/browser/sessions/{id}/click", scope: "admin", summary: "点击(快照引用 / 选择器 / 截图坐标;需持有控制权)", params: browser.BrowserSessionParams, body: browser.BrowserClick, response: browser.BrowserActionResult, }), type: ep({ method: "post", path: "/browser/sessions/{id}/type", scope: "admin", summary: "输入文本 / 站点账号凭证 / 按键(需持有控制权;凭证由服务端注入)", params: browser.BrowserSessionParams, body: browser.BrowserType, response: browser.BrowserActionResult, }), scroll: ep({ method: "post", path: "/browser/sessions/{id}/scroll", scope: "admin", summary: "滚动页面(需持有控制权)", params: browser.BrowserSessionParams, body: browser.BrowserScroll, response: browser.BrowserActionResult, }), select: ep({ method: "post", path: "/browser/sessions/{id}/select", scope: "admin", summary: "下拉框选择(需持有控制权)", params: browser.BrowserSessionParams, body: browser.BrowserSelect, response: browser.BrowserActionResult, }), extract: ep({ method: "post", path: "/browser/sessions/{id}/extract", scope: "admin", summary: "按 JSON Schema(x-ref)提取页面字段", params: browser.BrowserSessionParams, body: browser.BrowserExtract, response: browser.BrowserExtractResult, }), resource: ep({ method: "post", path: "/browser/sessions/{id}/resource", scope: "organize.execute", summary: "解析当前原站资源详情,供独立手动下载使用", params: browser.BrowserSessionParams, body: browser.BrowserResourceRequest, response: browser.BrowserResourceResult, }), download: ep({ method: "post", path: "/browser/sessions/{id}/download", scope: "organize.execute", summary: "捕获浏览器下载 → 后端暂存并核验 infohash、匹配本地资源;给出 submit 时经 POST /acquisition-plans 同一流程提交", params: browser.BrowserSessionParams, body: browser.BrowserDownload, response: browser.BrowserDownloadResult, }), reconnect: ep({ method: "post", path: "/browser/sessions/{id}/reconnect", scope: "admin", summary: "浏览器服务重启后重新连接(幂等):上游上下文还在就什么都不做;否则注入账号 Cookie、新开标签页回到原地址,控制权不变;失败 503 browser_reconnect_failed", params: browser.BrowserSessionParams, response: browser.BrowserSessionView, }), acquire: ep({ method: "post", path: "/browser/sessions/{id}/control/acquire", scope: "admin", summary: "人工接管:先停止该会话新的自动化输入并等进行中的操作收尾,再发放人工租约", params: browser.BrowserSessionParams, body: browser.BrowserControlAcquire, response: browser.BrowserControlResult, }), release: ep({ method: "post", path: "/browser/sessions/{id}/control/release", scope: "admin", summary: "交还控制权:智能体恢复前必须重新读取页面,旧元素引用失效", params: browser.BrowserSessionParams, body: browser.BrowserControlRelease, response: browser.BrowserControlResult, }), }, } as const;