import type { IncomingMessage, ServerResponse } from 'node:http'; /** * `/workbench-doctor` — 手机自助诊断页。 * * 背景:工作台终端在某台 iPhone 上持续空白,而桌面浏览器、Chromium 的移动端模拟 * 全都正常。真机与开发机之间差的是网络(办公网只放行 dashboard 端口)、Cookie * 携带策略(iOS WebView 的第三方/跨站 Cookie)、以及客户端缓存的旧 bundle——这三 * 类差异隔着屏幕都看不见,只能让真机自己说。 * * 于是这一页刻意做成**零依赖**:不加载 SPA bundle、不引任何外部资源,整页 HTML + * 内联 JS 一次发完。SPA 自己挂掉时它照样能开,这正是需要它的场景。用户在手机上打 * 开、等它自己跑完、截一张图发回来即可定位。 * * 安全口径(与静态壳同级放行,无需登录): * - 只探测「当前访问者自己」的可达性,不代表任何人做事,也不读服务端状态; * - 全程不回显任何 token / secret:Cookie 只报有无(连值都不取),view-link 只 * 报 origin 与 pathname,查询串里的 viewToken 一律以「已隐藏」占位; * - 所有网络调用带 credentials: 'include'——诊断的就是「这台设备的登录态带没带上」。 */ export declare const WORKBENCH_DOCTOR_PATH = "/workbench-doctor"; /** 页面标题,同时是测试用的标志字符串。 */ export declare const WORKBENCH_DOCTOR_TITLE = "workbench-doctor"; /** 页尾提示。用户看到这行就知道下一步该干什么。 */ export declare const WORKBENCH_DOCTOR_FOOTER = "\u622A\u56FE\u672C\u9875\u53D1\u7ED9\u673A\u5668\u4EBA\u5373\u53EF"; /** 每项检查的独立超时。手机上被网关静默丢包时,卡死的那一项不能拖住后面。 */ export declare const WORKBENCH_DOCTOR_STEP_TIMEOUT_MS = 8000; /** * WS 探针自带的握手超时,须不长于外层步骤预算:正常情况下由它先到点收尾(关 * socket、写一行超时),外层 8 秒只是兜底闸门。 */ export declare const WORKBENCH_DOCTOR_WS_TIMEOUT_MS = 7500; /** `/api/sessions` 行里与「终端可探测」判定相关的字段子集。 */ export interface DoctorSessionRow { sessionId?: unknown; id?: unknown; status?: unknown; cliId?: unknown; webPort?: unknown; proxyPort?: unknown; } /** * 会话不可探测的原因;空串表示可探测。「可探测」= 终端真的走本机 worker:webPort * 是 worker 终端页、proxyPort 是 dashboard 的 /s/ 反代,两个都在 view-link 链路 * 才通。riff 等外部后端的终端在沙箱侧,从这条链路探出来的失败不是故障,是误诊。 */ export declare function doctorProbeableWhy(s: DoctorSessionRow | null | undefined): string; export interface DoctorSessionPick { /** * picked = 选中可探测会话;missing = ?session 点名的 id 不在列表; * unprobeable = 点名的会话存在但探不了(reason 给原因); * none = 自动挑选时一个可探测会话都没有(reason 给统计),绝不拿第一个凑数。 */ kind: 'picked' | 'missing' | 'unprobeable' | 'none'; sessionId: string; status: string; reason: string; /** picked 且 status 不是 active/working/idle:页面提示「退而用这个」。 */ fallback: boolean; } /** * 从会话列表挑诊断对象。wanted 非空时只认点名的那个(存在性与可探测性都校验); * 自动挑选只在可探测会话里挑,优先 active/working/idle。 */ export declare function pickDoctorSession(list: DoctorSessionRow[] | null | undefined, wanted: string): DoctorSessionPick; /** runStep 每一步的闸门:外层判超时后 closed 置真并触发 cancel 钩子。 */ export interface DoctorWsGate { closed: boolean; cancel: Array<() => void>; } /** probeWs 关心的 WebSocket 最小面;单测注入假实现,不起真服务。 */ export interface DoctorWsLike { onopen: (() => void) | null; onerror: (() => void) | null; onclose: ((ev?: { code?: unknown; reason?: unknown; }) => void) | null; close(): void; } /** probeWs 的全部外部依赖,页面喂真的,单测喂假的。 */ export interface DoctorWsEnv { /** WebSocket 工厂;宿主没有 WebSocket 时传 undefined,记一行后直接放行。 */ makeWs: ((url: string) => DoctorWsLike) | undefined; line: (icon: string, label: string, value: string, note?: string) => void; timeoutMs: number; setTimeout: (fn: () => void, ms: number) => unknown; clearTimeout: (t: unknown) => void; now: () => number; } /** * WS 握手探针。三条硬规矩(对应上一版的泄漏与误诊): * 1. 自带 timeoutMs 兜底:到点自己收尾,不等外层 8 秒闸门; * 2. 无论成败必 close socket——half-open socket 一直挂着就是 WS 泄漏; * 3. 外层闸门(gate.closed)合上之后一行都不再写:迟到的握手结果只会串到后面 * 检查项下面造成误读。cancel 钩子由外层超时触发,负责关 socket 并放行 Promise。 */ export declare function doctorProbeWs(env: DoctorWsEnv, label: string, wsUrl: string, shown: string, gate: DoctorWsGate | null): Promise; /** * 整页 HTML。模板只内联**构建期常量与上面共享函数的源码**,不含任何请求期数据—— * 页面上出现的每一个动态值都是访问者浏览器自己在运行时算出来的,服务端不往里塞任 * 何东西,也就不存在把密钥渲染进页面的可能。 * * 内联 JS(含被内联的共享函数)刻意写成不含反引号、不含 `${`、不含正则字面量的朴 * 素 ES5 风格:前两者会和外层模板字符串打架,正则里的反斜杠则会被模板字符串吃掉一 * 层(`\/` 变成 `/`,正则当场破相)。用 slice/split/trim 代替正则,问题从根上消失。 */ export declare function workbenchDoctorHtml(): string; /** * 挂在 dashboard 的 `/workbench-doctor`。与静态壳同级放行(见 dashboard/auth.ts 的 * `isStaticShell`):这一页只探测访问者自己的可达性,不读服务端状态、不回显任何凭证, * 而**恰恰是登录态坏掉的时候最需要它能打开**。 */ export declare function handleWorkbenchDoctor(req: IncomingMessage, res: ServerResponse, url: URL): boolean; //# sourceMappingURL=workbench-doctor.d.ts.map