dsh-deeppilot 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["cryptoSign","http2Connect","fsConstants"],"sources":["../src/token.ts","../src/connection.ts","../src/host-bridge.ts","../src/report-service.ts","../src/report-wire.ts","../src/report-remote.ts","../src/relay-test.ts","../src/apns.ts","../src/relay-client.ts","../src/remote-supervisor.ts","../src/local-address.ts","../src/index.ts"],"sourcesContent":["import { randomBytes, timingSafeEqual } from 'node:crypto'\nimport { access, mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, resolve } from 'node:path'\n\n/** Expand a leading ~ using the process home directory. */\nexport function expandHome(p: string): string {\n if (p === '~') return homedir()\n if (p.startsWith('~/')) return resolve(homedir(), p.slice(2))\n return p\n}\n\nfunction dshDataRoot(): string {\n const dshHome = process.env.DSH_HOME\n if (dshHome && dshHome.trim().length > 0) return resolve(dshHome.trim())\n return resolve(homedir(), '.dsh')\n}\n\n/** DeepPilot data directory: under $DSH_HOME when set, else ~/.dsh. */\nexport function bridgeDataDir(): string {\n return resolve(dshDataRoot(), 'deeppilot')\n}\n\n/**\n * Move the pre-DeepPilot data directory as one atomic directory rename.\n * Existing canonical data always wins; secrets are never merged or replaced.\n */\nexport async function migrateLegacyBridgeDataDir(): Promise<string | null> {\n const target = bridgeDataDir()\n try {\n await access(target)\n return null\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n\n const legacy = resolve(dshDataRoot(), 'pocket-bridge')\n try {\n await rename(legacy, target)\n return legacy\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n}\n\n/**\n * Load the pairing token from disk or generate and persist a fresh one.\n * The file is written 0600; the token never appears in logs.\n */\nexport async function loadOrCreateToken(tokenPath: string): Promise<string> {\n const full = expandHome(tokenPath)\n try {\n const existing = (await readFile(full, 'utf8')).trim()\n if (existing.length >= 32) return existing\n } catch {\n // fall through to generation\n }\n const token = randomBytes(32).toString('base64url')\n await mkdir(dirname(full), { recursive: true })\n await writeFile(full, token + '\\n', { mode: 0o600 })\n return token\n}\n\n/**\n * Generate a fresh pairing token and replace the stored one, invalidating\n * every copy of the old secret. The write goes to a same-directory temp file\n * renamed over the target so a crash can never leave a truncated token file.\n */\nexport async function writeNewToken(tokenPath: string): Promise<string> {\n const full = expandHome(tokenPath)\n const token = randomBytes(32).toString('base64url')\n await mkdir(dirname(full), { recursive: true })\n const temp = `${full}.${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temp, token + '\\n', { mode: 0o600 })\n await rename(temp, full)\n return token\n}\n\n/** Constant-time token comparison; both sides are high-entropy secrets. */\nexport function tokenMatches(presented: string | null | undefined, expected: string): boolean {\n if (!presented) return false\n const a = Buffer.from(presented)\n const b = Buffer.from(expected)\n if (a.length !== b.length) {\n // Burn a same-length comparison so wrong-length probes keep flat timing.\n timingSafeEqual(b, b)\n return false\n }\n return timingSafeEqual(a, b)\n}\n\nexport type ApnsEnvironment = 'development' | 'production'\n\n/** Per-device APNs registration (F-9 离线推送). The raw token never leaves the registry file. */\nexport interface DeviceApnsInfo {\n /** Hex device token, lowercased, 32–512 chars. */\n token: string\n environment: ApnsEnvironment\n updatedAt: number\n /** Per-category opt-out mirror; absent keys mean enabled. */\n categories?: Record<string, boolean>\n}\n\nexport interface DeviceRecord {\n deviceId: string\n deviceName: string\n appVersion: string\n firstSeenTs: number\n lastSeenTs: number\n apns?: DeviceApnsInfo\n}\n\n/** Hex shape of an APNs device token as delivered by iOS (usually 64 chars). */\nconst APNS_TOKEN_PATTERN = /^[0-9a-f]{32,512}$/\n\nexport function isValidApnsToken(token: unknown): token is string {\n return typeof token === 'string' && APNS_TOKEN_PATTERN.test(token)\n}\n\n/** Registry row cap: hello spam with fresh ids must not grow the file forever. */\nexport const MAX_DEVICES = 64\n\n/**\n * Paired-device registry persisted as one JSON document. Whole-document\n * writes (serialized, never interleaved); a corrupt file falls back to an\n * empty registry rather than failing the plugin.\n */\nexport class DeviceStore {\n private devices = new Map<string, DeviceRecord>()\n private flushTail: Promise<void> = Promise.resolve()\n\n private constructor(private readonly filePath: string) {}\n\n static async load(filePath: string): Promise<DeviceStore> {\n const store = new DeviceStore(filePath)\n try {\n const raw = JSON.parse(await readFile(expandHome(filePath), 'utf8')) as {\n devices?: DeviceRecord[]\n }\n for (const rec of raw.devices ?? []) {\n if (typeof rec.deviceId === 'string') store.devices.set(rec.deviceId, rec)\n }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code\n if (code === 'ENOENT') {\n // First run on this home: nothing registered yet — normal.\n } else {\n // A truncated/corrupt registry silently wiping pairings and push\n // registrations would be indistinguishable from \"never registered\".\n // Make data loss loud so the cause (crash mid-write, disk, …) shows.\n console.log('[deeppilot] device registry unreadable, starting empty: ' + String(error))\n }\n }\n return store\n }\n\n touch(record: Omit<DeviceRecord, 'firstSeenTs' | 'lastSeenTs'>, now: number): void {\n const existing = this.devices.get(record.deviceId)\n if (existing) {\n existing.lastSeenTs = now\n existing.deviceName = record.deviceName || existing.deviceName\n existing.appVersion = record.appVersion || existing.appVersion\n } else {\n if (this.devices.size >= MAX_DEVICES) {\n // Evict the least-recently-seen device to make room for the newcomer.\n let oldestId: string | undefined\n let oldestTs = Number.POSITIVE_INFINITY\n for (const [id, value] of this.devices) {\n if (value.lastSeenTs < oldestTs) {\n oldestTs = value.lastSeenTs\n oldestId = id\n }\n }\n if (oldestId !== undefined) this.devices.delete(oldestId)\n }\n this.devices.set(record.deviceId, {\n ...record,\n firstSeenTs: now,\n lastSeenTs: now,\n })\n }\n void this.flush()\n }\n\n list(): DeviceRecord[] {\n return [...this.devices.values()]\n }\n\n /**\n * Store (or refresh) the APNs registration of a paired device. Idempotent:\n * an unchanged registration does not rewrite the registry file, so the\n * app's re-register-on-every-handshake policy stays write-quiet.\n */\n setPushToken(\n deviceId: string,\n token: string,\n environment: ApnsEnvironment,\n categories: Record<string, boolean> | undefined,\n now: number,\n ): void {\n const normalized = token.toLowerCase()\n if (!isValidApnsToken(normalized)) return\n let record = this.devices.get(deviceId)\n if (!record) {\n // A live authenticated socket always has a row (hello touches it), but\n // a rotation could clear the registry mid-session; self-heal instead of\n // dropping the registration.\n record = { deviceId, deviceName: 'unknown', appVersion: 'unknown', firstSeenTs: now, lastSeenTs: now }\n this.devices.set(deviceId, record)\n }\n const next: DeviceApnsInfo = { token: normalized, environment, updatedAt: now }\n if (categories && typeof categories === 'object') {\n const clean: Record<string, boolean> = {}\n for (const [key, value] of Object.entries(categories)) {\n if (/^[a-z.]{1,64}$/.test(key) && typeof value === 'boolean') clean[key] = value\n }\n if (Object.keys(clean).length > 0) next.categories = clean\n }\n const current = record.apns\n if (\n current &&\n current.token === next.token &&\n current.environment === next.environment &&\n JSON.stringify(current.categories ?? {}) === JSON.stringify(next.categories ?? {})\n ) {\n return\n }\n record.apns = next\n void this.flush()\n }\n\n /** Drop a device's APNs registration (APNs reported the token unregistered). */\n clearPushToken(deviceId: string): void {\n const record = this.devices.get(deviceId)\n if (!record?.apns) return\n delete record.apns\n void this.flush()\n }\n\n /**\n * Drop every paired-device record. Used by token rotation: devices paired\n * under the old token can no longer authenticate, so keeping their rows\n * would paint a misleading \"still paired\" picture.\n */\n clear(): void {\n this.devices.clear()\n void this.flush()\n }\n\n /** Serialized so concurrent touches can never interleave half-written JSON. */\n private flush(): Promise<void> {\n const next = this.flushTail.then(() => this.writeFile())\n this.flushTail = next.catch(() => {\n // best-effort persistence; the bridge still works without it\n })\n return next\n }\n\n /** Resolves once every queued registry write has landed (test support). */\n async drain(): Promise<void> {\n await this.flushTail\n }\n\n private async writeFile(): Promise<void> {\n const full = expandHome(this.filePath)\n const body = JSON.stringify(\n { version: 1, devices: this.list() },\n null,\n 2,\n )\n try {\n await mkdir(dirname(full), { recursive: true })\n // Atomic replace: a restart mid-write previously left a truncated JSON\n // behind, which load() treats as corrupt and drops ENTIRELY — losing\n // pairings and push registrations on every unlucky shutdown.\n const temp = `${full}.${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temp, body + '\\n', { mode: 0o600 })\n await rename(temp, full)\n } catch {\n // best-effort persistence; the bridge still works without it\n }\n }\n}\n","import type { WebSocket } from 'ws'\nimport {\n PROTOCOL_VERSION,\n} from './protocol.ts'\nimport type { Envelope, HelloAuthPayload } from './protocol.ts'\nimport type { BridgeSink, HostBridge } from './host-bridge.ts'\nimport type { DeviceStore, ApnsEnvironment } from './token.ts'\nimport { isValidApnsToken, tokenMatches } from './token.ts'\n\nexport const AUTH_TIMEOUT_MS = 5_000\nconst IMAGE_MEDIA_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp', 'image/gif'])\nconst MAX_PROMPT_IMAGES = 4\nconst MAX_BASE64_CHARS_PER_IMAGE = 8 * 1024 * 1024\n/** Bounds a single prompt's text; the frame itself is capped by ws maxPayload. */\nconst MAX_PROMPT_TEXT_CHARS = 256 * 1024\n// Client-supplied identity fields land in logs and devices.json — keep them\n// short and free of control characters so they can neither flood the registry\n// nor forge log lines.\nconst MAX_DEVICE_ID_CHARS = 128\nconst MAX_DEVICE_NAME_CHARS = 64\nconst MAX_APP_VERSION_CHARS = 32\n\nfunction sanitizeDeviceField(value: unknown, maxChars: number): string {\n const raw = typeof value === 'string' ? value : String(value ?? '')\n return raw.replace(/[\\u0000-\\u001f\\u007f]/g, ' ').trim().slice(0, maxChars)\n}\n\nexport interface ConnectionStateDeps {\n bridge: HostBridge\n devices: DeviceStore\n serverVersion: string\n /** Pairing secret used when the HTTP upgrade was not already authenticated. */\n expectedToken: string\n /** True when Authorization or the legacy query parameter passed at upgrade. */\n transportAuthenticated?: boolean\n debug?: boolean\n log: (message: string) => void\n /** Called once when the socket closes (cleanly or not). */\n onClosed?: (connection: BridgeConnection) => void\n /**\n * Zero-touch push enrollment: fired when a distributed app presents the\n * distributor's shared enrollKey during c2s.push.register. May perform the\n * relay round-trip; the register handler awaits it before evaluating push\n * readiness, so the very first registration can switch the feature on.\n */\n onPushEnrollKey?: (enrollKey: string) => Promise<void> | void\n}\n\nexport function helloTokenAccepted(\n transportAuthenticated: boolean | undefined,\n presentedToken: string | undefined,\n expectedToken: string,\n): boolean {\n return transportAuthenticated === true || tokenMatches(presentedToken, expectedToken)\n}\n\n/**\n * One connected phone. Implements BridgeSink so the HostBridge can push\n * projected frames and replays. Bearer/query credentials may authenticate the\n * HTTP upgrade; otherwise the first hello frame is verified here.\n */\nexport class BridgeConnection implements BridgeSink {\n private authenticated = false\n private helloTimer: NodeJS.Timeout | undefined\n private readonly openSessions = new Set<string>()\n /** Sanitized device identity from hello; needed for push registration. */\n private deviceId: string | undefined\n\n constructor(\n private readonly ws: WebSocket,\n private readonly deps: ConnectionStateDeps,\n ) {\n ws.on('message', (data) => {\n void this.onMessage(String(data))\n })\n ws.on('close', () => {\n this.onClose()\n deps.onClosed?.(this)\n })\n ws.on('error', () => {\n /* close follows */\n })\n this.helloTimer = setTimeout(() => {\n if (!this.authenticated) this.close(4402, 'auth timeout')\n }, AUTH_TIMEOUT_MS)\n }\n\n /** Hard-drop the socket (server-side stale sweep). */\n terminate(): void {\n this.ws.terminate()\n }\n\n /** Device identity once hello succeeded; undefined before that. */\n get connectedDeviceId(): string | undefined {\n return this.authenticated ? this.deviceId : undefined\n }\n\n // ---------- BridgeSink ----------\n\n push(type: string, payload: unknown, seq?: number): void {\n if (this.deps.debug === true) this.deps.log('push ' + type + ' seq=' + String(seq))\n this.send(type, payload, undefined, seq)\n }\n\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void {\n for (const entry of entries) this.push(entry.type, entry.payload, entry.seq)\n }\n\n replayDone(): void {\n this.push('s2c.resume.done', {})\n }\n\n resync(): void {\n this.push('s2c.resync', { reason: 'gap' })\n }\n\n lastCursor(): number {\n return this.deps.bridge.currentCursor()\n }\n\n // ---------- lifecycle ----------\n\n private onClose(): void {\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n for (const id of this.openSessions) {\n this.deps.bridge.markSinkClosed(this, id)\n }\n this.openSessions.clear()\n this.deps.bridge.dropSinkSessions(this)\n if (this.authenticated) this.deps.bridge.removeSink(this)\n }\n\n private close(code: number, reason: string): void {\n try {\n this.ws.close(code, reason)\n } catch {\n this.ws.terminate()\n }\n }\n\n private send(type: string, payload: unknown, id?: string, seq?: number): void {\n const envelope: Envelope = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n ...(id !== undefined ? { id } : {}),\n ...(seq !== undefined ? { seq } : {}),\n payload,\n }\n if (this.ws.readyState === this.ws.OPEN) this.ws.send(JSON.stringify(envelope))\n }\n\n private fail(id: string | undefined, code: keyof typeof ERROR_CODES, message: string): void {\n this.send('s2c.error', { code, message }, id)\n }\n\n // ---------- dispatch ----------\n\n private lastActivity = Date.now()\n\n /** True when no inbound frame arrived within maxIdleMs. */\n isStale(now: number, maxIdleMs: number): boolean {\n return now - this.lastActivity > maxIdleMs\n }\n\n private async onMessage(raw: string): Promise<void> {\n this.lastActivity = Date.now()\n let env: Envelope\n try {\n env = JSON.parse(raw) as Envelope\n } catch {\n this.fail(undefined, 'E_PROTOCOL', 'frame is not valid JSON')\n return\n }\n if (env.v !== PROTOCOL_VERSION) {\n this.fail(env.id, 'E_UNSUPPORTED', 'unsupported protocol version')\n this.close(4500, 'protocol version mismatch')\n return\n }\n if (!this.authenticated) {\n if (env.type === 'c2s.ping') {\n this.send('s2c.pong', { serverTime: Date.now() }, env.id)\n return\n }\n if (env.type === 'c2s.hello.auth') {\n await this.hello(env)\n return\n }\n this.fail(env.id, 'E_PROTOCOL', 'authenticate first')\n return\n }\n switch (env.type) {\n case 'c2s.ping': {\n this.send('s2c.pong', { serverTime: Date.now() }, env.id)\n return\n }\n case 'c2s.sessions.list': {\n this.send('s2c.sessions.snapshot', { full: true, sessions: this.deps.bridge.listSessions() }, env.id)\n return\n }\n case 'c2s.workspaces.list': {\n if (!this.deps.bridge.capabilities.projectSelection) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'project selection unavailable on this host version')\n }\n const result = await this.deps.bridge.listWorkspaces()\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.workspaces.snapshot', { workspaces: result.value }, env.id)\n return\n }\n case 'c2s.directory.list': {\n const p = env.payload as { path?: unknown } | undefined\n if (p?.path !== undefined && typeof p.path !== 'string') {\n return this.fail(env.id, 'E_PROTOCOL', 'path must be a string')\n }\n const result = await this.deps.bridge.listDirectory(p?.path as string | undefined)\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.directory.listing', result.value, env.id)\n return\n }\n case 'c2s.directory.pick': {\n const result = await this.deps.bridge.pickDirectory()\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.directory.picked', { path: result.value }, env.id)\n return\n }\n case 'c2s.workspace.create': {\n const p = env.payload as { path?: unknown } | undefined\n const path = typeof p?.path === 'string' ? p.path.trim() : ''\n if (!path) return this.fail(env.id, 'E_PROTOCOL', 'non-empty path required')\n if (!this.deps.bridge.capabilities.projectSelection) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'project selection unavailable on this host version')\n }\n const result = await this.deps.bridge.createWorkspace(path)\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.workspace.created', result.value, env.id)\n return\n }\n case 'c2s.session.open': {\n const p = env.payload as { sessionId?: string; tailCount?: number }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n this.openSessions.add(p.sessionId)\n this.deps.bridge.markSinkOpen(this, p.sessionId)\n const ok = await this.deps.bridge.openSession(this, p.sessionId, p.tailCount ?? 100)\n if (!ok) this.fail(env.id, 'E_NOT_FOUND', 'session history unavailable')\n return\n }\n case 'c2s.session.close': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n this.openSessions.delete(p.sessionId)\n this.deps.bridge.markSinkClosed(this, p.sessionId)\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.session.create': {\n const p = env.payload as { workspaceId?: unknown; cwd?: unknown } | undefined\n const workspaceId = typeof p?.workspaceId === 'string' ? p.workspaceId.trim() : ''\n const cwd = typeof p?.cwd === 'string' ? p.cwd.trim() : ''\n if (workspaceId && cwd) return this.fail(env.id, 'E_PROTOCOL', 'workspaceId and cwd are mutually exclusive')\n const newId = await this.deps.bridge.createSession({\n ...(workspaceId ? { workspaceId } : {}),\n ...(cwd ? { cwd } : {}),\n })\n if (!newId) return this.fail(env.id, 'E_INTERNAL', 'session create failed')\n this.send('s2c.ack', { sessionId: newId }, env.id)\n return\n }\n case 'c2s.session.rename': {\n const p = env.payload as { sessionId?: string; title?: string }\n const title = typeof p?.title === 'string' ? p.title.trim() : ''\n if (!p?.sessionId || title.length === 0) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and non-empty title required')\n }\n if (!this.deps.bridge.capabilities.sessionManagement) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'session management unavailable on this host version')\n }\n const result = await this.deps.bridge.renameSession(p.sessionId, title)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.renamed', { sessionId: p.sessionId, title: result.value }, env.id)\n return\n }\n case 'c2s.session.archive': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n if (!this.deps.bridge.capabilities.sessionManagement) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'session management unavailable on this host version')\n }\n const result = await this.deps.bridge.archiveSession(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.archived', { sessionId: p.sessionId }, env.id)\n return\n }\n case 'c2s.session.cancel': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n const result = await this.deps.bridge.cancelSession(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.ack', { sessionId: p.sessionId }, env.id)\n return\n }\n case 'c2s.session.history': {\n const p = env.payload as { sessionId?: string; beforeSeq?: number; limit?: number }\n if (!p?.sessionId || typeof p.beforeSeq !== 'number') {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and beforeSeq required')\n }\n const ok = await this.deps.bridge.historyPage(this, p.sessionId, p.beforeSeq, Math.min(p.limit ?? 100, 500))\n if (!ok) this.fail(env.id, 'E_NOT_FOUND', 'history unavailable')\n return\n }\n case 'c2s.session.attachment': {\n const p = env.payload as { sessionId?: string; attachmentId?: string }\n if (!p?.sessionId || typeof p.attachmentId !== 'string' || p.attachmentId.length === 0) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and attachmentId required')\n }\n const image = await this.deps.bridge.attachmentData(p.sessionId, p.attachmentId)\n if (!image) return this.fail(env.id, 'E_NOT_FOUND', 'attachment unavailable')\n this.send('s2c.ack', image, env.id)\n return\n }\n case 'c2s.session.models': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n if (!this.deps.bridge.capabilities.models) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'model selection unavailable on this host version')\n }\n const result = await this.deps.bridge.sessionModels(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'unavailable' ? 'E_NOT_FOUND'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.models', { sessionId: p.sessionId, ...result.value }, env.id)\n return\n }\n case 'c2s.session.selectModel': {\n const p = env.payload as {\n sessionId?: string\n provider?: string\n model?: string\n reasoningEffort?: string\n }\n if (!p?.sessionId || !p.provider?.trim() || !p.model?.trim()) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId, provider and model required')\n }\n if (!this.deps.bridge.capabilities.models) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'model selection unavailable on this host version')\n }\n const result = await this.deps.bridge.selectSessionModel(p.sessionId, {\n provider: p.provider.trim(),\n model: p.model.trim(),\n ...(p.reasoningEffort?.trim() ? { reasoningEffort: p.reasoningEffort.trim() } : {}),\n })\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'unavailable' ? 'E_NOT_FOUND'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.modelSelected', { sessionId: p.sessionId, selected: result.value }, env.id)\n return\n }\n case 'c2s.session.sendPrompt': {\n const p = env.payload as {\n sessionId?: string\n text?: string\n images?: Array<{ mediaType?: string; data?: string; name?: string }>\n }\n const text = typeof p?.text === 'string' ? p.text : ''\n const rawImages = Array.isArray(p?.images) ? p.images : []\n if (!p?.sessionId || (text.trim().length === 0 && rawImages.length === 0)) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and text or images required')\n }\n if (text.length > MAX_PROMPT_TEXT_CHARS) {\n return this.fail(env.id, 'E_PROTOCOL', 'prompt text too long')\n }\n if (rawImages.length > MAX_PROMPT_IMAGES) {\n return this.fail(env.id, 'E_PROTOCOL', 'too many images')\n }\n const images: Array<{\n mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'\n data: string\n name?: string\n }> = []\n for (const image of rawImages) {\n if (!IMAGE_MEDIA_TYPES.has(String(image?.mediaType)) ||\n typeof image?.data !== 'string' || image.data.length === 0 ||\n image.data.length > MAX_BASE64_CHARS_PER_IMAGE) {\n return this.fail(env.id, 'E_PROTOCOL', 'invalid image attachment')\n }\n images.push({\n mediaType: image.mediaType as 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif',\n data: image.data,\n ...(typeof image.name === 'string' && image.name.trim().length > 0\n ? { name: image.name.trim().slice(0, 120) }\n : {}),\n })\n }\n const userSeq = await this.deps.bridge.sendPrompt(p.sessionId, text, images)\n if (userSeq === null) return this.fail(env.id, 'E_BUSY', 'session busy or unavailable')\n this.send('s2c.ack', { userSeq }, env.id)\n return\n }\n case 'c2s.approval.respond': {\n const p = env.payload as { requestId?: string; decision?: string; reason?: string }\n if (!p?.requestId || (p.decision !== 'allow' && p.decision !== 'deny')) {\n return this.fail(env.id, 'E_PROTOCOL', 'requestId and decision required')\n }\n const ok = await this.deps.bridge.respondApproval(p.requestId, p.decision)\n if (!ok) return this.fail(env.id, 'E_NOT_FOUND', 'approval not pending')\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.question.respond': {\n const p = env.payload as { requestId?: string; answers?: unknown }\n if (!p?.requestId || !Array.isArray(p.answers)) {\n return this.fail(env.id, 'E_PROTOCOL', 'requestId and answers required')\n }\n const ok = await this.deps.bridge.respondQuestion(p.requestId, p.answers)\n if (!ok) return this.fail(env.id, 'E_NOT_FOUND', 'question not pending')\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.push.register': {\n const p = env.payload as { deviceToken?: unknown; environment?: unknown; categories?: unknown; enrollKey?: unknown }\n const token = typeof p?.deviceToken === 'string' ? p.deviceToken.trim() : ''\n if (!isValidApnsToken(token)) {\n return this.fail(env.id, 'E_PROTOCOL', 'hex deviceToken (32-512 chars) required')\n }\n const environment: ApnsEnvironment = p?.environment === 'production' ? 'production' : 'development'\n const categories = typeof p?.categories === 'object' && p.categories !== null\n ? p.categories as Record<string, boolean>\n : undefined\n if (!this.deviceId || !this.authenticated) {\n return this.fail(env.id, 'E_PROTOCOL', 'authenticate first')\n }\n // Zero-touch enrollment MUST run before the capability gate: a fresh,\n // unconfigured bridge receives its first register precisely when push\n // is not yet enabled, and the key itself flips relay mode on. The\n // relay round-trip is awaited so readiness reflects reality.\n if (typeof p?.enrollKey === 'string') {\n const enrollKey = p.enrollKey.trim().replace(/[^\\x20-\\x7e]/g, '').slice(0, 128)\n if (enrollKey.length >= 8 && enrollKey.length <= 128) {\n await this.deps.onPushEnrollKey?.(enrollKey)\n }\n }\n // Store the registration BEFORE the readiness gate: if push is still\n // mid-bootstrap (or the relay is temporarily down), the token is\n // already on file for when it recovers — no extra register needed.\n this.deps.devices.setPushToken(this.deviceId, token, environment, categories, Date.now())\n if (!this.deps.bridge.capabilities.push) {\n if (this.deps.debug === true) this.deps.log('push register held: bridge not ready')\n return this.fail(env.id, 'E_UNSUPPORTED', 'push is not configured on this bridge')\n }\n if (this.deps.debug === true) {\n this.deps.log('push token registered env=' + environment)\n }\n // `enabled` lets the app flip its local capability immediately instead\n // of waiting for the next handshake.\n this.send('s2c.ack', { enabled: true }, env.id)\n return\n }\n default:\n this.fail(env.id, 'E_PROTOCOL', 'unknown type: ' + env.type)\n }\n }\n\n private async hello(env: Envelope): Promise<void> {\n const p = (env.payload ?? {}) as Partial<HelloAuthPayload>\n if (!helloTokenAccepted(this.deps.transportAuthenticated, p.token, this.deps.expectedToken)) {\n this.fail(env.id, 'E_AUTH', 'token missing or invalid')\n this.close(4401, 'invalid token')\n return\n }\n if (!p.deviceId) {\n this.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.close(4403, 'deviceId required')\n return\n }\n const deviceId = sanitizeDeviceField(p.deviceId, MAX_DEVICE_ID_CHARS)\n if (!deviceId) {\n this.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.close(4403, 'deviceId required')\n return\n }\n const deviceName = sanitizeDeviceField(p.deviceName, MAX_DEVICE_NAME_CHARS) || 'unknown'\n const appVersion = sanitizeDeviceField(p.appVersion, MAX_APP_VERSION_CHARS) || 'unknown'\n this.authenticated = true\n this.deviceId = deviceId\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n this.deps.devices.touch({ deviceId, deviceName, appVersion }, Date.now())\n this.deps.log('device paired: ' + deviceName + ' (' + deviceId + ')')\n\n const cursor = typeof p.resumeCursor === 'number' && p.resumeCursor >= 0\n ? p.resumeCursor\n : undefined\n const canResume = cursor !== undefined && this.deps.bridge.canResumeFrom(cursor)\n // Welcome strictly precedes any replayed pushes.\n this.send('s2c.welcome', {\n protocolVersion: PROTOCOL_VERSION,\n serverVersion: this.deps.serverVersion,\n capabilities: this.deps.bridge.capabilities,\n cursor: this.deps.bridge.currentCursor(),\n resumed: canResume,\n }, env.id)\n this.deps.bridge.addSink(this)\n if (cursor !== undefined) {\n if (canResume) {\n this.deps.bridge.resumeFrom(cursor, this)\n } else {\n this.resync()\n }\n }\n }\n}\n\nconst ERROR_CODES = {\n E_AUTH: 'token missing or invalid',\n E_PROTOCOL: 'unknown type or malformed payload',\n E_NOT_FOUND: 'session or request not found',\n E_BUSY: 'session is busy',\n E_UNSUPPORTED: 'protocol version or capability unsupported',\n E_INTERNAL: 'internal error',\n} as const\n\nfunction managementErrorCode(\n kind: 'unsupported' | 'not-found' | 'busy' | 'invalid' | 'internal',\n): keyof typeof ERROR_CODES {\n switch (kind) {\n case 'unsupported': return 'E_UNSUPPORTED'\n case 'not-found': return 'E_NOT_FOUND'\n case 'busy': return 'E_BUSY'\n case 'invalid': return 'E_PROTOCOL'\n case 'internal': return 'E_INTERNAL'\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { Envelope, MessageProjection, NotifyCategory, PushNotification, SessionEventKind, SessionSummary, SessionTodoItem, SessionTodoStatus } from './protocol.ts'\n\n/* Minimal structural faces over the host apiProxy service. The real types\n * live in @deepseek-ai/dsh-host-apiproxy; keeping them structural lets this\n * plugin compile and degrade independently of host package versions. */\n\ninterface RpcOk<T> { ok: true; value: T }\ninterface RpcErr { ok: false; error: { code: string; message?: string } }\ntype RpcResult<T> = RpcOk<T> | RpcErr\n\ninterface RpcRequestLike<T> { rpcId?: string; payload?: T }\ninterface RpcResponseLike<T> { result?: RpcResult<T> }\n\ninterface SessionsApiLike {\n list(req?: RpcRequestLike<{ cursor?: string }>): Promise<RpcResponseLike<{ items: PhoneSessionRow[] }>>\n history(req: RpcRequestLike<{ sessionId: string; beforeSeq?: number; maxMessages?: number }>):\n Promise<RpcResponseLike<HistoryResult>>\n prompt(req: RpcRequestLike<PromptArgs>): Promise<RpcResponseLike<{ accepted: true }>>\n create(req: RpcRequestLike<{ workspaceId?: string; cwd?: string; agentPreset?: string }>): Promise<RpcResponseLike<{ sessionId: string; agentPreset?: string }>>\n models?(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<HostSessionModels>>\n selectModel?(req: RpcRequestLike<{\n sessionId: string\n provider: string\n model: string\n reasoningEffort?: string\n }>): Promise<RpcResponseLike<{ selected: HostModelSelection }>>\n rename?(req: RpcRequestLike<{ sessionId: string; title: string }>):\n Promise<RpcResponseLike<{ title: string; seq: number }>>\n cancel?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<Record<string, unknown> | undefined>>\n /** Reads one durable image back after the host verifies the session log references its id. */\n attachment?(req: RpcRequestLike<{ sessionId: string; attachmentId: string }>):\n Promise<RpcResponseLike<{ attachment: { mediaType?: string }; data: string }>>\n}\n\ninterface WorkspaceApiLike {\n list?(req: RpcRequestLike<Record<string, never>>):\n Promise<RpcResponseLike<{ items: WorkspaceViewLike[]; archivedSessionIds: string[] }>>\n create?(req: RpcRequestLike<{ path: string }>):\n Promise<RpcResponseLike<{ workspace: WorkspaceViewLike; created: boolean }>>\n archiveSession?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ archivedSessionIds: string[] }>>\n}\n\ninterface HostApiLike {\n listDirectory?(req: RpcRequestLike<{ path?: string }>, signal?: AbortSignal):\n Promise<RpcResponseLike<DirectoryListingLike>>\n pickDirectory?(req: RpcRequestLike<Record<string, never>>, signal?: AbortSignal):\n Promise<RpcResponseLike<{ path: string | null }>>\n}\n\ninterface WorkspaceViewLike {\n workspaceId: string\n path: string\n title: string\n sessionIds: string[]\n createdAt?: string\n updatedAt?: string\n}\n\nexport interface DirectoryEntryLike {\n name: string\n path: string\n hidden: boolean\n}\n\nexport interface DirectoryListingLike {\n path: string\n home: string\n crumbs: DirectoryEntryLike[]\n entries: DirectoryEntryLike[]\n truncated: boolean\n}\n\ninterface HostModelSelection {\n provider: string\n model: string\n reasoningEffort?: string\n}\n\ninterface HostSessionModels {\n current: HostModelSelection\n routable: boolean\n groups: Array<{\n id: string\n name: string\n models: Array<{\n id: string\n name: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n }>\n }>\n failures: Array<{ id: string; name: string; message: string }>\n}\n\nexport type ModelBridgeResult<T> =\n | { ok: true; value: T }\n | { ok: false; kind: 'unsupported' | 'not-found' | 'busy' | 'unavailable' | 'internal'; message: string }\n\nexport type SessionManagementResult<T> =\n | { ok: true; value: T }\n | { ok: false; kind: 'unsupported' | 'not-found' | 'busy' | 'invalid' | 'internal'; message: string }\n\ninterface PromptArgs {\n sessionId: string\n mode: 'queue' | 'steer'\n content: Array<\n | { type: 'text'; text: string }\n | { type: 'image'; mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }\n >\n clientTimeZone?: string\n}\n\ninterface PhoneSessionRow {\n sessionId: string\n updatedAt: number\n running: boolean\n blank: boolean\n cwd?: string\n origin?: string\n parentSessionId?: string\n projections?: { asOfSeq?: number; values?: Record<string, unknown> }\n}\n\n/**\n * Subagent sessions are host-internal workers of a parent conversation.\n * They must never surface on the phone: not in the project/session list,\n * and not as turn-completion pushes for a session the device cannot open.\n */\nfunction isSubagentRow(row: PhoneSessionRow): boolean {\n return row.origin === 'subagent' ||\n (typeof row.parentSessionId === 'string' && row.parentSessionId.length > 0);\n}\n\ninterface HistoryResult {\n events: Array<{ event: SessionEventLike; view?: unknown }>\n hasMore: boolean\n projections?: { values?: Record<string, unknown> }\n}\n\nexport interface SessionEventLike {\n type: string\n seq: number\n time?: number\n data?: unknown\n}\n\nexport interface MuxFrameLike {\n type: string\n rpcId?: string\n payload?: any\n sessionId?: string\n event?: SessionEventLike\n key?: string\n value?: unknown\n approvalId?: string\n toolName?: string\n reason?: string\n questions?: unknown\n questionRpcId?: string\n running?: boolean\n}\n\n/**\n * apiProxy stream items are server-request envelopes: the frame lives in\n * `payload`, while the stable request id used to answer approval/question\n * waits lives beside it. Flattening only `payload` loses that id and makes\n * both interactions silently disappear.\n */\ninterface ApiStreamItemLike {\n rpcId?: string\n payload: MuxFrameLike\n}\n\nfunction unwrapStreamItem(item: MuxFrameLike | ApiStreamItemLike): MuxFrameLike {\n const nested = item.payload\n if (nested && typeof nested === 'object' && typeof nested.type === 'string') {\n return nested.rpcId || !item.rpcId ? nested : { ...nested, rpcId: item.rpcId }\n }\n return item as MuxFrameLike\n}\n\nexport interface ApiProxyLike {\n sessions: SessionsApiLike\n workspace?: WorkspaceApiLike\n host?: HostApiLike\n respond(message: { type: 'client-response'; rpcId: string; result: RpcResult<unknown> }):\n Promise<{ accepted: boolean; reason?: string }>\n events: {\n mux(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n host(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n }\n}\n\n/** Downward sink every connected phone registers (one per WebSocket). */\nexport interface BridgeSink {\n push(type: string, payload: unknown, seq?: number): void\n lastCursor(): number\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void\n replayDone(): void\n resync(): void\n}\n\n/**\n * Offline push fan-out (F-9 离线推送). The bridge forwards every\n * notification-worthy event here; the outlet decides which paired devices\n * (token holders without a live socket) receive an APNs delivery.\n */\nexport interface PushOutlet {\n fanOut(notification: PushNotification): void\n /**\n * Whether offline push is currently configured and usable. Drives the\n * welcome capability bit: advertising push while no APNs credentials are\n * loaded would make clients suppress their own local banners and lose\n * notifications entirely.\n */\n isAvailable(): boolean\n}\n\ninterface PendingApproval {\n rpcId: string\n sessionId: string\n toolName: string\n reason: string\n}\n\ninterface PendingQuestion {\n rpcId: string\n sessionId: string\n questions: unknown\n}\n\nconst MAX_RING_DEFAULT = 2000;\n\n/**\n * Process-wide bridge state: session mirror, pending approvals/questions,\n * and the per-device replay ring. Consumes the in-process mux/host streams\n * and fans projected pushes out to every registered sink.\n */\nlet BRIDGE_SEQ = 0;\n\nexport class HostBridge {\n readonly id = ++BRIDGE_SEQ;\n private summaries = new Map<string, SessionSummary>()\n private approvals = new Map<string, PendingApproval>()\n private questions = new Map<string, PendingQuestion>()\n private archivedSessionIds = new Set<string>()\n private subagentSessionIds = new Set<string>()\n private sinks = new Set<BridgeSink>()\n private ring: Array<{ seq: number; type: string; payload: unknown }> = []\n private cursor = 0;\n private abort = new AbortController();\n\n constructor(\n private readonly apiProxy: ApiProxyLike,\n private readonly historyBufferMax: number = MAX_RING_DEFAULT,\n ) {}\n\n private pushOutlet: PushOutlet | undefined;\n\n /**\n * Wire the offline-push fan-out. Present ⇒ welcome advertises the `push`\n * capability and notify-worthy events are mirrored to APNs.\n */\n setPushOutlet(outlet: PushOutlet | undefined): void {\n this.pushOutlet = outlet;\n }\n\n get capabilities() {\n return {\n historyPaging: true,\n replay: true,\n approvals: true,\n questions: true,\n models: typeof this.apiProxy.sessions.models === 'function' &&\n typeof this.apiProxy.sessions.selectModel === 'function',\n sessionManagement: typeof this.apiProxy.sessions.rename === 'function' &&\n typeof this.apiProxy.workspace?.archiveSession === 'function',\n projectSelection: typeof this.apiProxy.workspace?.list === 'function' &&\n typeof this.apiProxy.workspace?.create === 'function',\n push: this.pushOutlet?.isAvailable() === true,\n };\n }\n\n diagnostic(message: string): void {\n console.log('[deeppilot] ' + message);\n }\n\n currentCursor(): number {\n return this.cursor;\n }\n\n // ---------- sinks ----------\n\n addSink(sink: BridgeSink): void {\n this.sinks.add(sink);\n }\n\n removeSink(sink: BridgeSink): void {\n this.sinks.delete(sink);\n }\n\n /** Whether the ring still holds everything after the cursor. */\n canResumeFrom(cursor: number): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n return cursor + 1 >= oldest;\n }\n\n private sinkSessions = new Map<BridgeSink, Set<string>>();\n private lastAssistantText = new Map<string, string>();\n\n /** Mark a sink as actively viewing a session (suppresses its turn notifications). */\n markSinkOpen(sink: BridgeSink, sessionId: string): void {\n let set = this.sinkSessions.get(sink);\n if (!set) { set = new Set(); this.sinkSessions.set(sink, set); }\n set.add(sessionId);\n }\n\n markSinkClosed(sink: BridgeSink, sessionId: string): void {\n this.sinkSessions.get(sink)?.delete(sessionId);\n }\n\n dropSinkSessions(sink: BridgeSink): void {\n this.sinkSessions.delete(sink);\n }\n\n private isViewedBy(sink: BridgeSink, sessionId: string): boolean {\n return this.sinkSessions.get(sink)?.has(sessionId) ?? false;\n }\n\n /** F-9: when a turn completes, notify every device not viewing the session. */\n private emitTurnCompletedNotify(sessionId: string, ok: boolean): void {\n if (this.subagentSessionIds.has(sessionId)) return;\n\n const row = this.summaries.get(sessionId);\n const title = ok ? '任务完成' : '任务异常结束';\n const body = this.lastAssistantText.get(sessionId) ?? row?.title ?? '';\n const truncatedBody = body.length > 120 ? body.slice(0, 119) + '…' : body;\n const category: NotifyCategory = ok ? 'turn.completed' : 'session.error';\n const notificationId = 'n-' + (this.cursor + 1);\n this.record('s2c.notify', {\n notificationId,\n category,\n sessionId,\n title,\n body: truncatedBody,\n ts: Date.now(),\n }, (sink) => this.isViewedBy(sink, sessionId));\n this.fanOutPush({ notificationId, category, sessionId, title, body: truncatedBody });\n }\n\n /**\n * Mirror one notification-worthy event to offline devices. Fire-and-forget:\n * push failures must never block or break the WS data plane.\n */\n private fanOutPush(notification: PushNotification): void {\n try {\n this.pushOutlet?.fanOut(notification);\n } catch {\n // outlet misbehavior must not take down the bridge\n }\n }\n\n\n /** Remember the latest assistant text so notifications can quote it. */\n private captureAssistantText(sessionId: string, event: SessionEventLike): void {\n\n if (event.type !== 'assistant/message') return;\n\n const text = messageText(event.data).trim();\n\n if (text.length > 0) this.lastAssistantText.set(sessionId, text.slice(-160));\n }\n\n /**\n * Replay buffered pushes after the given cursor; false when the gap is\n * unrecoverable. Frames go to `target` only — replaying into every sink\n * duplicated the whole window onto devices that never asked for it.\n */\n resumeFrom(cursor: number, target?: BridgeSink): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n if (cursor + 1 < oldest) return false;\n const receivers = target !== undefined ? [target] : [...this.sinks];\n for (const entry of this.ring) {\n if (entry.seq > cursor) {\n for (const sink of receivers) sink.replay([entry]);\n }\n }\n for (const sink of receivers) sink.replayDone();\n return true;\n }\n\n private record(type: string, payload: unknown, except?: (sink: BridgeSink) => boolean): void {\n this.cursor += 1;\n const entry = { seq: this.cursor, type, payload };\n this.ring.push(entry);\n if (this.ring.length > this.historyBufferMax) {\n this.ring.splice(0, this.ring.length - this.historyBufferMax);\n }\n for (const sink of this.sinks) {\n if (except && except(sink)) continue;\n sink.push(type, payload, entry.seq);\n }\n }\n\n\n // ---------- lifecycle ----------\n\n /** Start consuming host + mux streams. Idempotent; aborts on dispose(). */\n start(): void {\n void this.runHostStream();\n void this.runMuxStream();\n void this.refreshSummaries();\n }\n\n dispose(): void {\n this.abort.abort();\n this.sinks.clear();\n }\n\n private async runHostStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.host({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onHostFrame(frame);\n }\n } catch {\n // stream ended (dispose or host shutdown); bridges degrade to RPC-only\n }\n }\n\n private async runMuxStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.mux({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onMuxFrame(frame);\n }\n } catch {\n // ditto\n }\n }\n\n private onHostFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'host/session-added':\n case 'host/session-removed':\n case 'host/workspace-changed':\n case 'host/workspace-removed':\n case 'host/workspace-order-changed':\n void this.refreshSummaries();\n break;\n case 'host/archived-sessions-changed': {\n const archived = (frame as { archivedSessionIds?: unknown }).archivedSessionIds\n if (Array.isArray(archived)) {\n this.archivedSessionIds = new Set(archived.map(String))\n }\n void this.refreshSummaries();\n break;\n }\n case 'host/session-status': {\n const p = frame as { sessionId?: string; running?: boolean };\n const row = this.summaries.get(String(p.sessionId));\n if (row && typeof p?.running === 'boolean') {\n row.status = p.running ? 'running' : 'idle';\n this.pushSummary(row);\n }\n break;\n }\n default:\n break;\n }\n }\n\n private onMuxFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'session/event': {\n const event = frame.event as SessionEventLike | undefined;\n const sessionId = String(frame.sessionId ?? '');\n if (!event || !sessionId) break;\n this.noteActivity(sessionId, event);\n this.captureAssistantText(sessionId, event);\n const projection = projectEvent(sessionId, event);\n if (projection) {\n this.record('s2c.session.event', { sessionId, kind: projection.kind, seq: event.seq, data: projection.data });\n }\n if (projection?.kind === 'turn.end') {\n this.emitTurnCompletedNotify(sessionId, projection.data.ok === true);\n }\n break;\n }\n case 'session/projection': {\n const p = frame as { sessionId?: string; key?: string; value?: unknown };\n if (!p.sessionId) break;\n this.applyProjection(p.sessionId, String(p.key ?? ''), p.value);\n break;\n }\n case 'approval/requested': {\n const p = frame as { sessionId?: string; approvalId?: string; toolName?: string; reason?: string };\n if (!p.approvalId || !frame.rpcId) break;\n const toolName = String(p.toolName ?? 'tool');\n const summary = String(p.reason ?? '');\n this.approvals.set(p.approvalId, {\n rpcId: frame.rpcId,\n sessionId: String(p.sessionId ?? ''),\n toolName,\n reason: summary,\n });\n this.record('s2c.pending.approval', {\n requestId: p.approvalId,\n sessionId: String(p.sessionId ?? ''),\n toolName,\n summary,\n riskLevel: riskOf(toolName),\n });\n this.fanOutPush({\n notificationId: 'apr-' + p.approvalId,\n category: 'approval.required',\n sessionId: String(p.sessionId ?? ''),\n title: '需要批准',\n body: toolName + ': ' + summary,\n });\n this.bumpPendingFlags(String(p.sessionId ?? ''));\n break;\n }\n case 'approval/resolved': {\n const p = frame as { approvalId?: string };\n if (!p.approvalId) break;\n const pending = this.approvals.get(p.approvalId);\n this.approvals.delete(p.approvalId);\n this.record('s2c.pending.cleared', { requestId: p.approvalId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n case 'question/requested': {\n const p = frame as { sessionId?: string; questions?: unknown };\n if (!frame.rpcId) break;\n const requestId = 'q-' + frame.rpcId;\n const sessionId = String(p?.sessionId ?? '');\n this.questions.set(requestId, { rpcId: frame.rpcId, sessionId, questions: p?.questions });\n this.record('s2c.pending.question', { requestId, sessionId, questions: p?.questions ?? [] });\n this.fanOutPush({\n notificationId: requestId,\n category: 'question.asked',\n sessionId,\n title: '有问题需要回答',\n body: firstQuestionText(p?.questions),\n });\n this.bumpPendingFlags(sessionId);\n break;\n }\n case 'question/resolved': {\n const p = frame as { questionRpcId?: string };\n if (!p.questionRpcId) break;\n const requestId = 'q-' + p.questionRpcId;\n const pending = this.questions.get(requestId);\n this.questions.delete(requestId);\n this.record('s2c.pending.cleared', { requestId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n default:\n break;\n }\n }\n\n\n // ---------- session mirror ----------\n\n async refreshSummaries(): Promise<void> {\n try {\n const response = await this.apiProxy.sessions.list({ rpcId: randomUUID(), payload: {} });\n if (!response.result || !response.result.ok) {\n this.diagnostic('sessions.list rejected: ' + JSON.stringify(response.result ?? null).slice(0, 200));\n return;\n }\n let workspaces: WorkspaceViewLike[] = []\n const workspaceList = this.apiProxy.workspace?.list\n if (typeof workspaceList === 'function') {\n const workspaceResponse = await workspaceList.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: {},\n })\n if (workspaceResponse.result?.ok) {\n workspaces = workspaceResponse.result.value.items ?? []\n this.archivedSessionIds = new Set(\n (workspaceResponse.result.value.archivedSessionIds ?? []).map(String),\n )\n }\n }\n const previousIds = new Set(this.summaries.keys())\n const workspaceBySession = new Map<string, WorkspaceViewLike>()\n for (const workspace of workspaces) {\n for (const sessionId of workspace.sessionIds ?? []) {\n workspaceBySession.set(String(sessionId), workspace)\n }\n }\n const next = new Map<string, SessionSummary>();\n const subagentIds = new Set<string>();\n for (const row of response.result.value.items ?? []) {\n if (this.archivedSessionIds.has(row.sessionId)) continue\n if (isSubagentRow(row)) {\n subagentIds.add(row.sessionId);\n continue;\n }\n next.set(row.sessionId, toSummary(row, this.approvals, this.questions, workspaceBySession.get(row.sessionId)));\n }\n this.subagentSessionIds = subagentIds;\n this.summaries = next;\n const removedIds = [...previousIds].filter((id) => !next.has(id))\n for (const id of removedIds) this.lastAssistantText.delete(id);\n this.record('s2c.sessions.delta', { upserted: [...next.values()], removedIds });\n } catch {\n // apiProxy absent or transient failure; keep last mirror\n }\n }\n\n /** Cold sessions may lack a title projection; fall back to first user text. */\n private deriveTitleFallback(sessionId: string, messages: MessageProjection[]): void {\n const row = this.summaries.get(sessionId);\n if (!row || row.title.length > 0) return;\n const firstUser = messages.find(m => m.role === 'user' && (m.text ?? '').trim().length > 0);\n if (!firstUser) return;\n row.title = firstUser!.text!.replace(/\\s+/g, ' ').trim().slice(0, 60);\n this.pushSummary(row);\n }\n\n private noteActivity(sessionId: string, event?: SessionEventLike): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n // Recency must reflect USER-VISIBLE milestones only: a new prompt moves\n // a session to the top and a finished turn settles it into place. Token\n // chunks / tool calls / per-step assistant messages fire constantly while\n // an agent works; counting them made concurrently running sessions fight\n // over the top slot in every recency-sorted list.\n switch (event?.type) {\n case 'user/message':\n case 'turn/start':\n case 'turn/end':\n break;\n default:\n return;\n }\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n\n private applyProjection(sessionId: string, key: string, value: unknown): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n if (key === 'title') {\n row.title = typeof value === 'string' ? value : '';\n } else if (key === 'todos') {\n const items = Array.isArray(value) ? value as Array<{ content?: unknown; status?: unknown }> : null;\n const sanitized = sanitizeTodoItems(items);\n row.todoItems = sanitized.length > 0 ? sanitized : null;\n row.todos = sanitized.length > 0\n ? { done: sanitized.filter(i => i.status === 'completed').length, total: sanitized.length }\n : null;\n } else if (key === 'sessionListMetadata') {\n const meta = value as { lastPromptAt?: number } | null;\n if (meta?.lastPromptAt) row.lastActivityTs = Math.max(row.lastActivityTs, meta.lastPromptAt);\n } else {\n return;\n }\n this.pushSummary(row);\n }\n\n private bumpPendingFlags(sessionId: string): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n let approval = false;\n for (const pending of this.approvals.values()) {\n if (pending.sessionId === sessionId) approval = true;\n }\n let question = false;\n for (const pending of this.questions.values()) {\n if (pending.sessionId === sessionId) question = true;\n }\n row.pendingApproval = approval;\n row.pendingQuestion = question;\n this.pushSummary(row);\n }\n\n private pushSummary(row: SessionSummary): void {\n this.record('s2c.sessions.delta', { upserted: [row], removedIds: [] });\n }\n\n listSessions(): SessionSummary[] {\n return [...this.summaries.values()].sort((a, b) => b.lastActivityTs - a.lastActivityTs);\n }\n\n // ---------- data operations ----------\n\n /** Tail history for an opened session; pushes s2c.session.tail to the sink. */\n async openSession(sink: BridgeSink, sessionId: string, tailCount: number): Promise<boolean> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, maxMessages: clampTail(tailCount) },\n });\n if (!response.result || !response.result.ok) return false;\n const result = response.result.value;\n const messages = projectHistory(result.events ?? []);\n const oldestSeq = messages.length > 0 ? messages[0]!.seq : 0;\n sink.push('s2c.session.tail', {\n sessionId,\n messages,\n oldestSeq,\n hasMore: Boolean(result.hasMore),\n });\n this.deriveTitleFallback(sessionId, messages);\n return true;\n } catch {\n return false;\n }\n }\n\n async historyPage(sink: BridgeSink, sessionId: string, beforeSeq: number, limit: number): Promise<boolean> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, beforeSeq, maxMessages: clampTail(limit) },\n });\n if (!response.result || !response.result.ok) return false;\n const result = response.result.value;\n const messages = projectHistory(result.events ?? []);\n sink.push('s2c.history.page', { sessionId, messages, hasMore: Boolean(result.hasMore) });\n return true;\n } catch {\n return false;\n }\n }\n\n /** Result of one attachment read-back for the phone. */\n async attachmentData(\n sessionId: string,\n attachmentId: string,\n ): Promise<{ mediaType?: string; data: string } | null> {\n const read = this.apiProxy.sessions.attachment\n if (typeof read !== 'function') return null;\n try {\n const response = await read.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, attachmentId },\n });\n if (!response.result || !response.result.ok) return null;\n const data = response.result.value.data;\n if (typeof data !== 'string' || data.length === 0) return null;\n return {\n ...(typeof response.result.value.attachment?.mediaType === 'string'\n ? { mediaType: response.result.value.attachment.mediaType as string }\n : {}),\n data,\n };\n } catch {\n return null;\n }\n }\n\n async sessionModels(sessionId: string): Promise<ModelBridgeResult<HostSessionModels>> {\n const models = this.apiProxy.sessions.models\n if (typeof models !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'model catalog unavailable on this host version' }\n }\n try {\n const response = await models.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'model catalog returned no result' }\n if (!response.result.ok) return hostModelError(response.result.error)\n return { ok: true, value: projectSessionModels(response.result.value) }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async selectSessionModel(\n sessionId: string,\n selection: HostModelSelection,\n ): Promise<ModelBridgeResult<HostModelSelection>> {\n const selectModel = this.apiProxy.sessions.selectModel\n if (typeof selectModel !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'model selection unavailable on this host version' }\n }\n try {\n const response = await selectModel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: {\n sessionId,\n provider: selection.provider,\n model: selection.model,\n ...(selection.reasoningEffort ? { reasoningEffort: selection.reasoningEffort } : {}),\n },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'model selection returned no result' }\n if (!response.result.ok) return hostModelError(response.result.error)\n return { ok: true, value: { ...response.result.value.selected } }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async renameSession(sessionId: string, title: string): Promise<SessionManagementResult<string>> {\n const rename = this.apiProxy.sessions.rename\n if (typeof rename !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session rename unavailable on this host version' }\n }\n try {\n const response = await rename.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, title },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session rename returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n const acceptedTitle = String(response.result.value.title)\n const row = this.summaries.get(sessionId)\n if (row) {\n row.title = acceptedTitle\n this.pushSummary(row)\n }\n return { ok: true, value: acceptedTitle }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async archiveSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const archive = this.apiProxy.workspace?.archiveSession\n if (typeof archive !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session archive unavailable on this host version' }\n }\n try {\n const response = await archive.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session archive returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n this.archivedSessionIds = new Set(\n (response.result.value.archivedSessionIds ?? []).map(String),\n )\n this.summaries.delete(sessionId)\n this.lastAssistantText.delete(sessionId)\n this.record('s2c.sessions.delta', { upserted: [], removedIds: [sessionId] })\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async cancelSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const cancel = this.apiProxy.sessions.cancel\n if (typeof cancel !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session cancel unavailable on this host version' }\n }\n try {\n const response = await cancel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session cancel returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async listWorkspaces(): Promise<SessionManagementResult<Array<{\n id: string; title: string; path: string; sessionIds: string[]\n }>>> {\n const list = this.apiProxy.workspace?.list\n if (typeof list !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'workspace list unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.workspace, { rpcId: randomUUID(), payload: {} })\n if (!response.result) return { ok: false, kind: 'internal', message: 'workspace list returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: (response.result.value.items ?? []).map(projectWorkspace) }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async createWorkspace(path: string): Promise<SessionManagementResult<{\n workspace: { id: string; title: string; path: string; sessionIds: string[] }; created: boolean\n }>> {\n const create = this.apiProxy.workspace?.create\n if (typeof create !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'workspace create unavailable on this host version' }\n }\n try {\n const response = await create.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { path },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'workspace create returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n await this.refreshSummaries()\n return {\n ok: true,\n value: { workspace: projectWorkspace(response.result.value.workspace), created: response.result.value.created === true },\n }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async listDirectory(path?: string): Promise<SessionManagementResult<DirectoryListingLike>> {\n const list = this.apiProxy.host?.listDirectory\n if (typeof list !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'directory browsing unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: path && path.trim().length > 0 ? { path } : {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, kind: 'internal', message: 'directory list returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: response.result.value }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async pickDirectory(): Promise<SessionManagementResult<string | null>> {\n const pick = this.apiProxy.host?.pickDirectory\n if (typeof pick !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'native directory picker unavailable on this host version' }\n }\n try {\n const response = await pick.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, kind: 'internal', message: 'directory picker returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: response.result.value.path }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n /** Create a fresh blank session in an existing workspace or legacy cwd. */\n async createSession(destination: { workspaceId?: string; cwd?: string } = {}): Promise<string | null> {\n try {\n const response = await this.apiProxy.sessions.create({\n rpcId: randomUUID(),\n payload: {\n ...(destination.workspaceId?.trim() ? { workspaceId: destination.workspaceId.trim() } : {}),\n ...(destination.cwd?.trim() ? { cwd: destination.cwd.trim() } : {}),\n },\n });\n if (!response.result || !response.result.ok) return null;\n const sessionId = response.result.value.sessionId;\n await this.refreshSummaries();\n return sessionId;\n } catch {\n return null;\n }\n }\n\n async sendPrompt(\n sessionId: string,\n text: string,\n images: Array<{ mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }> = [],\n ): Promise<number | null> {\n try {\n const content: PromptArgs['content'] = [];\n if (text.trim().length > 0) content.push({ type: 'text', text });\n for (const image of images) content.push({ type: 'image', ...image });\n const response = await this.apiProxy.sessions.prompt({\n rpcId: randomUUID(),\n payload: {\n sessionId,\n mode: 'queue',\n content,\n clientTimeZone: localTimeZone(),\n },\n });\n if (!response.result || !response.result.ok) return null;\n const row = this.summaries.get(sessionId);\n if (row) {\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n return Date.now();\n } catch {\n return null;\n }\n }\n\n\n async respondApproval(requestId: string, decision: 'allow' | 'deny'): Promise<boolean> {\n const pending = this.approvals.get(requestId);\n if (!pending) return false;\n // Claim the pending entry before responding: a second phone (or a double\n // tap) must not fire another client-response for the same rpcId while the\n // resolved frame is still in flight.\n this.approvals.delete(requestId);\n const outcome = decision === 'allow' ? 'allowed-once' : 'rejected';\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: { ok: true, value: { sessionId: pending.sessionId, approvalId: requestId, outcome } },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted && !this.approvals.has(requestId)) this.approvals.set(requestId, pending);\n return accepted;\n } catch {\n // Transport-level failure: give the request back so the user can retry.\n if (!this.approvals.has(requestId)) this.approvals.set(requestId, pending);\n return false;\n }\n }\n\n async respondQuestion(requestId: string, answers: unknown): Promise<boolean> {\n const pending = this.questions.get(requestId);\n if (!pending) return false;\n this.questions.delete(requestId);\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: { ok: true, value: { sessionId: pending.sessionId, answer: { answers } } },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted && !this.questions.has(requestId)) this.questions.set(requestId, pending);\n return accepted;\n } catch {\n if (!this.questions.has(requestId)) this.questions.set(requestId, pending);\n return false;\n }\n }\n}\n\n// ---------- projection helpers ----------\n\nfunction clampTail(n: number): number {\n if (!Number.isFinite(n)) return 100;\n return Math.max(10, Math.min(500, Math.floor(n)));\n}\n\nfunction localTimeZone(): string | undefined {\n try {\n return new Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction riskOf(toolName: string): 'read' | 'write' | 'destructive' {\n if (/bash|pwsh|terminal/.test(toolName)) return 'write';\n if (/edit|write|str_replace|create/.test(toolName)) return 'write';\n if (/delete|remove|kill/.test(toolName)) return 'destructive';\n return 'read';\n}\n\n/** First question's text for the push banner; the questions payload shape is\n * host-version dependent, so extract defensively. */\nexport function firstQuestionText(questions: unknown): string {\n if (!Array.isArray(questions) || questions.length === 0) return 'Agent 等待你的输入';\n const first = questions[0] as { question?: unknown } | undefined;\n const text = String(first?.question ?? '').trim();\n return text || 'Agent 等待你的输入';\n}\n\nconst TODO_STATUSES = new Set(['pending', 'in_progress', 'completed']);\n\n/** Validate a host todo projection once; progress counts and the full\n * checklist both derive from this sanitized list so they never disagree. */\nfunction sanitizeTodoItems(items: Array<{ content?: unknown; status?: unknown }> | null): SessionTodoItem[] {\n if (!items) return [];\n return items\n .map(i => ({ content: String(i.content ?? '').trim(), status: String(i.status ?? '') }))\n .filter(i => i.content.length > 0 && TODO_STATUSES.has(i.status))\n .slice(0, 100)\n .map(i => ({ content: i.content, status: i.status as SessionTodoStatus }));\n}\n\nfunction toSummary(\n row: PhoneSessionRow,\n approvals: Map<string, PendingApproval>,\n questions: Map<string, PendingQuestion>,\n workspace?: WorkspaceViewLike,\n): SessionSummary {\n const values = row.projections?.values ?? {};\n const todos = Array.isArray(values.todos) ? values.todos as Array<{ content?: unknown; status?: unknown }> : null;\n let pendingApproval = false;\n for (const pending of approvals.values()) {\n if (pending.sessionId === row.sessionId) pendingApproval = true;\n }\n let pendingQuestion = false;\n for (const pending of questions.values()) {\n if (pending.sessionId === row.sessionId) pendingQuestion = true;\n }\n const cwd = typeof row.cwd === 'string' ? row.cwd : '';\n const label = workspace?.title ?? (cwd ? cwd.split('/').filter(Boolean).pop() : undefined);\n const todoItems = sanitizeTodoItems(todos);\n return {\n id: row.sessionId,\n title: typeof values.title === 'string' ? values.title : '',\n status: row.running ? 'running' : (row.blank ? 'unknown' : 'idle'),\n lastActivityTs: Number(row.updatedAt ?? Date.now()),\n todos: todoItems.length > 0\n ? {\n done: todoItems.filter(i => i.status === 'completed').length,\n total: todoItems.length,\n }\n : null,\n todoItems: todoItems.length > 0 ? todoItems : null,\n pendingApproval,\n pendingQuestion,\n workspaceLabel: label ?? null,\n workspaceId: workspace?.workspaceId ?? null,\n workspacePath: workspace?.path ?? (cwd || null),\n };\n}\n\nfunction projectWorkspace(workspace: WorkspaceViewLike): {\n id: string; title: string; path: string; sessionIds: string[]\n} {\n return {\n id: String(workspace.workspaceId),\n title: String(workspace.title),\n path: String(workspace.path),\n sessionIds: (workspace.sessionIds ?? []).map(String),\n }\n}\n\n/** Project one raw session event into a protocol push, when it maps to one. */\nfunction projectEvent(sessionId: string, event: SessionEventLike): { kind: SessionEventKind; data: Record<string, unknown> } | null {\n switch (event.type) {\n case 'turn/start':\n return { kind: 'turn.start', data: {} };\n case 'turn/end': {\n const data = event.data as { reason?: { kind?: string } } | undefined;\n return { kind: 'turn.end', data: { ok: data?.reason?.kind === 'completed' } };\n }\n case 'user/message':\n return {\n kind: 'message.final',\n data: {\n seq: event.seq,\n role: 'user',\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n ts: tsOf(event),\n },\n };\n case 'assistant/chunk': {\n if (chunkTypeOf(event.data) === 'reasoning-delta') {\n return { kind: 'thinking.delta', data: { text: chunkText(event.data), ts: tsOf(event) } };\n }\n return { kind: 'message.delta', data: { text: chunkText(event.data), ts: tsOf(event) } };\n }\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n // Pure-reasoning or tool-only steps carry no visible answer; emitting\n // them produced empty bubbles on the phone.\n if (!text.trim() && !thinking.trim()) return null;\n return {\n kind: 'message.final',\n data: { seq: event.seq, role: 'assistant', text, ...(thinking ? { thinking } : {}), ts: tsOf(event) },\n };\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string } | undefined;\n return {\n kind: 'tool.start',\n data: {\n seq: event.seq,\n role: 'tool',\n tool: { name: String(data?.name ?? 'tool'), state: 'running', summary: summarizeArgs(data?.arguments) },\n ts: tsOf(event)\n }\n };\n }\n case 'tool/result':\n return { kind: 'tool.end', data: { seq: event.seq, role: 'tool', ok: !event.data || (event.data as { error?: unknown }).error === undefined, ts: tsOf(event) } };\n default:\n return null;\n }\n}\n\nfunction tsOf(event: SessionEventLike): number {\n return typeof event.time === 'number' ? event.time : Date.now();\n}\n\n/** Extract plain text from user/assistant message payloads across shapes. */\nfunction messageText(data: unknown): string {\n if (typeof data === 'string') return data;\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; text?: unknown; message?: unknown };\n if (typeof obj.text === 'string') return obj.text;\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageText(obj.message);\n return contentText(obj.content);\n}\n\nfunction contentText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content.map(part => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'text' && typeof piece.text === 'string') return piece.text;\n }\n return '';\n }).join('');\n }\n return '';\n}\n\nfunction messageAttachments(data: unknown): Array<{\n kind: 'image'\n name?: string\n mediaType?: string\n attachmentId?: string\n width?: number\n height?: number\n}> {\n if (!data || typeof data !== 'object') return [];\n const obj = data as { content?: unknown; message?: unknown };\n if (obj.message && typeof obj.message === 'object') return messageAttachments(obj.message);\n if (!Array.isArray(obj.content)) return [];\n return obj.content.flatMap(part => {\n if (!part || typeof part !== 'object') return [];\n // The host normalizes uploaded bytes into an ImageAttachmentRef\n // (attachmentId/mediaType/bytes/width/height...); the id is what clients\n // need to read the image back through c2s.session.attachment.\n const block = part as {\n type?: string\n attachment?: {\n name?: unknown\n mediaType?: unknown\n attachmentId?: unknown\n width?: unknown\n height?: unknown\n }\n };\n if (block.type !== 'image' || !block.attachment) return [];\n const attachmentId = typeof block.attachment.attachmentId === 'string' && block.attachment.attachmentId.length > 0\n ? block.attachment.attachmentId\n : undefined;\n const width = typeof block.attachment.width === 'number' && Number.isFinite(block.attachment.width)\n ? block.attachment.width\n : undefined;\n const height = typeof block.attachment.height === 'number' && Number.isFinite(block.attachment.height)\n ? block.attachment.height\n : undefined;\n return [{\n kind: 'image' as const,\n ...(typeof block.attachment.name === 'string' ? { name: block.attachment.name } : {}),\n ...(typeof block.attachment.mediaType === 'string' ? { mediaType: block.attachment.mediaType } : {}),\n ...(attachmentId ? { attachmentId } : {}),\n ...(width !== undefined ? { width } : {}),\n ...(height !== undefined ? { height } : {}),\n }];\n });\n}\n\nfunction attachmentProjection(data: unknown): { attachments?: ReturnType<typeof messageAttachments> } {\n const attachments = messageAttachments(data);\n return attachments.length > 0 ? { attachments } : {};\n}\n\n/** Extract reasoning (\"thinking\") text from assistant message payloads. */\nfunction messageThinking(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; message?: unknown };\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageThinking(obj.message);\n return reasoningContent(obj.content);\n}\n\nfunction reasoningContent(content: unknown): string {\n if (!Array.isArray(content)) return '';\n return content.map(part => {\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'reasoning' && typeof piece.text === 'string') return piece.text;\n }\n return '';\n }).join('');\n}\n\n/** Stream chunk type of an assistant/chunk payload ('' when unwrapped). */\nfunction chunkTypeOf(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n return String((obj.chunk as { type?: unknown }).type ?? '');\n }\n // Unwrapped payloads carry plain text deltas.\n return 'text-delta';\n}\n\nfunction chunkText(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n // StreamChunk shape: {turn, step, chunk: {type: 'text-delta'|'reasoning-delta', text}}\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n const inner = obj.chunk as { type?: string; text?: string };\n if ((inner.type === 'text-delta' || inner.type === 'reasoning-delta') && typeof inner.text === 'string') {\n return inner.text;\n }\n return '';\n }\n const direct = data as { text?: unknown };\n return typeof direct.text === 'string' ? direct.text : '';\n}\n\nfunction summarizeArgs(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '';\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const parts: string[] = [];\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'string') parts.push(key + '=' + truncate(value.replace(/\\s+/g, ' '), 60));\n }\n return truncate(parts.join(' '), 90);\n } catch {\n return truncate(raw, 90);\n }\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : text.slice(0, max - 1) + '…';\n}\n\nfunction hostModelError(error: { code: string; message?: string }): ModelBridgeResult<never> {\n const message = error.message ?? error.code\n switch (error.code) {\n case 'session-not-found':\n return { ok: false, kind: 'not-found', message }\n case 'agent-busy':\n case 'session-conflict':\n return { ok: false, kind: 'busy', message }\n case 'model-unavailable':\n return { ok: false, kind: 'unavailable', message }\n default:\n return { ok: false, kind: 'internal', message }\n }\n}\n\nfunction hostSessionManagementError(\n error: { code: string; message?: string },\n): SessionManagementResult<never> {\n const message = error.message ?? error.code\n switch (error.code) {\n case 'session-not-found':\n return { ok: false, kind: 'not-found', message }\n case 'agent-busy':\n case 'session-conflict':\n return { ok: false, kind: 'busy', message }\n case 'title-invalid':\n case 'workspace-invalid-path':\n case 'workspace-name-conflict':\n case 'directory-unreadable':\n case 'directory-exists':\n case 'directory-create-failed':\n return { ok: false, kind: 'invalid', message }\n case 'directory-picker-unavailable':\n return { ok: false, kind: 'unsupported', message }\n case 'workspace-not-found':\n return { ok: false, kind: 'not-found', message }\n default:\n return { ok: false, kind: 'internal', message }\n }\n}\n\nfunction projectSessionModels(value: HostSessionModels): HostSessionModels {\n return {\n current: {\n provider: String(value.current.provider),\n model: String(value.current.model),\n ...(value.current.reasoningEffort ? { reasoningEffort: String(value.current.reasoningEffort) } : {}),\n },\n routable: value.routable === true,\n groups: (value.groups ?? []).map((group) => ({\n id: String(group.id),\n name: String(group.name),\n models: (group.models ?? []).map((model) => ({\n id: String(model.id),\n name: String(model.name),\n ...(model.description ? { description: String(model.description) } : {}),\n ...(model.reasoning ? {\n reasoning: {\n efforts: (model.reasoning.efforts ?? []).map((effort) => ({\n id: String(effort.id),\n name: String(effort.name),\n ...(effort.description ? { description: String(effort.description) } : {}),\n })),\n ...(model.reasoning.defaultEffort\n ? { defaultEffort: String(model.reasoning.defaultEffort) }\n : {}),\n },\n } : {}),\n })),\n })),\n failures: (value.failures ?? []).map((failure) => ({\n id: String(failure.id),\n name: String(failure.name),\n message: String(failure.message),\n })),\n }\n}\n\n/** Project a history page (raw events) into MessageProjection rows. */\nexport function projectHistory(events: Array<{ event: SessionEventLike; view?: unknown }>): MessageProjection[] {\n const messages: MessageProjection[] = [];\n const toolByCall = new Map<string, MessageProjection>();\n for (const entry of events) {\n const event = entry.event;\n const base = { seq: event.seq, ts: tsOf(event) };\n switch (event.type) {\n case 'user/message':\n messages.push({\n ...base,\n role: 'user',\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n });\n break;\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n if (!text.trim() && !thinking.trim()) break;\n messages.push({ ...base, role: 'assistant', text, ...(thinking ? { thinking } : {}) });\n break;\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string; callId?: string } | undefined;\n const row: MessageProjection = {\n ...base,\n role: 'tool',\n tool: { name: String(data?.name ?? 'tool'), state: 'running', summary: summarizeArgs(data?.arguments) },\n };\n messages.push(row);\n if (data?.callId) toolByCall.set(String(data.callId), row);\n break;\n }\n case 'tool/result': {\n const data = event.data as { callId?: string; message?: { content?: unknown }; error?: unknown } | undefined;\n const callId = data?.callId ? String(data.callId) : undefined;\n const target = callId ? toolByCall.get(callId) : undefined;\n const failed = data?.error !== undefined;\n const summary = failed ? '失败' : summarizeResult(data?.message?.content);\n if (target?.tool) {\n target.tool = { ...target.tool, state: failed ? 'error' : 'ok', summary };\n } else {\n messages.push({ ...base, role: 'tool', tool: { name: 'result', state: failed ? 'error' : 'ok', summary } });\n }\n break;\n }\n default:\n break;\n }\n }\n return messages.sort((a, b) => a.seq - b.seq);\n}\n\nfunction summarizeResult(content: unknown): string {\n const text = contentText(content).replace(/\\s+/g, ' ').trim();\n return truncate(text, 90);\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'\nimport type { DeepPilotReport, PushTestResult, RelayTestResult } from './report-wire.ts'\n\n/** Async snapshot source wired by the plugin entry. */\nexport type ReportSnapshot = () => Promise<DeepPilotReport>\n\n/** Reads the pairing secret only for an explicit reveal action. */\nexport type PairingTokenSnapshot = () => Promise<string>\n\n/**\n * Replaces the pairing secret for an explicit rotation action: persists the\n * new token, invalidates the old one, and drops live phone connections.\n */\nexport type PairingTokenRotator = () => Promise<string>\n\n/** Runs the relay connectivity self-test (health + enrollment round-trip). */\nexport type RelayTester = () => Promise<RelayTestResult>\n\n/** Forces one synthetic notification down the active push pathway. */\nexport type PushTester = () => Promise<PushTestResult>\n\n/**\n * The Typert receiver the Gateway resolves for the DeepPilot Bridge report.\n * Snapshot data stays non-secret; the token crosses the boundary only through\n * the explicit, user-triggered revealToken/rotateToken invocations.\n */\nexport class DeepPilotReportService extends TypertRemoteService {\n private readonly snapshot: ReportSnapshot\n private readonly pairingToken: PairingTokenSnapshot\n private readonly rotatePairingToken: PairingTokenRotator\n\n private readonly relayTester: RelayTester\n private readonly pushTester: PushTester\n\n constructor(ctx: Context, snapshot: ReportSnapshot, pairingToken: PairingTokenSnapshot, rotatePairingToken: PairingTokenRotator, relayTester: RelayTester, pushTester: PushTester) {\n super(ctx, 'deeppilotReport', { namespace: 'deeppilot' })\n this.snapshot = snapshot\n this.pairingToken = pairingToken\n this.rotatePairingToken = rotatePairingToken\n this.relayTester = relayTester\n this.pushTester = pushTester\n }\n\n async report(): Promise<DeepPilotReport> {\n return this.snapshot()\n }\n\n async revealToken(): Promise<string> {\n return this.pairingToken()\n }\n\n async rotateToken(): Promise<string> {\n return this.rotatePairingToken()\n }\n\n async testRelay(): Promise<RelayTestResult> {\n return this.relayTester()\n }\n\n async testPush(): Promise<PushTestResult> {\n return this.pushTester()\n }\n}\n","/**\n * Wire contract for the DeepPilot report Remote (`deeppilot/report`).\n *\n * The web settings page needs the bridge's live facts (paired devices,\n * connections, token state) — those live Host-side. This module is the single\n * source both halves share: the strict result codec (hand-rolled, so the\n * client bundle needs no schema library) and the exact descriptor object.\n *\n * @module dsh-deeppilot/report-wire\n */\n\nimport type { InvocationDescriptor, TypertRemoteContribution, TypertSchema } from '@deepseek-ai/dsh-typert-protocol'\n\n/** One paired device row in the report. */\nexport interface ReportDevice {\n deviceId: string\n deviceName: string\n appVersion: string\n firstSeenTs: number\n lastSeenTs: number\n /** Present once the device registered an APNs token (token itself never leaves the host). */\n apns?: { environment: 'development' | 'production'; updatedAt: number }\n}\n\n/** One step of the relay connectivity self-test. */\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n message: string\n latencyMs?: number\n}\n\n/** Result of deeppilot/testRelay. */\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\n/** Per-device delivery result of deeppilot/testPush. */\nexport interface PushTestDeviceResult {\n name: string\n environment: string\n /** sent | invalid-token | failed — mirrors the dispatch outcome contract. */\n outcome: string\n /** Apple's raw rejection reason when present (BadDeviceToken, …). */\n reason?: string\n /** First 10 hex chars of the stored token, for freshness verification. */\n tokenFingerprint?: string\n}\n\nexport interface PushTestResult {\n transport: 'apns' | 'relay' | 'none'\n /** sent = at least one accepted delivery; no-targets / not-configured / failed. */\n overall: 'sent' | 'failed' | 'no-targets' | 'not-configured'\n message?: string\n results: PushTestDeviceResult[]\n}\n\n/** Everything the settings page renders for the bridge. */\nexport interface DeepPilotReport {\n protocolVersion: number\n serverVersion: string\n /** Whether the master bridge switch is on. */\n enabled: boolean\n /** Absolute token file path (never the token itself). */\n tokenPath: string\n /** Whether the token file exists and is readable. */\n tokenReady: boolean\n /** Currently connected phone sockets. */\n activeConnections: number\n historyBufferMax: number\n debug: boolean\n /** Private IPv4 candidates for local QR pairing. */\n lanAddresses: string[]\n remote: {\n provider: 'tailscale-funnel'\n phase: 'disabled' | 'starting' | 'login_required' | 'online' | 'error' | 'unavailable' | 'stopped'\n publicURL?: string\n authURL?: string\n message?: string\n updatedAt: number\n }\n devices: ReportDevice[]\n}\n\n/** The npm package identity both contribution registrations claim. */\nexport const REPORT_REMOTE_PACKAGE = 'dsh-deeppilot'\n\n/** Canonical `<namespace>/<method>` endpoint of the report Remote. */\nexport const REPORT_ENDPOINT = 'deeppilot/report'\n\n/** Explicit, user-triggered endpoint for revealing the pairing secret. */\nexport const REVEAL_TOKEN_ENDPOINT = 'deeppilot/revealToken'\n\n/**\n * Explicit, user-triggered endpoint that replaces the pairing secret. The old\n * token stops working immediately; the fresh one is returned so the page can\n * show/QR it right away.\n */\nexport const ROTATE_TOKEN_ENDPOINT = 'deeppilot/rotateToken'\n\nfunction reject(field: string): never {\n throw new TypeError(`deeppilot/report result: invalid ${field}`)\n}\n\nfunction str(source: Record<string, unknown>, key: string, field: string): string {\n const value = source[key]\n if (typeof value !== 'string') reject(field)\n return value\n}\nfunction num(source: Record<string, unknown>, key: string, field: string): number {\n const value = source[key]\n if (typeof value !== 'number' || !Number.isFinite(value)) reject(field)\n return value\n}\nfunction bool(source: Record<string, unknown>, key: string, field: string): boolean {\n const value = source[key]\n if (typeof value !== 'boolean') reject(field)\n return value\n}\nfunction rec(value: unknown, field: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) reject(field)\n return value as Record<string, unknown>\n}\n\nfunction parseDevice(value: unknown): ReportDevice {\n const s = rec(value, 'device')\n let apns: ReportDevice['apns']\n if (s.apns !== undefined) {\n const a = rec(s.apns, 'device.apns')\n const environment = str(a, 'environment', 'device.apns.environment')\n if (environment !== 'development' && environment !== 'production') reject('device.apns.environment')\n apns = { environment, updatedAt: num(a, 'updatedAt', 'device.apns.updatedAt') }\n }\n return {\n deviceId: str(s, 'deviceId', 'device.deviceId'),\n deviceName: str(s, 'deviceName', 'device.deviceName'),\n appVersion: str(s, 'appVersion', 'device.appVersion'),\n firstSeenTs: num(s, 'firstSeenTs', 'device.firstSeenTs'),\n lastSeenTs: num(s, 'lastSeenTs', 'device.lastSeenTs'),\n ...(apns ? { apns } : {}),\n }\n}\n\nfunction parseRemote(value: unknown): DeepPilotReport['remote'] {\n const s = rec(value, 'remote')\n const provider = str(s, 'provider', 'remote.provider')\n const phase = str(s, 'phase', 'remote.phase')\n if (provider !== 'tailscale-funnel') reject('remote.provider')\n const phases = ['disabled', 'starting', 'login_required', 'online', 'error', 'unavailable', 'stopped']\n if (!phases.includes(phase)) reject('remote.phase')\n const publicURL = s.publicURL\n const authURL = s.authURL\n const message = s.message\n if (publicURL !== undefined && typeof publicURL !== 'string') reject('remote.publicURL')\n if (authURL !== undefined && typeof authURL !== 'string') reject('remote.authURL')\n if (message !== undefined && typeof message !== 'string') reject('remote.message')\n return {\n provider,\n phase: phase as DeepPilotReport['remote']['phase'],\n ...(typeof publicURL === 'string' ? { publicURL } : {}),\n ...(typeof authURL === 'string' ? { authURL } : {}),\n ...(typeof message === 'string' ? { message } : {}),\n updatedAt: num(s, 'updatedAt', 'remote.updatedAt'),\n }\n}\n\nfunction parseRelayTestStep(value: unknown): RelayTestStep {\n const st = rec(value, 'step')\n const id = str(st, 'id', 'step.id')\n if (id !== 'health' && id !== 'enroll') reject('step.id')\n const latencyMs = st.latencyMs\n if (latencyMs !== undefined && typeof latencyMs !== 'number') reject('step.latencyMs')\n return {\n id,\n ok: bool(st, 'ok', 'step.ok'),\n message: str(st, 'message', 'step.message'),\n ...(typeof latencyMs === 'number' ? { latencyMs } : {}),\n }\n}\n\nfunction parseRelayTestResult(value: unknown): RelayTestResult {\n const s = rec(value, 'result')\n const overall = str(s, 'overall', 'overall')\n if (overall !== 'ok' && overall !== 'failed') reject('overall')\n const stepsRaw = s.steps\n if (!Array.isArray(stepsRaw)) reject('steps')\n return {\n url: str(s, 'url', 'url'),\n overall,\n tokenIssued: bool(s, 'tokenIssued', 'tokenIssued'),\n steps: (stepsRaw as unknown[]).map(parseRelayTestStep),\n }\n}\n\nfunction parsePushTestResult(value: unknown): PushTestResult {\n const s = rec(value, 'result')\n const transport = str(s, 'transport', 'transport')\n if (transport !== 'apns' && transport !== 'relay' && transport !== 'none') reject('transport')\n const overall = str(s, 'overall', 'overall')\n const overalls = ['sent', 'failed', 'no-targets', 'not-configured']\n if (!overalls.includes(overall)) reject('overall')\n const resultsRaw = s.results\n if (!Array.isArray(resultsRaw)) reject('results')\n const results = (resultsRaw as unknown[]).map((value) => {\n const r = rec(value, 'device result')\n const reason = r.reason\n const tokenFingerprint = r.tokenFingerprint\n return {\n name: str(r, 'name', 'result.name'),\n environment: str(r, 'environment', 'result.environment'),\n outcome: str(r, 'outcome', 'result.outcome'),\n ...(typeof reason === 'string' && reason.length > 0 ? { reason } : {}),\n ...(typeof tokenFingerprint === 'string' && /^[0-9a-f]{1,32}$/.test(tokenFingerprint)\n ? { tokenFingerprint }\n : {}),\n }\n })\n const message = s.message\n return {\n transport,\n overall: overall as PushTestResult['overall'],\n ...(typeof message === 'string' && message.length > 0 ? { message } : {}),\n results,\n }\n}\n\nfunction parseReport(value: unknown): DeepPilotReport {\n const s = rec(value, 'report')\n const devices = s.devices\n const lanAddresses = s.lanAddresses\n if (!Array.isArray(devices)) reject('devices')\n if (!Array.isArray(lanAddresses) || lanAddresses.some((value) => typeof value !== 'string')) reject('lanAddresses')\n return {\n protocolVersion: num(s, 'protocolVersion', 'protocolVersion'),\n serverVersion: str(s, 'serverVersion', 'serverVersion'),\n enabled: bool(s, 'enabled', 'enabled'),\n tokenPath: str(s, 'tokenPath', 'tokenPath'),\n tokenReady: bool(s, 'tokenReady', 'tokenReady'),\n activeConnections: num(s, 'activeConnections', 'activeConnections'),\n historyBufferMax: num(s, 'historyBufferMax', 'historyBufferMax'),\n debug: bool(s, 'debug', 'debug'),\n lanAddresses: lanAddresses as string[],\n remote: parseRemote(s.remote),\n devices: (devices as unknown[]).map(parseDevice),\n }\n}\n\nexport const reportSchema: TypertSchema<DeepPilotReport> = { parse: parseReport }\n\nexport const relayTestSchema: TypertSchema<RelayTestResult> = { parse: parseRelayTestResult }\n\nexport const pushTestSchema: TypertSchema<PushTestResult> = { parse: parsePushTestResult }\n\nexport const pairingTokenSchema: TypertSchema<string> = {\n parse(value: unknown): string {\n if (typeof value !== 'string' || value.length < 32) {\n throw new TypeError('deeppilot/revealToken result: invalid token')\n }\n return value\n },\n}\n\n/**\n * Connectivity self-test for the push relay: health probe plus (when an\n * enroll key is on file) a real enrollment round-trip. Doubles as repair —\n * a token it issues is cached by the bridge.\n */\nexport const TEST_RELAY_ENDPOINT = 'deeppilot/testRelay'\n\n/**\n * Real-delivery push test: forces one synthetic notification down the active\n * push pathway to every registered device, ignoring connected/muted filters —\n * an explicit user action must always be able to prove the pipeline works.\n */\nexport const TEST_PUSH_ENDPOINT = 'deeppilot/testPush'\n\nexport const REPORT_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REPORT_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'report',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#DeepPilotReport`,\n schema: reportSchema,\n },\n}\n\nexport const REVEAL_TOKEN_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REVEAL_TOKEN_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'revealToken',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,\n schema: pairingTokenSchema,\n },\n}\n\nexport const ROTATE_TOKEN_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${ROTATE_TOKEN_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'rotateToken',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,\n schema: pairingTokenSchema,\n },\n}\n\nexport const TEST_RELAY_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_RELAY_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testRelay',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#RelayTestResult`,\n schema: relayTestSchema,\n },\n}\n\nexport const TEST_PUSH_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_PUSH_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testPush',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PushTestResult`,\n schema: pushTestSchema,\n },\n}\n\nconst INVOCATION_DESCRIPTORS = [\n REPORT_DESCRIPTOR,\n REVEAL_TOKEN_DESCRIPTOR,\n ROTATE_TOKEN_DESCRIPTOR,\n TEST_RELAY_DESCRIPTOR,\n TEST_PUSH_DESCRIPTOR,\n]\n\nexport const REPORT_HOST_CONTRIBUTION = {\n package: REPORT_REMOTE_PACKAGE,\n face: 'host' as const,\n schemas: [],\n invocations: INVOCATION_DESCRIPTORS,\n}\n\nexport const REPORT_REMOTE_CONTRIBUTION: TypertRemoteContribution = {\n package: REPORT_REMOTE_PACKAGE,\n descriptors: INVOCATION_DESCRIPTORS,\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { DeepPilotReportService } from './report-service.ts'\nimport type { PairingTokenRotator, PairingTokenSnapshot, PushTester, RelayTester, ReportSnapshot } from './report-service.ts'\nimport { REPORT_HOST_CONTRIBUTION } from './report-wire.ts'\n\ninterface TypertContributionRegistry {\n register(contribution: typeof REPORT_HOST_CONTRIBUTION): () => void | Promise<void>\n}\n\n/**\n * Provide the report service and register its Remote descriptor. Rides an\n * optional `typert` inject: profiles without the web stack never activate it.\n */\nexport function applyReportRemote(\n ctx: Context,\n snapshot: ReportSnapshot,\n pairingToken: PairingTokenSnapshot,\n rotatePairingToken: PairingTokenRotator,\n relayTester: RelayTester,\n pushTester: PushTester,\n): void {\n ctx.inject(['typert'], (remoteCtx) => {\n new DeepPilotReportService(remoteCtx, snapshot, pairingToken, rotatePairingToken, relayTester, pushTester)\n const registry = remoteCtx.typert as unknown as TypertContributionRegistry\n const unregister = registry.register(REPORT_HOST_CONTRIBUTION)\n remoteCtx.effect(() => () => void unregister(), 'dsh-deeppilot: report remote')\n })\n}\n","/**\n * Relay connectivity self-test used by the settings page \"测试中继\" button.\n *\n * Two steps mirror what zero-touch enrollment actually does:\n * 1. health — GET {url}/healthz → is the relay reachable?\n * 2. enroll — POST {url}/v1/enroll → does the shared key grant a token?\n *\n * The enroll step doubles as a repair path: a token it issues is handed back\n * via `onEnrolled` so the bridge caches it and flips push readiness without\n * waiting for the next app registration.\n *\n * Transport is injectable (`fetchImpl`) so tests never touch the network.\n */\n\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n /** Human-readable, user-facing (zh) outcome; safe to render verbatim. */\n message: string\n latencyMs?: number\n}\n\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n /** True when the probe produced/cached a usable relay token. */\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\nexport interface RelayProbeOptions {\n url: string\n /** Stable per-bridge identity for enrollment; generated upstream if absent. */\n clientId?: string\n /** Distributor's shared key; absent → enroll step reports why it cannot run. */\n enrollKey?: string\n /** True when the user configured relayToken manually (enroll unnecessary). */\n manualToken?: boolean\n timeoutMs?: number\n fetchImpl?: typeof fetch\n /** Called with an issued token so the caller can persist it. */\n onEnrolled?: (token: string) => void\n}\n\nconst DEFAULT_TIMEOUT_MS = 6_000\n\nasync function requestJson(\n fetchImpl: typeof fetch,\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<{ status: number; body: unknown }> {\n const response = await fetchImpl(url, {\n ...init,\n signal: AbortSignal.timeout(timeoutMs),\n })\n let body: unknown = null\n try {\n body = await response.json()\n } catch {\n // non-JSON body keeps body null; status still drives the verdict\n }\n return { status: response.status, body }\n}\n\nexport async function runRelayProbe(options: RelayProbeOptions): Promise<RelayTestResult> {\n const base = options.url.trim().replace(/\\/+$/, '')\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const fetchImpl = options.fetchImpl ?? fetch\n const steps: RelayTestStep[] = []\n let tokenIssued = false\n\n // ---- step 1: reachability ----\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/healthz`,\n { method: 'GET' },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n if (status === 200 && (body as { ok?: unknown })?.ok === true) {\n steps.push({ id: 'health', ok: true, message: '中继服务可达', latencyMs })\n } else {\n steps.push({ id: 'health', ok: false, message: `中继响应异常(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'health', ok: false, message: '无法连接中继:' + reason })\n }\n\n // ---- step 2: enrollment ----\n if ((options.manualToken ?? false)) {\n steps.push({ id: 'enroll', ok: true, message: '已手动配置 relayToken,跳过注册验证' })\n } else if (!options.enrollKey) {\n steps.push({ id: 'enroll', ok: false, message: '尚无注册密钥:等待分发版 App 首次注册后才能验证注册' })\n } else {\n const clientId = options.clientId ?? 'u_' + Math.random().toString(36).slice(2)\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/v1/enroll`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey: options.enrollKey }),\n },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n const token = (body as { token?: unknown })?.token\n if (status === 200 && typeof token === 'string' && token.startsWith('rl_')) {\n tokenIssued = true\n options.onEnrolled?.(token)\n steps.push({ id: 'enroll', ok: true, message: '注册成功,已取得推送凭证', latencyMs })\n } else if (status === 403) {\n steps.push({ id: 'enroll', ok: false, message: '注册被拒:注册密钥不匹配(检查 App 内 DSPushEnrollKey 与服务器 RELAY_ENROLL_KEY)', latencyMs })\n } else if (status === 429) {\n steps.push({ id: 'enroll', ok: false, message: '尝试过于频繁,稍后再试', latencyMs })\n } else {\n steps.push({ id: 'enroll', ok: false, message: `注册失败(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'enroll', ok: false, message: '注册请求失败:' + reason })\n }\n }\n\n const executed = steps.filter((step) => step.ok !== undefined)\n const overall: RelayTestResult['overall'] =\n steps.length > 0 && steps.some((step) => step.id === 'health' && step.ok) &&\n executed.every((step) => step.ok)\n ? 'ok'\n : 'failed'\n return { url: base, overall, tokenIssued, steps }\n}\n","/**\n * Minimal APNs provider client (HTTP/2) with zero npm dependencies.\n *\n * Implements exactly what the bridge needs:\n * - ES256 provider token (JWT) signed with an Apple .p8 key, refreshed under\n * the 1-hour freshness window Apple enforces;\n * - one long-lived HTTP/2 session per environment, recreated transparently\n * after GOAWAY/errors;\n * - alert pushes carrying the notify projection (category/thread/collapse),\n * with `interruption-level: time-sensitive` for approval/question events;\n * - outcome classification so callers can prune dead device tokens.\n *\n * Privacy: logs carry outcomes and masked token prefixes only — never message\n * bodies or full tokens.\n */\n\nimport { connect as http2Connect, type ClientHttp2Session } from 'node:http2'\nimport { createPrivateKey, sign as cryptoSign, type KeyObject } from 'node:crypto'\nimport { readFile } from 'node:fs/promises'\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushNotification } from './protocol.ts'\n\nexport interface ApnsClientOptions {\n teamId: string\n keyId: string\n /** Absolute path to the .p8 private key file. */\n keyPath: string\n bundleId: string\n debug?: boolean\n log: (message: string) => void\n}\n\nexport type ApnsOutcome = 'sent' | 'invalid-token' | 'failed'\n\n/** Outcome plus Apple's rejection reason (when present) for diagnosis. */\nexport interface ApnsSendResult {\n outcome: ApnsOutcome\n reason?: string\n}\n\nconst PROVIDER_TOKEN_TTL_MS = 50 * 60 * 1000 // Apple allows 1h; refresh early.\nconst REQUEST_TIMEOUT_MS = 10_000\n\n/** base64url without padding. */\nfunction b64url(input: Buffer | string): string {\n return Buffer.from(input).toString('base64url')\n}\n\n/** Sign one ES256 JWT for the given signing input with a P-256 private key. */\nexport function es256Jwt(signingInput: string, key: KeyObject): string {\n const signature = cryptoSign('sha256', Buffer.from(signingInput, 'utf8'), {\n key,\n // JOSE/ES256 wants raw r||s; node emits DER by default.\n dsaEncoding: 'ieee-p1363',\n })\n return signingInput + '.' + b64url(signature)\n}\n\n/** Strip PEM armor from a .p8 file and decode to PKCS#8 DER. */\nexport function p8ToDer(pem: string): Buffer {\n const body = pem\n .replace(/-----BEGIN PRIVATE KEY-----/, '')\n .replace(/-----END PRIVATE KEY-----/, '')\n .replace(/\\s+/g, '')\n return Buffer.from(body, 'base64')\n}\n\nexport interface LoadedKey {\n key: KeyObject\n}\n\n/** Pure payload builder so tests can assert the wire format without sockets. */\nexport function apnsPayload(notification: PushNotification): Record<string, unknown> {\n const timeSensitive = notification.category === 'approval.required' || notification.category === 'question.asked'\n return {\n aps: {\n alert: {\n title: notification.title.slice(0, 120),\n body: notification.body.slice(0, 200),\n },\n sound: 'default',\n category: notification.category,\n 'thread-id': notification.sessionId.slice(0, 64),\n ...(timeSensitive ? { 'interruption-level': 'time-sensitive' } : {}),\n },\n sessionId: notification.sessionId,\n notificationId: notification.notificationId,\n kind: notification.category,\n }\n}\n\n/** One delivery request: the notify projection plus its target device token.\n * The environment comes from the device's own registration (its build kind),\n * so one client serves both sandbox and production devices. */\nexport interface ApnsSendRequest extends PushNotification {\n deviceToken: string\n environment: ApnsEnvironment\n}\n\n/** collapse-id accepts ≤64 bytes of ASCII; keep it stable per session+event. */\nexport function collapseIdFor(notification: PushNotification): string {\n const raw = `${notification.category}:${notification.sessionId}`\n return raw.replace(/[^a-zA-Z0-9.:-]/g, '').slice(0, 64)\n}\n\nfunction authorityFor(environment: ApnsEnvironment): string {\n return environment === 'production' ? 'api.push.apple.com' : 'api.sandbox.push.apple.com'\n}\n\nexport class ApnsClient {\n private readonly log: (message: string) => void\n private readonly debug: boolean\n private readonly opts: ApnsClientOptions\n\n private providerToken = ''\n private providerTokenIssuedAt = 0\n private key: KeyObject | undefined\n\n constructor(opts: ApnsClientOptions) {\n this.opts = opts\n this.log = opts.log\n this.debug = opts.debug === true\n }\n\n async dispose(): Promise<void> {\n const sessions = [...this.sessions.values()]\n this.sessions.clear()\n await Promise.all(\n sessions\n .filter((session) => !session.destroyed)\n .map((session) => new Promise<void>((resolve) => session.close(() => resolve()))),\n )\n }\n\n // ---------- credentials ----------\n\n private async ensureProviderToken(): Promise<string> {\n if (this.providerToken && Date.now() - this.providerTokenIssuedAt < PROVIDER_TOKEN_TTL_MS) {\n return this.providerToken\n }\n if (!this.key) {\n const pem = await readFile(this.opts.keyPath, 'utf8')\n this.key = createPrivateKey({ key: p8ToDer(pem), format: 'der', type: 'pkcs8' })\n }\n const issuedAt = Math.floor(Date.now() / 1000)\n const header = b64url(JSON.stringify({ alg: 'ES256', kid: this.opts.keyId }))\n const claims = b64url(JSON.stringify({ iss: this.opts.teamId, iat: issuedAt }))\n this.providerToken = es256Jwt(`${header}.${claims}`, this.key)\n this.providerTokenIssuedAt = Date.now()\n return this.providerToken\n }\n\n // ---------- transport ----------\n\n /** One long-lived HTTP/2 session per Apple host (sandbox + production). */\n private readonly sessions = new Map<string, ClientHttp2Session>()\n\n private ensureSession(authority: string): ClientHttp2Session {\n const existing = this.sessions.get(authority)\n if (existing && !existing.destroyed && !existing.closed) return existing\n const session = http2Connect(`https://${authority}`)\n // Keep a handler attached so background errors never become uncaught\n // exceptions; the next send() recreates the session when dead.\n session.on('error', (error) => {\n if (this.debug) this.log('apns session error (' + authority + '): ' + String(error))\n this.sessions.delete(authority)\n })\n this.sessions.set(authority, session)\n return session\n }\n\n /**\n * Deliver one alert. Never throws — every failure path resolves to an\n * outcome so fan-out loops cannot crash the host on a flaky network.\n */\n async send(request: ApnsSendRequest): Promise<ApnsSendResult> {\n const { deviceToken, environment, ...notification } = request\n let stream: ReturnType<ClientHttp2Session['request']> | undefined\n try {\n const token = await this.ensureProviderToken()\n const body = JSON.stringify(apnsPayload(notification))\n const session = this.ensureSession(authorityFor(environment))\n const done = new Promise<ApnsSendResult>((resolve) => {\n const req = session.request({\n [':method']: 'POST',\n [':path']: '/3/device/' + deviceToken,\n authorization: 'bearer ' + token,\n 'apns-topic': this.opts.bundleId,\n 'apns-push-type': 'alert',\n 'apns-priority': '10',\n // Stale approvals arriving hours later are worse than none.\n 'apns-expiration': String(Math.floor(Date.now() / 1000) + 3600),\n 'apns-collapse-id': collapseIdFor(notification),\n 'content-type': 'application/json',\n 'content-length': String(Buffer.byteLength(body)),\n })\n stream = req\n let status = 0\n let responseBody = ''\n const settle = (outcome: ApnsOutcome, reason?: string): void => {\n if (this.debug) {\n this.log(`apns ${outcome}${reason ? ' (' + reason + ')' : ''} (${this.maskToken(deviceToken)})`)\n }\n resolve(reason !== undefined && reason !== '' ? { outcome, reason } : { outcome })\n }\n const timer = setTimeout(() => {\n req.close()\n settle('failed')\n }, REQUEST_TIMEOUT_MS)\n timer.unref?.()\n req.on('response', (headers) => {\n status = Number(headers[':status'] ?? 0)\n })\n req.on('data', (chunk: Buffer) => {\n responseBody += chunk.toString('utf8')\n })\n req.on('error', () => {\n clearTimeout(timer)\n settle('failed')\n })\n req.on('end', () => {\n clearTimeout(timer)\n if (status === 200) return settle('sent')\n let reason = ''\n try {\n reason = String((JSON.parse(responseBody) as { reason?: string }).reason ?? '')\n } catch {\n // non-JSON body; fall through with empty reason\n }\n if (status !== 200 && !reason) reason = 'HTTP ' + String(status)\n if (reason === 'Unregistered' || reason === 'BadDeviceToken') return settle('invalid-token', reason)\n if (this.debug) this.log(`apns rejected status=${status} reason=${reason}`)\n settle('failed', reason)\n })\n req.end(body)\n })\n return await done\n } catch (error) {\n // Credential load failures and dead sessions land here; force both to be\n // rebuilt on the next attempt.\n this.key = undefined\n this.providerToken = ''\n this.sessions.clear()\n if (this.debug) this.log('apns send failed: ' + String(error))\n return { outcome: 'failed', reason: String(error).slice(0, 120) }\n } finally {\n try { stream?.close() } catch { /* already closed */ }\n }\n }\n\n maskToken(token: string): string {\n return token.length <= 10 ? '…' : token.slice(0, 6) + '…' + token.slice(-4)\n }\n}\n","/**\n * Client for the operator-run push relay (relay/server.js). Used when the App\n * is distributed under the operator's team: bridges cannot hold the\n * distributor's .p8, so they forward notify projections over HTTPS and let\n * the relay do APNs.\n *\n * Outcome contract mirrors src/apns.ts exactly so the dispatcher prunes dead\n * tokens identically on either path:\n * sent | invalid-token (Unregistered/BadDeviceToken) | failed\n */\n\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushNotification } from './protocol.ts'\n\nexport interface RelayClientOptions {\n /** Base URL of the relay, e.g. https://relay.example.com */\n url: string\n /** Per-user bearer token issued by the relay operator (or via enroll). */\n token?: string\n timeoutMs?: number\n debug?: boolean\n log: (message: string) => void\n}\n\nexport interface RelaySendRequest {\n deviceToken: string\n environment: ApnsEnvironment\n notification: PushNotification\n}\n\nexport class RelayClient {\n private readonly base: string\n private readonly token: string\n private readonly timeoutMs: number\n private readonly debug: boolean\n private readonly log: (message: string) => void\n\n constructor(opts: RelayClientOptions) {\n this.base = opts.url.trim().replace(/\\/+$/, '')\n this.token = (opts.token ?? '').trim()\n this.timeoutMs = opts.timeoutMs ?? 10_000\n this.debug = opts.debug === true\n this.log = opts.log\n }\n\n /**\n * Zero-touch enrollment: exchange the distributor's shared key (baked into\n * the distributed app) for a stable per-bridge bearer token. Idempotent —\n * relays derive the same token for the same clientId. Returns null on any\n * failure; callers treat that as \"not enrolled yet\", not as an error.\n */\n async enroll(clientId: string, enrollKey: string): Promise<string | null> {\n try {\n const response = await fetch(this.base + '/v1/enroll', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (!response.ok) {\n if (this.debug) this.log(`enroll http ${response.status}`)\n return null\n }\n const body = (await response.json()) as { token?: unknown }\n if (typeof body.token === 'string' && body.token.startsWith('rl_')) return body.token\n if (this.debug) this.log('enroll returned no usable token')\n return null\n } catch (error) {\n if (this.debug) this.log('enroll failed: ' + String(error))\n return null\n }\n }\n\n async send(request: RelaySendRequest): Promise<{ outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }> {\n try {\n const response = await fetch(this.base + '/v1/push', {\n method: 'POST',\n headers: {\n authorization: 'Bearer ' + this.token,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n deviceToken: request.deviceToken,\n environment: request.environment,\n notification: request.notification,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (response.status === 401 || response.status === 429) {\n // Credential/quota problems are configuration errors, not token death;\n // surface as failed so the user fixes the config instead of silently\n // losing registrations.\n if (this.debug) this.log(`relay rejected status=${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n if (!response.ok) {\n if (this.debug) this.log(`relay http ${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n const body = (await response.json()) as { outcome?: string; reason?: string }\n if (body.outcome === 'sent') return { outcome: 'sent' }\n if (body.outcome === 'invalid-token') return { outcome: 'invalid-token', reason: body.reason }\n if (this.debug) this.log('relay outcome=' + String(body.outcome) + ' reason=' + String(body.reason ?? ''))\n return { outcome: 'failed', reason: body.reason }\n } catch (error) {\n if (this.debug) this.log('relay send failed: ' + String(error))\n const message = error instanceof Error ? error.message.slice(0, 120) : String(error).slice(0, 120)\n return { outcome: 'failed', reason: message }\n }\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { access, mkdir } from 'node:fs/promises'\nimport { constants as fsConstants } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { expandHome } from './token.ts'\n\nexport type RemotePhase =\n | 'disabled'\n | 'starting'\n | 'login_required'\n | 'online'\n | 'error'\n | 'unavailable'\n | 'stopped'\n\nexport interface RemoteStatus {\n provider: 'tailscale-funnel'\n phase: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n updatedAt: number\n}\n\nexport interface RemoteSupervisorOptions {\n enabled: boolean\n hostname: string\n statePath: string\n helperPath?: string\n funnelPort?: 443 | 8443 | 10000\n log: (message: string) => void\n}\n\ninterface HelperEvent {\n phase?: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n}\n\nconst RESTART_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 16_000, 30_000]\n\nexport const DEFAULT_REMOTE_HOSTNAME = 'dsh-deeppilot'\n\n/** Preserve custom node names while migrating every pre-DeepPilot default. */\nexport function normalizeRemoteHostname(value: string | undefined): string {\n const hostname = value?.trim() ?? ''\n if (hostname === '' || ['dsh-phone', 'dsh-pocket', 'harnesspocket'].includes(hostname.toLowerCase())) {\n return DEFAULT_REMOTE_HOSTNAME\n }\n return hostname\n}\n\n/** Parse one helper IPC line without ever evaluating or interpolating it. */\nexport function parseHelperEvent(line: string): HelperEvent | null {\n try {\n const value = JSON.parse(line) as Record<string, unknown>\n const phases: RemotePhase[] = ['starting', 'login_required', 'online', 'error', 'stopped']\n if (typeof value.phase !== 'string' || !phases.includes(value.phase as RemotePhase)) return null\n return {\n phase: value.phase as RemotePhase,\n ...(typeof value.publicURL === 'string' ? { publicURL: value.publicURL } : {}),\n ...(typeof value.authURL === 'string' ? { authURL: value.authURL } : {}),\n ...(typeof value.message === 'string' ? { message: value.message.slice(0, 500) } : {}),\n }\n } catch {\n return null\n }\n}\n\nfunction bundledHelperPath(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n return resolve(here, '..', 'bin', `${process.platform}-${process.arch}`, 'dsh-deeppilot-tunnel')\n}\n\n/** Owns exactly one embedded tunnel helper and restarts it after failures. */\nexport class RemoteSupervisor {\n private child: ChildProcess | undefined\n private restartTimer: NodeJS.Timeout | undefined\n private restartAttempt = 0\n private stopping = false\n private statusValue: RemoteStatus\n\n constructor(private readonly options: RemoteSupervisorOptions) {\n this.statusValue = {\n provider: 'tailscale-funnel',\n phase: options.enabled ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n }\n }\n\n status(): RemoteStatus {\n return { ...this.statusValue }\n }\n\n async start(originURL: string): Promise<void> {\n if (!this.options.enabled || this.child !== undefined || this.stopping) return\n const helper = expandHome(this.options.helperPath ?? bundledHelperPath())\n const statePath = expandHome(this.options.statePath)\n try {\n await access(helper, fsConstants.X_OK)\n await mkdir(statePath, { recursive: true, mode: 0o700 })\n } catch (error) {\n this.setStatus({\n phase: 'unavailable',\n message: `embedded tunnel helper unavailable: ${String(error)}`,\n })\n return\n }\n\n this.setStatus({ phase: 'starting', message: undefined })\n const child = spawn(helper, [\n '--origin', originURL,\n '--hostname', normalizeRemoteHostname(this.options.hostname),\n '--state-dir', statePath,\n '--port', String(this.options.funnelPort ?? 443),\n ], {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n PATH: process.env.PATH ?? '/usr/bin:/bin',\n TMPDIR: process.env.TMPDIR ?? '/tmp',\n },\n })\n this.child = child\n if (child.stdout === null || child.stderr === null) {\n this.setStatus({ phase: 'error', message: 'helper stdio unavailable' })\n child.kill('SIGTERM')\n return\n }\n\n let stdoutBuffer = ''\n child.stdout.setEncoding('utf8')\n child.stdout.on('data', (chunk: string) => {\n stdoutBuffer += chunk\n const lines = stdoutBuffer.split('\\n')\n stdoutBuffer = lines.pop() ?? ''\n for (const line of lines) this.acceptLine(line)\n })\n\n let stderrBuffer = ''\n child.stderr.setEncoding('utf8')\n child.stderr.on('data', (chunk: string) => {\n stderrBuffer = (stderrBuffer + chunk).slice(-2_000)\n })\n\n child.once('error', (error) => {\n this.setStatus({ phase: 'error', message: `helper launch failed: ${String(error)}` })\n })\n child.once('exit', (code, signal) => {\n if (this.child === child) this.child = undefined\n if (this.stopping) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n const detail = stderrBuffer.trim().split('\\n').at(-1)\n this.setStatus({\n phase: 'error',\n message: detail || `helper exited (${signal ?? String(code)})`,\n })\n this.scheduleRestart(originURL)\n })\n }\n\n async dispose(): Promise<void> {\n this.stopping = true\n if (this.restartTimer !== undefined) clearTimeout(this.restartTimer)\n this.restartTimer = undefined\n const child = this.child\n this.child = undefined\n if (child === undefined) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n await new Promise<void>((resolveDone) => {\n const force = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n }, 3_000)\n child.once('exit', () => {\n clearTimeout(force)\n resolveDone()\n })\n child.kill('SIGTERM')\n })\n this.setStatus({ phase: 'stopped', message: undefined })\n }\n\n private acceptLine(line: string): void {\n const event = parseHelperEvent(line)\n if (event === null || event.phase === undefined) return\n if (event.phase === 'online') this.restartAttempt = 0\n this.setStatus({ ...event, phase: event.phase })\n }\n\n private scheduleRestart(originURL: string): void {\n if (this.stopping || this.restartTimer !== undefined) return\n const delay = RESTART_DELAYS_MS[Math.min(this.restartAttempt, RESTART_DELAYS_MS.length - 1)]\n this.restartAttempt += 1\n this.restartTimer = setTimeout(() => {\n this.restartTimer = undefined\n void this.start(originURL)\n }, delay)\n }\n\n private setStatus(next: Partial<RemoteStatus> & Pick<RemoteStatus, 'phase'>): void {\n const cleared: Partial<RemoteStatus> = next.phase === 'online'\n ? { authURL: undefined, message: undefined }\n : next.phase === 'login_required'\n ? { publicURL: undefined, message: undefined }\n : next.phase === 'starting' || next.phase === 'stopped' || next.phase === 'disabled'\n ? { publicURL: undefined, authURL: undefined, message: undefined }\n : {}\n this.statusValue = {\n ...this.statusValue,\n ...cleared,\n ...next,\n updatedAt: Date.now(),\n }\n if (next.phase === 'online') {\n this.options.log('remote Funnel online')\n } else if (next.phase === 'login_required') {\n this.options.log('remote Funnel requires browser authorization')\n } else if (next.phase === 'error' || next.phase === 'unavailable') {\n this.options.log(`remote Funnel ${next.phase}: ${next.message ?? 'unknown error'}`)\n }\n }\n}\n","import { networkInterfaces } from 'node:os'\n\nexport function isPrivateIPv4(address: string): boolean {\n const octets = address.split('.').map(Number)\n if (octets.length !== 4 || octets.some((value) => !Number.isInteger(value) || value < 0 || value > 255)) {\n return false\n }\n const [a, b] = octets as [number, number, number, number]\n return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168)\n}\n\n/** Private IPv4 candidates, preferring physical en* interfaces over tunnels. */\nexport function localLANIPv4Addresses(): string[] {\n const candidates: Array<{ name: string; address: string }> = []\n for (const [name, entries] of Object.entries(networkInterfaces())) {\n for (const entry of entries ?? []) {\n if (entry.family === 'IPv4' && !entry.internal && isPrivateIPv4(entry.address)) {\n candidates.push({ name, address: entry.address })\n }\n }\n }\n const priority = (name: string): number => name === 'en0' ? 0 : name.startsWith('en') ? 1 : name.startsWith('bridge') ? 2 : 3\n candidates.sort((left, right) => priority(left.name) - priority(right.name) || left.name.localeCompare(right.name))\n return [...new Set(candidates.map(({ address }) => address))]\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'\nimport { randomBytes } from 'node:crypto'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { Duplex } from 'node:stream'\nimport type { Context } from '@deepseek-ai/cordis'\nimport z from '@deepseek-ai/schemastery'\nimport { WebSocketServer } from 'ws'\nimport { BridgeConnection } from './connection.ts'\nimport { HostBridge } from './host-bridge.ts'\nimport type { ApiProxyLike, PushOutlet } from './host-bridge.ts'\nimport { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'\nimport { applyReportRemote } from './report-remote.ts'\nimport { runRelayProbe } from './relay-test.ts'\nimport type { PushTestResult } from './report-wire.ts'\nimport { DeviceStore, bridgeDataDir, expandHome, loadOrCreateToken, migrateLegacyBridgeDataDir, tokenMatches, writeNewToken } from './token.ts'\nimport type { ApnsEnvironment } from './token.ts'\nimport { ApnsClient } from './apns.ts'\nimport { RelayClient } from './relay-client.ts'\nimport type { PushNotification } from './protocol.ts'\nimport {\n DEFAULT_REMOTE_HOSTNAME,\n normalizeRemoteHostname,\n RemoteSupervisor,\n type RemoteStatus,\n} from './remote-supervisor.ts'\nimport { localLANIPv4Addresses } from './local-address.ts'\n\n/**\n * dsh-deeppilot — data bridge between the DSH host and DeepPilot\n * clients. Registers exactly one WebSocket upgrade route (/phone) plus an\n * optional health probe (/phone/health) on the existing web server. The web\n * UI is never touched.\n *\n * Data plane: an in-process HostBridge consumes apiProxy.events.mux()/host()\n * streams, mirrors session summaries, tracks pending approvals/questions,\n * and fans projected protocol-v1 pushes out to every connected device.\n *\n * Protocol: src/protocol.ts, v1. The private app repository carries the\n * matching normative document and Swift models.\n */\n\nexport const name = 'deeppilot'\n\nexport { HostBridge } from './host-bridge.ts'\n\n/** No eager service requirement: profiles without a web stack simply skip. */\nexport const inject: string[] = []\n\nexport interface Config {\n /** Master switch; when false the plugin activates and does nothing. */\n enabled?: boolean\n /** Pairing token file (0600); generated on first boot when missing. */\n authTokenPath?: string\n /** Paired-device registry JSON path. */\n devicesPath?: string\n /** Replay ring buffer bound (frames) per deployment. */\n historyBufferMax?: number\n /** Verbose per-frame diagnostics (never prints token or message bodies). */\n debug?: boolean\n /** Optional embedded remote transport. Reconciled when settings change. */\n remote?: {\n enabled?: boolean\n provider?: string\n hostname?: string\n statePath?: string\n helperPath?: string\n funnelPort?: number\n }\n /**\n * Offline push (F-9). provider 'apns' sends direct Apple Push Notification\n * deliveries from this Mac — outbound-only, no relay server, requires the\n * user's own Apple developer credentials. provider 'relay' forwards notify\n * projections to an operator-run relay (relay/server.js) holding the\n * distributor's key — used when the App ships via TestFlight/App Store.\n *\n * Deliberately NO environment knob here: each device reports its own\n * environment when registering (derived from its build kind), and\n * deliveries route per device — mixed dev/TestFlight phones coexist.\n */\n push?: {\n /** 'none' (default) | 'apns' | 'relay'. */\n provider?: string\n // --- apns ---\n /** Apple Developer team id (JWT iss claim). */\n teamId?: string\n /** APNs auth key id (JWT kid header). */\n keyId?: string\n /** .p8 private key path; generated keys live under the bridge data dir. */\n keyPath?: string\n /** App bundle id — the apns-topic header. */\n bundleId?: string\n // --- relay ---\n /** Relay base URL (https). See relay/README.md. */\n relayUrl?: string\n /** Per-user bearer token issued by the relay operator. */\n relayToken?: string\n }\n}\n\n/** Operator-run relay used by distributed builds; overridable via config. */\nconst DEFAULT_RELAY_URL = 'https://pilot.hailab.dev'\n\nexport const Config = z.object({\n enabled: z.boolean().default(true),\n authTokenPath: z.string().default(join(bridgeDataDir(), 'auth-token')),\n devicesPath: z.string().default(join(bridgeDataDir(), 'devices.json')),\n historyBufferMax: z.natural().min(100).default(2000),\n debug: z.boolean().default(false),\n remote: z.object({\n enabled: z.boolean().default(false),\n provider: z.string().default('tailscale-funnel'),\n hostname: z.string().default(DEFAULT_REMOTE_HOSTNAME),\n statePath: z.string().default(join(bridgeDataDir(), 'tailscale')),\n helperPath: z.string().default(''),\n funnelPort: z.natural().default(443),\n }).default({\n enabled: false,\n provider: 'tailscale-funnel',\n hostname: DEFAULT_REMOTE_HOSTNAME,\n statePath: join(bridgeDataDir(), 'tailscale'),\n helperPath: '',\n funnelPort: 443,\n }),\n push: z.object({\n provider: z.string().default('none'),\n teamId: z.string().default(''),\n keyId: z.string().default(''),\n keyPath: z.string().default(join(bridgeDataDir(), 'apns', 'AuthKey.p8')),\n bundleId: z.string().default('dev.hailab.deeppilot'),\n relayUrl: z.string().default(DEFAULT_RELAY_URL),\n relayToken: z.string().default(''),\n }).default({\n provider: 'none',\n teamId: '',\n keyId: '',\n keyPath: join(bridgeDataDir(), 'apns', 'AuthKey.p8'),\n bundleId: 'dev.hailab.deeppilot',\n relayUrl: DEFAULT_RELAY_URL,\n relayToken: '',\n }),\n})\n\ninterface WebServerLike {\n register(route: {\n kind: 'exact' | 'prefix'\n path: string\n handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>\n }): () => void\n registerUpgrade(route: {\n path: string\n handler: (req: IncomingMessage, socket: Duplex, head: Buffer) => void\n }): () => void\n}\n\ntype SubContext = {\n effect: (setup: () => unknown, name?: string) => unknown\n}\n\nconst SERVER_VERSION = '0.2.0'\nconst MAX_CLIENT_CONNECTIONS = 16\n/**\n * Single-frame bound. Covers the protocol maximum (4 × 8 MB base64 images\n * plus prompt text) with headroom while keeping an unauthenticated client's\n * pre-hello buffering far below ws's 100 MiB default.\n */\nconst MAX_FRAME_BYTES = 64 * 1024 * 1024\n\nfunction rejectUpgrade(socket: Duplex, status: number, reason: string): void {\n const body = JSON.stringify({ error: reason })\n socket.end(\n 'HTTP/1.1 ' + status + ' Forbidden\\r\\n' +\n 'Content-Type: application/json\\r\\n' +\n 'Content-Length: ' + Buffer.byteLength(body) + '\\r\\n' +\n 'Connection: close\\r\\n' +\n '\\r\\n' +\n body,\n )\n}\n\n/** Authorization is preferred; the query form remains for older app builds. */\nexport function requestToken(req: Pick<IncomingMessage, 'url' | 'headers'>): string | null {\n const authorization = req.headers.authorization\n if (typeof authorization === 'string') {\n const match = /^Bearer\\s+(.+)$/i.exec(authorization.trim())\n if (match?.[1]) return match[1]\n }\n try {\n return new URL(req.url ?? '/', 'http://phone.local').searchParams.get('token')\n } catch {\n return null\n }\n}\n\n/**\n * Cordis hands the second argument in different shapes depending on host\n * composition: a reactive options getter, the resolved config value, or\n * nothing when the patch row omits `config`. Normalize all of them.\n */\nfunction normalizeOptions(options: unknown): Config {\n if (typeof options === 'function') {\n return (options as () => Config)()\n }\n if (options && typeof options === 'object') {\n return options as Config\n }\n // Validate undefined through the schema so every default applies.\n const validated = (Config as unknown as (data: unknown) => Config)(undefined)\n return validated ?? {}\n}\n\nexport function apply(ctx: Context, options: unknown): void {\n const cfg = normalizeOptions(options)\n\n const log = (message: string): void => {\n console.log('[deeppilot] ' + message)\n }\n\n /**\n * Settings-section source: while a settings service is attached this holds\n * the user-edited section value; otherwise the composition defaults. Read\n * through currentConfig() everywhere (normalizeOptions prefers it).\n */\n let liveSource: (() => Config) | undefined\n let scheduleRemoteReconcile: (() => void) | undefined\n const currentConfig = (): Config => {\n if (liveSource !== undefined) return normalizeOptions(liveSource())\n return normalizeOptions(options)\n }\n const enabledNow = (): boolean => currentConfig().enabled === true\n\n // Web settings page: a \"deeppilot\" section with the plugin knobs.\n // Values persist through the settings document and re-enter via setSource;\n // `enabled` decides whether the bridge starts at all (next restart). This is\n // registered unconditionally so the master switch stays reachable even while\n // the bridge is off — otherwise a disabled bridge could never be re-enabled.\n installSettingsSection(ctx, settingsNamespace('deeppilot'), Config, normalizeOptions(undefined), {\n setSource: (source) => {\n liveSource = source\n // Settings can attach after webServer. Defer one microtask so the\n // settings service can finish publishing the new source before the\n // remote runtime reads it.\n queueMicrotask(() => scheduleRemoteReconcile?.())\n },\n onChange: () => queueMicrotask(() => scheduleRemoteReconcile?.()),\n })\n\n // Master switch, resolved against the latest available settings document.\n // Individual injected services also read currentConfig() when they activate.\n if (currentConfig().enabled !== true) {\n log('disabled via settings; bridge stays inactive (rumors of /phone below are skipped)')\n }\n\n // Resolved asynchronously; route handlers await readiness. Never rejects:\n // a storage failure degrades the bridge instead of killing the host.\n // The token lives in a mutable cell so rotation can swap the secret (and\n // invalidate the old one) without restarting the host.\n const dataDir = bridgeDataDir()\n\n // ---------- zero-touch push enrollment (distributed builds) ----------\n\n /**\n * Persistent relay-enrollment cell (deeppilot/push-relay.json). A\n * distributed app presents the distributor's shared enrollKey during\n * c2s.push.register; the bridge then auto-enables relay mode, enrolls with\n * the operator's relay and caches the issued token. Users never fill in\n * anything; explicit config always wins over the auto flag.\n */\n interface RelayEnrollmentCell {\n clientId?: string\n autoRelay?: boolean\n enrollKey?: string\n token?: string\n }\n const pushRelayPath = join(dataDir, 'push-relay.json')\n const enrollmentCell: RelayEnrollmentCell = {}\n\n function persistEnrollment(): void {\n void (async () => {\n try {\n await mkdir(dataDir, { recursive: true })\n await writeFile(pushRelayPath, JSON.stringify({ version: 1, ...enrollmentCell }, null, 2) + '\\n', { mode: 0o600 })\n } catch {\n // best-effort persistence; enrollment retries on next trigger\n }\n })()\n }\n\n /** Fired from BridgeConnection when an app presents its built-in key. */\n const handlePushEnrollKey = async (enrollKey: string): Promise<void> => {\n if (enrollmentCell.enrollKey !== enrollKey) {\n enrollmentCell.enrollKey = enrollKey\n }\n const configuredProvider = currentConfig().push?.provider\n // Only flip when the user has not made an explicit choice.\n if (!configuredProvider || configuredProvider === 'none') {\n if (!enrollmentCell.autoRelay) {\n enrollmentCell.autoRelay = true\n log('push relay mode auto-enabled by enrolled app')\n }\n }\n persistEnrollment()\n // Enroll inline so the register handler sees final readiness: the first\n // offline notification must not depend on a reconnect.\n const url = (currentConfig().push?.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n await ensureRelayEnrolled(url)\n }\n const auth: { token: string | null; tokenPath: string; devices: DeviceStore | null } = {\n token: null,\n tokenPath: cfg.authTokenPath ?? join(dataDir, 'auth-token'),\n devices: null,\n }\n const ready = (async () => {\n try {\n try {\n const migratedFrom = await migrateLegacyBridgeDataDir()\n if (migratedFrom !== null) log(`migrated legacy plugin state from ${migratedFrom} to ${dataDir}`)\n } catch (error) {\n log('legacy plugin-state migration skipped: ' + String(error))\n }\n auth.tokenPath = cfg.authTokenPath ?? join(dataDir, 'auth-token')\n auth.token = await loadOrCreateToken(auth.tokenPath)\n auth.devices = await DeviceStore.load(cfg.devicesPath ?? join(dataDir, 'devices.json'))\n {\n // Startup visibility: makes \"registrations vanished after restart\"\n // instantly diagnosable (0 push rows ⇒ the file itself lost data).\n const rows = auth.devices.list()\n const registered = rows.filter((row) => row.apns !== undefined).length\n log(`device registry loaded from ${expandHome(cfg.devicesPath ?? join(dataDir, 'devices.json'))}: ${rows.length} device(s), ${registered} push registration(s)`)\n }\n // Restore zero-touch push enrollment state (best effort).\n try {\n const raw = JSON.parse(await readFile(pushRelayPath, 'utf8')) as RelayEnrollmentCell\n if (typeof raw.clientId === 'string') enrollmentCell.clientId = raw.clientId\n if (typeof raw.enrollKey === 'string') enrollmentCell.enrollKey = raw.enrollKey\n if (typeof raw.token === 'string') enrollmentCell.token = raw.token\n if (raw.autoRelay === true) enrollmentCell.autoRelay = true\n } catch {\n // first boot: no enrollment yet\n }\n } catch (error) {\n log('auth material unavailable, bridge degraded: ' + String(error))\n return { token: null, devices: null }\n }\n return { token: auth.token, devices: auth.devices }\n })()\n\n /**\n * Replace the pairing secret: persist a fresh token, clear the paired-device\n * registry, and drop every live phone socket. Handshake-time auth means an\n * already-open socket would otherwise outlive its token; terminating forces\n * each device to re-pair with the new secret. The old token is invalid the\n * moment the file is rewritten.\n *\n * Serialized through rotateTail so two overlapping invocations can never\n * return a token that a later write already invalidated.\n */\n const doRotate = async (): Promise<string> => {\n const { devices } = await ready\n if (auth.token === null || devices === null) {\n throw new Error('pairing token unavailable')\n }\n auth.token = await writeNewToken(auth.tokenPath)\n devices.clear()\n let dropped = 0\n for (const connection of connections) {\n connection.terminate()\n dropped += 1\n }\n connections.clear()\n log(`pairing token rotated; ${dropped} live phone connection(s) dropped`)\n return auth.token\n }\n let rotateTail: Promise<unknown> = Promise.resolve()\n const rotatePairingToken = (): Promise<string> => {\n const next = rotateTail.then(doRotate)\n rotateTail = next.catch(() => {})\n return next\n }\n\n /**\n * Settings-page push self-test: force one synthetic notification down the\n * active pathway to EVERY registered device, deliberately ignoring the\n * connected-skip and category-mute filters — an explicit user action must\n * always be able to prove delivery end to end.\n */\n const runPushSelfTest = async (): Promise<PushTestResult> => {\n const resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok) {\n return {\n transport: 'none',\n overall: 'not-configured',\n message: '推送未启用(' + resolved.reason + ')。可先用「测试访问与注册」完成中继注册,或在配置中设置 push.provider',\n results: [],\n }\n }\n const tokenized = (auth.devices?.list() ?? []).filter((device) => device.apns !== undefined)\n if (!auth.devices || tokenized.length === 0) {\n return {\n transport: resolved.value.kind,\n overall: 'no-targets',\n message: '还没有设备注册离线推送——在手机上打开 DeepPilot 并允许系统通知,等状态变为「已就绪」后再试',\n results: [],\n }\n }\n const send = await senderFor(resolved.value)\n if (!send) {\n return { transport: resolved.value.kind, overall: 'failed', message: '发送通道不可用(检查 .p8 密钥文件或中继配置)', results: [] }\n }\n const notification: PushNotification = {\n notificationId: 'test-' + Date.now(),\n category: 'turn.completed',\n sessionId: 'push-test',\n title: 'DeepPilot 测试推送',\n body: '收到这条通知说明离线推送链路正常',\n }\n const results = await Promise.all(tokenized.map(async (device) => {\n const registration = device.apns!\n const { outcome, reason } = await send({\n deviceToken: registration.token,\n environment: registration.environment,\n notification,\n })\n return {\n name: device.deviceName,\n environment: registration.environment,\n outcome,\n // First 10 hex chars let the operator verify the stored token matches\n // what the device currently holds (tokens rotate on reinstall).\n tokenFingerprint: registration.token.slice(0, 10),\n ...(reason !== undefined ? { reason } : {}),\n }\n }))\n const overall: PushTestResult['overall'] = results.some((r) => r.outcome === 'sent') ? 'sent' : 'failed'\n log('push self-test: ' + overall + ' (' + results.map((r) => `\"${r.name}\"=${r.outcome}${r.reason ? '/' + r.reason : ''}`).join(', ') + ')')\n return { transport: resolved.value.kind, overall, results }\n }\n\n const connections = new Set<BridgeConnection>()\n\n // ---------- offline push outlet (F-9) ----------\n\n type PushConfigSnapshot =\n | { kind: 'apns'; teamId: string; keyId: string; keyPath: string; bundleId: string }\n | { kind: 'relay'; url: string; token: string }\n\n const resolvePushConfig = (config: Config): { ok: true; value: PushConfigSnapshot } | { ok: false; reason: string } => {\n const push = config.push ?? {}\n // Auto-enabled by an enrolled distributed app when the user made no\n // explicit provider choice.\n const configured = push.provider ?? 'none'\n const effectiveProvider = configured === 'none' && enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effectiveProvider === 'relay') {\n const url = (push.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n const token = (push.relayToken ?? '').trim() || enrollmentCell.token || ''\n if (!/^https:\\/\\//i.test(url)) return { ok: false, reason: 'relayUrl must be an https URL' }\n if (!token) return { ok: false, reason: 'relay token not enrolled yet' }\n return { ok: true, value: { kind: 'relay', url, token } }\n }\n if (effectiveProvider === 'apns') {\n const teamId = (push.teamId ?? '').trim()\n const keyId = (push.keyId ?? '').trim()\n const keyPath = expandHome((push.keyPath ?? '').trim() || join(dataDir, 'apns', 'AuthKey.p8'))\n const bundleId = (push.bundleId ?? '').trim()\n if (!teamId || !keyId || !bundleId) return { ok: false, reason: 'teamId/keyId/bundleId missing' }\n return { ok: true, value: { kind: 'apns', teamId, keyId, keyPath, bundleId } }\n }\n return { ok: false, reason: 'provider disabled' }\n }\n\n /**\n * Zero-touch enrollment against the operator's relay. Idempotent and\n * cached in the persistent cell; a failure disables push for this config\n * fingerprint with one log line until something changes.\n */\n let enrollAttemptFor: string | undefined\n let enrollLastAttemptAt = 0\n const ensureRelayEnrolled = async (url: string): Promise<string | undefined> => {\n if (enrollmentCell.token) return enrollmentCell.token\n const fingerprint = url + ':' + String(enrollmentCell.enrollKey ?? '')\n if (fingerprint !== enrollAttemptFor) {\n enrollAttemptFor = fingerprint\n enrollLastAttemptAt = 0\n }\n // Same fingerprint failing repeatedly: throttle to one attempt/minute so\n // a down relay cannot turn every notification into an outbound storm,\n // while transient failures still recover quickly.\n if (Date.now() - enrollLastAttemptAt < 60_000) return undefined\n enrollLastAttemptAt = Date.now()\n try {\n if (!enrollmentCell.clientId) {\n enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n persistEnrollment()\n }\n const client = new RelayClient({ url, debug: currentConfig().debug === true, log })\n const token = await client.enroll(enrollmentCell.clientId, enrollmentCell.enrollKey ?? '')\n if (!token) {\n log('push relay enrollment failed (' + url + '); will retry on next trigger')\n return undefined\n }\n enrollmentCell.token = token\n persistEnrollment()\n log('push relay enrollment succeeded')\n return token\n } catch (error) {\n log('push relay enrollment error: ' + String(error))\n return undefined\n }\n }\n\n interface SendOutcome { outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }\n type PushSender = (\n request: { deviceToken: string; environment: ApnsEnvironment; notification: Parameters<PushOutlet['fanOut']>[0] },\n ) => Promise<SendOutcome>\n\n interface CachedSender {\n fingerprint: string\n send: PushSender\n dispose?: () => Promise<void>\n }\n let cachedSender: CachedSender | undefined\n let senderFailedFor: string | undefined\n\n /**\n * Lazily build the push sender for the current config. A broken config\n * (unreadable .p8) disables push for that fingerprint with exactly one log\n * line instead of failing on every event.\n */\n const senderFor = async (resolved: PushConfigSnapshot): Promise<PushSender | undefined> => {\n const fingerprint = JSON.stringify(resolved)\n if (cachedSender?.fingerprint === fingerprint) return cachedSender.send\n if (senderFailedFor === fingerprint) return undefined\n if (cachedSender) {\n await cachedSender.dispose?.().catch(() => {})\n cachedSender = undefined\n }\n if (resolved.kind === 'relay') {\n // resolvePushConfig already guarantees a token exists in the cell\n // (config token or a completed enrollment).\n const client = new RelayClient({ url: resolved.url, token: resolved.token, debug: currentConfig().debug === true, log })\n cachedSender = {\n fingerprint,\n send: (request) => client.send(request),\n }\n log('push relay enabled')\n } else {\n try {\n await readFile(expandHome(resolved.keyPath), 'utf8')\n } catch (error) {\n senderFailedFor = fingerprint\n log('apns push unavailable (key unreadable at ' + resolved.keyPath + '): ' + String(error))\n return undefined\n }\n const client = new ApnsClient({\n teamId: resolved.teamId,\n keyId: resolved.keyId,\n keyPath: resolved.keyPath,\n bundleId: resolved.bundleId,\n debug: currentConfig().debug === true,\n log,\n })\n cachedSender = {\n fingerprint,\n send: (request) => client.send({\n ...request.notification,\n deviceToken: request.deviceToken,\n environment: request.environment,\n }),\n dispose: () => client.dispose(),\n }\n log('apns push enabled')\n }\n senderFailedFor = undefined\n return cachedSender.send\n }\n\n /**\n * Fan one notification-worthy event out to paired devices holding an APNs\n * token. Rules:\n * - devices with a live WebSocket are skipped (they already got the WS\n * frame and will raise the local notification themselves);\n * - each device is delivered on ITS registered environment (the build\n * kind it self-reported), so sandbox and production devices coexist;\n * - the device's per-category switches suppress muted categories;\n * - Unregistered/BadDeviceToken outcomes prune the stored token.\n */\n const makePushOutlet = (): PushOutlet => ({\n // The capability bit must tell the truth: only advertise push when the\n // provider is fully configured, otherwise clients would suppress their\n // local banners expecting a delivery that never happens.\n isAvailable: () => {\n const resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok) return false\n // Relay mode is only truly ready once enrollment produced a token;\n // advertising earlier would make clients suppress their local banners\n // for deliveries that cannot happen yet.\n if (resolved.value.kind === 'relay' && !resolved.value.token) return false\n return true\n },\n fanOut: (notification) => {\n void (async () => {\n let resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok && resolved.reason === 'relay token not enrolled yet') {\n // A registration carried the enrollKey but enrollment has not run —\n // try once now, then re-resolve.\n const relayUrl = (currentConfig().push?.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n await ensureRelayEnrolled(relayUrl)\n resolved = resolvePushConfig(currentConfig())\n }\n if (!resolved.ok) return\n const devices = auth.devices\n if (!devices) return\n const send = await senderFor(resolved.value)\n if (!send) return\n const transport = resolved.value.kind\n const connectedIds = new Set<string>()\n for (const connection of connections) {\n const id = connection.connectedDeviceId\n if (id) connectedIds.add(id)\n }\n // Observability first: push failures used to be completely silent\n // (outcomes were debug-gated), which made field diagnosis impossible.\n // Every dispatch and every skip now leaves one flat log line —\n // categories and outcomes only, never message bodies.\n const candidates = devices.list().filter((device) => {\n const registration = device.apns\n if (!registration) return false\n if (connectedIds.has(device.deviceId)) return false\n if (registration.categories?.[notification.category] === false) {\n if (currentConfig().debug === true) {\n log(`push skip \"${device.deviceName}\": category ${notification.category} muted`)\n }\n return false\n }\n return true\n })\n if (candidates.length === 0) {\n const tokenized = devices.list().filter((device) => device.apns !== undefined).length\n log(`push(${transport}) ${notification.category}: no offline targets (connected=${connectedIds.size}, tokenized=${tokenized})`)\n return\n }\n for (const device of candidates) {\n const registration = device.apns!\n void send({ deviceToken: registration.token, environment: registration.environment, notification })\n .then(({ outcome, reason }) => {\n log(`push(${transport}) ${notification.category} → \"${device.deviceName}\" [${registration.environment}] = ${outcome}${reason ? ' (' + reason + ')' : ''}`)\n if (outcome === 'invalid-token') {\n devices.clearPushToken(device.deviceId)\n log(`push: pruned stale token of \"${device.deviceName}\" (${reason ?? 'unknown'}) — app re-registers on next launch`)\n }\n })\n .catch(() => {})\n }\n })()\n },\n })\n\n\n const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_FRAME_BYTES })\n let remoteSupervisor: RemoteSupervisor | undefined\n const remoteStatus = (): RemoteStatus => remoteSupervisor?.status() ?? {\n provider: 'tailscale-funnel',\n phase: currentConfig().remote?.enabled === true ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n }\n\n // Typert Remote for the web settings page (deeppilot/report).\n applyReportRemote(ctx, async () => {\n let tokenReady = false\n let devices: Array<{ deviceId: string; deviceName: string; appVersion: string; firstSeenTs: number; lastSeenTs: number; apns?: { environment: 'development' | 'production'; updatedAt: number } }> = []\n try {\n await ready\n tokenReady = auth.token !== null\n // Strip the raw APNs token at the source; the report carries only the\n // registration fact (environment + freshness).\n devices = (auth.devices?.list() ?? []).map(({ deviceId, deviceName, appVersion, firstSeenTs, lastSeenTs, apns }) => ({\n deviceId,\n deviceName,\n appVersion,\n firstSeenTs,\n lastSeenTs,\n ...(apns ? { apns: { environment: apns.environment, updatedAt: apns.updatedAt } } : {}),\n }))\n } catch {\n // degraded: report the minimum without token facts\n }\n return {\n protocolVersion: 1,\n serverVersion: SERVER_VERSION,\n enabled: currentConfig().enabled === true,\n tokenPath: expandHome(currentConfig().authTokenPath ?? join(bridgeDataDir(), 'auth-token')),\n tokenReady,\n activeConnections: connections.size,\n historyBufferMax: currentConfig().historyBufferMax ?? 2000,\n debug: currentConfig().debug === true,\n lanAddresses: localLANIPv4Addresses(),\n remote: remoteStatus(),\n devices,\n }\n }, async () => {\n await ready\n if (auth.token === null) throw new Error('pairing token unavailable')\n return auth.token\n }, rotatePairingToken, async () => {\n const push = currentConfig().push ?? {}\n const configured = push.provider ?? 'none'\n const effective = configured === 'none' && enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effective !== 'relay') {\n return {\n url: '',\n overall: 'failed' as const,\n tokenIssued: false,\n steps: [{ id: 'health' as const, ok: false, message: `当前推送模式不是中继(provider=${configured})。启用方式二选一:① 零配置——在 ios/project.yml 填写 DSPushEnrollKey(与服务器 RELAY_ENROLL_KEY 一致)并重新安装 App,打开 App 即自动启用;② 手动——将 push.provider 设为 relay 并填入 relayToken` }],\n }\n }\n const url = (push.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n // The enroll step needs an identity; mint one now so a successful test\n // doubles as a completed enrollment.\n if (!enrollmentCell.clientId && enrollmentCell.enrollKey) {\n enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n persistEnrollment()\n }\n return await runRelayProbe({\n url,\n clientId: enrollmentCell.clientId,\n enrollKey: enrollmentCell.enrollKey,\n manualToken: Boolean((push.relayToken ?? '').trim()),\n onEnrolled: (token) => {\n enrollmentCell.token = token\n persistEnrollment()\n log('push relay enrollment succeeded (via settings self-test)')\n },\n })\n }, async () => {\n return await runPushSelfTest()\n })\n const state: { bridge?: HostBridge } = {}\n\n const handleUpgrade = (req: IncomingMessage, socket: Duplex, head: Buffer): void => {\n void (async () => {\n try {\n if (!enabledNow()) {\n rejectUpgrade(socket, 503, 'bridge disabled')\n return\n }\n if (connections.size >= MAX_CLIENT_CONNECTIONS) {\n rejectUpgrade(socket, 429, 'too many connections')\n return\n }\n const { devices } = await ready\n const token = auth.token\n if (!token || !devices) {\n rejectUpgrade(socket, 503, 'bridge degraded')\n return\n }\n const presentedToken = requestToken(req)\n if (presentedToken !== null && !tokenMatches(presentedToken, token)) {\n rejectUpgrade(socket, 401, 'invalid token')\n return\n }\n const bridge = state.bridge\n if (!bridge) {\n rejectUpgrade(socket, 503, 'bridge not ready')\n return\n }\n // Rotation may have swapped the secret while this handshake was in\n // flight; a connection validated against the previous token must not\n // survive it.\n if (auth.token !== token) {\n rejectUpgrade(socket, 401, 'invalid token')\n return\n }\n wss.handleUpgrade(req, socket, head, (ws) => {\n const connection = new BridgeConnection(ws, {\n bridge,\n devices,\n serverVersion: SERVER_VERSION,\n expectedToken: token,\n transportAuthenticated: presentedToken !== null,\n log,\n debug: currentConfig().debug === true,\n onClosed: (closed) => connections.delete(closed),\n onPushEnrollKey: handlePushEnrollKey,\n })\n connections.add(connection)\n })\n } catch (error) {\n log('upgrade failed: ' + String(error))\n rejectUpgrade(socket, 500, 'internal error')\n }\n })()\n }\n\n const handleHealth = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n await ready\n const token = auth.token\n res.setHeader('Content-Type', 'application/json')\n if (!token) {\n res.statusCode = 503\n res.end(JSON.stringify({ ok: false, degraded: true }))\n return\n }\n if (!tokenMatches(requestToken(req), token)) {\n res.statusCode = 401\n res.end(JSON.stringify({ ok: false }))\n return\n }\n res.statusCode = 200\n res.end(JSON.stringify({\n ok: true,\n enabled: enabledNow(),\n protocolVersion: 1,\n serverVersion: SERVER_VERSION,\n dataPlane: Boolean(state.bridge),\n }))\n } catch {\n res.statusCode = 500\n res.end(JSON.stringify({ ok: false }))\n }\n }\n\n // Data plane: consume mux/host streams once the API gateway exists.\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['apiProxy'],\n (sub) => {\n if (currentConfig().enabled !== true) {\n log('bridge disabled; data plane stays inactive')\n return\n }\n const apiCtx = sub as unknown as { apiProxy?: ApiProxyLike } & SubContext\n const proxy = apiCtx.apiProxy\n if (!proxy) {\n log('apiProxy service absent; data plane stays inactive')\n return\n }\n const bridge = new HostBridge(proxy, cfg.historyBufferMax)\n bridge.setPushOutlet(makePushOutlet())\n state.bridge = bridge\n bridge.start()\n log('data plane active (mux + host streams)')\n apiCtx.effect(() => () => bridge.dispose(), 'deeppilot: host streams')\n },\n )\n\n // Transport plane: /phone WebSocket + /phone/health probe.\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['webServer'],\n (sub) => {\n const webCtx = sub as unknown as { webServer?: WebServerLike } & SubContext\n const web = webCtx.webServer\n if (!web) {\n log('webServer service absent in this profile; bridge stays inactive')\n return\n }\n // Routes are registered regardless of the master switch so a mid-session\n // toggle always stays serviceable; every handler refuses work when the\n // bridge is currently disabled (evaluated per request so it is robust to\n // the settings scope attaching before or after webServer).\n webCtx.effect(\n () =>\n web.registerUpgrade({\n path: '/phone',\n handler: handleUpgrade,\n }),\n 'deeppilot: /phone WebSocket',\n )\n\n webCtx.effect(\n () =>\n web.register({\n kind: 'exact',\n path: '/phone/health',\n handler: handleHealth,\n }),\n 'deeppilot: /phone/health',\n )\n\n const sweep = setInterval(() => {\n const now = Date.now()\n for (const connection of connections) {\n if (connection.isStale(now, 60_000)) {\n log('dropping stale connection')\n connection.terminate()\n connections.delete(connection)\n }\n }\n }, 30_000)\n webCtx.effect(() => () => clearInterval(sweep), 'deeppilot: stale sweep')\n\n // The settings source is not guaranteed to attach before webServer.\n // Keep a loopback origin ready and reconcile the helper whenever that\n // source appears or changes, instead of freezing the schema defaults at\n // injection time.\n const originServer = createServer((req, res) => {\n let path = '/'\n try { path = new URL(req.url ?? '/', 'http://phone.local').pathname } catch { /* reject below */ }\n if (path === '/phone/health') {\n void handleHealth(req, res)\n } else {\n res.statusCode = 404\n res.end('not found')\n }\n })\n originServer.on('upgrade', (req, socket, head) => {\n let path = '/'\n try { path = new URL(req.url ?? '/', 'http://phone.local').pathname } catch { /* reject below */ }\n if (path !== '/phone') {\n socket.end('HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\n\\r\\n')\n return\n }\n handleUpgrade(req, socket, head)\n })\n\n let originURL: string | undefined\n let appliedRemoteKey: string | undefined\n let remoteDisposed = false\n let reconcileTail = Promise.resolve()\n const reconcileRemote = async (): Promise<void> => {\n if (remoteDisposed || originURL === undefined) return\n const config = currentConfig()\n const remoteConfig = config.remote ?? {}\n const remotePort: 443 | 8443 | 10000 = remoteConfig.funnelPort === 8443 || remoteConfig.funnelPort === 10000\n ? remoteConfig.funnelPort\n : 443\n const helperPath = remoteConfig.helperPath?.trim() || undefined\n const next = {\n enabled: config.enabled === true && remoteConfig.enabled === true && remoteConfig.provider === 'tailscale-funnel',\n hostname: normalizeRemoteHostname(remoteConfig.hostname),\n statePath: remoteConfig.statePath?.trim() || join(dataDir, 'tailscale'),\n helperPath,\n funnelPort: remotePort,\n }\n const nextKey = JSON.stringify(next)\n if (nextKey === appliedRemoteKey) return\n\n const previous = remoteSupervisor\n remoteSupervisor = undefined\n if (previous !== undefined) await previous.dispose()\n if (remoteDisposed) return\n\n const supervisor = new RemoteSupervisor({\n enabled: next.enabled,\n hostname: next.hostname,\n statePath: next.statePath,\n ...(next.helperPath ? { helperPath: next.helperPath } : {}),\n funnelPort: next.funnelPort,\n log,\n })\n remoteSupervisor = supervisor\n appliedRemoteKey = nextKey\n await supervisor.start(originURL)\n }\n scheduleRemoteReconcile = () => {\n reconcileTail = reconcileTail\n .then(reconcileRemote)\n .catch((error) => log('remote reconcile failed: ' + String(error)))\n }\n\n originServer.listen(0, '127.0.0.1', () => {\n const address = originServer.address()\n if (address && typeof address === 'object') {\n originURL = `http://127.0.0.1:${address.port}`\n scheduleRemoteReconcile?.()\n }\n })\n originServer.on('error', (error) => log('remote origin failed: ' + String(error)))\n webCtx.effect(() => () => {\n remoteDisposed = true\n scheduleRemoteReconcile = undefined\n originServer.close()\n reconcileTail = reconcileTail.then(async () => {\n const supervisor = remoteSupervisor\n remoteSupervisor = undefined\n if (supervisor !== undefined) await supervisor.dispose()\n })\n }, 'deeppilot: embedded Funnel')\n if (enabledNow()) {\n log('/phone WebSocket registered')\n } else {\n log('bridge disabled; /phone refuses connections until re-enabled and restarted')\n }\n },\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["cryptoSign","http2Connect","fsConstants","httpsRequest"],"sources":["../src/token.ts","../src/connection.ts","../src/host-bridge.ts","../src/report-service.ts","../src/report-wire.ts","../src/report-remote.ts","../src/relay-test.ts","../src/apns.ts","../src/relay-client.ts","../src/remote-supervisor.ts","../src/local-address.ts","../src/update-check.ts","../src/index.ts"],"sourcesContent":["import { randomBytes, timingSafeEqual } from 'node:crypto'\nimport { access, mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, resolve } from 'node:path'\n\n/** Expand a leading ~ using the process home directory. */\nexport function expandHome(p: string): string {\n if (p === '~') return homedir()\n if (p.startsWith('~/')) return resolve(homedir(), p.slice(2))\n return p\n}\n\nfunction dshDataRoot(): string {\n const dshHome = process.env.DSH_HOME\n if (dshHome && dshHome.trim().length > 0) return resolve(dshHome.trim())\n return resolve(homedir(), '.dsh')\n}\n\n/** DeepPilot data directory: under $DSH_HOME when set, else ~/.dsh. */\nexport function bridgeDataDir(): string {\n return resolve(dshDataRoot(), 'deeppilot')\n}\n\n/**\n * Move the pre-DeepPilot data directory as one atomic directory rename.\n * Existing canonical data always wins; secrets are never merged or replaced.\n */\nexport async function migrateLegacyBridgeDataDir(): Promise<string | null> {\n const target = bridgeDataDir()\n try {\n await access(target)\n return null\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n\n const legacy = resolve(dshDataRoot(), 'pocket-bridge')\n try {\n await rename(legacy, target)\n return legacy\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n}\n\n/**\n * Load the pairing token from disk or generate and persist a fresh one.\n * The file is written 0600; the token never appears in logs.\n */\nexport async function loadOrCreateToken(tokenPath: string): Promise<string> {\n const full = expandHome(tokenPath)\n try {\n const existing = (await readFile(full, 'utf8')).trim()\n if (existing.length >= 32) return existing\n } catch {\n // fall through to generation\n }\n const token = randomBytes(32).toString('base64url')\n await mkdir(dirname(full), { recursive: true })\n await writeFile(full, token + '\\n', { mode: 0o600 })\n return token\n}\n\n/**\n * Generate a fresh pairing token and replace the stored one, invalidating\n * every copy of the old secret. The write goes to a same-directory temp file\n * renamed over the target so a crash can never leave a truncated token file.\n */\nexport async function writeNewToken(tokenPath: string): Promise<string> {\n const full = expandHome(tokenPath)\n const token = randomBytes(32).toString('base64url')\n await mkdir(dirname(full), { recursive: true })\n const temp = `${full}.${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temp, token + '\\n', { mode: 0o600 })\n await rename(temp, full)\n return token\n}\n\n/** Constant-time token comparison; both sides are high-entropy secrets. */\nexport function tokenMatches(presented: string | null | undefined, expected: string): boolean {\n if (!presented) return false\n const a = Buffer.from(presented)\n const b = Buffer.from(expected)\n if (a.length !== b.length) {\n // Burn a same-length comparison so wrong-length probes keep flat timing.\n timingSafeEqual(b, b)\n return false\n }\n return timingSafeEqual(a, b)\n}\n\nexport type ApnsEnvironment = 'development' | 'production'\n\n/** Per-device APNs registration (F-9 离线推送). The raw token never leaves the registry file. */\nexport interface DeviceApnsInfo {\n /** Hex device token, lowercased, 32–512 chars. */\n token: string\n environment: ApnsEnvironment\n updatedAt: number\n /** Per-category opt-out mirror; absent keys mean enabled. */\n categories?: Record<string, boolean>\n}\n\nexport interface DeviceRecord {\n deviceId: string\n deviceName: string\n appVersion: string\n firstSeenTs: number\n lastSeenTs: number\n apns?: DeviceApnsInfo\n}\n\n/** Hex shape of an APNs device token as delivered by iOS (usually 64 chars). */\nconst APNS_TOKEN_PATTERN = /^[0-9a-f]{32,512}$/\n\nexport function isValidApnsToken(token: unknown): token is string {\n return typeof token === 'string' && APNS_TOKEN_PATTERN.test(token)\n}\n\n/** Registry row cap: hello spam with fresh ids must not grow the file forever. */\nexport const MAX_DEVICES = 64\n\n/**\n * Paired-device registry persisted as one JSON document. Whole-document\n * writes (serialized, never interleaved); a corrupt file falls back to an\n * empty registry rather than failing the plugin.\n */\nexport class DeviceStore {\n private devices = new Map<string, DeviceRecord>()\n private flushTail: Promise<void> = Promise.resolve()\n\n private constructor(private readonly filePath: string) {}\n\n static async load(filePath: string): Promise<DeviceStore> {\n const store = new DeviceStore(filePath)\n try {\n const raw = JSON.parse(await readFile(expandHome(filePath), 'utf8')) as {\n devices?: DeviceRecord[]\n }\n for (const rec of raw.devices ?? []) {\n if (typeof rec.deviceId === 'string') store.devices.set(rec.deviceId, rec)\n }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code\n if (code === 'ENOENT') {\n // First run on this home: nothing registered yet — normal.\n } else {\n // A truncated/corrupt registry silently wiping pairings and push\n // registrations would be indistinguishable from \"never registered\".\n // Make data loss loud so the cause (crash mid-write, disk, …) shows.\n console.log('[deeppilot] device registry unreadable, starting empty: ' + String(error))\n }\n }\n return store\n }\n\n touch(record: Omit<DeviceRecord, 'firstSeenTs' | 'lastSeenTs'>, now: number): void {\n const existing = this.devices.get(record.deviceId)\n if (existing) {\n existing.lastSeenTs = now\n existing.deviceName = record.deviceName || existing.deviceName\n existing.appVersion = record.appVersion || existing.appVersion\n } else {\n if (this.devices.size >= MAX_DEVICES) {\n // Evict the least-recently-seen device to make room for the newcomer.\n let oldestId: string | undefined\n let oldestTs = Number.POSITIVE_INFINITY\n for (const [id, value] of this.devices) {\n if (value.lastSeenTs < oldestTs) {\n oldestTs = value.lastSeenTs\n oldestId = id\n }\n }\n if (oldestId !== undefined) this.devices.delete(oldestId)\n }\n this.devices.set(record.deviceId, {\n ...record,\n firstSeenTs: now,\n lastSeenTs: now,\n })\n }\n void this.flush()\n }\n\n list(): DeviceRecord[] {\n return [...this.devices.values()]\n }\n\n /**\n * Store (or refresh) the APNs registration of a paired device. Idempotent:\n * an unchanged registration does not rewrite the registry file, so the\n * app's re-register-on-every-handshake policy stays write-quiet.\n */\n setPushToken(\n deviceId: string,\n token: string,\n environment: ApnsEnvironment,\n categories: Record<string, boolean> | undefined,\n now: number,\n ): void {\n const normalized = token.toLowerCase()\n if (!isValidApnsToken(normalized)) return\n let record = this.devices.get(deviceId)\n if (!record) {\n // A live authenticated socket always has a row (hello touches it), but\n // a rotation could clear the registry mid-session; self-heal instead of\n // dropping the registration.\n record = { deviceId, deviceName: 'unknown', appVersion: 'unknown', firstSeenTs: now, lastSeenTs: now }\n this.devices.set(deviceId, record)\n }\n const next: DeviceApnsInfo = { token: normalized, environment, updatedAt: now }\n if (categories && typeof categories === 'object') {\n const clean: Record<string, boolean> = {}\n for (const [key, value] of Object.entries(categories)) {\n if (/^[a-z.]{1,64}$/.test(key) && typeof value === 'boolean') clean[key] = value\n }\n if (Object.keys(clean).length > 0) next.categories = clean\n }\n const current = record.apns\n if (\n current &&\n current.token === next.token &&\n current.environment === next.environment &&\n JSON.stringify(current.categories ?? {}) === JSON.stringify(next.categories ?? {})\n ) {\n return\n }\n record.apns = next\n void this.flush()\n }\n\n /** Drop a device's APNs registration (APNs reported the token unregistered). */\n clearPushToken(deviceId: string): void {\n const record = this.devices.get(deviceId)\n if (!record?.apns) return\n delete record.apns\n void this.flush()\n }\n\n /**\n * Drop every paired-device record. Used by token rotation: devices paired\n * under the old token can no longer authenticate, so keeping their rows\n * would paint a misleading \"still paired\" picture.\n */\n clear(): void {\n this.devices.clear()\n void this.flush()\n }\n\n /** Serialized so concurrent touches can never interleave half-written JSON. */\n private flush(): Promise<void> {\n const next = this.flushTail.then(() => this.writeFile())\n this.flushTail = next.catch(() => {\n // best-effort persistence; the bridge still works without it\n })\n return next\n }\n\n /** Resolves once every queued registry write has landed (test support). */\n async drain(): Promise<void> {\n await this.flushTail\n }\n\n private async writeFile(): Promise<void> {\n const full = expandHome(this.filePath)\n const body = JSON.stringify(\n { version: 1, devices: this.list() },\n null,\n 2,\n )\n try {\n await mkdir(dirname(full), { recursive: true })\n // Atomic replace: a restart mid-write previously left a truncated JSON\n // behind, which load() treats as corrupt and drops ENTIRELY — losing\n // pairings and push registrations on every unlucky shutdown.\n const temp = `${full}.${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temp, body + '\\n', { mode: 0o600 })\n await rename(temp, full)\n } catch {\n // best-effort persistence; the bridge still works without it\n }\n }\n}\n","import type { WebSocket } from 'ws'\nimport {\n PROTOCOL_VERSION,\n} from './protocol.ts'\nimport type { Envelope, HelloAuthPayload } from './protocol.ts'\nimport type { BridgeSink, HostBridge } from './host-bridge.ts'\nimport type { DeviceStore, ApnsEnvironment } from './token.ts'\nimport { isValidApnsToken, tokenMatches } from './token.ts'\n\nexport const AUTH_TIMEOUT_MS = 5_000\nconst IMAGE_MEDIA_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp', 'image/gif'])\nconst MAX_PROMPT_IMAGES = 4\nconst MAX_BASE64_CHARS_PER_IMAGE = 8 * 1024 * 1024\n/** Bounds a single prompt's text; the frame itself is capped by ws maxPayload. */\nconst MAX_PROMPT_TEXT_CHARS = 256 * 1024\n// Client-supplied identity fields land in logs and devices.json — keep them\n// short and free of control characters so they can neither flood the registry\n// nor forge log lines.\nconst MAX_DEVICE_ID_CHARS = 128\nconst MAX_DEVICE_NAME_CHARS = 64\nconst MAX_APP_VERSION_CHARS = 32\n\nfunction sanitizeDeviceField(value: unknown, maxChars: number): string {\n const raw = typeof value === 'string' ? value : String(value ?? '')\n return raw.replace(/[\\u0000-\\u001f\\u007f]/g, ' ').trim().slice(0, maxChars)\n}\n\nexport interface ConnectionStateDeps {\n bridge: HostBridge\n devices: DeviceStore\n serverVersion: string\n /** Pairing secret used when the HTTP upgrade was not already authenticated. */\n expectedToken: string\n /** True when Authorization or the legacy query parameter passed at upgrade. */\n transportAuthenticated?: boolean\n debug?: boolean\n log: (message: string) => void\n /** Called once when the socket closes (cleanly or not). */\n onClosed?: (connection: BridgeConnection) => void\n /**\n * Zero-touch push enrollment: fired when a distributed app presents the\n * distributor's shared enrollKey during c2s.push.register. May perform the\n * relay round-trip; the register handler awaits it before evaluating push\n * readiness, so the very first registration can switch the feature on.\n */\n onPushEnrollKey?: (enrollKey: string) => Promise<void> | void\n}\n\nexport function helloTokenAccepted(\n transportAuthenticated: boolean | undefined,\n presentedToken: string | undefined,\n expectedToken: string,\n): boolean {\n return transportAuthenticated === true || tokenMatches(presentedToken, expectedToken)\n}\n\n/**\n * One connected phone. Implements BridgeSink so the HostBridge can push\n * projected frames and replays. Bearer/query credentials may authenticate the\n * HTTP upgrade; otherwise the first hello frame is verified here.\n */\nexport class BridgeConnection implements BridgeSink {\n private authenticated = false\n private helloTimer: NodeJS.Timeout | undefined\n private readonly openSessions = new Set<string>()\n /** Sanitized device identity from hello; needed for push registration. */\n private deviceId: string | undefined\n\n constructor(\n private readonly ws: WebSocket,\n private readonly deps: ConnectionStateDeps,\n ) {\n ws.on('message', (data) => {\n void this.onMessage(String(data))\n })\n ws.on('close', () => {\n this.onClose()\n deps.onClosed?.(this)\n })\n ws.on('error', () => {\n /* close follows */\n })\n this.helloTimer = setTimeout(() => {\n if (!this.authenticated) this.close(4402, 'auth timeout')\n }, AUTH_TIMEOUT_MS)\n }\n\n /** Hard-drop the socket (server-side stale sweep). */\n terminate(): void {\n this.ws.terminate()\n }\n\n /** Device identity once hello succeeded; undefined before that. */\n get connectedDeviceId(): string | undefined {\n return this.authenticated ? this.deviceId : undefined\n }\n\n // ---------- BridgeSink ----------\n\n push(type: string, payload: unknown, seq?: number): void {\n if (this.deps.debug === true) this.deps.log('push ' + type + ' seq=' + String(seq))\n this.send(type, payload, undefined, seq)\n }\n\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void {\n for (const entry of entries) this.push(entry.type, entry.payload, entry.seq)\n }\n\n replayDone(): void {\n this.push('s2c.resume.done', {})\n }\n\n resync(): void {\n this.push('s2c.resync', { reason: 'gap' })\n }\n\n lastCursor(): number {\n return this.deps.bridge.currentCursor()\n }\n\n // ---------- lifecycle ----------\n\n private onClose(): void {\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n for (const id of this.openSessions) {\n this.deps.bridge.markSinkClosed(this, id)\n }\n this.openSessions.clear()\n this.deps.bridge.dropSinkSessions(this)\n if (this.authenticated) this.deps.bridge.removeSink(this)\n }\n\n private close(code: number, reason: string): void {\n try {\n this.ws.close(code, reason)\n } catch {\n this.ws.terminate()\n }\n }\n\n private send(type: string, payload: unknown, id?: string, seq?: number): void {\n const envelope: Envelope = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n ...(id !== undefined ? { id } : {}),\n ...(seq !== undefined ? { seq } : {}),\n payload,\n }\n if (this.ws.readyState === this.ws.OPEN) this.ws.send(JSON.stringify(envelope))\n }\n\n private fail(id: string | undefined, code: keyof typeof ERROR_CODES, message: string): void {\n this.send('s2c.error', { code, message }, id)\n }\n\n // ---------- dispatch ----------\n\n private lastActivity = Date.now()\n\n /** True when no inbound frame arrived within maxIdleMs. */\n isStale(now: number, maxIdleMs: number): boolean {\n return now - this.lastActivity > maxIdleMs\n }\n\n private async onMessage(raw: string): Promise<void> {\n this.lastActivity = Date.now()\n let env: Envelope\n try {\n env = JSON.parse(raw) as Envelope\n } catch {\n this.fail(undefined, 'E_PROTOCOL', 'frame is not valid JSON')\n return\n }\n if (env.v !== PROTOCOL_VERSION) {\n this.fail(env.id, 'E_UNSUPPORTED', 'unsupported protocol version')\n this.close(4500, 'protocol version mismatch')\n return\n }\n if (!this.authenticated) {\n if (env.type === 'c2s.ping') {\n this.send('s2c.pong', { serverTime: Date.now() }, env.id)\n return\n }\n if (env.type === 'c2s.hello.auth') {\n await this.hello(env)\n return\n }\n this.fail(env.id, 'E_PROTOCOL', 'authenticate first')\n return\n }\n switch (env.type) {\n case 'c2s.ping': {\n this.send('s2c.pong', { serverTime: Date.now() }, env.id)\n return\n }\n case 'c2s.sessions.list': {\n this.send('s2c.sessions.snapshot', { full: true, sessions: this.deps.bridge.listSessions() }, env.id)\n return\n }\n case 'c2s.pending.list': {\n this.send('s2c.pending.snapshot', this.deps.bridge.pendingSnapshot(), env.id)\n return\n }\n case 'c2s.workspaces.list': {\n if (!this.deps.bridge.capabilities.projectSelection) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'project selection unavailable on this host version')\n }\n const result = await this.deps.bridge.listWorkspaces()\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.workspaces.snapshot', { workspaces: result.value }, env.id)\n return\n }\n case 'c2s.directory.list': {\n const p = env.payload as { path?: unknown } | undefined\n if (p?.path !== undefined && typeof p.path !== 'string') {\n return this.fail(env.id, 'E_PROTOCOL', 'path must be a string')\n }\n const result = await this.deps.bridge.listDirectory(p?.path as string | undefined)\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.directory.listing', result.value, env.id)\n return\n }\n case 'c2s.directory.pick': {\n const result = await this.deps.bridge.pickDirectory()\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.directory.picked', { path: result.value }, env.id)\n return\n }\n case 'c2s.workspace.create': {\n const p = env.payload as { path?: unknown } | undefined\n const path = typeof p?.path === 'string' ? p.path.trim() : ''\n if (!path) return this.fail(env.id, 'E_PROTOCOL', 'non-empty path required')\n if (!this.deps.bridge.capabilities.projectSelection) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'project selection unavailable on this host version')\n }\n const result = await this.deps.bridge.createWorkspace(path)\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.workspace.created', result.value, env.id)\n return\n }\n case 'c2s.session.open': {\n const p = env.payload as { sessionId?: string; tailCount?: number }\n if (!p?.sessionId || typeof p.sessionId !== 'string') return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n const sessionId = p.sessionId\n this.openSessions.add(sessionId)\n this.deps.bridge.markSinkOpen(this, sessionId)\n const ok = await this.deps.bridge.openSession(this, sessionId, p.tailCount ?? 100)\n if (!ok) {\n // Roll the viewer registration back: a failed open must not keep\n // suppressing turn notifications for a session this device never\n // actually received.\n this.openSessions.delete(sessionId)\n this.deps.bridge.markSinkClosed(this, sessionId)\n return this.fail(env.id, 'E_NOT_FOUND', 'session history unavailable')\n }\n return\n }\n case 'c2s.session.close': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n this.openSessions.delete(p.sessionId)\n this.deps.bridge.markSinkClosed(this, p.sessionId)\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.session.create': {\n const p = env.payload as { workspaceId?: unknown; cwd?: unknown } | undefined\n const workspaceId = typeof p?.workspaceId === 'string' ? p.workspaceId.trim() : ''\n const cwd = typeof p?.cwd === 'string' ? p.cwd.trim() : ''\n if (workspaceId && cwd) return this.fail(env.id, 'E_PROTOCOL', 'workspaceId and cwd are mutually exclusive')\n const newId = await this.deps.bridge.createSession({\n ...(workspaceId ? { workspaceId } : {}),\n ...(cwd ? { cwd } : {}),\n })\n if (!newId) return this.fail(env.id, 'E_INTERNAL', 'session create failed')\n this.send('s2c.ack', { sessionId: newId }, env.id)\n return\n }\n case 'c2s.session.rename': {\n const p = env.payload as { sessionId?: string; title?: string }\n const title = typeof p?.title === 'string' ? p.title.trim() : ''\n if (!p?.sessionId || title.length === 0) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and non-empty title required')\n }\n if (!this.deps.bridge.capabilities.sessionManagement) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'session management unavailable on this host version')\n }\n const result = await this.deps.bridge.renameSession(p.sessionId, title)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.renamed', { sessionId: p.sessionId, title: result.value }, env.id)\n return\n }\n case 'c2s.session.archive': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n if (!this.deps.bridge.capabilities.sessionManagement) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'session management unavailable on this host version')\n }\n const result = await this.deps.bridge.archiveSession(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.archived', { sessionId: p.sessionId }, env.id)\n return\n }\n case 'c2s.session.cancel': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n const result = await this.deps.bridge.cancelSession(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.ack', { sessionId: p.sessionId }, env.id)\n return\n }\n case 'c2s.session.history': {\n const p = env.payload as { sessionId?: string; beforeSeq?: number; limit?: number }\n if (!p?.sessionId || typeof p.beforeSeq !== 'number') {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and beforeSeq required')\n }\n const ok = await this.deps.bridge.historyPage(this, p.sessionId, p.beforeSeq, Math.min(p.limit ?? 100, 500))\n if (!ok) this.fail(env.id, 'E_NOT_FOUND', 'history unavailable')\n return\n }\n case 'c2s.session.attachment': {\n const p = env.payload as { sessionId?: string; attachmentId?: string }\n if (!p?.sessionId || typeof p.attachmentId !== 'string' || p.attachmentId.length === 0) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and attachmentId required')\n }\n const image = await this.deps.bridge.attachmentData(p.sessionId, p.attachmentId)\n if (!image) return this.fail(env.id, 'E_NOT_FOUND', 'attachment unavailable')\n this.send('s2c.ack', image, env.id)\n return\n }\n case 'c2s.session.models': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n if (!this.deps.bridge.capabilities.models) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'model selection unavailable on this host version')\n }\n const result = await this.deps.bridge.sessionModels(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'unavailable' ? 'E_NOT_FOUND'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.models', { sessionId: p.sessionId, ...result.value }, env.id)\n return\n }\n case 'c2s.session.selectModel': {\n const p = env.payload as {\n sessionId?: string\n provider?: string\n model?: string\n reasoningEffort?: string\n }\n if (!p?.sessionId || !p.provider?.trim() || !p.model?.trim()) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId, provider and model required')\n }\n if (!this.deps.bridge.capabilities.models) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'model selection unavailable on this host version')\n }\n const result = await this.deps.bridge.selectSessionModel(p.sessionId, {\n provider: p.provider.trim(),\n model: p.model.trim(),\n ...(p.reasoningEffort?.trim() ? { reasoningEffort: p.reasoningEffort.trim() } : {}),\n })\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'unavailable' ? 'E_NOT_FOUND'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.modelSelected', { sessionId: p.sessionId, selected: result.value }, env.id)\n return\n }\n case 'c2s.session.sendPrompt': {\n const p = env.payload as {\n sessionId?: string\n text?: string\n images?: Array<{ mediaType?: string; data?: string; name?: string }>\n }\n const text = typeof p?.text === 'string' ? p.text : ''\n const rawImages = Array.isArray(p?.images) ? p.images : []\n if (!p?.sessionId || (text.trim().length === 0 && rawImages.length === 0)) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and text or images required')\n }\n if (text.length > MAX_PROMPT_TEXT_CHARS) {\n return this.fail(env.id, 'E_PROTOCOL', 'prompt text too long')\n }\n if (rawImages.length > MAX_PROMPT_IMAGES) {\n return this.fail(env.id, 'E_PROTOCOL', 'too many images')\n }\n const images: Array<{\n mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'\n data: string\n name?: string\n }> = []\n for (const image of rawImages) {\n if (!IMAGE_MEDIA_TYPES.has(String(image?.mediaType)) ||\n typeof image?.data !== 'string' || image.data.length === 0 ||\n image.data.length > MAX_BASE64_CHARS_PER_IMAGE) {\n return this.fail(env.id, 'E_PROTOCOL', 'invalid image attachment')\n }\n images.push({\n mediaType: image.mediaType as 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif',\n data: image.data,\n ...(typeof image.name === 'string' && image.name.trim().length > 0\n ? { name: image.name.trim().slice(0, 120) }\n : {}),\n })\n }\n const userSeq = await this.deps.bridge.sendPrompt(p.sessionId, text, images)\n if (!userSeq.ok) return this.fail(env.id, managementErrorCode(userSeq.kind), userSeq.message)\n this.send('s2c.ack', { userSeq: userSeq.value }, env.id)\n return\n }\n case 'c2s.approval.respond': {\n const p = env.payload as { requestId?: string; decision?: string; reason?: string }\n if (!p?.requestId || (p.decision !== 'allow' && p.decision !== 'deny')) {\n return this.fail(env.id, 'E_PROTOCOL', 'requestId and decision required')\n }\n // PROTOCOL v1: the optional deny reason must reach the host so the\n // model learns why its tool call was refused.\n const outcome = await this.deps.bridge.respondApproval(\n p.requestId,\n p.decision,\n typeof p.reason === 'string' ? p.reason : undefined,\n )\n if (!outcome.ok) return this.fail(env.id, pendingResponseErrorCode(outcome.reason), pendingResponseMessage('approval', outcome.reason))\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.question.respond': {\n const p = env.payload as { requestId?: string; answers?: unknown }\n if (!p?.requestId || !Array.isArray(p.answers)) {\n return this.fail(env.id, 'E_PROTOCOL', 'requestId and answers required')\n }\n const outcome = await this.deps.bridge.respondQuestion(p.requestId, p.answers)\n if (!outcome.ok) return this.fail(env.id, pendingResponseErrorCode(outcome.reason), pendingResponseMessage('question', outcome.reason))\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.push.register': {\n const p = env.payload as { deviceToken?: unknown; environment?: unknown; categories?: unknown; enrollKey?: unknown }\n const token = typeof p?.deviceToken === 'string' ? p.deviceToken.trim() : ''\n if (!isValidApnsToken(token)) {\n return this.fail(env.id, 'E_PROTOCOL', 'hex deviceToken (32-512 chars) required')\n }\n const environment: ApnsEnvironment = p?.environment === 'production' ? 'production' : 'development'\n const categories = typeof p?.categories === 'object' && p.categories !== null\n ? p.categories as Record<string, boolean>\n : undefined\n if (!this.deviceId || !this.authenticated) {\n return this.fail(env.id, 'E_PROTOCOL', 'authenticate first')\n }\n // Zero-touch enrollment MUST run before the capability gate: a fresh,\n // unconfigured bridge receives its first register precisely when push\n // is not yet enabled, and the key itself flips relay mode on. The\n // relay round-trip is awaited so readiness reflects reality.\n if (typeof p?.enrollKey === 'string') {\n const enrollKey = p.enrollKey.trim().replace(/[^\\x20-\\x7e]/g, '').slice(0, 128)\n if (enrollKey.length >= 8 && enrollKey.length <= 128) {\n await this.deps.onPushEnrollKey?.(enrollKey)\n }\n }\n // Store the registration BEFORE the readiness gate: if push is still\n // mid-bootstrap (or the relay is temporarily down), the token is\n // already on file for when it recovers — no extra register needed.\n this.deps.devices.setPushToken(this.deviceId, token, environment, categories, Date.now())\n if (!this.deps.bridge.capabilities.push) {\n if (this.deps.debug === true) this.deps.log('push register held: bridge not ready')\n return this.fail(env.id, 'E_UNSUPPORTED', 'push is not configured on this bridge')\n }\n if (this.deps.debug === true) {\n this.deps.log('push token registered env=' + environment)\n }\n // `enabled` lets the app flip its local capability immediately instead\n // of waiting for the next handshake.\n this.send('s2c.ack', { enabled: true }, env.id)\n return\n }\n default:\n this.fail(env.id, 'E_PROTOCOL', 'unknown type: ' + env.type)\n }\n }\n\n private async hello(env: Envelope): Promise<void> {\n const p = (env.payload ?? {}) as Partial<HelloAuthPayload>\n if (!helloTokenAccepted(this.deps.transportAuthenticated, p.token, this.deps.expectedToken)) {\n this.fail(env.id, 'E_AUTH', 'token missing or invalid')\n this.close(4401, 'invalid token')\n return\n }\n if (!p.deviceId) {\n this.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.close(4403, 'deviceId required')\n return\n }\n const deviceId = sanitizeDeviceField(p.deviceId, MAX_DEVICE_ID_CHARS)\n if (!deviceId) {\n this.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.close(4403, 'deviceId required')\n return\n }\n const deviceName = sanitizeDeviceField(p.deviceName, MAX_DEVICE_NAME_CHARS) || 'unknown'\n const appVersion = sanitizeDeviceField(p.appVersion, MAX_APP_VERSION_CHARS) || 'unknown'\n this.authenticated = true\n this.deviceId = deviceId\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n this.deps.devices.touch({ deviceId, deviceName, appVersion }, Date.now())\n this.deps.log('device paired: ' + deviceName + ' (' + deviceId + ')')\n\n const cursor = typeof p.resumeCursor === 'number' && p.resumeCursor >= 0\n ? p.resumeCursor\n : undefined\n const canResume = cursor !== undefined && this.deps.bridge.canResumeFrom(cursor)\n // Welcome strictly precedes any replayed pushes.\n this.send('s2c.welcome', {\n protocolVersion: PROTOCOL_VERSION,\n serverVersion: this.deps.serverVersion,\n capabilities: this.deps.bridge.capabilities,\n cursor: this.deps.bridge.currentCursor(),\n resumed: canResume,\n }, env.id)\n this.deps.bridge.addSink(this)\n if (cursor !== undefined) {\n if (canResume) {\n this.deps.bridge.resumeFrom(cursor, this)\n } else {\n this.resync()\n }\n }\n }\n}\n\nconst ERROR_CODES = {\n E_AUTH: 'token missing or invalid',\n E_PROTOCOL: 'unknown type or malformed payload',\n E_NOT_FOUND: 'session or request not found',\n E_BUSY: 'session is busy',\n E_UNSUPPORTED: 'protocol version or capability unsupported',\n E_INTERNAL: 'internal error',\n} as const\n\n/** Error code for a failed approval/question response outcome. */\nfunction pendingResponseErrorCode(reason: 'not-pending' | 'bad-response' | 'transport'): keyof typeof ERROR_CODES {\n switch (reason) {\n case 'not-pending': return 'E_NOT_FOUND'\n // The host refused the answer batch (shape/labels mismatch) — a client\n // payload problem, not a missing pending request.\n case 'bad-response': return 'E_PROTOCOL'\n case 'transport': return 'E_INTERNAL'\n }\n}\n\n/** Human-readable failure detail; `question not pending` must only ever mean\n * \"nothing pending\", never \"the host rejected the answer\". */\nfunction pendingResponseMessage(\n kind: 'approval' | 'question',\n reason: 'not-pending' | 'bad-response' | 'transport',\n): string {\n switch (reason) {\n case 'not-pending': return kind + ' not pending'\n case 'bad-response': return kind + ' answer rejected by host: answer does not match the asked questions'\n case 'transport': return 'host connection failed while answering ' + kind\n }\n}\n\nfunction managementErrorCode(\n kind: 'unsupported' | 'not-found' | 'busy' | 'invalid' | 'internal',\n): keyof typeof ERROR_CODES {\n switch (kind) {\n case 'unsupported': return 'E_UNSUPPORTED'\n case 'not-found': return 'E_NOT_FOUND'\n case 'busy': return 'E_BUSY'\n case 'invalid': return 'E_PROTOCOL'\n case 'internal': return 'E_INTERNAL'\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { Envelope, MessageProjection, NotifyCategory, PendingSnapshotPayload, PushNotification, SessionEventKind, SessionSummary, SessionTodoItem, SessionTodoStatus } from './protocol.ts'\n\n/* Minimal structural faces over the host apiProxy service. The real types\n * live in @deepseek-ai/dsh-host-apiproxy; keeping them structural lets this\n * plugin compile and degrade independently of host package versions. */\n\ninterface RpcOk<T> { ok: true; value: T }\ninterface RpcErr { ok: false; error: { code: string; message?: string } }\ntype RpcResult<T> = RpcOk<T> | RpcErr\n\ninterface RpcRequestLike<T> { rpcId?: string; payload?: T }\ninterface RpcResponseLike<T> { result?: RpcResult<T> }\n\ninterface SessionsApiLike {\n list(req?: RpcRequestLike<{ cursor?: string }>): Promise<RpcResponseLike<{ items: PhoneSessionRow[] }>>\n history(req: RpcRequestLike<{ sessionId: string; beforeSeq?: number; maxMessages?: number }>):\n Promise<RpcResponseLike<HistoryResult>>\n prompt(req: RpcRequestLike<PromptArgs>): Promise<RpcResponseLike<{ accepted: true }>>\n create(req: RpcRequestLike<{ workspaceId?: string; cwd?: string; agentPreset?: string }>): Promise<RpcResponseLike<{ sessionId: string; agentPreset?: string }>>\n models?(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<HostSessionModels>>\n selectModel?(req: RpcRequestLike<{\n sessionId: string\n provider: string\n model: string\n reasoningEffort?: string\n }>): Promise<RpcResponseLike<{ selected: HostModelSelection }>>\n rename?(req: RpcRequestLike<{ sessionId: string; title: string }>):\n Promise<RpcResponseLike<{ title: string; seq: number }>>\n cancel?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<Record<string, unknown> | undefined>>\n /** Reads one durable image back after the host verifies the session log references its id. */\n attachment?(req: RpcRequestLike<{ sessionId: string; attachmentId: string }>):\n Promise<RpcResponseLike<{ attachment: { mediaType?: string }; data: string }>>\n}\n\ninterface WorkspaceApiLike {\n list?(req: RpcRequestLike<Record<string, never>>):\n Promise<RpcResponseLike<{ items: WorkspaceViewLike[]; archivedSessionIds: string[] }>>\n create?(req: RpcRequestLike<{ path: string }>):\n Promise<RpcResponseLike<{ workspace: WorkspaceViewLike; created: boolean }>>\n archiveSession?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ archivedSessionIds: string[] }>>\n}\n\ninterface HostApiLike {\n listDirectory?(req: RpcRequestLike<{ path?: string }>, signal?: AbortSignal):\n Promise<RpcResponseLike<DirectoryListingLike>>\n pickDirectory?(req: RpcRequestLike<Record<string, never>>, signal?: AbortSignal):\n Promise<RpcResponseLike<{ path: string | null }>>\n}\n\ninterface WorkspaceViewLike {\n workspaceId: string\n path: string\n title: string\n sessionIds: string[]\n createdAt?: string\n updatedAt?: string\n}\n\nexport interface DirectoryEntryLike {\n name: string\n path: string\n hidden: boolean\n}\n\nexport interface DirectoryListingLike {\n path: string\n home: string\n crumbs: DirectoryEntryLike[]\n entries: DirectoryEntryLike[]\n truncated: boolean\n}\n\ninterface HostModelSelection {\n provider: string\n model: string\n reasoningEffort?: string\n}\n\ninterface HostSessionModels {\n current: HostModelSelection\n routable: boolean\n groups: Array<{\n id: string\n name: string\n models: Array<{\n id: string\n name: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n }>\n }>\n failures: Array<{ id: string; name: string; message: string }>\n}\n\nexport type ModelBridgeResult<T> =\n | { ok: true; value: T }\n | { ok: false; kind: 'unsupported' | 'not-found' | 'busy' | 'unavailable' | 'internal'; message: string }\n\nexport type SessionManagementResult<T> =\n | { ok: true; value: T }\n | { ok: false; kind: 'unsupported' | 'not-found' | 'busy' | 'invalid' | 'internal'; message: string }\n\ninterface PromptArgs {\n sessionId: string\n mode: 'queue' | 'steer'\n content: Array<\n | { type: 'text'; text: string }\n | { type: 'image'; mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }\n >\n clientTimeZone?: string\n}\n\ninterface PhoneSessionRow {\n sessionId: string\n updatedAt: number\n running: boolean\n blank: boolean\n cwd?: string\n origin?: string\n parentSessionId?: string\n projections?: { asOfSeq?: number; values?: Record<string, unknown> }\n}\n\n/**\n * Subagent sessions are host-internal workers of a parent conversation.\n * They must never surface on the phone: not in the project/session list,\n * and not as turn-completion pushes for a session the device cannot open.\n */\nfunction isSubagentRow(row: PhoneSessionRow): boolean {\n return row.origin === 'subagent' ||\n (typeof row.parentSessionId === 'string' && row.parentSessionId.length > 0);\n}\n\ninterface HistoryResult {\n events: Array<{ event: SessionEventLike; view?: unknown }>\n hasMore: boolean\n projections?: { values?: Record<string, unknown> }\n}\n\nexport interface SessionEventLike {\n type: string\n seq: number\n time?: number\n data?: unknown\n}\n\nexport interface MuxFrameLike {\n type: string\n rpcId?: string\n payload?: any\n sessionId?: string\n event?: SessionEventLike\n key?: string\n value?: unknown\n approvalId?: string\n toolName?: string\n reason?: string\n questions?: unknown\n questionRpcId?: string\n running?: boolean\n}\n\n/**\n * apiProxy stream items are server-request envelopes: the frame lives in\n * `payload`, while the stable request id used to answer approval/question\n * waits lives beside it. Flattening only `payload` loses that id and makes\n * both interactions silently disappear.\n */\ninterface ApiStreamItemLike {\n rpcId?: string\n payload: MuxFrameLike\n}\n\nfunction unwrapStreamItem(item: MuxFrameLike | ApiStreamItemLike): MuxFrameLike {\n const nested = item.payload\n if (nested && typeof nested === 'object' && typeof nested.type === 'string') {\n return nested.rpcId || !item.rpcId ? nested : { ...nested, rpcId: item.rpcId }\n }\n return item as MuxFrameLike\n}\n\nexport interface ApiProxyLike {\n sessions: SessionsApiLike\n workspace?: WorkspaceApiLike\n host?: HostApiLike\n respond(message: { type: 'client-response'; rpcId: string; result: RpcResult<unknown> }):\n Promise<{ accepted: boolean; reason?: string }>\n events: {\n mux(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n host(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n }\n}\n\n/**\n * Outcome of answering a pending approval/question. The host distinguishes\n * \"never/no longer pending\" from \"payload rejected\", and collapsing both into\n * a boolean made every rejection read as `question not pending` on the phone.\n */\nexport type PendingResponseOutcome =\n | { ok: true }\n | { ok: false; reason: 'not-pending' | 'bad-response' | 'transport' }\n\n/** Downward sink every connected phone registers (one per WebSocket). */\nexport interface BridgeSink {\n push(type: string, payload: unknown, seq?: number): void\n lastCursor(): number\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void\n replayDone(): void\n resync(): void\n}\n\n/**\n * Offline push fan-out (F-9 离线推送). The bridge forwards every\n * notification-worthy event here; the outlet decides which paired devices\n * (token holders without a live socket) receive an APNs delivery.\n */\nexport interface PushOutlet {\n fanOut(notification: PushNotification): void\n /**\n * Whether offline push is currently configured and usable. Drives the\n * welcome capability bit: advertising push while no APNs credentials are\n * loaded would make clients suppress their own local banners and lose\n * notifications entirely.\n */\n isAvailable(): boolean\n}\n\ninterface PendingApproval {\n rpcId: string\n sessionId: string\n toolName: string\n reason: string\n}\n\ninterface PendingQuestion {\n rpcId: string\n sessionId: string\n questions: unknown\n}\n\nconst MAX_RING_DEFAULT = 2000;\n\n/**\n * Process-wide bridge state: session mirror, pending approvals/questions,\n * and the per-device replay ring. Consumes the in-process mux/host streams\n * and fans projected pushes out to every registered sink.\n */\nlet BRIDGE_SEQ = 0;\n\nexport class HostBridge {\n readonly id = ++BRIDGE_SEQ;\n private summaries = new Map<string, SessionSummary>()\n private approvals = new Map<string, PendingApproval>()\n private questions = new Map<string, PendingQuestion>()\n private archivedSessionIds = new Set<string>()\n private subagentSessionIds = new Set<string>()\n private sinks = new Set<BridgeSink>()\n private ring: Array<{ seq: number; type: string; payload: unknown }> = []\n private cursor = 0;\n private abort = new AbortController();\n\n constructor(\n private readonly apiProxy: ApiProxyLike,\n private readonly historyBufferMax: number = MAX_RING_DEFAULT,\n ) {}\n\n private pushOutlet: PushOutlet | undefined;\n\n /**\n * Wire the offline-push fan-out. Present ⇒ welcome advertises the `push`\n * capability and notify-worthy events are mirrored to APNs.\n */\n setPushOutlet(outlet: PushOutlet | undefined): void {\n this.pushOutlet = outlet;\n }\n\n get capabilities() {\n return {\n historyPaging: true,\n replay: true,\n approvals: true,\n questions: true,\n pendingSnapshot: true,\n models: typeof this.apiProxy.sessions.models === 'function' &&\n typeof this.apiProxy.sessions.selectModel === 'function',\n sessionManagement: typeof this.apiProxy.sessions.rename === 'function' &&\n typeof this.apiProxy.workspace?.archiveSession === 'function',\n projectSelection: typeof this.apiProxy.workspace?.list === 'function' &&\n typeof this.apiProxy.workspace?.create === 'function',\n push: this.pushOutlet?.isAvailable() === true,\n };\n }\n\n diagnostic(message: string): void {\n console.log('[deeppilot] ' + message);\n }\n\n currentCursor(): number {\n return this.cursor;\n }\n\n // ---------- sinks ----------\n\n addSink(sink: BridgeSink): void {\n this.sinks.add(sink);\n }\n\n removeSink(sink: BridgeSink): void {\n this.sinks.delete(sink);\n }\n\n /** Whether the ring still holds everything after the cursor. */\n canResumeFrom(cursor: number): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n // A cursor ahead of this process belongs to an older bridge lifetime.\n // Treat it as a gap instead of incorrectly claiming a successful resume.\n return cursor <= this.cursor && cursor + 1 >= oldest;\n }\n\n private sinkSessions = new Map<BridgeSink, Set<string>>();\n private lastAssistantText = new Map<string, string>();\n\n /** Mark a sink as actively viewing a session (suppresses its turn notifications). */\n markSinkOpen(sink: BridgeSink, sessionId: string): void {\n let set = this.sinkSessions.get(sink);\n if (!set) { set = new Set(); this.sinkSessions.set(sink, set); }\n set.add(sessionId);\n }\n\n markSinkClosed(sink: BridgeSink, sessionId: string): void {\n this.sinkSessions.get(sink)?.delete(sessionId);\n }\n\n dropSinkSessions(sink: BridgeSink): void {\n this.sinkSessions.delete(sink);\n }\n\n private isViewedBy(sink: BridgeSink, sessionId: string): boolean {\n return this.sinkSessions.get(sink)?.has(sessionId) ?? false;\n }\n\n /** F-9: when a turn completes, notify every device not viewing the session. */\n private emitTurnCompletedNotify(sessionId: string, ok: boolean): void {\n if (this.subagentSessionIds.has(sessionId)) return;\n\n const row = this.summaries.get(sessionId);\n const title = ok ? '任务完成' : '任务异常结束';\n const body = this.lastAssistantText.get(sessionId) ?? row?.title ?? '';\n const truncatedBody = body.length > 120 ? body.slice(0, 119) + '…' : body;\n const category: NotifyCategory = ok ? 'turn.completed' : 'session.error';\n const notificationId = 'n-' + (this.cursor + 1);\n this.record('s2c.notify', {\n notificationId,\n category,\n sessionId,\n title,\n body: truncatedBody,\n ts: Date.now(),\n }, (sink) => this.isViewedBy(sink, sessionId));\n this.fanOutPush({ notificationId, category, sessionId, title, body: truncatedBody });\n }\n\n /**\n * Mirror one notification-worthy event to offline devices. Fire-and-forget:\n * push failures must never block or break the WS data plane.\n */\n private fanOutPush(notification: PushNotification): void {\n try {\n this.pushOutlet?.fanOut(notification);\n } catch {\n // outlet misbehavior must not take down the bridge\n }\n }\n\n\n /** Remember the latest assistant text so notifications can quote it. */\n private captureAssistantText(sessionId: string, event: SessionEventLike): void {\n\n if (event.type !== 'assistant/message') return;\n\n const text = messageText(event.data).trim();\n\n if (text.length > 0) this.lastAssistantText.set(sessionId, text.slice(-160));\n }\n\n /**\n * Replay buffered pushes after the given cursor; false when the gap is\n * unrecoverable. Frames go to `target` only — replaying into every sink\n * duplicated the whole window onto devices that never asked for it.\n */\n resumeFrom(cursor: number, target?: BridgeSink): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n if (cursor + 1 < oldest) return false;\n const receivers = target !== undefined ? [target] : [...this.sinks];\n for (const entry of this.ring) {\n if (entry.seq > cursor) {\n for (const sink of receivers) sink.replay([entry]);\n }\n }\n for (const sink of receivers) sink.replayDone();\n return true;\n }\n\n private record(type: string, payload: unknown, except?: (sink: BridgeSink) => boolean): void {\n this.cursor += 1;\n const entry = { seq: this.cursor, type, payload };\n this.ring.push(entry);\n if (this.ring.length > this.historyBufferMax) {\n this.ring.splice(0, this.ring.length - this.historyBufferMax);\n }\n for (const sink of this.sinks) {\n if (except && except(sink)) continue;\n sink.push(type, payload, entry.seq);\n }\n }\n\n\n // ---------- lifecycle ----------\n\n /** Start consuming host + mux streams. Idempotent; aborts on dispose(). */\n start(): void {\n void this.runHostStream();\n void this.runMuxStream();\n void this.refreshSummaries();\n }\n\n dispose(): void {\n this.abort.abort();\n this.sinks.clear();\n }\n\n private async runHostStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.host({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onHostFrame(frame);\n }\n } catch {\n // stream ended (dispose or host shutdown); bridges degrade to RPC-only\n }\n }\n\n private async runMuxStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.mux({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onMuxFrame(frame);\n }\n } catch {\n // ditto\n }\n }\n\n private onHostFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'host/session-added':\n case 'host/session-removed':\n case 'host/workspace-changed':\n case 'host/workspace-removed':\n case 'host/workspace-order-changed':\n void this.refreshSummaries();\n break;\n case 'host/archived-sessions-changed': {\n const archived = (frame as { archivedSessionIds?: unknown }).archivedSessionIds\n if (Array.isArray(archived)) {\n this.archivedSessionIds = new Set(archived.map(String))\n }\n void this.refreshSummaries();\n break;\n }\n case 'host/session-status': {\n const p = frame as { sessionId?: string; running?: boolean };\n const row = this.summaries.get(String(p.sessionId));\n if (row && typeof p?.running === 'boolean') {\n row.status = p.running ? 'running' : 'idle';\n this.pushSummary(row);\n }\n break;\n }\n default:\n break;\n }\n }\n\n private onMuxFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'session/event': {\n const event = frame.event as SessionEventLike | undefined;\n const sessionId = String(frame.sessionId ?? '');\n if (!event || !sessionId) break;\n this.noteActivity(sessionId, event);\n this.captureAssistantText(sessionId, event);\n const projection = projectEvent(sessionId, event);\n if (projection) {\n this.record('s2c.session.event', { sessionId, kind: projection.kind, seq: event.seq, data: projection.data });\n }\n if (projection?.kind === 'turn.end') {\n this.emitTurnCompletedNotify(sessionId, projection.data.ok === true);\n }\n break;\n }\n case 'session/projection': {\n const p = frame as { sessionId?: string; key?: string; value?: unknown };\n if (!p.sessionId) break;\n this.applyProjection(p.sessionId, String(p.key ?? ''), p.value);\n break;\n }\n case 'approval/requested': {\n const p = frame as { sessionId?: string; approvalId?: string; toolName?: string; reason?: string };\n if (!p.approvalId || !frame.rpcId) break;\n const toolName = String(p.toolName ?? 'tool');\n const summary = String(p.reason ?? '');\n this.approvals.set(p.approvalId, {\n rpcId: frame.rpcId,\n sessionId: String(p.sessionId ?? ''),\n toolName,\n reason: summary,\n });\n this.record('s2c.pending.approval', {\n requestId: p.approvalId,\n sessionId: String(p.sessionId ?? ''),\n toolName,\n summary,\n riskLevel: riskOf(toolName),\n });\n this.fanOutPush({\n notificationId: 'apr-' + p.approvalId,\n category: 'approval.required',\n sessionId: String(p.sessionId ?? ''),\n title: '需要批准',\n body: toolName + ': ' + summary,\n });\n this.bumpPendingFlags(String(p.sessionId ?? ''));\n break;\n }\n case 'approval/resolved': {\n const p = frame as { approvalId?: string };\n if (!p.approvalId) break;\n const pending = this.approvals.get(p.approvalId);\n this.approvals.delete(p.approvalId);\n this.record('s2c.pending.cleared', { requestId: p.approvalId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n case 'question/requested': {\n const p = frame as { sessionId?: string; questions?: unknown };\n if (!frame.rpcId) break;\n const requestId = 'q-' + frame.rpcId;\n const sessionId = String(p?.sessionId ?? '');\n this.questions.set(requestId, { rpcId: frame.rpcId, sessionId, questions: p?.questions });\n this.record('s2c.pending.question', { requestId, sessionId, questions: p?.questions ?? [] });\n this.fanOutPush({\n notificationId: requestId,\n category: 'question.asked',\n sessionId,\n title: '有问题需要回答',\n body: firstQuestionText(p?.questions),\n });\n this.bumpPendingFlags(sessionId);\n break;\n }\n case 'question/resolved': {\n const p = frame as { questionRpcId?: string };\n if (!p.questionRpcId) break;\n const requestId = 'q-' + p.questionRpcId;\n const pending = this.questions.get(requestId);\n this.questions.delete(requestId);\n this.record('s2c.pending.cleared', { requestId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n default:\n break;\n }\n }\n\n\n // ---------- session mirror ----------\n\n async refreshSummaries(): Promise<void> {\n try {\n const response = await this.apiProxy.sessions.list({ rpcId: randomUUID(), payload: {} });\n if (!response.result || !response.result.ok) {\n this.diagnostic('sessions.list rejected: ' + JSON.stringify(response.result ?? null).slice(0, 200));\n return;\n }\n let workspaces: WorkspaceViewLike[] = []\n const workspaceList = this.apiProxy.workspace?.list\n if (typeof workspaceList === 'function') {\n const workspaceResponse = await workspaceList.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: {},\n })\n if (workspaceResponse.result?.ok) {\n workspaces = workspaceResponse.result.value.items ?? []\n this.archivedSessionIds = new Set(\n (workspaceResponse.result.value.archivedSessionIds ?? []).map(String),\n )\n }\n }\n const previousIds = new Set(this.summaries.keys())\n const workspaceBySession = new Map<string, WorkspaceViewLike>()\n for (const workspace of workspaces) {\n for (const sessionId of workspace.sessionIds ?? []) {\n workspaceBySession.set(String(sessionId), workspace)\n }\n }\n const next = new Map<string, SessionSummary>();\n const subagentIds = new Set<string>();\n for (const row of response.result.value.items ?? []) {\n if (this.archivedSessionIds.has(row.sessionId)) continue\n if (isSubagentRow(row)) {\n subagentIds.add(row.sessionId);\n continue;\n }\n next.set(row.sessionId, toSummary(row, this.approvals, this.questions, workspaceBySession.get(row.sessionId)));\n }\n this.subagentSessionIds = subagentIds;\n this.summaries = next;\n const removedIds = [...previousIds].filter((id) => !next.has(id))\n for (const id of removedIds) this.lastAssistantText.delete(id);\n this.record('s2c.sessions.delta', { upserted: [...next.values()], removedIds });\n } catch {\n // apiProxy absent or transient failure; keep last mirror\n }\n }\n\n /** Cold sessions may lack a title projection; fall back to first user text. */\n private deriveTitleFallback(sessionId: string, messages: MessageProjection[]): void {\n const row = this.summaries.get(sessionId);\n if (!row || row.title.length > 0) return;\n const firstUser = messages.find(m => m.role === 'user' && (m.text ?? '').trim().length > 0);\n if (!firstUser) return;\n row.title = firstUser!.text!.replace(/\\s+/g, ' ').trim().slice(0, 60);\n this.pushSummary(row);\n }\n\n private noteActivity(sessionId: string, event?: SessionEventLike): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n // Recency must reflect USER-VISIBLE milestones only: a new prompt moves\n // a session to the top and a finished turn settles it into place. Token\n // chunks / tool calls / per-step assistant messages fire constantly while\n // an agent works; counting them made concurrently running sessions fight\n // over the top slot in every recency-sorted list.\n switch (event?.type) {\n case 'user/message':\n case 'turn/start':\n case 'turn/end':\n break;\n default:\n return;\n }\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n\n private applyProjection(sessionId: string, key: string, value: unknown): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n if (key === 'title') {\n row.title = typeof value === 'string' ? value : '';\n } else if (key === 'todos') {\n const items = Array.isArray(value) ? value as Array<{ content?: unknown; status?: unknown }> : null;\n const sanitized = sanitizeTodoItems(items);\n row.todoItems = sanitized.length > 0 ? sanitized : null;\n row.todos = sanitized.length > 0\n ? { done: sanitized.filter(i => i.status === 'completed').length, total: sanitized.length }\n : null;\n } else if (key === 'sessionListMetadata') {\n const meta = value as { lastPromptAt?: number } | null;\n if (meta?.lastPromptAt) row.lastActivityTs = Math.max(row.lastActivityTs, meta.lastPromptAt);\n } else {\n return;\n }\n this.pushSummary(row);\n }\n\n private bumpPendingFlags(sessionId: string): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n let approval = false;\n for (const pending of this.approvals.values()) {\n if (pending.sessionId === sessionId) approval = true;\n }\n let question = false;\n for (const pending of this.questions.values()) {\n if (pending.sessionId === sessionId) question = true;\n }\n row.pendingApproval = approval;\n row.pendingQuestion = question;\n this.pushSummary(row);\n }\n\n private pushSummary(row: SessionSummary): void {\n this.record('s2c.sessions.delta', { upserted: [row], removedIds: [] });\n }\n\n listSessions(): SessionSummary[] {\n return [...this.summaries.values()].sort((a, b) => b.lastActivityTs - a.lastActivityTs);\n }\n\n /**\n * Complete transient interaction state. Unlike the replay ring, this remains\n * authoritative after a long disconnect and is rehydrated by apiProxy's mux\n * stream when the bridge itself restarts.\n */\n pendingSnapshot(): PendingSnapshotPayload {\n return {\n approvals: [...this.approvals.entries()].map(([requestId, pending]) => ({\n requestId,\n sessionId: pending.sessionId,\n toolName: pending.toolName,\n summary: pending.reason,\n riskLevel: riskOf(pending.toolName),\n })),\n questions: [...this.questions.entries()].map(([requestId, pending]) => ({\n requestId,\n sessionId: pending.sessionId,\n questions: Array.isArray(pending.questions)\n ? pending.questions as PendingSnapshotPayload['questions'][number]['questions']\n : [],\n })),\n };\n }\n\n // ---------- data operations ----------\n\n /** Tail history for an opened session; pushes s2c.session.tail to the sink. */\n async openSession(sink: BridgeSink, sessionId: string, tailCount: number): Promise<boolean> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, maxMessages: clampTail(tailCount) },\n });\n if (!response.result || !response.result.ok) return false;\n const result = response.result.value;\n const messages = projectHistory(result.events ?? []);\n const oldestSeq = messages.length > 0 ? messages[0]!.seq : 0;\n sink.push('s2c.session.tail', {\n sessionId,\n messages,\n oldestSeq,\n hasMore: Boolean(result.hasMore),\n });\n this.deriveTitleFallback(sessionId, messages);\n return true;\n } catch {\n return false;\n }\n }\n\n async historyPage(sink: BridgeSink, sessionId: string, beforeSeq: number, limit: number): Promise<boolean> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, beforeSeq, maxMessages: clampTail(limit) },\n });\n if (!response.result || !response.result.ok) return false;\n const result = response.result.value;\n const messages = projectHistory(result.events ?? []);\n sink.push('s2c.history.page', { sessionId, messages, hasMore: Boolean(result.hasMore) });\n return true;\n } catch {\n return false;\n }\n }\n\n /** Result of one attachment read-back for the phone. */\n async attachmentData(\n sessionId: string,\n attachmentId: string,\n ): Promise<{ mediaType?: string; data: string } | null> {\n const read = this.apiProxy.sessions.attachment\n if (typeof read !== 'function') return null;\n try {\n const response = await read.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, attachmentId },\n });\n if (!response.result || !response.result.ok) return null;\n const data = response.result.value.data;\n if (typeof data !== 'string' || data.length === 0) return null;\n return {\n ...(typeof response.result.value.attachment?.mediaType === 'string'\n ? { mediaType: response.result.value.attachment.mediaType as string }\n : {}),\n data,\n };\n } catch {\n return null;\n }\n }\n\n async sessionModels(sessionId: string): Promise<ModelBridgeResult<HostSessionModels>> {\n const models = this.apiProxy.sessions.models\n if (typeof models !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'model catalog unavailable on this host version' }\n }\n try {\n const response = await models.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'model catalog returned no result' }\n if (!response.result.ok) return hostModelError(response.result.error)\n return { ok: true, value: projectSessionModels(response.result.value) }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async selectSessionModel(\n sessionId: string,\n selection: HostModelSelection,\n ): Promise<ModelBridgeResult<HostModelSelection>> {\n const selectModel = this.apiProxy.sessions.selectModel\n if (typeof selectModel !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'model selection unavailable on this host version' }\n }\n try {\n const response = await selectModel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: {\n sessionId,\n provider: selection.provider,\n model: selection.model,\n ...(selection.reasoningEffort ? { reasoningEffort: selection.reasoningEffort } : {}),\n },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'model selection returned no result' }\n if (!response.result.ok) return hostModelError(response.result.error)\n return { ok: true, value: { ...response.result.value.selected } }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async renameSession(sessionId: string, title: string): Promise<SessionManagementResult<string>> {\n const rename = this.apiProxy.sessions.rename\n if (typeof rename !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session rename unavailable on this host version' }\n }\n try {\n const response = await rename.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, title },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session rename returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n const acceptedTitle = String(response.result.value.title)\n const row = this.summaries.get(sessionId)\n if (row) {\n row.title = acceptedTitle\n this.pushSummary(row)\n }\n return { ok: true, value: acceptedTitle }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async archiveSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const archive = this.apiProxy.workspace?.archiveSession\n if (typeof archive !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session archive unavailable on this host version' }\n }\n try {\n const response = await archive.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session archive returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n this.archivedSessionIds = new Set(\n (response.result.value.archivedSessionIds ?? []).map(String),\n )\n this.summaries.delete(sessionId)\n this.lastAssistantText.delete(sessionId)\n this.record('s2c.sessions.delta', { upserted: [], removedIds: [sessionId] })\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async cancelSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const cancel = this.apiProxy.sessions.cancel\n if (typeof cancel !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session cancel unavailable on this host version' }\n }\n try {\n const response = await cancel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session cancel returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async listWorkspaces(): Promise<SessionManagementResult<Array<{\n id: string; title: string; path: string; sessionIds: string[]\n }>>> {\n const list = this.apiProxy.workspace?.list\n if (typeof list !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'workspace list unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.workspace, { rpcId: randomUUID(), payload: {} })\n if (!response.result) return { ok: false, kind: 'internal', message: 'workspace list returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: (response.result.value.items ?? []).map(projectWorkspace) }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async createWorkspace(path: string): Promise<SessionManagementResult<{\n workspace: { id: string; title: string; path: string; sessionIds: string[] }; created: boolean\n }>> {\n const create = this.apiProxy.workspace?.create\n if (typeof create !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'workspace create unavailable on this host version' }\n }\n try {\n const response = await create.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { path },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'workspace create returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n await this.refreshSummaries()\n return {\n ok: true,\n value: { workspace: projectWorkspace(response.result.value.workspace), created: response.result.value.created === true },\n }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async listDirectory(path?: string): Promise<SessionManagementResult<DirectoryListingLike>> {\n const list = this.apiProxy.host?.listDirectory\n if (typeof list !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'directory browsing unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: path && path.trim().length > 0 ? { path } : {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, kind: 'internal', message: 'directory list returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: response.result.value }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async pickDirectory(): Promise<SessionManagementResult<string | null>> {\n const pick = this.apiProxy.host?.pickDirectory\n if (typeof pick !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'native directory picker unavailable on this host version' }\n }\n try {\n const response = await pick.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, kind: 'internal', message: 'directory picker returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: response.result.value.path }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n /** Create a fresh blank session in an existing workspace or legacy cwd. */\n async createSession(destination: { workspaceId?: string; cwd?: string } = {}): Promise<string | null> {\n try {\n const response = await this.apiProxy.sessions.create({\n rpcId: randomUUID(),\n payload: {\n ...(destination.workspaceId?.trim() ? { workspaceId: destination.workspaceId.trim() } : {}),\n ...(destination.cwd?.trim() ? { cwd: destination.cwd.trim() } : {}),\n },\n });\n if (!response.result || !response.result.ok) return null;\n const sessionId = response.result.value.sessionId;\n await this.refreshSummaries();\n return sessionId;\n } catch {\n return null;\n }\n }\n\n async sendPrompt(\n sessionId: string,\n text: string,\n images: Array<{ mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }> = [],\n ): Promise<SessionManagementResult<number>> {\n try {\n const content: PromptArgs['content'] = [];\n if (text.trim().length > 0) content.push({ type: 'text', text });\n for (const image of images) content.push({ type: 'image', ...image });\n const response = await this.apiProxy.sessions.prompt({\n rpcId: randomUUID(),\n payload: {\n sessionId,\n mode: 'queue',\n content,\n clientTimeZone: localTimeZone(),\n },\n });\n if (!response.result) return { ok: false, kind: 'internal', message: 'prompt returned no result' };\n // Preserve the host's error kind so the phone can tell \"retry later\"\n // (E_BUSY) from \"session is gone\" (E_NOT_FOUND) instead of mapping\n // every failure onto E_BUSY.\n if (!response.result.ok) return hostSessionManagementError(response.result.error);\n const row = this.summaries.get(sessionId);\n if (row) {\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n return { ok: true, value: Date.now() };\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) };\n }\n }\n\n\n async respondApproval(\n requestId: string,\n decision: 'allow' | 'deny',\n reason?: string,\n ): Promise<PendingResponseOutcome> {\n const pending = this.approvals.get(requestId);\n if (!pending) return { ok: false, reason: 'not-pending' };\n // Claim the pending entry before responding: a second phone (or a double\n // tap) must not fire another client-response for the same rpcId while the\n // resolved frame is still in flight.\n this.approvals.delete(requestId);\n const outcome = decision === 'allow' ? 'allowed-once' : 'rejected';\n const denialReason = typeof reason === 'string' ? reason.trim().slice(0, 500) : '';\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: {\n ok: true,\n value: {\n sessionId: pending.sessionId,\n approvalId: requestId,\n outcome,\n // PROTOCOL v1: a deny may carry the user's explanation so the\n // model can adjust course instead of blindly retrying.\n ...(denialReason.length > 0 ? { reason: denialReason } : {}),\n },\n },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted) {\n const failure = receiptFailureReason(receipt);\n // Restore only what the user can retry: `not-pending` means the host\n // already settled the request, and restoring would strand a ghost\n // entry that no future resolved frame will ever clear.\n if (failure !== 'not-pending' && !this.approvals.has(requestId)) {\n this.approvals.set(requestId, pending);\n }\n return { ok: false, reason: failure };\n }\n // The claim above removed the entry, so the upcoming approval/resolved\n // frame finds nothing left to bump — refresh the summary flags here or\n // the session badge stays \"pending\" until an unrelated refresh.\n this.bumpPendingFlags(pending.sessionId);\n return { ok: true };\n } catch {\n // Transport-level failure: give the request back so the user can retry.\n if (!this.approvals.has(requestId)) this.approvals.set(requestId, pending);\n return { ok: false, reason: 'transport' };\n }\n }\n\n async respondQuestion(requestId: string, answers: unknown): Promise<PendingResponseOutcome> {\n const pending = this.questions.get(requestId);\n if (!pending) return { ok: false, reason: 'not-pending' };\n this.questions.delete(requestId);\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: { ok: true, value: { sessionId: pending.sessionId, answer: { answers: normalizeAnswerItems(answers, pending.questions) } } },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted) {\n const failure = receiptFailureReason(receipt);\n // Same ghost-entry rule as approvals: `not-pending` is final, never\n // restore — the host has no record left to resolve against.\n if (failure !== 'not-pending' && !this.questions.has(requestId)) {\n this.questions.set(requestId, pending);\n }\n return { ok: false, reason: failure };\n }\n // Mirror the approval path: the claimed entry cannot bump flags when the\n // resolved frame arrives, so recompute them right after acceptance.\n this.bumpPendingFlags(pending.sessionId);\n return { ok: true };\n } catch {\n if (!this.questions.has(requestId)) this.questions.set(requestId, pending);\n return { ok: false, reason: 'transport' };\n }\n }\n}\n\n/**\n * The host validates question answers strictly (core dsh-user-questions via\n * apiProxy): a present-but-empty `custom` fails `matchesQuestions`, and a\n * single-select question rejects `custom` combined with a selection. Clients\n * may send lenient shapes (the phone historically always attached\n * `\"custom\": \"\"`, which made EVERY option-only answer fail), so normalize to\n * exactly what the host accepts before forwarding.\n */\nexport function normalizeAnswerItems(\n raw: unknown,\n questions?: unknown,\n): Array<{ id: string; selected: string[]; custom?: string }> {\n if (!Array.isArray(raw)) return [];\n const askedById = new Map<string, { multiSelect?: unknown }>();\n if (Array.isArray(questions)) {\n for (const q of questions) {\n if (typeof q === 'object' && q !== null && typeof (q as { id?: unknown }).id === 'string') {\n askedById.set((q as { id: string }).id, q as { multiSelect?: unknown });\n }\n }\n }\n const items: Array<{ id: string; selected: string[]; custom?: string }> = [];\n for (const entry of raw) {\n if (typeof entry !== 'object' || entry === null) continue;\n const r = entry as { id?: unknown; selected?: unknown; custom?: unknown };\n if (typeof r.id !== 'string') continue;\n // De-duplicate while preserving order; duplicate labels are rejected by the host.\n const selected = [...new Set(\n Array.isArray(r.selected) ? r.selected.filter((s): s is string => typeof s === 'string') : [],\n )];\n const customText = typeof r.custom === 'string' ? r.custom : '';\n let custom: string | undefined;\n if (customText.trim().length > 0) custom = customText;\n // A single-select answer must be either options or free text, never both.\n if (custom !== undefined && selected.length > 0 && askedById.get(r.id)?.multiSelect !== true) {\n custom = undefined;\n }\n items.push({ id: r.id, selected, ...(custom !== undefined ? { custom } : {}) });\n }\n return items;\n}\n\n/** Map an apiProxy respond receipt onto the failure vocabulary. */\nfunction receiptFailureReason(receipt: { accepted: boolean; reason?: string } | undefined):\n 'not-pending' | 'bad-response' {\n return receipt?.reason === 'not-pending' ? 'not-pending' : 'bad-response';\n}\n\n// ---------- projection helpers ----------\n\nfunction clampTail(n: number): number {\n if (!Number.isFinite(n)) return 100;\n return Math.max(10, Math.min(500, Math.floor(n)));\n}\n\nfunction localTimeZone(): string | undefined {\n try {\n return new Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction riskOf(toolName: string): 'read' | 'write' | 'destructive' {\n if (/bash|pwsh|terminal/.test(toolName)) return 'write';\n if (/edit|write|str_replace|create/.test(toolName)) return 'write';\n if (/delete|remove|kill/.test(toolName)) return 'destructive';\n return 'read';\n}\n\n/** First question's text for the push banner; the questions payload shape is\n * host-version dependent, so extract defensively. */\nexport function firstQuestionText(questions: unknown): string {\n if (!Array.isArray(questions) || questions.length === 0) return 'Agent 等待你的输入';\n const first = questions[0] as { question?: unknown } | undefined;\n const text = String(first?.question ?? '').trim();\n return text || 'Agent 等待你的输入';\n}\n\nconst TODO_STATUSES = new Set(['pending', 'in_progress', 'completed']);\n\n/** Validate a host todo projection once; progress counts and the full\n * checklist both derive from this sanitized list so they never disagree. */\nfunction sanitizeTodoItems(items: Array<{ content?: unknown; status?: unknown }> | null): SessionTodoItem[] {\n if (!items) return [];\n return items\n .map(i => ({ content: String(i.content ?? '').trim(), status: String(i.status ?? '') }))\n .filter(i => i.content.length > 0 && TODO_STATUSES.has(i.status))\n .slice(0, 100)\n .map(i => ({ content: i.content, status: i.status as SessionTodoStatus }));\n}\n\nfunction toSummary(\n row: PhoneSessionRow,\n approvals: Map<string, PendingApproval>,\n questions: Map<string, PendingQuestion>,\n workspace?: WorkspaceViewLike,\n): SessionSummary {\n const values = row.projections?.values ?? {};\n const todos = Array.isArray(values.todos) ? values.todos as Array<{ content?: unknown; status?: unknown }> : null;\n let pendingApproval = false;\n for (const pending of approvals.values()) {\n if (pending.sessionId === row.sessionId) pendingApproval = true;\n }\n let pendingQuestion = false;\n for (const pending of questions.values()) {\n if (pending.sessionId === row.sessionId) pendingQuestion = true;\n }\n const cwd = typeof row.cwd === 'string' ? row.cwd : '';\n const label = workspace?.title ?? (cwd ? cwd.split('/').filter(Boolean).pop() : undefined);\n const todoItems = sanitizeTodoItems(todos);\n return {\n id: row.sessionId,\n title: typeof values.title === 'string' ? values.title : '',\n status: row.running ? 'running' : (row.blank ? 'unknown' : 'idle'),\n lastActivityTs: Number(row.updatedAt ?? Date.now()),\n todos: todoItems.length > 0\n ? {\n done: todoItems.filter(i => i.status === 'completed').length,\n total: todoItems.length,\n }\n : null,\n todoItems: todoItems.length > 0 ? todoItems : null,\n pendingApproval,\n pendingQuestion,\n workspaceLabel: label ?? null,\n workspaceId: workspace?.workspaceId ?? null,\n workspacePath: workspace?.path ?? (cwd || null),\n };\n}\n\nfunction projectWorkspace(workspace: WorkspaceViewLike): {\n id: string; title: string; path: string; sessionIds: string[]\n} {\n return {\n id: String(workspace.workspaceId),\n title: String(workspace.title),\n path: String(workspace.path),\n sessionIds: (workspace.sessionIds ?? []).map(String),\n }\n}\n\n/** Project one raw session event into a protocol push, when it maps to one. */\nexport function projectEvent(sessionId: string, event: SessionEventLike): { kind: SessionEventKind; data: Record<string, unknown> } | null {\n switch (event.type) {\n case 'turn/start':\n return { kind: 'turn.start', data: {} };\n case 'turn/end': {\n const data = event.data as { reason?: { kind?: string } } | undefined;\n return { kind: 'turn.end', data: { ok: data?.reason?.kind === 'completed' } };\n }\n case 'user/message':\n return {\n kind: 'message.final',\n data: {\n seq: event.seq,\n role: userRoleOf(event.data),\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n ...(contextProjectionOf(event.data)),\n ts: tsOf(event),\n },\n };\n case 'assistant/chunk': {\n if (chunkTypeOf(event.data) === 'reasoning-delta') {\n return { kind: 'thinking.delta', data: { text: chunkText(event.data), ts: tsOf(event) } };\n }\n return { kind: 'message.delta', data: { text: chunkText(event.data), ts: tsOf(event) } };\n }\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n // Pure-reasoning or tool-only steps carry no visible answer; emitting\n // them produced empty bubbles on the phone.\n if (!text.trim() && !thinking.trim()) return null;\n return {\n kind: 'message.final',\n data: { seq: event.seq, role: 'assistant', text, ...(thinking ? { thinking } : {}), ts: tsOf(event) },\n };\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string; callId?: string } | undefined;\n return {\n kind: 'tool.start',\n data: {\n seq: event.seq,\n role: 'tool',\n tool: {\n name: String(data?.name ?? 'tool'),\n state: 'running',\n summary: summarizeArgs(data?.arguments),\n // Echo the invocation id so clients can pair the later result with\n // this exact row instead of guessing.\n ...(data?.callId ? { callId: String(data.callId) } : {}),\n },\n ts: tsOf(event)\n }\n };\n }\n case 'tool/result': {\n const data = event.data as { callId?: string; error?: unknown } | undefined;\n return {\n kind: 'tool.end',\n data: {\n seq: event.seq,\n role: 'tool',\n ok: !event.data || data?.error === undefined,\n // The seq above identifies this result event; callId is what ties it\n // back to the originating tool/call row on the client.\n ...(data?.callId ? { callId: String(data.callId) } : {}),\n ts: tsOf(event)\n }\n };\n }\n default:\n return null;\n }\n}\n\nfunction tsOf(event: SessionEventLike): number {\n return typeof event.time === 'number' ? event.time : Date.now();\n}\n\n// ---------- user-role source classification ----------\n//\n// The DSH host logs every model-visible user-role message as `user/message`,\n// but only some of them are human prompts: synthetic `agent.inject()` context\n// (runtime-context snapshots, background-job notices, workspace instructions,\n// skill content, …) rides the same event type. The durable message carries a\n// `source` whose `kind` tells them apart, and the host's own trajectory view\n// renders only `source.kind === 'user'` as a human prompt — everything else is\n// injected context. The bridge mirrors that classification so the phone can\n// keep the two apart instead of guessing from text shapes.\n\n/** Read the durable message source off one user/message payload. Handles both\n * bare-message payloads and older `{message: {...}}` wrappers; undefined when\n * the shape carries no readable source (legacy hosts). */\nfunction userMessageSource(data: unknown): Record<string, unknown> | undefined {\n if (!data || typeof data !== 'object') return undefined;\n const obj = data as { source?: unknown; message?: { source?: unknown } };\n if (obj.source && typeof obj.source === 'object') {\n return obj.source as Record<string, unknown>;\n }\n if (obj.message && typeof obj.message === 'object' &&\n obj.message.source && typeof obj.message.source === 'object') {\n return obj.message.source as Record<string, unknown>;\n }\n return undefined;\n}\n\n/** Wire role for one user/message payload. A payload without any readable\n * source degrades to 'user' so history written by older hosts stays visible;\n * a present source follows the host's own trajectory rule — anything whose\n * `kind` is not 'user' is injected context and projects as 'system'. */\nfunction userRoleOf(data: unknown): 'user' | 'system' {\n const source = userMessageSource(data);\n if (!source) return 'user';\n return source.kind === 'user' ? 'user' : 'system';\n}\n\n/** Producer name of one injected-context source, mirroring how the DSH client\n * runtime derives its trajectory label: plugin name, skill name, instruction\n * paths, session-reference labels, or the raw kind as fallback. */\nfunction contextLabelOf(source: Record<string, unknown>): string | undefined {\n const kind = typeof source.kind === 'string' ? source.kind : '';\n const joined = (member: string): string | undefined => {\n const list = source[member];\n if (!Array.isArray(list)) return undefined;\n const names = list.flatMap((entry) => {\n if (!entry || typeof entry !== 'object') return [];\n const record = entry as Record<string, unknown>;\n return [typeof record.label === 'string' ? record.label :\n (typeof record.path === 'string' ? record.path : '')];\n }).filter((name) => name.length > 0);\n return names.length > 0 ? names.join(', ') : undefined;\n };\n switch (kind) {\n case 'session-reference':\n return joined('references') ?? (kind || undefined);\n case 'agent-instructions':\n return joined('changes') ?? (kind || undefined);\n case 'plugin':\n return typeof source.plugin === 'string' && source.plugin.length > 0 ? source.plugin : kind || undefined;\n case 'skill-invocation':\n return typeof source.name === 'string' && source.name.length > 0 ? source.name : kind || undefined;\n default:\n return kind || undefined;\n }\n}\n\n/** Semantic ContextForm declared by the producer ('snapshot', 'notice', …);\n * anything unrecognized stays undefined so clients render it opaque. */\nfunction contextFormOf(source: Record<string, unknown>): string | undefined {\n if (typeof source.form !== 'string' || source.form.length === 0) return undefined;\n // Same known-vocabulary guard as the host's trajectory UI.\n const known = ['instructions', 'catalog', 'snapshot', 'notice', 'relay', 'recall'];\n return known.includes(source.form) ? source.form : undefined;\n}\n\n/** Optional `context` metadata for one system row; {} on user rows. */\nfunction contextProjectionOf(data: unknown): { context?: { label?: string; form?: string } } {\n if (userRoleOf(data) !== 'system') return {};\n const source = userMessageSource(data);\n if (!source) return {};\n const label = contextLabelOf(source);\n const form = contextFormOf(source);\n if (!label && !form) return {};\n return { context: { ...(label ? { label } : {}), ...(form ? { form } : {}) } };\n}\n\n/** Extract plain text from user/assistant message payloads across shapes. */\nfunction messageText(data: unknown): string {\n if (typeof data === 'string') return data;\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; text?: unknown; message?: unknown };\n if (typeof obj.text === 'string') return obj.text;\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageText(obj.message);\n return contentText(obj.content);\n}\n\nfunction contentText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content.map(part => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'text' && typeof piece.text === 'string') return piece.text;\n }\n return '';\n }).join('');\n }\n return '';\n}\n\nfunction messageAttachments(data: unknown): Array<{\n kind: 'image'\n name?: string\n mediaType?: string\n attachmentId?: string\n width?: number\n height?: number\n}> {\n if (!data || typeof data !== 'object') return [];\n const obj = data as { content?: unknown; message?: unknown };\n if (obj.message && typeof obj.message === 'object') return messageAttachments(obj.message);\n if (!Array.isArray(obj.content)) return [];\n return obj.content.flatMap(part => {\n if (!part || typeof part !== 'object') return [];\n // The host normalizes uploaded bytes into an ImageAttachmentRef\n // (attachmentId/mediaType/bytes/width/height...); the id is what clients\n // need to read the image back through c2s.session.attachment.\n const block = part as {\n type?: string\n attachment?: {\n name?: unknown\n mediaType?: unknown\n attachmentId?: unknown\n width?: unknown\n height?: unknown\n }\n };\n if (block.type !== 'image' || !block.attachment) return [];\n const attachmentId = typeof block.attachment.attachmentId === 'string' && block.attachment.attachmentId.length > 0\n ? block.attachment.attachmentId\n : undefined;\n const width = typeof block.attachment.width === 'number' && Number.isFinite(block.attachment.width)\n ? block.attachment.width\n : undefined;\n const height = typeof block.attachment.height === 'number' && Number.isFinite(block.attachment.height)\n ? block.attachment.height\n : undefined;\n return [{\n kind: 'image' as const,\n ...(typeof block.attachment.name === 'string' ? { name: block.attachment.name } : {}),\n ...(typeof block.attachment.mediaType === 'string' ? { mediaType: block.attachment.mediaType } : {}),\n ...(attachmentId ? { attachmentId } : {}),\n ...(width !== undefined ? { width } : {}),\n ...(height !== undefined ? { height } : {}),\n }];\n });\n}\n\nfunction attachmentProjection(data: unknown): { attachments?: ReturnType<typeof messageAttachments> } {\n const attachments = messageAttachments(data);\n return attachments.length > 0 ? { attachments } : {};\n}\n\n/** Extract reasoning (\"thinking\") text from assistant message payloads. */\nfunction messageThinking(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; message?: unknown };\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageThinking(obj.message);\n return reasoningContent(obj.content);\n}\n\nfunction reasoningContent(content: unknown): string {\n if (!Array.isArray(content)) return '';\n return content.map(part => {\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'reasoning' && typeof piece.text === 'string') return piece.text;\n }\n return '';\n }).join('');\n}\n\n/** Stream chunk type of an assistant/chunk payload ('' when unwrapped). */\nfunction chunkTypeOf(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n return String((obj.chunk as { type?: unknown }).type ?? '');\n }\n // Unwrapped payloads carry plain text deltas.\n return 'text-delta';\n}\n\nfunction chunkText(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n // StreamChunk shape: {turn, step, chunk: {type: 'text-delta'|'reasoning-delta', text}}\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n const inner = obj.chunk as { type?: string; text?: string };\n if ((inner.type === 'text-delta' || inner.type === 'reasoning-delta') && typeof inner.text === 'string') {\n return inner.text;\n }\n return '';\n }\n const direct = data as { text?: unknown };\n return typeof direct.text === 'string' ? direct.text : '';\n}\n\nfunction summarizeArgs(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '';\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const parts: string[] = [];\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'string') parts.push(key + '=' + truncate(value.replace(/\\s+/g, ' '), 60));\n }\n return truncate(parts.join(' '), 90);\n } catch {\n return truncate(raw, 90);\n }\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : text.slice(0, max - 1) + '…';\n}\n\nfunction hostModelError(error: { code: string; message?: string }): ModelBridgeResult<never> {\n const message = error.message ?? error.code\n switch (error.code) {\n case 'session-not-found':\n return { ok: false, kind: 'not-found', message }\n case 'agent-busy':\n case 'session-conflict':\n return { ok: false, kind: 'busy', message }\n case 'model-unavailable':\n return { ok: false, kind: 'unavailable', message }\n default:\n return { ok: false, kind: 'internal', message }\n }\n}\n\nfunction hostSessionManagementError(\n error: { code: string; message?: string },\n): SessionManagementResult<never> {\n const message = error.message ?? error.code\n switch (error.code) {\n case 'session-not-found':\n return { ok: false, kind: 'not-found', message }\n case 'agent-busy':\n case 'session-conflict':\n return { ok: false, kind: 'busy', message }\n case 'title-invalid':\n case 'workspace-invalid-path':\n case 'workspace-name-conflict':\n case 'directory-unreadable':\n case 'directory-exists':\n case 'directory-create-failed':\n return { ok: false, kind: 'invalid', message }\n case 'directory-picker-unavailable':\n return { ok: false, kind: 'unsupported', message }\n case 'workspace-not-found':\n return { ok: false, kind: 'not-found', message }\n default:\n return { ok: false, kind: 'internal', message }\n }\n}\n\nfunction projectSessionModels(value: HostSessionModels): HostSessionModels {\n return {\n current: {\n provider: String(value.current.provider),\n model: String(value.current.model),\n ...(value.current.reasoningEffort ? { reasoningEffort: String(value.current.reasoningEffort) } : {}),\n },\n routable: value.routable === true,\n groups: (value.groups ?? []).map((group) => ({\n id: String(group.id),\n name: String(group.name),\n models: (group.models ?? []).map((model) => ({\n id: String(model.id),\n name: String(model.name),\n ...(model.description ? { description: String(model.description) } : {}),\n ...(model.reasoning ? {\n reasoning: {\n efforts: (model.reasoning.efforts ?? []).map((effort) => ({\n id: String(effort.id),\n name: String(effort.name),\n ...(effort.description ? { description: String(effort.description) } : {}),\n })),\n ...(model.reasoning.defaultEffort\n ? { defaultEffort: String(model.reasoning.defaultEffort) }\n : {}),\n },\n } : {}),\n })),\n })),\n failures: (value.failures ?? []).map((failure) => ({\n id: String(failure.id),\n name: String(failure.name),\n message: String(failure.message),\n })),\n }\n}\n\n/** Project a history page (raw events) into MessageProjection rows. */\nexport function projectHistory(events: Array<{ event: SessionEventLike; view?: unknown }>): MessageProjection[] {\n const messages: MessageProjection[] = [];\n const toolByCall = new Map<string, MessageProjection>();\n for (const entry of events) {\n const event = entry.event;\n const base = { seq: event.seq, ts: tsOf(event) };\n switch (event.type) {\n case 'user/message':\n messages.push({\n ...base,\n role: userRoleOf(event.data),\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n ...(contextProjectionOf(event.data)),\n });\n break;\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n if (!text.trim() && !thinking.trim()) break;\n messages.push({ ...base, role: 'assistant', text, ...(thinking ? { thinking } : {}) });\n break;\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string; callId?: string } | undefined;\n const row: MessageProjection = {\n ...base,\n role: 'tool',\n tool: { name: String(data?.name ?? 'tool'), state: 'running', summary: summarizeArgs(data?.arguments) },\n };\n messages.push(row);\n if (data?.callId) toolByCall.set(String(data.callId), row);\n break;\n }\n case 'tool/result': {\n const data = event.data as { callId?: string; message?: { content?: unknown }; error?: unknown } | undefined;\n const callId = data?.callId ? String(data.callId) : undefined;\n const target = callId ? toolByCall.get(callId) : undefined;\n const failed = data?.error !== undefined;\n const summary = failed ? '失败' : summarizeResult(data?.message?.content);\n if (target?.tool) {\n target.tool = { ...target.tool, state: failed ? 'error' : 'ok', summary };\n } else {\n messages.push({ ...base, role: 'tool', tool: { name: 'result', state: failed ? 'error' : 'ok', summary } });\n }\n break;\n }\n default:\n break;\n }\n }\n return messages.sort((a, b) => a.seq - b.seq);\n}\n\nfunction summarizeResult(content: unknown): string {\n const text = contentText(content).replace(/\\s+/g, ' ').trim();\n return truncate(text, 90);\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'\nimport type { DeepPilotReport, PushTestResult, RelayTestResult } from './report-wire.ts'\n\n/** Async snapshot source wired by the plugin entry. */\nexport type ReportSnapshot = () => Promise<DeepPilotReport>\n\n/** Reads the pairing secret only for an explicit reveal action. */\nexport type PairingTokenSnapshot = () => Promise<string>\n\n/**\n * Replaces the pairing secret for an explicit rotation action: persists the\n * new token, invalidates the old one, and drops live phone connections.\n */\nexport type PairingTokenRotator = () => Promise<string>\n\n/** Runs the relay connectivity self-test (health + enrollment round-trip). */\nexport type RelayTester = () => Promise<RelayTestResult>\n\n/** Forces one synthetic notification down the active push pathway. */\nexport type PushTester = () => Promise<PushTestResult>\n\n/**\n * The Typert receiver the Gateway resolves for the DeepPilot Bridge report.\n * Snapshot data stays non-secret; the token crosses the boundary only through\n * the explicit, user-triggered revealToken/rotateToken invocations.\n */\nexport class DeepPilotReportService extends TypertRemoteService {\n private readonly snapshot: ReportSnapshot\n private readonly pairingToken: PairingTokenSnapshot\n private readonly rotatePairingToken: PairingTokenRotator\n\n private readonly relayTester: RelayTester\n private readonly pushTester: PushTester\n\n constructor(\n ctx: Context,\n snapshot: ReportSnapshot,\n pairingToken: PairingTokenSnapshot,\n rotatePairingToken: PairingTokenRotator,\n relayTester: RelayTester,\n pushTester: PushTester,\n ) {\n super(ctx, 'deeppilotReport', { namespace: 'deeppilot' })\n this.snapshot = snapshot\n this.pairingToken = pairingToken\n this.rotatePairingToken = rotatePairingToken\n this.relayTester = relayTester\n this.pushTester = pushTester\n }\n\n async report(): Promise<DeepPilotReport> {\n return this.snapshot()\n }\n\n async revealToken(): Promise<string> {\n return this.pairingToken()\n }\n\n async rotateToken(): Promise<string> {\n return this.rotatePairingToken()\n }\n\n async testRelay(): Promise<RelayTestResult> {\n return this.relayTester()\n }\n\n async testPush(): Promise<PushTestResult> {\n return this.pushTester()\n }\n}\n","/**\n * Wire contract for the DeepPilot report Remote (`deeppilot/report`).\n *\n * The web settings page needs the bridge's live facts (paired devices,\n * connections, token state) — those live Host-side. This module is the single\n * source both halves share: the strict result codec (hand-rolled, so the\n * client bundle needs no schema library) and the exact descriptor object.\n *\n * @module dsh-deeppilot/report-wire\n */\n\nimport type { InvocationDescriptor, TypertRemoteContribution, TypertSchema } from '@deepseek-ai/dsh-typert-protocol'\n\n/** One paired device row in the report. */\nexport interface ReportDevice {\n deviceId: string\n deviceName: string\n appVersion: string\n firstSeenTs: number\n lastSeenTs: number\n /** Present once the device registered an APNs token (token itself never leaves the host). */\n apns?: { environment: 'development' | 'production'; updatedAt: number }\n}\n\n/** One step of the relay connectivity self-test. */\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n message: string\n latencyMs?: number\n}\n\n/** Result of deeppilot/testRelay. */\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\n/** Per-device delivery result of deeppilot/testPush. */\nexport interface PushTestDeviceResult {\n name: string\n environment: string\n /** sent | invalid-token | failed — mirrors the dispatch outcome contract. */\n outcome: string\n /** Apple's raw rejection reason when present (BadDeviceToken, …). */\n reason?: string\n /** First 10 hex chars of the stored token, for freshness verification. */\n tokenFingerprint?: string\n}\n\nexport interface PushTestResult {\n transport: 'apns' | 'relay' | 'none'\n /** sent = at least one accepted delivery; no-targets / not-configured / failed. */\n overall: 'sent' | 'failed' | 'no-targets' | 'not-configured'\n message?: string\n results: PushTestDeviceResult[]\n}\n\n/** Everything the settings page renders for the bridge. */\nexport interface DeepPilotReport {\n protocolVersion: number\n serverVersion: string\n /** Host plugin version (from package.json). Same value as serverVersion,\n * surfaced under a stable name so the wire contract does not change if\n * the legacy field is ever repurposed. */\n pluginVersion: string\n /** True once the host's background GitHub check has confirmed a newer\n * stable release exists. Undefined / false ⇒ the UI shows only the\n * version line, never the \"new version\" hint. */\n updateAvailable?: boolean\n /** GitHub release page URL, when an update is available. */\n releaseUrl?: string\n /** Whether the master bridge switch is on. */\n enabled: boolean\n /** Absolute token file path (never the token itself). */\n tokenPath: string\n /** Whether the token file exists and is readable. */\n tokenReady: boolean\n /** Currently connected phone sockets. */\n activeConnections: number\n historyBufferMax: number\n debug: boolean\n /** Private IPv4 candidates for local QR pairing. */\n lanAddresses: string[]\n remote: {\n provider: 'tailscale-funnel'\n phase: 'disabled' | 'starting' | 'login_required' | 'online' | 'error' | 'unavailable' | 'stopped'\n publicURL?: string\n authURL?: string\n message?: string\n updatedAt: number\n }\n devices: ReportDevice[]\n}\n\n/** The npm package identity both contribution registrations claim. */\nexport const REPORT_REMOTE_PACKAGE = 'dsh-deeppilot'\n\n/** Canonical `<namespace>/<method>` endpoint of the report Remote. */\nexport const REPORT_ENDPOINT = 'deeppilot/report'\n\n/** Explicit, user-triggered endpoint for revealing the pairing secret. */\nexport const REVEAL_TOKEN_ENDPOINT = 'deeppilot/revealToken'\n\n/**\n * Explicit, user-triggered endpoint that replaces the pairing secret. The old\n * token stops working immediately; the fresh one is returned so the page can\n * show/QR it right away.\n */\nexport const ROTATE_TOKEN_ENDPOINT = 'deeppilot/rotateToken'\n\nfunction reject(field: string): never {\n throw new TypeError(`deeppilot/report result: invalid ${field}`)\n}\n\nfunction str(source: Record<string, unknown>, key: string, field: string): string {\n const value = source[key]\n if (typeof value !== 'string') reject(field)\n return value\n}\nfunction num(source: Record<string, unknown>, key: string, field: string): number {\n const value = source[key]\n if (typeof value !== 'number' || !Number.isFinite(value)) reject(field)\n return value\n}\nfunction bool(source: Record<string, unknown>, key: string, field: string): boolean {\n const value = source[key]\n if (typeof value !== 'boolean') reject(field)\n return value\n}\nfunction rec(value: unknown, field: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) reject(field)\n return value as Record<string, unknown>\n}\n\nfunction parseDevice(value: unknown): ReportDevice {\n const s = rec(value, 'device')\n let apns: ReportDevice['apns']\n if (s.apns !== undefined) {\n const a = rec(s.apns, 'device.apns')\n const environment = str(a, 'environment', 'device.apns.environment')\n if (environment !== 'development' && environment !== 'production') reject('device.apns.environment')\n apns = { environment, updatedAt: num(a, 'updatedAt', 'device.apns.updatedAt') }\n }\n return {\n deviceId: str(s, 'deviceId', 'device.deviceId'),\n deviceName: str(s, 'deviceName', 'device.deviceName'),\n appVersion: str(s, 'appVersion', 'device.appVersion'),\n firstSeenTs: num(s, 'firstSeenTs', 'device.firstSeenTs'),\n lastSeenTs: num(s, 'lastSeenTs', 'device.lastSeenTs'),\n ...(apns ? { apns } : {}),\n }\n}\n\nfunction parseRemote(value: unknown): DeepPilotReport['remote'] {\n const s = rec(value, 'remote')\n const provider = str(s, 'provider', 'remote.provider')\n const phase = str(s, 'phase', 'remote.phase')\n if (provider !== 'tailscale-funnel') reject('remote.provider')\n const phases = ['disabled', 'starting', 'login_required', 'online', 'error', 'unavailable', 'stopped']\n if (!phases.includes(phase)) reject('remote.phase')\n const publicURL = s.publicURL\n const authURL = s.authURL\n const message = s.message\n if (publicURL !== undefined && typeof publicURL !== 'string') reject('remote.publicURL')\n if (authURL !== undefined && typeof authURL !== 'string') reject('remote.authURL')\n if (message !== undefined && typeof message !== 'string') reject('remote.message')\n return {\n provider,\n phase: phase as DeepPilotReport['remote']['phase'],\n ...(typeof publicURL === 'string' ? { publicURL } : {}),\n ...(typeof authURL === 'string' ? { authURL } : {}),\n ...(typeof message === 'string' ? { message } : {}),\n updatedAt: num(s, 'updatedAt', 'remote.updatedAt'),\n }\n}\n\nfunction parseRelayTestStep(value: unknown): RelayTestStep {\n const st = rec(value, 'step')\n const id = str(st, 'id', 'step.id')\n if (id !== 'health' && id !== 'enroll') reject('step.id')\n const latencyMs = st.latencyMs\n if (latencyMs !== undefined && typeof latencyMs !== 'number') reject('step.latencyMs')\n return {\n id,\n ok: bool(st, 'ok', 'step.ok'),\n message: str(st, 'message', 'step.message'),\n ...(typeof latencyMs === 'number' ? { latencyMs } : {}),\n }\n}\n\nfunction parseRelayTestResult(value: unknown): RelayTestResult {\n const s = rec(value, 'result')\n const overall = str(s, 'overall', 'overall')\n if (overall !== 'ok' && overall !== 'failed') reject('overall')\n const stepsRaw = s.steps\n if (!Array.isArray(stepsRaw)) reject('steps')\n return {\n url: str(s, 'url', 'url'),\n overall,\n tokenIssued: bool(s, 'tokenIssued', 'tokenIssued'),\n steps: (stepsRaw as unknown[]).map(parseRelayTestStep),\n }\n}\n\nfunction parsePushTestResult(value: unknown): PushTestResult {\n const s = rec(value, 'result')\n const transport = str(s, 'transport', 'transport')\n if (transport !== 'apns' && transport !== 'relay' && transport !== 'none') reject('transport')\n const overall = str(s, 'overall', 'overall')\n const overalls = ['sent', 'failed', 'no-targets', 'not-configured']\n if (!overalls.includes(overall)) reject('overall')\n const resultsRaw = s.results\n if (!Array.isArray(resultsRaw)) reject('results')\n const results = (resultsRaw as unknown[]).map((value) => {\n const r = rec(value, 'device result')\n const reason = r.reason\n const tokenFingerprint = r.tokenFingerprint\n return {\n name: str(r, 'name', 'result.name'),\n environment: str(r, 'environment', 'result.environment'),\n outcome: str(r, 'outcome', 'result.outcome'),\n ...(typeof reason === 'string' && reason.length > 0 ? { reason } : {}),\n ...(typeof tokenFingerprint === 'string' && /^[0-9a-f]{1,32}$/.test(tokenFingerprint)\n ? { tokenFingerprint }\n : {}),\n }\n })\n const message = s.message\n return {\n transport,\n overall: overall as PushTestResult['overall'],\n ...(typeof message === 'string' && message.length > 0 ? { message } : {}),\n results,\n }\n}\n\nfunction parseReport(value: unknown): DeepPilotReport {\n const s = rec(value, 'report')\n const devices = s.devices\n const lanAddresses = s.lanAddresses\n if (!Array.isArray(devices)) reject('devices')\n if (!Array.isArray(lanAddresses) || lanAddresses.some((value) => typeof value !== 'string')) reject('lanAddresses')\n // Update fields are optional: an older Host (without the background\n // GitHub check) won't include them, and the UI must keep rendering.\n const releaseUrl = s.releaseUrl\n return {\n protocolVersion: num(s, 'protocolVersion', 'protocolVersion'),\n serverVersion: str(s, 'serverVersion', 'serverVersion'),\n pluginVersion: str(s, 'pluginVersion', 'pluginVersion'),\n ...(s.updateAvailable === true ? { updateAvailable: true } : {}),\n ...(typeof releaseUrl === 'string' && releaseUrl.length > 0\n ? { releaseUrl } : {}),\n enabled: bool(s, 'enabled', 'enabled'),\n tokenPath: str(s, 'tokenPath', 'tokenPath'),\n tokenReady: bool(s, 'tokenReady', 'tokenReady'),\n activeConnections: num(s, 'activeConnections', 'activeConnections'),\n historyBufferMax: num(s, 'historyBufferMax', 'historyBufferMax'),\n debug: bool(s, 'debug', 'debug'),\n lanAddresses: lanAddresses as string[],\n remote: parseRemote(s.remote),\n devices: (devices as unknown[]).map(parseDevice),\n }\n}\n\nexport const reportSchema: TypertSchema<DeepPilotReport> = { parse: parseReport }\n\nexport const relayTestSchema: TypertSchema<RelayTestResult> = { parse: parseRelayTestResult }\n\nexport const pushTestSchema: TypertSchema<PushTestResult> = { parse: parsePushTestResult }\n\nexport const pairingTokenSchema: TypertSchema<string> = {\n parse(value: unknown): string {\n if (typeof value !== 'string' || value.length < 32) {\n throw new TypeError('deeppilot/revealToken result: invalid token')\n }\n return value\n },\n}\n\n/**\n * Connectivity self-test for the push relay: health probe plus (when an\n * enroll key is on file) a real enrollment round-trip. Doubles as repair —\n * a token it issues is cached by the bridge.\n */\nexport const TEST_RELAY_ENDPOINT = 'deeppilot/testRelay'\n\n/**\n * Real-delivery push test: forces one synthetic notification down the active\n * push pathway to every registered device, ignoring connected/muted filters —\n * an explicit user action must always be able to prove the pipeline works.\n */\nexport const TEST_PUSH_ENDPOINT = 'deeppilot/testPush'\n\nexport const REPORT_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REPORT_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'report',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#DeepPilotReport`,\n schema: reportSchema,\n },\n}\n\nexport const REVEAL_TOKEN_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REVEAL_TOKEN_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'revealToken',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,\n schema: pairingTokenSchema,\n },\n}\n\nexport const ROTATE_TOKEN_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${ROTATE_TOKEN_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'rotateToken',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,\n schema: pairingTokenSchema,\n },\n}\n\nexport const TEST_RELAY_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_RELAY_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testRelay',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#RelayTestResult`,\n schema: relayTestSchema,\n },\n}\n\nexport const TEST_PUSH_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_PUSH_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testPush',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PushTestResult`,\n schema: pushTestSchema,\n },\n}\n\nconst INVOCATION_DESCRIPTORS = [\n REPORT_DESCRIPTOR,\n REVEAL_TOKEN_DESCRIPTOR,\n ROTATE_TOKEN_DESCRIPTOR,\n TEST_RELAY_DESCRIPTOR,\n TEST_PUSH_DESCRIPTOR,\n]\n\nexport const REPORT_HOST_CONTRIBUTION = {\n package: REPORT_REMOTE_PACKAGE,\n face: 'host' as const,\n schemas: [],\n invocations: INVOCATION_DESCRIPTORS,\n}\n\nexport const REPORT_REMOTE_CONTRIBUTION: TypertRemoteContribution = {\n package: REPORT_REMOTE_PACKAGE,\n descriptors: INVOCATION_DESCRIPTORS,\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { DeepPilotReportService } from './report-service.ts'\nimport type { PairingTokenRotator, PairingTokenSnapshot, PushTester, RelayTester, ReportSnapshot } from './report-service.ts'\nimport { REPORT_HOST_CONTRIBUTION } from './report-wire.ts'\n\ninterface TypertContributionRegistry {\n register(contribution: typeof REPORT_HOST_CONTRIBUTION): () => void | Promise<void>\n}\n\n/**\n * Provide the report service and register its Remote descriptor. Rides an\n * optional `typert` inject: profiles without the web stack never activate it.\n */\nexport function applyReportRemote(\n ctx: Context,\n snapshot: ReportSnapshot,\n pairingToken: PairingTokenSnapshot,\n rotatePairingToken: PairingTokenRotator,\n relayTester: RelayTester,\n pushTester: PushTester,\n): void {\n ctx.inject(['typert'], (remoteCtx) => {\n new DeepPilotReportService(remoteCtx, snapshot, pairingToken, rotatePairingToken, relayTester, pushTester)\n const registry = remoteCtx.typert as unknown as TypertContributionRegistry\n const unregister = registry.register(REPORT_HOST_CONTRIBUTION)\n remoteCtx.effect(() => () => void unregister(), 'dsh-deeppilot: report remote')\n })\n}\n","/**\n * Relay connectivity self-test used by the settings page \"测试中继\" button.\n *\n * Two steps mirror what zero-touch enrollment actually does:\n * 1. health — GET {url}/healthz → is the relay reachable?\n * 2. enroll — POST {url}/v1/enroll → does the shared key grant a token?\n *\n * The enroll step doubles as a repair path: a token it issues is handed back\n * via `onEnrolled` so the bridge caches it and flips push readiness without\n * waiting for the next app registration.\n *\n * Transport is injectable (`fetchImpl`) so tests never touch the network.\n */\n\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n /** Human-readable, user-facing (zh) outcome; safe to render verbatim. */\n message: string\n latencyMs?: number\n}\n\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n /** True when the probe produced/cached a usable relay token. */\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\nexport interface RelayProbeOptions {\n url: string\n /** Stable per-bridge identity for enrollment; generated upstream if absent. */\n clientId?: string\n /** Distributor's shared key; absent → enroll step reports why it cannot run. */\n enrollKey?: string\n /** True when the user configured relayToken manually (enroll unnecessary). */\n manualToken?: boolean\n timeoutMs?: number\n fetchImpl?: typeof fetch\n /** Called with an issued token so the caller can persist it. */\n onEnrolled?: (token: string) => void\n}\n\nconst DEFAULT_TIMEOUT_MS = 6_000\n\nasync function requestJson(\n fetchImpl: typeof fetch,\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<{ status: number; body: unknown }> {\n const response = await fetchImpl(url, {\n ...init,\n signal: AbortSignal.timeout(timeoutMs),\n })\n let body: unknown = null\n try {\n body = await response.json()\n } catch {\n // non-JSON body keeps body null; status still drives the verdict\n }\n return { status: response.status, body }\n}\n\nexport async function runRelayProbe(options: RelayProbeOptions): Promise<RelayTestResult> {\n const base = options.url.trim().replace(/\\/+$/, '')\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const fetchImpl = options.fetchImpl ?? fetch\n const steps: RelayTestStep[] = []\n let tokenIssued = false\n\n // ---- step 1: reachability ----\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/healthz`,\n { method: 'GET' },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n if (status === 200 && (body as { ok?: unknown })?.ok === true) {\n steps.push({ id: 'health', ok: true, message: '中继服务可达', latencyMs })\n } else {\n steps.push({ id: 'health', ok: false, message: `中继响应异常(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'health', ok: false, message: '无法连接中继:' + reason })\n }\n\n // ---- step 2: enrollment ----\n if ((options.manualToken ?? false)) {\n steps.push({ id: 'enroll', ok: true, message: '已手动配置 relayToken,跳过注册验证' })\n } else if (!options.enrollKey) {\n steps.push({ id: 'enroll', ok: false, message: '尚无注册密钥:等待分发版 App 首次注册后才能验证注册' })\n } else {\n const clientId = options.clientId ?? 'u_' + Math.random().toString(36).slice(2)\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/v1/enroll`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey: options.enrollKey }),\n },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n const token = (body as { token?: unknown })?.token\n if (status === 200 && typeof token === 'string' && token.startsWith('rl_')) {\n tokenIssued = true\n options.onEnrolled?.(token)\n steps.push({ id: 'enroll', ok: true, message: '注册成功,已取得推送凭证', latencyMs })\n } else if (status === 403) {\n steps.push({ id: 'enroll', ok: false, message: '注册被拒:注册密钥不匹配(检查 App 内 DSPushEnrollKey 与服务器 RELAY_ENROLL_KEY)', latencyMs })\n } else if (status === 429) {\n steps.push({ id: 'enroll', ok: false, message: '尝试过于频繁,稍后再试', latencyMs })\n } else {\n steps.push({ id: 'enroll', ok: false, message: `注册失败(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'enroll', ok: false, message: '注册请求失败:' + reason })\n }\n }\n\n const executed = steps.filter((step) => step.ok !== undefined)\n const overall: RelayTestResult['overall'] =\n steps.length > 0 && steps.some((step) => step.id === 'health' && step.ok) &&\n executed.every((step) => step.ok)\n ? 'ok'\n : 'failed'\n return { url: base, overall, tokenIssued, steps }\n}\n","/**\n * Minimal APNs provider client (HTTP/2) with zero npm dependencies.\n *\n * Implements exactly what the bridge needs:\n * - ES256 provider token (JWT) signed with an Apple .p8 key, refreshed under\n * the 1-hour freshness window Apple enforces;\n * - one long-lived HTTP/2 session per environment, recreated transparently\n * after GOAWAY/errors;\n * - alert pushes carrying the notify projection (category/thread/collapse),\n * with `interruption-level: time-sensitive` for approval/question events;\n * - outcome classification so callers can prune dead device tokens.\n *\n * Privacy: logs carry outcomes and masked token prefixes only — never message\n * bodies or full tokens.\n */\n\nimport { connect as http2Connect, type ClientHttp2Session } from 'node:http2'\nimport { createPrivateKey, sign as cryptoSign, type KeyObject } from 'node:crypto'\nimport { readFile } from 'node:fs/promises'\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushNotification } from './protocol.ts'\n\nexport interface ApnsClientOptions {\n teamId: string\n keyId: string\n /** Absolute path to the .p8 private key file. */\n keyPath: string\n bundleId: string\n debug?: boolean\n log: (message: string) => void\n}\n\nexport type ApnsOutcome = 'sent' | 'invalid-token' | 'failed'\n\n/** Outcome plus Apple's rejection reason (when present) for diagnosis. */\nexport interface ApnsSendResult {\n outcome: ApnsOutcome\n reason?: string\n}\n\nconst PROVIDER_TOKEN_TTL_MS = 50 * 60 * 1000 // Apple allows 1h; refresh early.\nconst REQUEST_TIMEOUT_MS = 10_000\n\n/** base64url without padding. */\nfunction b64url(input: Buffer | string): string {\n return Buffer.from(input).toString('base64url')\n}\n\n/** Sign one ES256 JWT for the given signing input with a P-256 private key. */\nexport function es256Jwt(signingInput: string, key: KeyObject): string {\n const signature = cryptoSign('sha256', Buffer.from(signingInput, 'utf8'), {\n key,\n // JOSE/ES256 wants raw r||s; node emits DER by default.\n dsaEncoding: 'ieee-p1363',\n })\n return signingInput + '.' + b64url(signature)\n}\n\n/** Strip PEM armor from a .p8 file and decode to PKCS#8 DER. */\nexport function p8ToDer(pem: string): Buffer {\n const body = pem\n .replace(/-----BEGIN PRIVATE KEY-----/, '')\n .replace(/-----END PRIVATE KEY-----/, '')\n .replace(/\\s+/g, '')\n return Buffer.from(body, 'base64')\n}\n\nexport interface LoadedKey {\n key: KeyObject\n}\n\n/** Pure payload builder so tests can assert the wire format without sockets. */\nexport function apnsPayload(notification: PushNotification): Record<string, unknown> {\n const timeSensitive = notification.category === 'approval.required' || notification.category === 'question.asked'\n return {\n aps: {\n alert: {\n title: notification.title.slice(0, 120),\n body: notification.body.slice(0, 200),\n },\n sound: 'default',\n category: notification.category,\n 'thread-id': notification.sessionId.slice(0, 64),\n ...(timeSensitive ? { 'interruption-level': 'time-sensitive' } : {}),\n },\n sessionId: notification.sessionId,\n notificationId: notification.notificationId,\n kind: notification.category,\n }\n}\n\n/** One delivery request: the notify projection plus its target device token.\n * The environment comes from the device's own registration (its build kind),\n * so one client serves both sandbox and production devices. */\nexport interface ApnsSendRequest extends PushNotification {\n deviceToken: string\n environment: ApnsEnvironment\n}\n\n/** collapse-id accepts ≤64 bytes of ASCII; keep it stable per session+event. */\nexport function collapseIdFor(notification: PushNotification): string {\n const raw = `${notification.category}:${notification.sessionId}`\n return raw.replace(/[^a-zA-Z0-9.:-]/g, '').slice(0, 64)\n}\n\nfunction authorityFor(environment: ApnsEnvironment): string {\n return environment === 'production' ? 'api.push.apple.com' : 'api.sandbox.push.apple.com'\n}\n\nexport class ApnsClient {\n private readonly log: (message: string) => void\n private readonly debug: boolean\n private readonly opts: ApnsClientOptions\n\n private providerToken = ''\n private providerTokenIssuedAt = 0\n private key: KeyObject | undefined\n\n constructor(opts: ApnsClientOptions) {\n this.opts = opts\n this.log = opts.log\n this.debug = opts.debug === true\n }\n\n async dispose(): Promise<void> {\n const sessions = [...this.sessions.values()]\n this.sessions.clear()\n await Promise.all(\n sessions\n .filter((session) => !session.destroyed)\n .map((session) => new Promise<void>((resolve) => session.close(() => resolve()))),\n )\n }\n\n // ---------- credentials ----------\n\n private async ensureProviderToken(): Promise<string> {\n if (this.providerToken && Date.now() - this.providerTokenIssuedAt < PROVIDER_TOKEN_TTL_MS) {\n return this.providerToken\n }\n if (!this.key) {\n const pem = await readFile(this.opts.keyPath, 'utf8')\n this.key = createPrivateKey({ key: p8ToDer(pem), format: 'der', type: 'pkcs8' })\n }\n const issuedAt = Math.floor(Date.now() / 1000)\n const header = b64url(JSON.stringify({ alg: 'ES256', kid: this.opts.keyId }))\n const claims = b64url(JSON.stringify({ iss: this.opts.teamId, iat: issuedAt }))\n this.providerToken = es256Jwt(`${header}.${claims}`, this.key)\n this.providerTokenIssuedAt = Date.now()\n return this.providerToken\n }\n\n // ---------- transport ----------\n\n /** One long-lived HTTP/2 session per Apple host (sandbox + production). */\n private readonly sessions = new Map<string, ClientHttp2Session>()\n\n private ensureSession(authority: string): ClientHttp2Session {\n const existing = this.sessions.get(authority)\n if (existing && !existing.destroyed && !existing.closed) return existing\n const session = http2Connect(`https://${authority}`)\n // Keep a handler attached so background errors never become uncaught\n // exceptions; the next send() recreates the session when dead.\n session.on('error', (error) => {\n if (this.debug) this.log('apns session error (' + authority + '): ' + String(error))\n this.sessions.delete(authority)\n })\n this.sessions.set(authority, session)\n return session\n }\n\n /**\n * Deliver one alert. Never throws — every failure path resolves to an\n * outcome so fan-out loops cannot crash the host on a flaky network.\n */\n async send(request: ApnsSendRequest): Promise<ApnsSendResult> {\n const { deviceToken, environment, ...notification } = request\n let stream: ReturnType<ClientHttp2Session['request']> | undefined\n try {\n const token = await this.ensureProviderToken()\n const body = JSON.stringify(apnsPayload(notification))\n const session = this.ensureSession(authorityFor(environment))\n const done = new Promise<ApnsSendResult>((resolve) => {\n const req = session.request({\n [':method']: 'POST',\n [':path']: '/3/device/' + deviceToken,\n authorization: 'bearer ' + token,\n 'apns-topic': this.opts.bundleId,\n 'apns-push-type': 'alert',\n 'apns-priority': '10',\n // Stale approvals arriving hours later are worse than none.\n 'apns-expiration': String(Math.floor(Date.now() / 1000) + 3600),\n 'apns-collapse-id': collapseIdFor(notification),\n 'content-type': 'application/json',\n 'content-length': String(Buffer.byteLength(body)),\n })\n stream = req\n let status = 0\n let responseBody = ''\n const settle = (outcome: ApnsOutcome, reason?: string): void => {\n if (this.debug) {\n this.log(`apns ${outcome}${reason ? ' (' + reason + ')' : ''} (${this.maskToken(deviceToken)})`)\n }\n resolve(reason !== undefined && reason !== '' ? { outcome, reason } : { outcome })\n }\n const timer = setTimeout(() => {\n req.close()\n settle('failed')\n }, REQUEST_TIMEOUT_MS)\n timer.unref?.()\n req.on('response', (headers) => {\n status = Number(headers[':status'] ?? 0)\n })\n req.on('data', (chunk: Buffer) => {\n responseBody += chunk.toString('utf8')\n })\n req.on('error', () => {\n clearTimeout(timer)\n settle('failed')\n })\n req.on('end', () => {\n clearTimeout(timer)\n if (status === 200) return settle('sent')\n let reason = ''\n try {\n reason = String((JSON.parse(responseBody) as { reason?: string }).reason ?? '')\n } catch {\n // non-JSON body; fall through with empty reason\n }\n if (status !== 200 && !reason) reason = 'HTTP ' + String(status)\n if (reason === 'Unregistered' || reason === 'BadDeviceToken') return settle('invalid-token', reason)\n if (this.debug) this.log(`apns rejected status=${status} reason=${reason}`)\n settle('failed', reason)\n })\n req.end(body)\n })\n return await done\n } catch (error) {\n // Credential load failures and dead sessions land here; force both to be\n // rebuilt on the next attempt.\n this.key = undefined\n this.providerToken = ''\n this.sessions.clear()\n if (this.debug) this.log('apns send failed: ' + String(error))\n return { outcome: 'failed', reason: String(error).slice(0, 120) }\n } finally {\n try { stream?.close() } catch { /* already closed */ }\n }\n }\n\n maskToken(token: string): string {\n return token.length <= 10 ? '…' : token.slice(0, 6) + '…' + token.slice(-4)\n }\n}\n","/**\n * Client for the operator-run push relay (relay/server.js). Used when the App\n * is distributed under the operator's team: bridges cannot hold the\n * distributor's .p8, so they forward notify projections over HTTPS and let\n * the relay do APNs.\n *\n * Outcome contract mirrors src/apns.ts exactly so the dispatcher prunes dead\n * tokens identically on either path:\n * sent | invalid-token (Unregistered/BadDeviceToken) | failed\n */\n\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushNotification } from './protocol.ts'\n\nexport interface RelayClientOptions {\n /** Base URL of the relay, e.g. https://relay.example.com */\n url: string\n /** Per-user bearer token issued by the relay operator (or via enroll). */\n token?: string\n timeoutMs?: number\n debug?: boolean\n log: (message: string) => void\n}\n\nexport interface RelaySendRequest {\n deviceToken: string\n environment: ApnsEnvironment\n notification: PushNotification\n}\n\nexport class RelayClient {\n private readonly base: string\n private readonly token: string\n private readonly timeoutMs: number\n private readonly debug: boolean\n private readonly log: (message: string) => void\n\n constructor(opts: RelayClientOptions) {\n this.base = opts.url.trim().replace(/\\/+$/, '')\n this.token = (opts.token ?? '').trim()\n this.timeoutMs = opts.timeoutMs ?? 10_000\n this.debug = opts.debug === true\n this.log = opts.log\n }\n\n /**\n * Zero-touch enrollment: exchange the distributor's shared key (baked into\n * the distributed app) for a stable per-bridge bearer token. Idempotent —\n * relays derive the same token for the same clientId. Returns null on any\n * failure; callers treat that as \"not enrolled yet\", not as an error.\n */\n async enroll(clientId: string, enrollKey: string): Promise<string | null> {\n try {\n const response = await fetch(this.base + '/v1/enroll', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (!response.ok) {\n if (this.debug) this.log(`enroll http ${response.status}`)\n return null\n }\n const body = (await response.json()) as { token?: unknown }\n if (typeof body.token === 'string' && body.token.startsWith('rl_')) return body.token\n if (this.debug) this.log('enroll returned no usable token')\n return null\n } catch (error) {\n if (this.debug) this.log('enroll failed: ' + String(error))\n return null\n }\n }\n\n async send(request: RelaySendRequest): Promise<{ outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }> {\n try {\n const response = await fetch(this.base + '/v1/push', {\n method: 'POST',\n headers: {\n authorization: 'Bearer ' + this.token,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n deviceToken: request.deviceToken,\n environment: request.environment,\n notification: request.notification,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (response.status === 401 || response.status === 429) {\n // Credential/quota problems are configuration errors, not token death;\n // surface as failed so the user fixes the config instead of silently\n // losing registrations.\n if (this.debug) this.log(`relay rejected status=${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n if (!response.ok) {\n if (this.debug) this.log(`relay http ${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n const body = (await response.json()) as { outcome?: string; reason?: string }\n if (body.outcome === 'sent') return { outcome: 'sent' }\n if (body.outcome === 'invalid-token') return { outcome: 'invalid-token', reason: body.reason }\n if (this.debug) this.log('relay outcome=' + String(body.outcome) + ' reason=' + String(body.reason ?? ''))\n return { outcome: 'failed', reason: body.reason }\n } catch (error) {\n if (this.debug) this.log('relay send failed: ' + String(error))\n const message = error instanceof Error ? error.message.slice(0, 120) : String(error).slice(0, 120)\n return { outcome: 'failed', reason: message }\n }\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { access, mkdir } from 'node:fs/promises'\nimport { constants as fsConstants } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { expandHome } from './token.ts'\n\nexport type RemotePhase =\n | 'disabled'\n | 'starting'\n | 'login_required'\n | 'online'\n | 'error'\n | 'unavailable'\n | 'stopped'\n\nexport interface RemoteStatus {\n provider: 'tailscale-funnel'\n phase: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n updatedAt: number\n}\n\nexport interface RemoteSupervisorOptions {\n enabled: boolean\n hostname: string\n statePath: string\n helperPath?: string\n funnelPort?: 443 | 8443 | 10000\n log: (message: string) => void\n}\n\ninterface HelperEvent {\n phase?: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n}\n\nconst RESTART_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 16_000, 30_000]\n\nexport const DEFAULT_REMOTE_HOSTNAME = 'dsh-deeppilot'\n\n/** Preserve custom node names while migrating every pre-DeepPilot default. */\nexport function normalizeRemoteHostname(value: string | undefined): string {\n const hostname = value?.trim() ?? ''\n if (hostname === '' || ['dsh-phone', 'dsh-pocket', 'harnesspocket'].includes(hostname.toLowerCase())) {\n return DEFAULT_REMOTE_HOSTNAME\n }\n return hostname\n}\n\n/** Parse one helper IPC line without ever evaluating or interpolating it. */\nexport function parseHelperEvent(line: string): HelperEvent | null {\n try {\n const value = JSON.parse(line) as Record<string, unknown>\n const phases: RemotePhase[] = ['starting', 'login_required', 'online', 'error', 'stopped']\n if (typeof value.phase !== 'string' || !phases.includes(value.phase as RemotePhase)) return null\n return {\n phase: value.phase as RemotePhase,\n ...(typeof value.publicURL === 'string' ? { publicURL: value.publicURL } : {}),\n ...(typeof value.authURL === 'string' ? { authURL: value.authURL } : {}),\n ...(typeof value.message === 'string' ? { message: value.message.slice(0, 500) } : {}),\n }\n } catch {\n return null\n }\n}\n\n/** Build the list of candidate locations for the embedded tunnel helper, in\n * priority order. The first existing executable wins at start() time. The\n * order matters: explicit config (handled by the caller) > npm install\n * layout > DSH-bundled layout > user data dir. */\nexport function bundledHelperCandidates(): string[] {\n const here = dirname(fileURLToPath(import.meta.url))\n const pkgRoot = resolve(here, '..')\n const fileName = process.platform === 'win32' ? 'dsh-deeppilot-tunnel.exe' : 'dsh-deeppilot-tunnel'\n const platformDir = `${process.platform}-${process.arch}`\n\n const candidates: string[] = []\n // 1. Standard npm layout: <pkgRoot>/bin/<os>-<arch>/<file>\n candidates.push(resolve(pkgRoot, 'bin', platformDir, fileName))\n // 2. DSH sometimes relocates lib/ into apps/web or a build cache while\n // leaving bin/ beside the original package root. Try a few hops up.\n candidates.push(resolve(pkgRoot, '..', '..', '..', 'node_modules', 'dsh-deeppilot', 'bin', platformDir, fileName))\n candidates.push(resolve(pkgRoot, '..', '..', 'dsh-deeppilot', 'bin', platformDir, fileName))\n candidates.push(resolve(pkgRoot, '..', '..', '..', '..', 'node_modules', 'dsh-deeppilot', 'bin', platformDir, fileName))\n\n // 3. As a last resort, ask the loader to resolve the binary via Node's\n // package resolution. Works when bin/ is shipped but import.meta.url\n // points somewhere unexpected.\n try {\n const require = createRequire(import.meta.url)\n const resolved = require.resolve(`dsh-deeppilot/bin/${platformDir}/${fileName}`)\n if (!candidates.includes(resolved)) candidates.push(resolved)\n } catch {\n /* package.json exports field may not list bin/ — ignore */\n }\n\n // 4. User data dir fallback so power users can drop a custom build in.\n const home = process.env.DSH_HOME?.trim()\n || process.env.HOME\n || process.env.USERPROFILE\n if (home && home.length > 0) {\n const dataDir = resolve(home, '.dsh')\n candidates.push(join(dataDir, 'deeppilot', 'bin', platformDir, fileName))\n }\n\n return candidates\n}\n\n/** Owns exactly one embedded tunnel helper and restarts it after failures. */\nexport class RemoteSupervisor {\n private child: ChildProcess | undefined\n private restartTimer: NodeJS.Timeout | undefined\n private restartAttempt = 0\n private stopping = false\n private statusValue: RemoteStatus\n\n constructor(private readonly options: RemoteSupervisorOptions) {\n this.statusValue = {\n provider: 'tailscale-funnel',\n phase: options.enabled ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n }\n }\n\n status(): RemoteStatus {\n return { ...this.statusValue }\n }\n\n async start(originURL: string): Promise<void> {\n if (!this.options.enabled || this.child !== undefined || this.stopping) return\n const statePath = expandHome(this.options.statePath)\n // Resolve a usable helper path. An explicit remote.helperPath wins; if it\n // is missing, fall back to a small set of candidate locations so that\n // DSH-bundled layouts and manually-dropped builds still work.\n const configured = this.options.helperPath?.trim() ?? ''\n const candidates = configured\n ? [expandHome(configured)]\n : bundledHelperCandidates()\n let helper: string | undefined\n let lastError: unknown\n for (const candidate of candidates) {\n try {\n await access(candidate, fsConstants.X_OK)\n helper = candidate\n break\n } catch (error) {\n lastError = error\n }\n }\n if (helper === undefined) {\n // A dispose() that raced these checks must not be overwritten by a\n // late failure report.\n if (this.stopping) return\n const platform = `${process.platform}-${process.arch}`\n const message = configured\n ? `embedded tunnel helper unavailable: ${configured}: ${String(lastError ?? 'not found')}`\n : `embedded tunnel helper not found for ${platform} (tried: ${candidates.join(', ')}); set remote.helperPath to override`\n this.setStatus({ phase: 'unavailable', message })\n return\n }\n try {\n await mkdir(statePath, { recursive: true, mode: 0o700 })\n } catch (error) {\n if (this.stopping) return\n this.setStatus({ phase: 'unavailable', message: `cannot create remote state dir: ${String(error)}` })\n return\n }\n\n // dispose() may have run while the filesystem checks above were in\n // flight; spawning afterwards would orphan a helper process that nothing\n // will ever reap (dispose already returned seeing no child).\n if (this.stopping) return\n\n this.setStatus({ phase: 'starting', message: undefined })\n const child = spawn(helper, [\n '--origin', originURL,\n '--hostname', normalizeRemoteHostname(this.options.hostname),\n '--state-dir', statePath,\n '--port', String(this.options.funnelPort ?? 443),\n ], {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n PATH: process.env.PATH ?? '/usr/bin:/bin',\n TMPDIR: process.env.TMPDIR ?? '/tmp',\n },\n })\n this.child = child\n if (child.stdout === null || child.stderr === null) {\n this.setStatus({ phase: 'error', message: 'helper stdio unavailable' })\n child.kill('SIGTERM')\n return\n }\n\n let stdoutBuffer = ''\n child.stdout.setEncoding('utf8')\n child.stdout.on('data', (chunk: string) => {\n stdoutBuffer += chunk\n const lines = stdoutBuffer.split('\\n')\n stdoutBuffer = lines.pop() ?? ''\n for (const line of lines) this.acceptLine(line)\n })\n\n let stderrBuffer = ''\n child.stderr.setEncoding('utf8')\n child.stderr.on('data', (chunk: string) => {\n stderrBuffer = (stderrBuffer + chunk).slice(-2_000)\n })\n\n child.once('error', (error) => {\n this.setStatus({ phase: 'error', message: `helper launch failed: ${String(error)}` })\n })\n child.once('exit', (code, signal) => {\n if (this.child === child) this.child = undefined\n if (this.stopping) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n const detail = stderrBuffer.trim().split('\\n').at(-1)\n this.setStatus({\n phase: 'error',\n message: detail || `helper exited (${signal ?? String(code)})`,\n })\n this.scheduleRestart(originURL)\n })\n }\n\n async dispose(): Promise<void> {\n this.stopping = true\n if (this.restartTimer !== undefined) clearTimeout(this.restartTimer)\n this.restartTimer = undefined\n const child = this.child\n this.child = undefined\n if (child === undefined) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n await new Promise<void>((resolveDone) => {\n const force = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n }, 3_000)\n child.once('exit', () => {\n clearTimeout(force)\n resolveDone()\n })\n child.kill('SIGTERM')\n })\n this.setStatus({ phase: 'stopped', message: undefined })\n }\n\n private acceptLine(line: string): void {\n const event = parseHelperEvent(line)\n if (event === null || event.phase === undefined) return\n if (event.phase === 'online') this.restartAttempt = 0\n this.setStatus({ ...event, phase: event.phase })\n }\n\n private scheduleRestart(originURL: string): void {\n if (this.stopping || this.restartTimer !== undefined) return\n const delay = RESTART_DELAYS_MS[Math.min(this.restartAttempt, RESTART_DELAYS_MS.length - 1)]\n this.restartAttempt += 1\n this.restartTimer = setTimeout(() => {\n this.restartTimer = undefined\n void this.start(originURL)\n }, delay)\n }\n\n private setStatus(next: Partial<RemoteStatus> & Pick<RemoteStatus, 'phase'>): void {\n const cleared: Partial<RemoteStatus> = next.phase === 'online'\n ? { authURL: undefined, message: undefined }\n : next.phase === 'login_required'\n ? { publicURL: undefined, message: undefined }\n : next.phase === 'starting' || next.phase === 'stopped' || next.phase === 'disabled'\n ? { publicURL: undefined, authURL: undefined, message: undefined }\n : {}\n this.statusValue = {\n ...this.statusValue,\n ...cleared,\n ...next,\n updatedAt: Date.now(),\n }\n if (next.phase === 'online') {\n this.options.log('remote Funnel online')\n } else if (next.phase === 'login_required') {\n this.options.log('remote Funnel requires browser authorization')\n } else if (next.phase === 'error' || next.phase === 'unavailable') {\n this.options.log(`remote Funnel ${next.phase}: ${next.message ?? 'unknown error'}`)\n }\n }\n}\n","import { networkInterfaces } from 'node:os'\n\nexport function isPrivateIPv4(address: string): boolean {\n const octets = address.split('.').map(Number)\n if (octets.length !== 4 || octets.some((value) => !Number.isInteger(value) || value < 0 || value > 255)) {\n return false\n }\n const [a, b] = octets as [number, number, number, number]\n return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168)\n}\n\n/** Private IPv4 candidates, preferring physical en* interfaces over tunnels. */\nexport function localLANIPv4Addresses(): string[] {\n const candidates: Array<{ name: string; address: string }> = []\n for (const [name, entries] of Object.entries(networkInterfaces())) {\n for (const entry of entries ?? []) {\n if (entry.family === 'IPv4' && !entry.internal && isPrivateIPv4(entry.address)) {\n candidates.push({ name, address: entry.address })\n }\n }\n }\n const priority = (name: string): number => name === 'en0' ? 0 : name.startsWith('en') ? 1 : name.startsWith('bridge') ? 2 : 3\n candidates.sort((left, right) => priority(left.name) - priority(right.name) || left.name.localeCompare(right.name))\n return [...new Set(candidates.map(({ address }) => address))]\n}\n","/**\n * Lightweight self-update check for dsh-deeppilot.\n *\n * On Host boot we ask the GitHub Releases API (REST) which is the latest\n * stable tag, compare it to the installed plugin version, and surface a\n * \"newer release exists\" flag plus the GitHub release URL through the\n * report Remote. The settings page renders one small line at the bottom;\n * a successful check is enough — no manual button, no persistent cache\n * (the host process is the lifetime of the answer).\n *\n * Deliberately no third-party dependency: we use {@link https.request}\n * directly to keep parity with the rest of the project (remote-supervisor\n * uses node:http, host-bridge uses ws, etc).\n *\n * Failure policy: every network/parse error collapses to a single log\n * line and the in-memory snapshot stays \"unknown\". The bridge must never\n * crash because GitHub rate-limited us, returned a 5xx, or the user is\n * offline.\n */\n\nimport { request as httpsRequest, type RequestOptions } from 'node:https'\n\n/** GitHub repo (no .git suffix). Public, unauthenticated, low rate limit. */\nconst RELEASES_PATH = '/repos/Mars-Sea/dsh-deeppilot/releases'\n\n/** Hard ceiling on the network round-trip. The host must never hang. */\nconst FETCH_TIMEOUT_MS = 8_000\n\n/** Per-page limit. We only need the first stable release, but pre-releases\n * tend to be listed first; fetching 20 gives the comparator enough room. */\nconst PER_PAGE = 20\n\n/**\n * Public snapshot surfaced to the report Remote. `available` is `false`\n * until the network answer lands; on failure it stays `false` so the UI\n * simply doesn't render the \"new version\" hint.\n */\nexport interface UpdateInfo {\n /** Host plugin version (from package.json). */\n currentVersion: string\n /** True once a newer stable release has been found on GitHub. */\n available: boolean\n /** GitHub release page URL, when an update is available. */\n releaseUrl: string | null\n /** Newer stable tag, when an update is available. */\n latestVersion: string | null\n}\n\ninterface GitHubRelease {\n tag_name?: unknown\n prerelease?: unknown\n draft?: unknown\n html_url?: unknown\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction parseStableEntry(value: unknown): { tag: string; url: string | null } | null {\n if (!isPlainObject(value)) return null\n const tag = value.tag_name\n if (typeof tag !== 'string') return null\n if (value.prerelease === true || value.draft === true) return null\n if (parseStableTag(tag) === null) return null\n const url = value.html_url\n return { tag, url: typeof url === 'string' ? url : null }\n}\n\n/** Parse one stable release from the `tag_name` shape `vX.Y.Z` (the v is\n * optional; `1.2.3` is also accepted). Pre-release tags like `0.3.0-rc.1`\n * return null — the policy is \"stable channel only\". */\nexport function parseStableTag(tag: string): { major: number; minor: number; patch: number } | null {\n const match = /^v?(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(tag.trim())\n if (match === null) return null\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n }\n}\n\n/** Semver compare for X.Y.Z. Returns -1 / 0 / 1. */\nexport function compareSemver(a: string, b: string): -1 | 0 | 1 {\n const pa = parseStableTag(a)\n const pb = parseStableTag(b)\n if (pa === null && pb === null) return 0\n if (pa === null) return -1\n if (pb === null) return 1\n if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1\n if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1\n if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1\n return 0\n}\n\n/** Hit the GitHub Releases API. Resolves with the first stable release\n * GitHub returned, or null if the list contains no stable entries.\n * Network / parse errors reject — the caller is responsible for\n * collapsing them to a log line. */\nexport function fetchLatestStableRelease(): Promise<{ tag: string; url: string | null } | null> {\n return new Promise((resolve, reject) => {\n const options: RequestOptions = {\n method: 'GET',\n host: 'api.github.com',\n path: `${RELEASES_PATH}?per_page=${PER_PAGE}`,\n headers: {\n 'user-agent': 'dsh-deeppilot-update-check',\n 'accept': 'application/vnd.github+json',\n },\n }\n const req = httpsRequest(options, (res) => {\n const status = res.statusCode ?? 0\n if (status < 200 || status >= 300) {\n res.resume()\n reject(new Error(`github releases http ${status}`))\n return\n }\n const chunks: Buffer[] = []\n res.on('data', (chunk: Buffer) => chunks.push(chunk))\n res.on('end', () => {\n try {\n const body = Buffer.concat(chunks).toString('utf8')\n const parsed = JSON.parse(body) as unknown\n if (!Array.isArray(parsed)) {\n reject(new Error('github releases: response is not an array'))\n return\n }\n for (const entry of parsed) {\n const stable = parseStableEntry(entry)\n if (stable !== null) {\n resolve(stable)\n return\n }\n }\n resolve(null)\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)))\n }\n })\n res.on('error', (error) => reject(error))\n })\n req.setTimeout(FETCH_TIMEOUT_MS, () => {\n req.destroy(new Error('github releases: timeout after ' + FETCH_TIMEOUT_MS + 'ms'))\n })\n req.on('error', (error) => reject(error))\n req.end()\n })\n}\n\n/**\n * Process-wide check state. Constructed once in `apply()`, queried\n * synchronously by the report snapshot. The check itself fires once in\n * the background shortly after boot; the answer lives for the lifetime\n * of the host process — re-running the page in the Web UI does not\n * trigger another network call.\n */\nexport class UpdateChecker {\n private readonly log: (message: string) => void\n private readonly currentVersion: string\n private readonly fetchImpl: typeof fetchLatestStableRelease\n private readonly initialDelayMs: number\n private snapshot: UpdateInfo\n private inflight: Promise<void> | null = null\n\n constructor(options: {\n log: (message: string) => void\n currentVersion: string\n fetchImpl?: typeof fetchLatestStableRelease\n initialDelayMs?: number\n }) {\n this.log = options.log\n this.currentVersion = options.currentVersion\n this.fetchImpl = options.fetchImpl ?? fetchLatestStableRelease\n this.initialDelayMs = options.initialDelayMs ?? 2_000\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: false,\n releaseUrl: null,\n latestVersion: null,\n }\n }\n\n /** Return the current in-memory snapshot — safe to call from any host\n * thread. Never throws, never awaits. */\n get(): UpdateInfo {\n return this.snapshot\n }\n\n /**\n * Schedule one background refresh after the configured initial delay.\n * Used by the plugin entry to do the first check without blocking boot.\n */\n scheduleInitial(): void {\n if (this.initialDelayMs <= 0) {\n void this.runOnce()\n return\n }\n const timer = setTimeout(() => {\n void this.runOnce()\n }, this.initialDelayMs)\n if (typeof (timer as { unref?: () => void }).unref === 'function') {\n (timer as { unref: () => void }).unref()\n }\n }\n\n private async runOnce(): Promise<void> {\n if (this.inflight !== null) {\n await this.inflight\n return\n }\n const task = (async () => {\n try {\n const stable = await this.fetchImpl()\n if (stable === null) {\n // No stable release on GitHub (yet) — leave `available` false.\n return\n }\n if (compareSemver(stable.tag, this.currentVersion) > 0) {\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: true,\n releaseUrl: stable.url,\n latestVersion: stable.tag,\n }\n } else {\n // Up to date. Clear any previous \"available\" state so a host\n // restart after an upgrade immediately stops showing the hint.\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: false,\n releaseUrl: null,\n latestVersion: null,\n }\n }\n } catch (error) {\n this.log('update check failed: ' + (error instanceof Error ? error.message : String(error)))\n // Leave the previous snapshot in place. Even \"no answer\" is a\n // worse signal than the last successful check.\n }\n })()\n this.inflight = task.finally(() => {\n this.inflight = null\n })\n return this.inflight\n }\n\n /** No-op kept for API symmetry with the host lifecycle wiring. */\n dispose(): void {\n // The initial-delay timer is unref'd; nothing to track here.\n }\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'\nimport { randomBytes } from 'node:crypto'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport type { Duplex } from 'node:stream'\nimport type { Context } from '@deepseek-ai/cordis'\nimport z from '@deepseek-ai/schemastery'\nimport { WebSocketServer } from 'ws'\nimport { BridgeConnection } from './connection.ts'\nimport { HostBridge } from './host-bridge.ts'\nimport type { ApiProxyLike, PushOutlet } from './host-bridge.ts'\nimport { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'\nimport { applyReportRemote } from './report-remote.ts'\nimport { runRelayProbe } from './relay-test.ts'\nimport type { PushTestResult } from './report-wire.ts'\nimport { DeviceStore, bridgeDataDir, expandHome, loadOrCreateToken, migrateLegacyBridgeDataDir, tokenMatches, writeNewToken } from './token.ts'\nimport type { ApnsEnvironment } from './token.ts'\nimport { ApnsClient } from './apns.ts'\nimport { RelayClient } from './relay-client.ts'\nimport type { PushNotification } from './protocol.ts'\nimport {\n DEFAULT_REMOTE_HOSTNAME,\n normalizeRemoteHostname,\n RemoteSupervisor,\n type RemoteStatus,\n} from './remote-supervisor.ts'\nimport { localLANIPv4Addresses } from './local-address.ts'\nimport { UpdateChecker, type UpdateInfo } from './update-check.ts'\n\n/**\n * dsh-deeppilot — data bridge between the DSH host and DeepPilot\n * clients. Registers exactly one WebSocket upgrade route (/phone) plus an\n * optional health probe (/phone/health) on the existing web server. The web\n * UI is never touched.\n *\n * Data plane: an in-process HostBridge consumes apiProxy.events.mux()/host()\n * streams, mirrors session summaries, tracks pending approvals/questions,\n * and fans projected protocol-v1 pushes out to every connected device.\n *\n * Protocol: src/protocol.ts, v1. The private app repository carries the\n * matching normative document and Swift models.\n */\n\nexport const name = 'deeppilot'\n\nexport { HostBridge } from './host-bridge.ts'\n\n/** No eager service requirement: profiles without a web stack simply skip. */\nexport const inject: string[] = []\n\nexport interface Config {\n /** Master switch; when false the plugin activates and does nothing. */\n enabled?: boolean\n /** Pairing token file (0600); generated on first boot when missing. */\n authTokenPath?: string\n /** Paired-device registry JSON path. */\n devicesPath?: string\n /** Replay ring buffer bound (frames) per deployment. */\n historyBufferMax?: number\n /** Verbose per-frame diagnostics (never prints token or message bodies). */\n debug?: boolean\n /** Optional embedded remote transport. Reconciled when settings change. */\n remote?: {\n enabled?: boolean\n provider?: string\n hostname?: string\n statePath?: string\n helperPath?: string\n funnelPort?: number\n }\n /**\n * Offline push (F-9). provider 'apns' sends direct Apple Push Notification\n * deliveries from this Mac — outbound-only, no relay server, requires the\n * user's own Apple developer credentials. provider 'relay' forwards notify\n * projections to an operator-run relay (relay/server.js) holding the\n * distributor's key — used when the App ships via TestFlight/App Store.\n *\n * Deliberately NO environment knob here: each device reports its own\n * environment when registering (derived from its build kind), and\n * deliveries route per device — mixed dev/TestFlight phones coexist.\n */\n push?: {\n /** 'none' (default) | 'apns' | 'relay'. */\n provider?: string\n // --- apns ---\n /** Apple Developer team id (JWT iss claim). */\n teamId?: string\n /** APNs auth key id (JWT kid header). */\n keyId?: string\n /** .p8 private key path; generated keys live under the bridge data dir. */\n keyPath?: string\n /** App bundle id — the apns-topic header. */\n bundleId?: string\n // --- relay ---\n /** Relay base URL (https). See relay/README.md. */\n relayUrl?: string\n /** Per-user bearer token issued by the relay operator. */\n relayToken?: string\n }\n}\n\n/** Operator-run relay used by distributed builds; overridable via config. */\nconst DEFAULT_RELAY_URL = 'https://pilot.hailab.dev'\n\nexport const Config = z.object({\n enabled: z.boolean().default(true),\n authTokenPath: z.string().default(join(bridgeDataDir(), 'auth-token')),\n devicesPath: z.string().default(join(bridgeDataDir(), 'devices.json')),\n historyBufferMax: z.natural().min(100).default(2000),\n debug: z.boolean().default(false),\n remote: z.object({\n enabled: z.boolean().default(false),\n provider: z.string().default('tailscale-funnel'),\n hostname: z.string().default(DEFAULT_REMOTE_HOSTNAME),\n statePath: z.string().default(join(bridgeDataDir(), 'tailscale')),\n helperPath: z.string().default(''),\n funnelPort: z.natural().default(443),\n }).default({\n enabled: false,\n provider: 'tailscale-funnel',\n hostname: DEFAULT_REMOTE_HOSTNAME,\n statePath: join(bridgeDataDir(), 'tailscale'),\n helperPath: '',\n funnelPort: 443,\n }),\n push: z.object({\n provider: z.string().default('none'),\n teamId: z.string().default(''),\n keyId: z.string().default(''),\n keyPath: z.string().default(join(bridgeDataDir(), 'apns', 'AuthKey.p8')),\n bundleId: z.string().default('dev.hailab.deeppilot'),\n relayUrl: z.string().default(DEFAULT_RELAY_URL),\n relayToken: z.string().default(''),\n }).default({\n provider: 'none',\n teamId: '',\n keyId: '',\n keyPath: join(bridgeDataDir(), 'apns', 'AuthKey.p8'),\n bundleId: 'dev.hailab.deeppilot',\n relayUrl: DEFAULT_RELAY_URL,\n relayToken: '',\n }),\n})\n\ninterface WebServerLike {\n register(route: {\n kind: 'exact' | 'prefix'\n path: string\n handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>\n }): () => void\n registerUpgrade(route: {\n path: string\n handler: (req: IncomingMessage, socket: Duplex, head: Buffer) => void\n }): () => void\n}\n\ntype SubContext = {\n effect: (setup: () => unknown, name?: string) => unknown\n}\n\nconst SERVER_VERSION = readOwnPackageVersion()\nconst MAX_CLIENT_CONNECTIONS = 16\n/**\n * Single-frame bound. Covers the protocol maximum (4 × 8 MB base64 images\n * plus prompt text) with headroom while keeping an unauthenticated client's\n * pre-hello buffering far below ws's 100 MiB default.\n */\nconst MAX_FRAME_BYTES = 64 * 1024 * 1024\n\n/**\n * Resolve the host plugin's own version from the installed package.json.\n * Sourced at boot so the wire / UI always agrees with what npm published.\n * `createRequire(import.meta.url)` is the tsdown-bundled ESM equivalent of\n * CommonJS's `require`; the package.json sits next to lib/index.js after\n * the build, so `../package.json` resolves to the published manifest.\n */\nfunction readOwnPackageVersion(): string {\n try {\n const require_ = createRequire(import.meta.url)\n const pkg = require_('../package.json') as { version?: unknown }\n if (typeof pkg.version === 'string' && pkg.version.length > 0) return pkg.version\n } catch {\n // fall through to env / hardcoded default below\n }\n // `npm` injects this for `npm run` / `npm exec` / `npm start` invocations.\n // `process.env.npm_package_version` is unset when DSH loads the plugin\n // directly, so we keep it as a secondary source rather than the truth.\n const envVersion = process.env.npm_package_version\n if (typeof envVersion === 'string' && envVersion.length > 0) return envVersion\n return '0.0.0+unknown'\n}\n\nfunction rejectUpgrade(socket: Duplex, status: number, reason: string): void {\n const body = JSON.stringify({ error: reason })\n socket.end(\n 'HTTP/1.1 ' + status + ' Forbidden\\r\\n' +\n 'Content-Type: application/json\\r\\n' +\n 'Content-Length: ' + Buffer.byteLength(body) + '\\r\\n' +\n 'Connection: close\\r\\n' +\n '\\r\\n' +\n body,\n )\n}\n\n/** Authorization is preferred; the query form remains for older app builds. */\nexport function requestToken(req: Pick<IncomingMessage, 'url' | 'headers'>): string | null {\n const authorization = req.headers.authorization\n if (typeof authorization === 'string') {\n const match = /^Bearer\\s+(.+)$/i.exec(authorization.trim())\n if (match?.[1]) return match[1]\n }\n try {\n return new URL(req.url ?? '/', 'http://phone.local').searchParams.get('token')\n } catch {\n return null\n }\n}\n\n/**\n * Cordis hands the second argument in different shapes depending on host\n * composition: a reactive options getter, the resolved config value, or\n * nothing when the patch row omits `config`. Normalize all of them.\n */\nfunction normalizeOptions(options: unknown): Config {\n if (typeof options === 'function') {\n return (options as () => Config)()\n }\n if (options && typeof options === 'object') {\n return options as Config\n }\n // Validate undefined through the schema so every default applies.\n const validated = (Config as unknown as (data: unknown) => Config)(undefined)\n return validated ?? {}\n}\n\nexport function apply(ctx: Context, options: unknown): void {\n const cfg = normalizeOptions(options)\n\n const log = (message: string): void => {\n console.log('[deeppilot] ' + message)\n }\n\n /**\n * Settings-section source: while a settings service is attached this holds\n * the user-edited section value; otherwise the composition defaults. Read\n * through currentConfig() everywhere (normalizeOptions prefers it).\n */\n let liveSource: (() => Config) | undefined\n let scheduleRemoteReconcile: (() => void) | undefined\n const currentConfig = (): Config => {\n if (liveSource !== undefined) return normalizeOptions(liveSource())\n return normalizeOptions(options)\n }\n const enabledNow = (): boolean => currentConfig().enabled === true\n\n // Web settings page: a \"deeppilot\" section with the plugin knobs.\n // Values persist through the settings document and re-enter via setSource;\n // `enabled` decides whether the bridge starts at all (next restart). This is\n // registered unconditionally so the master switch stays reachable even while\n // the bridge is off — otherwise a disabled bridge could never be re-enabled.\n installSettingsSection(ctx, settingsNamespace('deeppilot'), Config, normalizeOptions(undefined), {\n setSource: (source) => {\n liveSource = source\n // Settings can attach after webServer. Defer one microtask so the\n // settings service can finish publishing the new source before the\n // remote runtime reads it.\n queueMicrotask(() => scheduleRemoteReconcile?.())\n },\n onChange: () => queueMicrotask(() => scheduleRemoteReconcile?.()),\n })\n\n // Master switch, resolved against the latest available settings document.\n // Individual injected services also read currentConfig() when they activate.\n if (currentConfig().enabled !== true) {\n log('disabled via settings; bridge stays inactive (rumors of /phone below are skipped)')\n }\n\n // Resolved asynchronously; route handlers await readiness. Never rejects:\n // a storage failure degrades the bridge instead of killing the host.\n // The token lives in a mutable cell so rotation can swap the secret (and\n // invalidate the old one) without restarting the host.\n const dataDir = bridgeDataDir()\n\n // ---------- zero-touch push enrollment (distributed builds) ----------\n\n /**\n * Persistent relay-enrollment cell (deeppilot/push-relay.json). A\n * distributed app presents the distributor's shared enrollKey during\n * c2s.push.register; the bridge then auto-enables relay mode, enrolls with\n * the operator's relay and caches the issued token. Users never fill in\n * anything; explicit config always wins over the auto flag.\n */\n interface RelayEnrollmentCell {\n clientId?: string\n autoRelay?: boolean\n enrollKey?: string\n token?: string\n }\n const pushRelayPath = join(dataDir, 'push-relay.json')\n const enrollmentCell: RelayEnrollmentCell = {}\n\n function persistEnrollment(): void {\n void (async () => {\n try {\n await mkdir(dataDir, { recursive: true })\n await writeFile(pushRelayPath, JSON.stringify({ version: 1, ...enrollmentCell }, null, 2) + '\\n', { mode: 0o600 })\n } catch {\n // best-effort persistence; enrollment retries on next trigger\n }\n })()\n }\n\n /** Fired from BridgeConnection when an app presents its built-in key. */\n const handlePushEnrollKey = async (enrollKey: string): Promise<void> => {\n if (enrollmentCell.enrollKey !== enrollKey) {\n enrollmentCell.enrollKey = enrollKey\n }\n const configuredProvider = currentConfig().push?.provider\n // Only flip when the user has not made an explicit choice.\n if (!configuredProvider || configuredProvider === 'none') {\n if (!enrollmentCell.autoRelay) {\n enrollmentCell.autoRelay = true\n log('push relay mode auto-enabled by enrolled app')\n }\n }\n persistEnrollment()\n // Enroll inline so the register handler sees final readiness: the first\n // offline notification must not depend on a reconnect.\n const url = (currentConfig().push?.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n await ensureRelayEnrolled(url)\n }\n const auth: { token: string | null; tokenPath: string; devices: DeviceStore | null } = {\n token: null,\n tokenPath: cfg.authTokenPath ?? join(dataDir, 'auth-token'),\n devices: null,\n }\n const ready = (async () => {\n try {\n try {\n const migratedFrom = await migrateLegacyBridgeDataDir()\n if (migratedFrom !== null) log(`migrated legacy plugin state from ${migratedFrom} to ${dataDir}`)\n } catch (error) {\n log('legacy plugin-state migration skipped: ' + String(error))\n }\n auth.tokenPath = cfg.authTokenPath ?? join(dataDir, 'auth-token')\n auth.token = await loadOrCreateToken(auth.tokenPath)\n auth.devices = await DeviceStore.load(cfg.devicesPath ?? join(dataDir, 'devices.json'))\n {\n // Startup visibility: makes \"registrations vanished after restart\"\n // instantly diagnosable (0 push rows ⇒ the file itself lost data).\n const rows = auth.devices.list()\n const registered = rows.filter((row) => row.apns !== undefined).length\n log(`device registry loaded from ${expandHome(cfg.devicesPath ?? join(dataDir, 'devices.json'))}: ${rows.length} device(s), ${registered} push registration(s)`)\n }\n // Restore zero-touch push enrollment state (best effort).\n try {\n const raw = JSON.parse(await readFile(pushRelayPath, 'utf8')) as RelayEnrollmentCell\n if (typeof raw.clientId === 'string') enrollmentCell.clientId = raw.clientId\n if (typeof raw.enrollKey === 'string') enrollmentCell.enrollKey = raw.enrollKey\n if (typeof raw.token === 'string') enrollmentCell.token = raw.token\n if (raw.autoRelay === true) enrollmentCell.autoRelay = true\n } catch {\n // first boot: no enrollment yet\n }\n } catch (error) {\n log('auth material unavailable, bridge degraded: ' + String(error))\n return { token: null, devices: null }\n }\n return { token: auth.token, devices: auth.devices }\n })()\n\n /**\n * Replace the pairing secret: persist a fresh token, clear the paired-device\n * registry, and drop every live phone socket. Handshake-time auth means an\n * already-open socket would otherwise outlive its token; terminating forces\n * each device to re-pair with the new secret. The old token is invalid the\n * moment the file is rewritten.\n *\n * Serialized through rotateTail so two overlapping invocations can never\n * return a token that a later write already invalidated.\n */\n const doRotate = async (): Promise<string> => {\n const { devices } = await ready\n if (auth.token === null || devices === null) {\n throw new Error('pairing token unavailable')\n }\n auth.token = await writeNewToken(auth.tokenPath)\n devices.clear()\n let dropped = 0\n for (const connection of connections) {\n connection.terminate()\n dropped += 1\n }\n connections.clear()\n log(`pairing token rotated; ${dropped} live phone connection(s) dropped`)\n return auth.token\n }\n let rotateTail: Promise<unknown> = Promise.resolve()\n const rotatePairingToken = (): Promise<string> => {\n const next = rotateTail.then(doRotate)\n rotateTail = next.catch(() => {})\n return next\n }\n\n /**\n * Settings-page push self-test: force one synthetic notification down the\n * active pathway to EVERY registered device, deliberately ignoring the\n * connected-skip and category-mute filters — an explicit user action must\n * always be able to prove delivery end to end.\n */\n const runPushSelfTest = async (): Promise<PushTestResult> => {\n const resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok) {\n return {\n transport: 'none',\n overall: 'not-configured',\n message: '推送未启用(' + resolved.reason + ')。可先用「测试访问与注册」完成中继注册,或在配置中设置 push.provider',\n results: [],\n }\n }\n const tokenized = (auth.devices?.list() ?? []).filter((device) => device.apns !== undefined)\n if (!auth.devices || tokenized.length === 0) {\n return {\n transport: resolved.value.kind,\n overall: 'no-targets',\n message: '还没有设备注册离线推送——在手机上打开 DeepPilot 并允许系统通知,等状态变为「已就绪」后再试',\n results: [],\n }\n }\n const send = await senderFor(resolved.value)\n if (!send) {\n return { transport: resolved.value.kind, overall: 'failed', message: '发送通道不可用(检查 .p8 密钥文件或中继配置)', results: [] }\n }\n const notification: PushNotification = {\n notificationId: 'test-' + Date.now(),\n category: 'turn.completed',\n sessionId: 'push-test',\n title: 'DeepPilot 测试推送',\n body: '收到这条通知说明离线推送链路正常',\n }\n const results = await Promise.all(tokenized.map(async (device) => {\n const registration = device.apns!\n const { outcome, reason } = await send({\n deviceToken: registration.token,\n environment: registration.environment,\n notification,\n })\n return {\n name: device.deviceName,\n environment: registration.environment,\n outcome,\n // First 10 hex chars let the operator verify the stored token matches\n // what the device currently holds (tokens rotate on reinstall).\n tokenFingerprint: registration.token.slice(0, 10),\n ...(reason !== undefined ? { reason } : {}),\n }\n }))\n const overall: PushTestResult['overall'] = results.some((r) => r.outcome === 'sent') ? 'sent' : 'failed'\n log('push self-test: ' + overall + ' (' + results.map((r) => `\"${r.name}\"=${r.outcome}${r.reason ? '/' + r.reason : ''}`).join(', ') + ')')\n return { transport: resolved.value.kind, overall, results }\n }\n\n const connections = new Set<BridgeConnection>()\n\n // ---------- offline push outlet (F-9) ----------\n\n type PushConfigSnapshot =\n | { kind: 'apns'; teamId: string; keyId: string; keyPath: string; bundleId: string }\n | { kind: 'relay'; url: string; token: string }\n\n const resolvePushConfig = (config: Config): { ok: true; value: PushConfigSnapshot } | { ok: false; reason: string } => {\n const push = config.push ?? {}\n // Auto-enabled by an enrolled distributed app when the user made no\n // explicit provider choice.\n const configured = push.provider ?? 'none'\n const effectiveProvider = configured === 'none' && enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effectiveProvider === 'relay') {\n const url = (push.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n const token = (push.relayToken ?? '').trim() || enrollmentCell.token || ''\n if (!/^https:\\/\\//i.test(url)) return { ok: false, reason: 'relayUrl must be an https URL' }\n if (!token) return { ok: false, reason: 'relay token not enrolled yet' }\n return { ok: true, value: { kind: 'relay', url, token } }\n }\n if (effectiveProvider === 'apns') {\n const teamId = (push.teamId ?? '').trim()\n const keyId = (push.keyId ?? '').trim()\n const keyPath = expandHome((push.keyPath ?? '').trim() || join(dataDir, 'apns', 'AuthKey.p8'))\n const bundleId = (push.bundleId ?? '').trim()\n if (!teamId || !keyId || !bundleId) return { ok: false, reason: 'teamId/keyId/bundleId missing' }\n return { ok: true, value: { kind: 'apns', teamId, keyId, keyPath, bundleId } }\n }\n return { ok: false, reason: 'provider disabled' }\n }\n\n /**\n * Zero-touch enrollment against the operator's relay. Idempotent and\n * cached in the persistent cell; a failure disables push for this config\n * fingerprint with one log line until something changes.\n */\n let enrollAttemptFor: string | undefined\n let enrollLastAttemptAt = 0\n const ensureRelayEnrolled = async (url: string): Promise<string | undefined> => {\n // The enrollment body carries the distributor's shared key; a mis-typed\n // http:// relayUrl must never leak it in cleartext. (Send-path requests\n // are already gated by resolvePushConfig — enrollment call sites are not.)\n if (!/^https:\\/\\//i.test(url.trim())) {\n log('push relay enrollment refused: relayUrl must be an https URL')\n return undefined\n }\n if (enrollmentCell.token) return enrollmentCell.token\n const fingerprint = url + ':' + String(enrollmentCell.enrollKey ?? '')\n if (fingerprint !== enrollAttemptFor) {\n enrollAttemptFor = fingerprint\n enrollLastAttemptAt = 0\n }\n // Same fingerprint failing repeatedly: throttle to one attempt/minute so\n // a down relay cannot turn every notification into an outbound storm,\n // while transient failures still recover quickly.\n if (Date.now() - enrollLastAttemptAt < 60_000) return undefined\n enrollLastAttemptAt = Date.now()\n try {\n if (!enrollmentCell.clientId) {\n enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n persistEnrollment()\n }\n const client = new RelayClient({ url, debug: currentConfig().debug === true, log })\n const token = await client.enroll(enrollmentCell.clientId, enrollmentCell.enrollKey ?? '')\n if (!token) {\n log('push relay enrollment failed (' + url + '); will retry on next trigger')\n return undefined\n }\n enrollmentCell.token = token\n persistEnrollment()\n log('push relay enrollment succeeded')\n return token\n } catch (error) {\n log('push relay enrollment error: ' + String(error))\n return undefined\n }\n }\n\n interface SendOutcome { outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }\n type PushSender = (\n request: { deviceToken: string; environment: ApnsEnvironment; notification: Parameters<PushOutlet['fanOut']>[0] },\n ) => Promise<SendOutcome>\n\n interface CachedSender {\n fingerprint: string\n send: PushSender\n dispose?: () => Promise<void>\n }\n let cachedSender: CachedSender | undefined\n /**\n * Last failed APNs-sender build. The config fingerprint cannot see the\n * filesystem, so remembering a failure forever meant \"copy the .p8 into\n * place later\" never recovered without an edit or restart; throttle the\n * retry by time instead — same pattern as relay enrollment below.\n */\n let senderFailedFor: { fingerprint: string; at: number } | undefined\n const SENDER_FAILURE_RETRY_MS = 60_000\n\n /**\n * Lazily build the push sender for the current config. A broken config\n * (unreadable .p8) disables push for that fingerprint with exactly one log\n * line instead of failing on every event.\n */\n const senderFor = async (resolved: PushConfigSnapshot): Promise<PushSender | undefined> => {\n const fingerprint = JSON.stringify(resolved)\n if (cachedSender?.fingerprint === fingerprint) return cachedSender.send\n // A recent failure only blocks retries for a short window: a permanently\n // broken config must not log-storm on every event, but the same config\n // with the key file since added MUST get another chance.\n if (\n senderFailedFor?.fingerprint === fingerprint &&\n Date.now() - senderFailedFor.at < SENDER_FAILURE_RETRY_MS\n ) {\n return undefined\n }\n if (cachedSender) {\n await cachedSender.dispose?.().catch(() => {})\n cachedSender = undefined\n }\n if (resolved.kind === 'relay') {\n // resolvePushConfig already guarantees a token exists in the cell\n // (config token or a completed enrollment).\n const client = new RelayClient({ url: resolved.url, token: resolved.token, debug: currentConfig().debug === true, log })\n cachedSender = {\n fingerprint,\n send: (request) => client.send(request),\n }\n log('push relay enabled')\n } else {\n try {\n await readFile(expandHome(resolved.keyPath), 'utf8')\n } catch (error) {\n senderFailedFor = { fingerprint, at: Date.now() }\n log('apns push unavailable (key unreadable at ' + resolved.keyPath + '): ' + String(error))\n return undefined\n }\n const client = new ApnsClient({\n teamId: resolved.teamId,\n keyId: resolved.keyId,\n keyPath: resolved.keyPath,\n bundleId: resolved.bundleId,\n debug: currentConfig().debug === true,\n log,\n })\n cachedSender = {\n fingerprint,\n send: (request) => client.send({\n ...request.notification,\n deviceToken: request.deviceToken,\n environment: request.environment,\n }),\n dispose: () => client.dispose(),\n }\n log('apns push enabled')\n }\n senderFailedFor = undefined\n return cachedSender.send\n }\n\n /**\n * Fan one notification-worthy event out to paired devices holding an APNs\n * token. Rules:\n * - devices with a live WebSocket are skipped (they already got the WS\n * frame and will raise the local notification themselves);\n * - each device is delivered on ITS registered environment (the build\n * kind it self-reported), so sandbox and production devices coexist;\n * - the device's per-category switches suppress muted categories;\n * - Unregistered/BadDeviceToken outcomes prune the stored token.\n */\n const makePushOutlet = (): PushOutlet => ({\n // The capability bit must tell the truth: only advertise push when the\n // provider is fully configured, otherwise clients would suppress their\n // local banners expecting a delivery that never happens.\n isAvailable: () => {\n const resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok) return false\n // Relay mode is only truly ready once enrollment produced a token;\n // advertising earlier would make clients suppress their local banners\n // for deliveries that cannot happen yet.\n if (resolved.value.kind === 'relay' && !resolved.value.token) return false\n return true\n },\n fanOut: (notification) => {\n void (async () => {\n let resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok && resolved.reason === 'relay token not enrolled yet') {\n // A registration carried the enrollKey but enrollment has not run —\n // try once now, then re-resolve.\n const relayUrl = (currentConfig().push?.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n await ensureRelayEnrolled(relayUrl)\n resolved = resolvePushConfig(currentConfig())\n }\n if (!resolved.ok) return\n const devices = auth.devices\n if (!devices) return\n const send = await senderFor(resolved.value)\n if (!send) return\n const transport = resolved.value.kind\n const connectedIds = new Set<string>()\n for (const connection of connections) {\n const id = connection.connectedDeviceId\n if (id) connectedIds.add(id)\n }\n // Observability first: push failures used to be completely silent\n // (outcomes were debug-gated), which made field diagnosis impossible.\n // Every dispatch and every skip now leaves one flat log line —\n // categories and outcomes only, never message bodies.\n const candidates = devices.list().filter((device) => {\n const registration = device.apns\n if (!registration) return false\n if (connectedIds.has(device.deviceId)) return false\n if (registration.categories?.[notification.category] === false) {\n if (currentConfig().debug === true) {\n log(`push skip \"${device.deviceName}\": category ${notification.category} muted`)\n }\n return false\n }\n return true\n })\n if (candidates.length === 0) {\n const tokenized = devices.list().filter((device) => device.apns !== undefined).length\n log(`push(${transport}) ${notification.category}: no offline targets (connected=${connectedIds.size}, tokenized=${tokenized})`)\n return\n }\n for (const device of candidates) {\n const registration = device.apns!\n void send({ deviceToken: registration.token, environment: registration.environment, notification })\n .then(({ outcome, reason }) => {\n log(`push(${transport}) ${notification.category} → \"${device.deviceName}\" [${registration.environment}] = ${outcome}${reason ? ' (' + reason + ')' : ''}`)\n if (outcome === 'invalid-token') {\n devices.clearPushToken(device.deviceId)\n log(`push: pruned stale token of \"${device.deviceName}\" (${reason ?? 'unknown'}) — app re-registers on next launch`)\n }\n })\n .catch(() => {})\n }\n })()\n },\n })\n\n\n const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_FRAME_BYTES })\n let remoteSupervisor: RemoteSupervisor | undefined\n const remoteStatus = (): RemoteStatus => remoteSupervisor?.status() ?? {\n provider: 'tailscale-funnel',\n phase: currentConfig().remote?.enabled === true ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n }\n\n // Self-update check: one process-wide instance. The initial schedule fires\n // a single background GitHub fetch shortly after boot. The result is\n // surfaced through the report snapshot; the UI shows nothing extra on\n // a quiet host, and one inline \"new version\" link when an update exists.\n const updateChecker = new UpdateChecker({ log, currentVersion: SERVER_VERSION })\n updateChecker.scheduleInitial()\n const updateInfo = (): UpdateInfo => updateChecker.get()\n\n // Typert Remote for the web settings page (deeppilot/report).\n applyReportRemote(ctx, async () => {\n let tokenReady = false\n let devices: Array<{ deviceId: string; deviceName: string; appVersion: string; firstSeenTs: number; lastSeenTs: number; apns?: { environment: 'development' | 'production'; updatedAt: number } }> = []\n try {\n await ready\n tokenReady = auth.token !== null\n // Strip the raw APNs token at the source; the report carries only the\n // registration fact (environment + freshness).\n devices = (auth.devices?.list() ?? []).map(({ deviceId, deviceName, appVersion, firstSeenTs, lastSeenTs, apns }) => ({\n deviceId,\n deviceName,\n appVersion,\n firstSeenTs,\n lastSeenTs,\n ...(apns ? { apns: { environment: apns.environment, updatedAt: apns.updatedAt } } : {}),\n }))\n } catch {\n // degraded: report the minimum without token facts\n }\n const update = updateInfo()\n return {\n protocolVersion: 1,\n serverVersion: SERVER_VERSION,\n pluginVersion: update.currentVersion,\n ...(update.available ? { updateAvailable: true } : {}),\n ...(update.releaseUrl !== null ? { releaseUrl: update.releaseUrl } : {}),\n enabled: currentConfig().enabled === true,\n tokenPath: expandHome(currentConfig().authTokenPath ?? join(bridgeDataDir(), 'auth-token')),\n tokenReady,\n activeConnections: connections.size,\n historyBufferMax: currentConfig().historyBufferMax ?? 2000,\n debug: currentConfig().debug === true,\n lanAddresses: localLANIPv4Addresses(),\n remote: remoteStatus(),\n devices,\n }\n }, async () => {\n await ready\n if (auth.token === null) throw new Error('pairing token unavailable')\n return auth.token\n }, rotatePairingToken, async () => {\n const push = currentConfig().push ?? {}\n const configured = push.provider ?? 'none'\n const effective = configured === 'none' && enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effective !== 'relay') {\n return {\n url: '',\n overall: 'failed' as const,\n tokenIssued: false,\n steps: [{ id: 'health' as const, ok: false, message: `当前推送模式不是中继(provider=${configured})。启用方式二选一:① 零配置——在 ios/project.yml 填写 DSPushEnrollKey(与服务器 RELAY_ENROLL_KEY 一致)并重新安装 App,打开 App 即自动启用;② 手动——将 push.provider 设为 relay 并填入 relayToken` }],\n }\n }\n const url = (push.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n if (!/^https:\\/\\//i.test(url)) {\n return {\n url,\n overall: 'failed' as const,\n tokenIssued: false,\n steps: [{ id: 'health' as const, ok: false, message: 'relayUrl 必须是 https 地址:注册请求携带共享密钥,明文 HTTP 会把它暴露给链路上的任何节点' }],\n }\n }\n // The enroll step needs an identity; mint one now so a successful test\n // doubles as a completed enrollment.\n if (!enrollmentCell.clientId && enrollmentCell.enrollKey) {\n enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n persistEnrollment()\n }\n return await runRelayProbe({\n url,\n clientId: enrollmentCell.clientId,\n enrollKey: enrollmentCell.enrollKey,\n manualToken: Boolean((push.relayToken ?? '').trim()),\n onEnrolled: (token) => {\n enrollmentCell.token = token\n persistEnrollment()\n log('push relay enrollment succeeded (via settings self-test)')\n },\n })\n }, async () => {\n return await runPushSelfTest()\n })\n const state: { bridge?: HostBridge } = {}\n\n const handleUpgrade = (req: IncomingMessage, socket: Duplex, head: Buffer): void => {\n void (async () => {\n try {\n if (!enabledNow()) {\n rejectUpgrade(socket, 503, 'bridge disabled')\n return\n }\n if (connections.size >= MAX_CLIENT_CONNECTIONS) {\n rejectUpgrade(socket, 429, 'too many connections')\n return\n }\n const { devices } = await ready\n const token = auth.token\n if (!token || !devices) {\n rejectUpgrade(socket, 503, 'bridge degraded')\n return\n }\n const presentedToken = requestToken(req)\n if (presentedToken !== null && !tokenMatches(presentedToken, token)) {\n rejectUpgrade(socket, 401, 'invalid token')\n return\n }\n const bridge = state.bridge\n if (!bridge) {\n rejectUpgrade(socket, 503, 'bridge not ready')\n return\n }\n // Rotation may have swapped the secret while this handshake was in\n // flight; a connection validated against the previous token must not\n // survive it.\n if (auth.token !== token) {\n rejectUpgrade(socket, 401, 'invalid token')\n return\n }\n wss.handleUpgrade(req, socket, head, (ws) => {\n const connection = new BridgeConnection(ws, {\n bridge,\n devices,\n serverVersion: SERVER_VERSION,\n expectedToken: token,\n transportAuthenticated: presentedToken !== null,\n log,\n debug: currentConfig().debug === true,\n onClosed: (closed) => connections.delete(closed),\n onPushEnrollKey: handlePushEnrollKey,\n })\n connections.add(connection)\n })\n } catch (error) {\n log('upgrade failed: ' + String(error))\n rejectUpgrade(socket, 500, 'internal error')\n }\n })()\n }\n\n const handleHealth = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n await ready\n const token = auth.token\n res.setHeader('Content-Type', 'application/json')\n if (!token) {\n res.statusCode = 503\n res.end(JSON.stringify({ ok: false, degraded: true }))\n return\n }\n if (!tokenMatches(requestToken(req), token)) {\n res.statusCode = 401\n res.end(JSON.stringify({ ok: false }))\n return\n }\n res.statusCode = 200\n res.end(JSON.stringify({\n ok: true,\n enabled: enabledNow(),\n protocolVersion: 1,\n serverVersion: SERVER_VERSION,\n dataPlane: Boolean(state.bridge),\n }))\n } catch {\n res.statusCode = 500\n res.end(JSON.stringify({ ok: false }))\n }\n }\n\n // Data plane: consume mux/host streams once the API gateway exists.\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['apiProxy'],\n (sub) => {\n if (currentConfig().enabled !== true) {\n log('bridge disabled; data plane stays inactive')\n return\n }\n const apiCtx = sub as unknown as { apiProxy?: ApiProxyLike } & SubContext\n const proxy = apiCtx.apiProxy\n if (!proxy) {\n log('apiProxy service absent; data plane stays inactive')\n return\n }\n const bridge = new HostBridge(proxy, cfg.historyBufferMax)\n bridge.setPushOutlet(makePushOutlet())\n state.bridge = bridge\n bridge.start()\n log('data plane active (mux + host streams)')\n apiCtx.effect(() => () => bridge.dispose(), 'deeppilot: host streams')\n },\n )\n\n // Transport plane: /phone WebSocket + /phone/health probe.\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['webServer'],\n (sub) => {\n const webCtx = sub as unknown as { webServer?: WebServerLike } & SubContext\n const web = webCtx.webServer\n if (!web) {\n log('webServer service absent in this profile; bridge stays inactive')\n return\n }\n // Routes are registered regardless of the master switch so a mid-session\n // toggle always stays serviceable; every handler refuses work when the\n // bridge is currently disabled (evaluated per request so it is robust to\n // the settings scope attaching before or after webServer).\n webCtx.effect(\n () =>\n web.registerUpgrade({\n path: '/phone',\n handler: handleUpgrade,\n }),\n 'deeppilot: /phone WebSocket',\n )\n\n webCtx.effect(\n () =>\n web.register({\n kind: 'exact',\n path: '/phone/health',\n handler: handleHealth,\n }),\n 'deeppilot: /phone/health',\n )\n\n const sweep = setInterval(() => {\n const now = Date.now()\n for (const connection of connections) {\n if (connection.isStale(now, 60_000)) {\n log('dropping stale connection')\n connection.terminate()\n connections.delete(connection)\n }\n }\n }, 30_000)\n webCtx.effect(() => () => clearInterval(sweep), 'deeppilot: stale sweep')\n\n // The settings source is not guaranteed to attach before webServer.\n // Keep a loopback origin ready and reconcile the helper whenever that\n // source appears or changes, instead of freezing the schema defaults at\n // injection time.\n const originServer = createServer((req, res) => {\n let path = '/'\n try { path = new URL(req.url ?? '/', 'http://phone.local').pathname } catch { /* reject below */ }\n if (path === '/phone/health') {\n void handleHealth(req, res)\n } else {\n res.statusCode = 404\n res.end('not found')\n }\n })\n originServer.on('upgrade', (req, socket, head) => {\n let path = '/'\n try { path = new URL(req.url ?? '/', 'http://phone.local').pathname } catch { /* reject below */ }\n if (path !== '/phone') {\n socket.end('HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\n\\r\\n')\n return\n }\n handleUpgrade(req, socket, head)\n })\n\n let originURL: string | undefined\n let appliedRemoteKey: string | undefined\n let remoteDisposed = false\n let reconcileTail = Promise.resolve()\n const reconcileRemote = async (): Promise<void> => {\n if (remoteDisposed || originURL === undefined) return\n const config = currentConfig()\n const remoteConfig = config.remote ?? {}\n const remotePort: 443 | 8443 | 10000 = remoteConfig.funnelPort === 8443 || remoteConfig.funnelPort === 10000\n ? remoteConfig.funnelPort\n : 443\n const helperPath = remoteConfig.helperPath?.trim() || undefined\n const next = {\n enabled: config.enabled === true && remoteConfig.enabled === true && remoteConfig.provider === 'tailscale-funnel',\n hostname: normalizeRemoteHostname(remoteConfig.hostname),\n statePath: remoteConfig.statePath?.trim() || join(dataDir, 'tailscale'),\n helperPath,\n funnelPort: remotePort,\n }\n const nextKey = JSON.stringify(next)\n if (nextKey === appliedRemoteKey) return\n\n const previous = remoteSupervisor\n remoteSupervisor = undefined\n if (previous !== undefined) await previous.dispose()\n if (remoteDisposed) return\n\n const supervisor = new RemoteSupervisor({\n enabled: next.enabled,\n hostname: next.hostname,\n statePath: next.statePath,\n ...(next.helperPath ? { helperPath: next.helperPath } : {}),\n funnelPort: next.funnelPort,\n log,\n })\n remoteSupervisor = supervisor\n appliedRemoteKey = nextKey\n await supervisor.start(originURL)\n }\n scheduleRemoteReconcile = () => {\n reconcileTail = reconcileTail\n .then(reconcileRemote)\n .catch((error) => log('remote reconcile failed: ' + String(error)))\n }\n\n originServer.listen(0, '127.0.0.1', () => {\n const address = originServer.address()\n if (address && typeof address === 'object') {\n originURL = `http://127.0.0.1:${address.port}`\n scheduleRemoteReconcile?.()\n }\n })\n originServer.on('error', (error) => log('remote origin failed: ' + String(error)))\n webCtx.effect(() => () => {\n remoteDisposed = true\n scheduleRemoteReconcile = undefined\n originServer.close()\n reconcileTail = reconcileTail.then(async () => {\n const supervisor = remoteSupervisor\n remoteSupervisor = undefined\n if (supervisor !== undefined) await supervisor.dispose()\n })\n }, 'deeppilot: embedded Funnel')\n if (enabledNow()) {\n log('/phone WebSocket registered')\n } else {\n log('bridge disabled; /phone refuses connections until re-enabled and restarted')\n }\n },\n )\n}\n"],"mappings":"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"}