toc-ocra 0.2.0

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.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/assets/icon.svg +1 -0
  4. package/dist/plugin/renderer.css +2 -0
  5. package/dist/plugin/renderer.css.map +7 -0
  6. package/dist/plugin/renderer.js +2 -0
  7. package/dist/plugin/renderer.js.map +7 -0
  8. package/dist/src/config.d.ts +66 -0
  9. package/dist/src/config.js +173 -0
  10. package/dist/src/config.js.map +1 -0
  11. package/dist/src/control/pairing.d.ts +44 -0
  12. package/dist/src/control/pairing.js +99 -0
  13. package/dist/src/control/pairing.js.map +1 -0
  14. package/dist/src/exec/script-runner.d.ts +9 -0
  15. package/dist/src/exec/script-runner.js +110 -0
  16. package/dist/src/exec/script-runner.js.map +1 -0
  17. package/dist/src/health/server.d.ts +74 -0
  18. package/dist/src/health/server.js +248 -0
  19. package/dist/src/health/server.js.map +1 -0
  20. package/dist/src/index.d.ts +2 -0
  21. package/dist/src/index.js +173 -0
  22. package/dist/src/index.js.map +1 -0
  23. package/dist/src/logging/diagnostic-log.d.ts +16 -0
  24. package/dist/src/logging/diagnostic-log.js +128 -0
  25. package/dist/src/logging/diagnostic-log.js.map +1 -0
  26. package/dist/src/providers/cli/claude.d.ts +2 -0
  27. package/dist/src/providers/cli/claude.js +48 -0
  28. package/dist/src/providers/cli/claude.js.map +1 -0
  29. package/dist/src/providers/cli/codex.d.ts +2 -0
  30. package/dist/src/providers/cli/codex.js +67 -0
  31. package/dist/src/providers/cli/codex.js.map +1 -0
  32. package/dist/src/providers/cli/gemini.d.ts +2 -0
  33. package/dist/src/providers/cli/gemini.js +30 -0
  34. package/dist/src/providers/cli/gemini.js.map +1 -0
  35. package/dist/src/providers/cli/grok.d.ts +2 -0
  36. package/dist/src/providers/cli/grok.js +29 -0
  37. package/dist/src/providers/cli/grok.js.map +1 -0
  38. package/dist/src/providers/cli/opencode.d.ts +2 -0
  39. package/dist/src/providers/cli/opencode.js +29 -0
  40. package/dist/src/providers/cli/opencode.js.map +1 -0
  41. package/dist/src/providers/detection.d.ts +11 -0
  42. package/dist/src/providers/detection.js +96 -0
  43. package/dist/src/providers/detection.js.map +1 -0
  44. package/dist/src/providers/model-catalog-collector.d.ts +22 -0
  45. package/dist/src/providers/model-catalog-collector.js +66 -0
  46. package/dist/src/providers/model-catalog-collector.js.map +1 -0
  47. package/dist/src/providers/model-catalog.d.ts +45 -0
  48. package/dist/src/providers/model-catalog.js +382 -0
  49. package/dist/src/providers/model-catalog.js.map +1 -0
  50. package/dist/src/providers/provider.d.ts +49 -0
  51. package/dist/src/providers/provider.js +63 -0
  52. package/dist/src/providers/provider.js.map +1 -0
  53. package/dist/src/providers/registry.d.ts +31 -0
  54. package/dist/src/providers/registry.js +60 -0
  55. package/dist/src/providers/registry.js.map +1 -0
  56. package/dist/src/runs/orphan-registry.d.ts +24 -0
  57. package/dist/src/runs/orphan-registry.js +63 -0
  58. package/dist/src/runs/orphan-registry.js.map +1 -0
  59. package/dist/src/runs/process-tree.d.ts +21 -0
  60. package/dist/src/runs/process-tree.js +95 -0
  61. package/dist/src/runs/process-tree.js.map +1 -0
  62. package/dist/src/runs/run-supervisor.d.ts +63 -0
  63. package/dist/src/runs/run-supervisor.js +285 -0
  64. package/dist/src/runs/run-supervisor.js.map +1 -0
  65. package/dist/src/runtime/agent-runtime.d.ts +68 -0
  66. package/dist/src/runtime/agent-runtime.js +340 -0
  67. package/dist/src/runtime/agent-runtime.js.map +1 -0
  68. package/dist/src/runtime/agent-service.d.ts +33 -0
  69. package/dist/src/runtime/agent-service.js +181 -0
  70. package/dist/src/runtime/agent-service.js.map +1 -0
  71. package/dist/src/runtime/cli/hydrate-shell-path.d.ts +33 -0
  72. package/dist/src/runtime/cli/hydrate-shell-path.js +173 -0
  73. package/dist/src/runtime/cli/hydrate-shell-path.js.map +1 -0
  74. package/dist/src/session/terminal-line-sanitizer.d.ts +7 -0
  75. package/dist/src/session/terminal-line-sanitizer.js +21 -0
  76. package/dist/src/session/terminal-line-sanitizer.js.map +1 -0
  77. package/dist/src/session/terminal-tail-buffer.d.ts +70 -0
  78. package/dist/src/session/terminal-tail-buffer.js +149 -0
  79. package/dist/src/session/terminal-tail-buffer.js.map +1 -0
  80. package/dist/src/shared/cli-detection-types.d.ts +19 -0
  81. package/dist/src/shared/cli-detection-types.js +2 -0
  82. package/dist/src/shared/cli-detection-types.js.map +1 -0
  83. package/dist/src/shared/node-cli-command-resolution.d.ts +19 -0
  84. package/dist/src/shared/node-cli-command-resolution.js +173 -0
  85. package/dist/src/shared/node-cli-command-resolution.js.map +1 -0
  86. package/dist/src/transport/gateway-client.d.ts +38 -0
  87. package/dist/src/transport/gateway-client.js +73 -0
  88. package/dist/src/transport/gateway-client.js.map +1 -0
  89. package/dist/src/types/protocol.d.ts +172 -0
  90. package/dist/src/types/protocol.js +55 -0
  91. package/dist/src/types/protocol.js.map +1 -0
  92. package/dist/src/workspace/directory-browser.d.ts +17 -0
  93. package/dist/src/workspace/directory-browser.js +47 -0
  94. package/dist/src/workspace/directory-browser.js.map +1 -0
  95. package/dist/src/workspace/workspace-manager.d.ts +41 -0
  96. package/dist/src/workspace/workspace-manager.js +436 -0
  97. package/dist/src/workspace/workspace-manager.js.map +1 -0
  98. package/locales/en.json +66 -0
  99. package/locales/zh-CN.json +66 -0
  100. package/package.json +90 -0
  101. package/scripts/service.mjs +174 -0
@@ -0,0 +1,99 @@
1
+ import { randomBytes, randomInt, timingSafeEqual } from 'node:crypto';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { chmod, mkdir, rename, rm, writeFile } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ /**
6
+ * Pairing between the local service and the desktop plugin.
7
+ *
8
+ * The plugin proves it runs for the same person who started the service by
9
+ * entering a 6-digit connection code. The service shows the code on start and
10
+ * keeps it in `<dataDir>/connection-code` (0600) so `toc-ocra code` can print
11
+ * it later. A correct code returns the control token (32 random bytes kept in
12
+ * `<dataDir>/control-token`, 0600), which the plugin stores and sends as
13
+ * `Authorization: Bearer` on every control request.
14
+ *
15
+ * The code rotates after it is used and after five wrong attempts, and wrong
16
+ * attempts lock pairing for a short while.
17
+ */
18
+ export const CODE_FILE = 'connection-code';
19
+ export const TOKEN_FILE = 'control-token';
20
+ const MAX_ATTEMPTS = 5;
21
+ const LOCK_MS = 30_000;
22
+ export class PairingStore {
23
+ dataDir;
24
+ now;
25
+ token = '';
26
+ code = '';
27
+ failures = 0;
28
+ lockedUntil = 0;
29
+ constructor(dataDir, now = Date.now) {
30
+ this.dataDir = dataDir;
31
+ this.now = now;
32
+ }
33
+ /** Loads or creates the control token and issues a fresh connection code. */
34
+ async init() {
35
+ await mkdir(this.dataDir, { recursive: true, mode: 0o700 });
36
+ const tokenPath = join(this.dataDir, TOKEN_FILE);
37
+ const stored = existsSync(tokenPath) ? readFileSync(tokenPath, 'utf8').trim() : '';
38
+ this.token = /^[a-f0-9]{64}$/.test(stored) ? stored : randomBytes(32).toString('hex');
39
+ if (this.token !== stored)
40
+ await writeSecret(tokenPath, this.token);
41
+ await this.rotate();
42
+ }
43
+ get connectionCode() {
44
+ return this.code;
45
+ }
46
+ /** Exchanges a connection code for the control token. */
47
+ async pair(input) {
48
+ const now = this.now();
49
+ if (now < this.lockedUntil)
50
+ return { ok: false, error: 'locked', retryAfterMs: this.lockedUntil - now };
51
+ const code = typeof input === 'string' ? input.replace(/\s+/g, '') : '';
52
+ if (code.length === this.code.length && safeEqual(code, this.code)) {
53
+ this.failures = 0;
54
+ await this.rotate();
55
+ return { ok: true, token: this.token };
56
+ }
57
+ this.failures++;
58
+ if (this.failures >= MAX_ATTEMPTS) {
59
+ this.failures = 0;
60
+ this.lockedUntil = now + LOCK_MS;
61
+ await this.rotate();
62
+ return { ok: false, error: 'locked', retryAfterMs: LOCK_MS };
63
+ }
64
+ return { ok: false, error: 'code_wrong' };
65
+ }
66
+ /** Checks an `Authorization: Bearer <token>` header. */
67
+ authorize(header) {
68
+ const match = /^Bearer\s+(\S+)$/i.exec(header?.trim() ?? '');
69
+ return Boolean(match && this.token && safeEqual(match[1] ?? '', this.token));
70
+ }
71
+ /** Removes the code file when the service stops. */
72
+ async dispose() {
73
+ await rm(join(this.dataDir, CODE_FILE), { force: true });
74
+ }
75
+ async rotate() {
76
+ this.code = String(randomInt(0, 1_000_000)).padStart(6, '0');
77
+ await writeSecret(join(this.dataDir, CODE_FILE), this.code);
78
+ }
79
+ }
80
+ /** Reads the code of the running service, for `toc-ocra code`. */
81
+ export function readConnectionCode(dataDir) {
82
+ const path = join(dataDir, CODE_FILE);
83
+ if (!existsSync(path))
84
+ return undefined;
85
+ const code = readFileSync(path, 'utf8').trim();
86
+ return /^\d{6}$/.test(code) ? code : undefined;
87
+ }
88
+ async function writeSecret(path, value) {
89
+ const temporary = `${path}.${process.pid}.tmp`;
90
+ await writeFile(temporary, value, { mode: 0o600 });
91
+ await rename(temporary, path);
92
+ await chmod(path, 0o600);
93
+ }
94
+ function safeEqual(left, right) {
95
+ const a = Buffer.from(left);
96
+ const b = Buffer.from(right);
97
+ return a.length === b.length && timingSafeEqual(a, b);
98
+ }
99
+ //# sourceMappingURL=pairing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../../src/control/pairing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type ScriptExecutionResult = {
2
+ exitCode: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ durationMs: number;
6
+ outputTruncated: boolean;
7
+ timedOut: boolean;
8
+ };
9
+ export declare function executeScript(command: string, workdir: string, timeoutSeconds: number): Promise<ScriptExecutionResult>;
@@ -0,0 +1,110 @@
1
+ import { spawn } from 'node:child_process';
2
+ // Runs one shell command for script.run. Output is capped per stream, the
3
+ // whole process group is killed on timeout, and nothing is persisted.
4
+ const MAX_SCRIPT_OUTPUT_BYTES = 112 * 1024;
5
+ export function executeScript(command, workdir, timeoutSeconds) {
6
+ return new Promise((resolveExecution) => {
7
+ const windows = process.platform === 'win32';
8
+ const executable = windows ? (process.env.ComSpec ?? 'cmd.exe') : '/bin/sh';
9
+ const args = windows ? ['/d', '/s', '/c', command] : ['-lc', command];
10
+ const startedAt = Date.now();
11
+ const stdout = [];
12
+ const stderr = [];
13
+ let stdoutBytes = 0;
14
+ let stderrBytes = 0;
15
+ let outputTruncated = false;
16
+ let timedOut = false;
17
+ let spawnError = '';
18
+ let settled = false;
19
+ const append = (target, chunk, currentBytes) => {
20
+ const remaining = MAX_SCRIPT_OUTPUT_BYTES - currentBytes;
21
+ if (remaining <= 0) {
22
+ outputTruncated = true;
23
+ return currentBytes;
24
+ }
25
+ if (chunk.length > remaining) {
26
+ target.push(chunk.subarray(0, remaining));
27
+ outputTruncated = true;
28
+ return MAX_SCRIPT_OUTPUT_BYTES;
29
+ }
30
+ target.push(chunk);
31
+ return currentBytes + chunk.length;
32
+ };
33
+ let child;
34
+ try {
35
+ child = spawn(executable, args, {
36
+ cwd: workdir,
37
+ env: process.env,
38
+ detached: !windows,
39
+ shell: false,
40
+ stdio: ['ignore', 'pipe', 'pipe']
41
+ });
42
+ }
43
+ catch (error) {
44
+ resolveExecution({
45
+ exitCode: -1,
46
+ stdout: '',
47
+ stderr: error instanceof Error ? error.message : String(error),
48
+ durationMs: Date.now() - startedAt,
49
+ outputTruncated: false,
50
+ timedOut: false
51
+ });
52
+ return;
53
+ }
54
+ const kill = () => {
55
+ if (child.exitCode !== null || child.killed)
56
+ return;
57
+ try {
58
+ if (!windows && child.pid)
59
+ process.kill(-child.pid, 'SIGKILL');
60
+ else
61
+ child.kill('SIGKILL');
62
+ }
63
+ catch {
64
+ try {
65
+ child.kill('SIGKILL');
66
+ }
67
+ catch {
68
+ // The process may have exited between the checks.
69
+ }
70
+ }
71
+ };
72
+ const timeout = setTimeout(() => {
73
+ timedOut = true;
74
+ kill();
75
+ }, timeoutSeconds * 1_000);
76
+ child.stdout.on('data', (chunk) => {
77
+ stdoutBytes = append(stdout, Buffer.from(chunk), stdoutBytes);
78
+ });
79
+ child.stderr.on('data', (chunk) => {
80
+ stderrBytes = append(stderr, Buffer.from(chunk), stderrBytes);
81
+ });
82
+ child.once('error', (error) => {
83
+ spawnError = error.message;
84
+ });
85
+ child.once('close', (code) => {
86
+ if (settled)
87
+ return;
88
+ settled = true;
89
+ clearTimeout(timeout);
90
+ let stderrText = Buffer.concat(stderr).toString('utf8');
91
+ if (spawnError)
92
+ stderrText = `${stderrText}${stderrText ? '\n' : ''}${spawnError}`;
93
+ if (timedOut) {
94
+ stderrText = `${stderrText}${stderrText ? '\n' : ''}Command timed out after ${timeoutSeconds} seconds.`;
95
+ }
96
+ if (outputTruncated) {
97
+ stderrText = `${stderrText}${stderrText ? '\n' : ''}[Output truncated to 112 KiB per stream.]`;
98
+ }
99
+ resolveExecution({
100
+ exitCode: timedOut || spawnError ? -1 : (code ?? -1),
101
+ stdout: Buffer.concat(stdout).toString('utf8'),
102
+ stderr: stderrText,
103
+ durationMs: Date.now() - startedAt,
104
+ outputTruncated,
105
+ timedOut
106
+ });
107
+ });
108
+ });
109
+ }
110
+ //# sourceMappingURL=script-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-runner.js","sourceRoot":"","sources":["../../../src/exec/script-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import type { DiagnosticLogger } from '../logging/diagnostic-log.js';
2
+ import type { CliDetectionSnapshot } from '../shared/cli-detection-types.js';
3
+ export type RunRecord = {
4
+ runId: string;
5
+ status: string;
6
+ sessionId?: string;
7
+ updatedAt: string;
8
+ };
9
+ export type HealthSnapshot = {
10
+ deviceId: string;
11
+ deviceName?: string;
12
+ serverUrl?: string;
13
+ connected: boolean;
14
+ activeSessions: number;
15
+ storedRuns: number;
16
+ startedAt: string;
17
+ runs: RunRecord[];
18
+ capabilities?: string[];
19
+ detection?: CliDetectionSnapshot;
20
+ /** Why the WebSocket is down. Present only while disconnected. */
21
+ connectionError?: {
22
+ status?: number;
23
+ reason: string;
24
+ at: string;
25
+ };
26
+ };
27
+ /**
28
+ * What the desktop plugin can do on this machine. Every route except
29
+ * `/control/info` and `/control/pair` requires the control token.
30
+ */
31
+ export interface ControlApi {
32
+ info(): Record<string, unknown>;
33
+ pair(code: unknown): Promise<{
34
+ ok: true;
35
+ token: string;
36
+ } | {
37
+ ok: false;
38
+ error: string;
39
+ retryAfterMs?: number;
40
+ }>;
41
+ authorize(header: string | undefined): boolean;
42
+ status(): Promise<unknown>;
43
+ enroll(input: {
44
+ serverUrl?: unknown;
45
+ code?: unknown;
46
+ }): Promise<unknown>;
47
+ unpair(): Promise<unknown>;
48
+ detect(): Promise<unknown>;
49
+ }
50
+ /** Thrown by control handlers to answer with a specific status and message. */
51
+ export declare class ControlError extends Error {
52
+ readonly status: number;
53
+ readonly code: string;
54
+ constructor(status: number, message: string, code?: string);
55
+ }
56
+ export declare class HealthServer {
57
+ private readonly host;
58
+ private readonly port;
59
+ private readonly snapshot;
60
+ private readonly logFile;
61
+ private readonly logger;
62
+ private readonly detection?;
63
+ private readonly control?;
64
+ private server;
65
+ constructor(host: string, port: number, snapshot: () => HealthSnapshot, logFile: string, logger: DiagnosticLogger, detection?: ((refresh: boolean) => Promise<CliDetectionSnapshot>) | undefined, control?: ControlApi | undefined);
66
+ get boundPort(): number;
67
+ start(): Promise<void>;
68
+ stop(): Promise<void>;
69
+ private handle;
70
+ private allowedHost;
71
+ private handleControl;
72
+ private json;
73
+ private recentEvents;
74
+ }
@@ -0,0 +1,248 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { createServer } from 'node:http';
3
+ /** Thrown by control handlers to answer with a specific status and message. */
4
+ export class ControlError extends Error {
5
+ status;
6
+ code;
7
+ constructor(status, message, code = 'bad_request') {
8
+ super(message);
9
+ this.status = status;
10
+ this.code = code;
11
+ }
12
+ }
13
+ const LOOPBACK_ORIGIN = /^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/;
14
+ const MAX_BODY_BYTES = 16 * 1024;
15
+ export class HealthServer {
16
+ host;
17
+ port;
18
+ snapshot;
19
+ logFile;
20
+ logger;
21
+ detection;
22
+ control;
23
+ server;
24
+ constructor(host, port, snapshot, logFile, logger, detection, control) {
25
+ this.host = host;
26
+ this.port = port;
27
+ this.snapshot = snapshot;
28
+ this.logFile = logFile;
29
+ this.logger = logger;
30
+ this.detection = detection;
31
+ this.control = control;
32
+ }
33
+ get boundPort() {
34
+ const address = this.server?.address();
35
+ return typeof address === 'object' && address ? address.port : this.port;
36
+ }
37
+ async start() {
38
+ this.server = createServer((request, response) => void this.handle(request, response));
39
+ await new Promise((resolveStart, rejectStart) => {
40
+ this.server?.once('error', rejectStart);
41
+ this.server?.listen(this.port, this.host, () => resolveStart());
42
+ });
43
+ const address = this.server.address();
44
+ this.logger.info('health-server-started', {
45
+ host: this.host,
46
+ port: typeof address === 'object' && address ? address.port : this.port
47
+ });
48
+ }
49
+ async stop() {
50
+ const server = this.server;
51
+ this.server = undefined;
52
+ if (!server)
53
+ return;
54
+ await new Promise((resolveStop) => server.close(() => resolveStop()));
55
+ }
56
+ async handle(request, response) {
57
+ const url = new URL(request.url ?? '/', 'http://localhost');
58
+ // Loopback only: a page on another site that resolves its own name to
59
+ // 127.0.0.1 (DNS rebinding) sends its own Host and is refused.
60
+ if (!this.allowedHost(request.headers.host)) {
61
+ this.json(response, 403, { error: 'forbidden_host' });
62
+ return;
63
+ }
64
+ if (url.pathname.startsWith('/control/')) {
65
+ await this.handleControl(request, response, url.pathname);
66
+ return;
67
+ }
68
+ if (request.method !== 'GET') {
69
+ response.writeHead(405).end();
70
+ return;
71
+ }
72
+ if (url.pathname === '/') {
73
+ response.writeHead(200, {
74
+ 'Content-Type': 'text/html; charset=utf-8',
75
+ 'Cache-Control': 'no-store',
76
+ 'Content-Security-Policy': "default-src 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'"
77
+ });
78
+ response.end(dashboardHtml());
79
+ return;
80
+ }
81
+ if (url.pathname === '/api/status') {
82
+ this.json(response, 200, this.snapshot());
83
+ return;
84
+ }
85
+ if (url.pathname === '/api/agents') {
86
+ const snapshot = this.detection ? await this.detection(url.searchParams.get('refresh') === '1' || url.searchParams.get('refresh') === 'true') : this.snapshot().detection;
87
+ this.json(response, 200, snapshot ?? { providers: [], detectedIds: [] });
88
+ return;
89
+ }
90
+ if (url.pathname === '/api/events') {
91
+ this.json(response, 200, { events: await this.recentEvents() });
92
+ return;
93
+ }
94
+ if (url.pathname === '/health' || url.pathname === '/ready') {
95
+ const status = this.snapshot();
96
+ const ready = url.pathname === '/health' || status.connected;
97
+ this.json(response, ready ? 200 : 503, { ok: ready, ...status });
98
+ return;
99
+ }
100
+ response.writeHead(404).end();
101
+ }
102
+ allowedHost(host) {
103
+ if (!host)
104
+ return false;
105
+ const port = this.boundPort;
106
+ return [`127.0.0.1:${port}`, `localhost:${port}`, `[::1]:${port}`].includes(host.toLowerCase());
107
+ }
108
+ async handleControl(request, response, path) {
109
+ const origin = request.headers.origin;
110
+ if (origin !== undefined && !LOOPBACK_ORIGIN.test(origin)) {
111
+ this.json(response, 403, { error: 'forbidden_origin' });
112
+ return;
113
+ }
114
+ if (origin) {
115
+ response.setHeader('Access-Control-Allow-Origin', origin);
116
+ response.setHeader('Vary', 'Origin');
117
+ }
118
+ if (request.method === 'OPTIONS') {
119
+ response.writeHead(204, {
120
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
121
+ 'Access-Control-Allow-Headers': 'authorization, content-type',
122
+ 'Access-Control-Max-Age': '600'
123
+ }).end();
124
+ return;
125
+ }
126
+ const control = this.control;
127
+ if (!control) {
128
+ this.json(response, 404, { error: 'control_unavailable' });
129
+ return;
130
+ }
131
+ try {
132
+ if (path === '/control/info' && request.method === 'GET') {
133
+ this.json(response, 200, control.info());
134
+ return;
135
+ }
136
+ if (path === '/control/pair' && request.method === 'POST') {
137
+ const body = await readJson(request);
138
+ const result = await control.pair(body.code);
139
+ if (result.ok)
140
+ this.json(response, 200, { token: result.token });
141
+ else
142
+ this.json(response, result.error === 'locked' ? 429 : 403, { error: result.error, ...(result.retryAfterMs ? { retry_after_ms: result.retryAfterMs } : {}) });
143
+ return;
144
+ }
145
+ if (!control.authorize(request.headers.authorization)) {
146
+ this.json(response, 401, { error: 'unauthorized' });
147
+ return;
148
+ }
149
+ if (path === '/control/status' && request.method === 'GET') {
150
+ this.json(response, 200, await control.status());
151
+ return;
152
+ }
153
+ if (path === '/control/enroll' && request.method === 'POST') {
154
+ const body = await readJson(request);
155
+ this.json(response, 200, await control.enroll({ serverUrl: body.server_url, code: body.code }));
156
+ return;
157
+ }
158
+ if (path === '/control/unpair' && request.method === 'POST') {
159
+ this.json(response, 200, await control.unpair());
160
+ return;
161
+ }
162
+ if (path === '/control/detect' && request.method === 'POST') {
163
+ this.json(response, 200, await control.detect());
164
+ return;
165
+ }
166
+ this.json(response, 404, { error: 'not_found' });
167
+ }
168
+ catch (error) {
169
+ if (error instanceof ControlError) {
170
+ this.json(response, error.status, { error: error.code, message: error.message });
171
+ return;
172
+ }
173
+ this.logger.warn('control-request-failed', { path, error: error instanceof Error ? error.message : String(error) });
174
+ this.json(response, 500, { error: 'internal', message: error instanceof Error ? error.message : String(error) });
175
+ }
176
+ }
177
+ json(response, status, body) {
178
+ response.writeHead(status, {
179
+ 'Content-Type': 'application/json; charset=utf-8',
180
+ 'Cache-Control': 'no-store'
181
+ });
182
+ response.end(JSON.stringify(body));
183
+ }
184
+ async recentEvents() {
185
+ try {
186
+ const raw = await readFile(this.logFile, 'utf8');
187
+ return raw
188
+ .trim()
189
+ .split('\n')
190
+ .slice(-60)
191
+ .flatMap((line) => {
192
+ try {
193
+ const parsed = JSON.parse(line);
194
+ return [{
195
+ ts: parsed.ts,
196
+ level: parsed.level,
197
+ event: parsed.event,
198
+ runId: parsed.runId,
199
+ taskId: parsed.taskId,
200
+ sessionId: parsed.sessionId,
201
+ agentType: parsed.agentType,
202
+ error: parsed.error,
203
+ code: parsed.code
204
+ }];
205
+ }
206
+ catch {
207
+ return [];
208
+ }
209
+ })
210
+ .reverse();
211
+ }
212
+ catch {
213
+ return [];
214
+ }
215
+ }
216
+ }
217
+ function dashboardHtml() {
218
+ return `<!doctype html>
219
+ <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
220
+ <title>toc-ocra Agent</title><style>
221
+ :root{color:#17202a;background:#f5f7f7;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}*{box-sizing:border-box}body{margin:0}.shell{max-width:1120px;margin:0 auto;padding:28px 20px 44px}.top{display:flex;justify-content:space-between;align-items:center;gap:18px;border-bottom:1px solid #d7dfdf;padding-bottom:18px}.brand{font-size:21px;font-weight:700}.sub{color:#5d6c72;font-size:14px;margin-top:4px}.refresh{width:34px;height:34px;border:1px solid #aebbbb;background:#fff;border-radius:5px;font-size:20px;line-height:1;cursor:pointer}.stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin:20px 0}.stat,.section{background:#fff;border:1px solid #d7dfdf;border-radius:6px}.stat{padding:15px}.label{color:#607077;font-size:12px}.value{font-size:22px;font-weight:650;margin-top:7px}.ok{color:#087f5b}.bad{color:#c0392b}.section{padding:18px;margin-top:14px}.section h2{font-size:15px;margin:0 0 13px}table{border-collapse:collapse;width:100%;font-size:13px}th{text-align:left;color:#607077;font-weight:500;border-bottom:1px solid #d7dfdf;padding:0 8px 9px}td{padding:10px 8px;border-bottom:1px solid #edf0f0;vertical-align:top}.tag{display:inline-block;border-radius:3px;padding:3px 6px;font-size:12px;background:#eef3f2;color:#365054}.tag.delivered,.tag.completed{background:#e5f6ee;color:#087f5b}.tag.failed{background:#fbeaea;color:#b33a32}.event{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:#34454b}.muted{color:#718087}.empty{color:#718087;padding:12px 0}@media(max-width:700px){.stats{grid-template-columns:repeat(2,minmax(0,1fr))}.shell{padding:20px 12px}.hide-small{display:none}th,td{padding-left:5px;padding-right:5px}}
222
+ </style></head><body><main class="shell"><header class="top"><div><div class="brand">toc-ocra Agent</div><div class="sub" id="device">正在连接本机服务</div></div><button class="refresh" id="refresh" title="刷新状态" aria-label="刷新状态">↻</button></header><section class="stats"><div class="stat"><div class="label">连接</div><div class="value" id="connected">-</div></div><div class="stat"><div class="label">运行中</div><div class="value" id="sessions">-</div></div><div class="stat"><div class="label">最近任务</div><div class="value" id="runs">-</div></div><div class="stat"><div class="label">启动时间</div><div class="value" id="started">-</div></div></section><section class="section"><h2>最近任务</h2><div id="run-list" class="empty">加载中</div></section><section class="section"><h2>本机事件</h2><div id="event-list" class="empty">加载中</div></section></main><script>
223
+ const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));const time=v=>v?new Date(v).toLocaleTimeString('zh-CN',{hour12:false}):'-';
224
+ function table(headers,rows,klass=''){return rows.length?'<table><thead><tr>'+headers.map(h=>'<th>'+h+'</th>').join('')+'</tr></thead><tbody>'+rows.map(r=>'<tr>'+r.map(c=>'<td>'+c+'</td>').join('')+'</tr>').join('')+'</tbody></table>':'<div class="empty">暂无记录</div>'}
225
+ async function load(){try{const [s,e]=await Promise.all([fetch('/api/status',{cache:'no-store'}),fetch('/api/events',{cache:'no-store'})]);const status=await s.json(),events=(await e.json()).events||[];document.querySelector('#device').textContent=(status.deviceName||status.deviceId||'未配对设备')+(status.serverUrl?' · '+status.serverUrl:'');const con=document.querySelector('#connected');con.textContent=status.connected?'已连接':'未连接';con.className='value '+(status.connected?'ok':'bad');document.querySelector('#sessions').textContent=status.activeSessions;document.querySelector('#runs').textContent=status.storedRuns;document.querySelector('#started').textContent=time(status.startedAt);document.querySelector('#run-list').innerHTML=table(['时间','任务','状态','会话'],(status.runs||[]).map(r=>[time(r.updatedAt),'<span class="event">'+esc(r.runId).slice(0,8)+'</span>','<span class="tag '+esc(r.status)+'">'+esc(r.status)+'</span>','<span class="hide-small">'+esc(r.sessionId||'-').slice(0,8)+'</span>']));document.querySelector('#event-list').innerHTML=table(['时间','事件','任务','状态'],events.map(x=>[time(x.ts),'<span class="event">'+esc(x.event||'-')+'</span>','<span class="hide-small">'+esc(x.runId||x.taskId||'-').slice(0,8)+'</span>',x.error?'<span class="bad">'+esc(x.error)+'</span>':esc(x.level||'-')]));}catch{document.querySelector('#device').textContent='无法连接本机服务'}}document.querySelector('#refresh').addEventListener('click',load);load();setInterval(load,3000);
226
+ </script></body></html>`;
227
+ }
228
+ async function readJson(request) {
229
+ const chunks = [];
230
+ let size = 0;
231
+ for await (const chunk of request) {
232
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
233
+ size += buffer.length;
234
+ if (size > MAX_BODY_BYTES)
235
+ throw new ControlError(413, 'request body is too large', 'too_large');
236
+ chunks.push(buffer);
237
+ }
238
+ if (size === 0)
239
+ return {};
240
+ try {
241
+ const value = JSON.parse(Buffer.concat(chunks).toString('utf8'));
242
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
243
+ }
244
+ catch {
245
+ throw new ControlError(400, 'invalid JSON body', 'invalid_json');
246
+ }
247
+ }
248
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/health/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};