@heyditto/cli 2.10.0 → 2.12.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.
- package/README.md +74 -0
- package/dist/agents/launch.d.ts +11 -0
- package/dist/agents/launch.js +207 -9
- package/dist/agents/launch.js.map +1 -1
- package/dist/api.d.ts +129 -0
- package/dist/api.js +82 -0
- package/dist/api.js.map +1 -1
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands.js +5 -1
- package/dist/commands.js.map +1 -1
- package/dist/org-commands.d.ts +52 -0
- package/dist/org-commands.js +511 -0
- package/dist/org-commands.js.map +1 -0
- package/dist/remote/attachments.d.ts +22 -0
- package/dist/remote/attachments.js +0 -0
- package/dist/remote/attachments.js.map +1 -0
- package/dist/remote/catalog.d.ts +18 -0
- package/dist/remote/catalog.js +309 -0
- package/dist/remote/catalog.js.map +1 -0
- package/dist/remote/controller.d.ts +76 -0
- package/dist/remote/controller.js +352 -0
- package/dist/remote/controller.js.map +1 -0
- package/dist/remote/fork-command.d.ts +8 -0
- package/dist/remote/fork-command.js +50 -0
- package/dist/remote/fork-command.js.map +1 -0
- package/dist/remote/fork.d.ts +22 -0
- package/dist/remote/fork.js +113 -0
- package/dist/remote/fork.js.map +1 -0
- package/dist/remote/headless.d.ts +30 -0
- package/dist/remote/headless.js +75 -0
- package/dist/remote/headless.js.map +1 -0
- package/dist/remote/hook.d.ts +2 -0
- package/dist/remote/hook.js +69 -0
- package/dist/remote/hook.js.map +1 -0
- package/dist/remote/hooks.d.ts +30 -0
- package/dist/remote/hooks.js +95 -0
- package/dist/remote/hooks.js.map +1 -0
- package/dist/remote/host.d.ts +63 -0
- package/dist/remote/host.js +240 -0
- package/dist/remote/host.js.map +1 -0
- package/dist/remote/protocol.d.ts +163 -0
- package/dist/remote/protocol.js +11 -0
- package/dist/remote/protocol.js.map +1 -0
- package/dist/remote/pty.d.ts +56 -0
- package/dist/remote/pty.js +100 -0
- package/dist/remote/pty.js.map +1 -0
- package/dist/store.d.ts +11 -0
- package/dist/store.js.map +1 -1
- package/dist/teleport/commands.d.ts +6 -0
- package/dist/teleport/commands.js +11 -0
- package/dist/teleport/commands.js.map +1 -1
- package/package.json +9 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { planClaude } from "../agents/claude.js";
|
|
3
|
+
import { planCodex } from "../agents/codex.js";
|
|
4
|
+
import { childEnv } from "../agents/types.js";
|
|
5
|
+
import { withHookArgs } from "./hooks.js";
|
|
6
|
+
/** One line of progress from Claude's stream-json output, or undefined when the event is not worth showing. */
|
|
7
|
+
export function summarizeClaudeEvent(line) {
|
|
8
|
+
let ev;
|
|
9
|
+
try {
|
|
10
|
+
ev = JSON.parse(line);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const type = ev.type;
|
|
16
|
+
if (type === "assistant") {
|
|
17
|
+
const message = ev.message;
|
|
18
|
+
for (const part of message?.content ?? []) {
|
|
19
|
+
if (part.type === "tool_use")
|
|
20
|
+
return `tool ${part.name ?? "?"}`;
|
|
21
|
+
if (part.type === "text" && part.text)
|
|
22
|
+
return part.text.replace(/\s+/g, " ").slice(0, 100);
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (type === "result") {
|
|
27
|
+
const result = typeof ev.result === "string" ? ev.result : "";
|
|
28
|
+
return `done${ev.is_error ? " (error)" : ""}${result ? `: ${result.replace(/\s+/g, " ").slice(0, 100)}` : ""}`;
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
export function startHeadlessTurn(input) {
|
|
33
|
+
const planInput = {
|
|
34
|
+
...input.base,
|
|
35
|
+
prompt: input.prompt,
|
|
36
|
+
resumeId: input.resumeId,
|
|
37
|
+
resumeLast: input.resumeLast,
|
|
38
|
+
passthrough: input.harness === "claude" ? ["--output-format", "stream-json", "--verbose"] : [],
|
|
39
|
+
};
|
|
40
|
+
const plan = input.harness === "claude" ? planClaude(planInput) : planCodex(planInput);
|
|
41
|
+
const args = withHookArgs(input.harness, plan.args, input.extraArgs ?? []);
|
|
42
|
+
const child = spawn(plan.command, args, {
|
|
43
|
+
cwd: input.cwd,
|
|
44
|
+
env: childEnv(plan, input.base.env),
|
|
45
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
46
|
+
});
|
|
47
|
+
let stdoutBuf = "";
|
|
48
|
+
child.stdout?.setEncoding("utf8");
|
|
49
|
+
child.stdout?.on("data", (chunk) => {
|
|
50
|
+
stdoutBuf += chunk;
|
|
51
|
+
let nl = stdoutBuf.indexOf("\n");
|
|
52
|
+
while (nl !== -1) {
|
|
53
|
+
const line = stdoutBuf.slice(0, nl).trim();
|
|
54
|
+
stdoutBuf = stdoutBuf.slice(nl + 1);
|
|
55
|
+
if (line) {
|
|
56
|
+
const summary = input.harness === "claude" ? summarizeClaudeEvent(line) : line.slice(0, 120);
|
|
57
|
+
if (summary)
|
|
58
|
+
input.onProgress?.(summary);
|
|
59
|
+
}
|
|
60
|
+
nl = stdoutBuf.indexOf("\n");
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
child.stderr?.setEncoding("utf8");
|
|
64
|
+
child.stderr?.on("data", (chunk) => {
|
|
65
|
+
const line = chunk.trim().split("\n").pop();
|
|
66
|
+
if (line)
|
|
67
|
+
input.onProgress?.(line.slice(0, 160));
|
|
68
|
+
});
|
|
69
|
+
const exit = new Promise((resolve) => {
|
|
70
|
+
child.on("error", () => resolve(null));
|
|
71
|
+
child.on("exit", (code, signal) => resolve(code ?? (signal ? 130 : null)));
|
|
72
|
+
});
|
|
73
|
+
return { child, exit, interrupt: () => child.kill("SIGINT") };
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=headless.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless.js","sourceRoot":"","sources":["../../src/remote/headless.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Hook relay run by the coding harness: `hook.js <socket> <source> [event] [json]`.
|
|
4
|
+
*
|
|
5
|
+
* Claude Code hooks pass their payload on stdin (with `hook_event_name`);
|
|
6
|
+
* Codex's `notify` passes a JSON payload as the last argument. Either way one
|
|
7
|
+
* JSON line goes to the Unix socket the CLI host opened, and the process exits
|
|
8
|
+
* 0 no matter what, so a missing socket can never block the harness.
|
|
9
|
+
*/
|
|
10
|
+
import net from "node:net";
|
|
11
|
+
function readStdin(timeoutMs) {
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
if (process.stdin.isTTY)
|
|
14
|
+
return resolve("");
|
|
15
|
+
let data = "";
|
|
16
|
+
const timer = setTimeout(() => resolve(data), timeoutMs);
|
|
17
|
+
process.stdin.setEncoding("utf8");
|
|
18
|
+
process.stdin.on("data", (c) => (data += c));
|
|
19
|
+
process.stdin.on("end", () => {
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
resolve(data);
|
|
22
|
+
});
|
|
23
|
+
process.stdin.on("error", () => {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
resolve(data);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function parse(json) {
|
|
30
|
+
try {
|
|
31
|
+
const v = JSON.parse(json);
|
|
32
|
+
return typeof v === "object" && v !== null ? v : undefined;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function main() {
|
|
39
|
+
const [socketPath, source, ...rest] = process.argv.slice(2);
|
|
40
|
+
if (!socketPath || !source)
|
|
41
|
+
return;
|
|
42
|
+
let event = rest[0] ?? "";
|
|
43
|
+
let payload;
|
|
44
|
+
if (source === "codex") {
|
|
45
|
+
// codex appends its JSON payload after our fixed arguments.
|
|
46
|
+
payload = parse(rest[rest.length - 1] ?? "");
|
|
47
|
+
event = typeof payload?.type === "string" ? payload.type : "agent-turn-complete";
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
payload = parse(await readStdin(1500));
|
|
51
|
+
if (!event && typeof payload?.hook_event_name === "string")
|
|
52
|
+
event = payload.hook_event_name;
|
|
53
|
+
}
|
|
54
|
+
await new Promise((resolve) => {
|
|
55
|
+
const timer = setTimeout(resolve, 1500);
|
|
56
|
+
const conn = net.createConnection(socketPath, () => {
|
|
57
|
+
conn.end(`${JSON.stringify({ source, event, payload })}\n`, () => {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
resolve();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
conn.on("error", () => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
resolve();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
main().finally(() => process.exit(0));
|
|
69
|
+
//# sourceMappingURL=hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/remote/hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { Harness } from "../agents/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Turn boundaries come from the harnesses themselves, not from parsing their
|
|
5
|
+
* screens: Claude Code runs its `UserPromptSubmit` and `Stop` hooks, Codex runs
|
|
6
|
+
* its `notify` command when a turn ends. Both are pointed at a tiny script
|
|
7
|
+
* (dist/remote/hook.js) that writes one JSON line to a Unix socket this
|
|
8
|
+
* process listens on. Nothing in the hook can stall the harness: the script
|
|
9
|
+
* exits 0 whatever happens.
|
|
10
|
+
*/
|
|
11
|
+
export interface HookEvent {
|
|
12
|
+
source: Harness;
|
|
13
|
+
/** Claude: UserPromptSubmit | Stop. Codex: agent-turn-complete (as notify reports it). */
|
|
14
|
+
event: string;
|
|
15
|
+
payload?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface HookServer {
|
|
18
|
+
socketPath: string;
|
|
19
|
+
events: EventEmitter;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** Absolute path of the compiled hook script next to this module. */
|
|
23
|
+
export declare function hookScriptPath(): string;
|
|
24
|
+
export declare function startHookServer(): Promise<HookServer>;
|
|
25
|
+
/** The `--settings <json>` value that makes Claude Code report turn boundaries to `socketPath`. */
|
|
26
|
+
export declare function claudeHookSettings(socketPath: string, script?: string, node?: string): string;
|
|
27
|
+
/** The `-c notify=[...]` override that makes Codex report `agent-turn-complete` to `socketPath`. */
|
|
28
|
+
export declare function codexNotifyOverride(socketPath: string, script?: string, node?: string): string;
|
|
29
|
+
/** Inserts flags where each harness accepts them: after Codex's leading subcommand words (before its positional prompt), anywhere for Claude. */
|
|
30
|
+
export declare function withHookArgs(harness: Harness, args: string[], extra: string[]): string[];
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
import { rm } from "node:fs/promises";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
/** Absolute path of the compiled hook script next to this module. */
|
|
9
|
+
export function hookScriptPath() {
|
|
10
|
+
return fileURLToPath(new URL("./hook.js", import.meta.url));
|
|
11
|
+
}
|
|
12
|
+
export async function startHookServer() {
|
|
13
|
+
const socketPath = path.join(os.tmpdir(), `heyditto-hook-${process.pid}-${randomBytes(4).toString("hex")}.sock`);
|
|
14
|
+
const events = new EventEmitter();
|
|
15
|
+
const server = net.createServer((conn) => {
|
|
16
|
+
let buf = "";
|
|
17
|
+
conn.setEncoding("utf8");
|
|
18
|
+
conn.on("data", (chunk) => {
|
|
19
|
+
buf += chunk;
|
|
20
|
+
let nl = buf.indexOf("\n");
|
|
21
|
+
while (nl !== -1) {
|
|
22
|
+
const line = buf.slice(0, nl).trim();
|
|
23
|
+
buf = buf.slice(nl + 1);
|
|
24
|
+
if (line)
|
|
25
|
+
emitLine(events, line);
|
|
26
|
+
nl = buf.indexOf("\n");
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
conn.on("end", () => {
|
|
30
|
+
const line = buf.trim();
|
|
31
|
+
buf = "";
|
|
32
|
+
if (line)
|
|
33
|
+
emitLine(events, line);
|
|
34
|
+
});
|
|
35
|
+
conn.on("error", () => undefined);
|
|
36
|
+
});
|
|
37
|
+
await new Promise((resolve, reject) => {
|
|
38
|
+
server.once("error", reject);
|
|
39
|
+
server.listen(socketPath, () => resolve());
|
|
40
|
+
});
|
|
41
|
+
server.unref();
|
|
42
|
+
return {
|
|
43
|
+
socketPath,
|
|
44
|
+
events,
|
|
45
|
+
close: async () => {
|
|
46
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
47
|
+
await rm(socketPath, { force: true }).catch(() => undefined);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function emitLine(events, line) {
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(line);
|
|
54
|
+
if (parsed && typeof parsed.event === "string")
|
|
55
|
+
events.emit("hook", parsed);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* ignore malformed hook lines */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function shellQuote(s) {
|
|
62
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
63
|
+
}
|
|
64
|
+
/** The `--settings <json>` value that makes Claude Code report turn boundaries to `socketPath`. */
|
|
65
|
+
export function claudeHookSettings(socketPath, script = hookScriptPath(), node = process.execPath) {
|
|
66
|
+
const command = (event) => `${shellQuote(node)} ${shellQuote(script)} ${shellQuote(socketPath)} claude ${event}`;
|
|
67
|
+
const settings = {
|
|
68
|
+
hooks: {
|
|
69
|
+
UserPromptSubmit: [{ hooks: [{ type: "command", command: command("UserPromptSubmit"), timeout: 5 }] }],
|
|
70
|
+
Stop: [{ hooks: [{ type: "command", command: command("Stop"), timeout: 5 }] }],
|
|
71
|
+
// Permission dialogs arrive as notification_type "permission_prompt".
|
|
72
|
+
Notification: [{ hooks: [{ type: "command", command: command("Notification"), timeout: 5 }] }],
|
|
73
|
+
// AskUserQuestion carries its question and options in tool_input.
|
|
74
|
+
PreToolUse: [{ matcher: "AskUserQuestion", hooks: [{ type: "command", command: command("PreToolUse"), timeout: 5 }] }],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
return JSON.stringify(settings);
|
|
78
|
+
}
|
|
79
|
+
/** The `-c notify=[...]` override that makes Codex report `agent-turn-complete` to `socketPath`. */
|
|
80
|
+
export function codexNotifyOverride(socketPath, script = hookScriptPath(), node = process.execPath) {
|
|
81
|
+
const toml = (s) => JSON.stringify(s);
|
|
82
|
+
return `notify=[${[node, script, socketPath, "codex"].map(toml).join(",")}]`;
|
|
83
|
+
}
|
|
84
|
+
/** Inserts flags where each harness accepts them: after Codex's leading subcommand words (before its positional prompt), anywhere for Claude. */
|
|
85
|
+
export function withHookArgs(harness, args, extra) {
|
|
86
|
+
if (extra.length === 0)
|
|
87
|
+
return args;
|
|
88
|
+
if (harness === "claude")
|
|
89
|
+
return [...args, ...extra];
|
|
90
|
+
let i = 0;
|
|
91
|
+
while (i < args.length && (args[i] === "exec" || args[i] === "resume"))
|
|
92
|
+
i += 1;
|
|
93
|
+
return [...args.slice(0, i), ...extra, ...args.slice(i)];
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/remote/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type CheckpointRequestFrame, type HostCapabilities, type HostFrame, type PromptAnswerFrame, type SessionAnnounceFrame, type SessionCommandsFrame, type SessionStatus, type TurnDeliverFrame } from "./protocol.js";
|
|
2
|
+
/**
|
|
3
|
+
* The host side of the bridge: one WebSocket to the backend that announces
|
|
4
|
+
* the coding sessions this machine runs and receives turns for them.
|
|
5
|
+
*
|
|
6
|
+
* The connection is best effort. A backend without the hosts endpoint, or a
|
|
7
|
+
* flaky network, never blocks the harness: the client keeps retrying in the
|
|
8
|
+
* background with backoff and re-announces every known session when it gets
|
|
9
|
+
* back in, and the harness keeps running locally the whole time.
|
|
10
|
+
*/
|
|
11
|
+
export interface HostHandlers {
|
|
12
|
+
onTurn: (turn: TurnDeliverFrame) => void;
|
|
13
|
+
onInterrupt: (turnId: string) => void;
|
|
14
|
+
onCheckpoint: (request: CheckpointRequestFrame) => void;
|
|
15
|
+
onPromptAnswer?: (answer: PromptAnswerFrame) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface HostClientOptions {
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
apiKey: string;
|
|
20
|
+
capabilities: HostCapabilities;
|
|
21
|
+
handlers: HostHandlers;
|
|
22
|
+
name?: string;
|
|
23
|
+
/** Called for status lines; defaults to silence. */
|
|
24
|
+
log?: (line: string) => void;
|
|
25
|
+
/** Overrides for tests. */
|
|
26
|
+
heartbeatSeconds?: number;
|
|
27
|
+
reconnectBaseMs?: number;
|
|
28
|
+
reconnectMaxMs?: number;
|
|
29
|
+
}
|
|
30
|
+
export declare class HostClient {
|
|
31
|
+
readonly options: HostClientOptions;
|
|
32
|
+
hostId?: string;
|
|
33
|
+
private socket?;
|
|
34
|
+
private connected;
|
|
35
|
+
private closing;
|
|
36
|
+
private attempt;
|
|
37
|
+
private heartbeat?;
|
|
38
|
+
private missedPongs;
|
|
39
|
+
private readonly sessions;
|
|
40
|
+
private readonly catalogs;
|
|
41
|
+
private readonly seenTurns;
|
|
42
|
+
private readonly seenSet;
|
|
43
|
+
private welcomeWaiters;
|
|
44
|
+
constructor(options: HostClientOptions);
|
|
45
|
+
get isConnected(): boolean;
|
|
46
|
+
/** Starts connecting; resolves after the first `welcome` or after `timeoutMs` (still retrying in the background). */
|
|
47
|
+
start(timeoutMs?: number): Promise<boolean>;
|
|
48
|
+
private connectLoop;
|
|
49
|
+
private connectOnce;
|
|
50
|
+
private onWelcome;
|
|
51
|
+
private dispatch;
|
|
52
|
+
private remember;
|
|
53
|
+
private startHeartbeat;
|
|
54
|
+
private stopHeartbeat;
|
|
55
|
+
send(frame: HostFrame): boolean;
|
|
56
|
+
announce(session: Omit<SessionAnnounceFrame, "type">): void;
|
|
57
|
+
/** Announces a session's command catalog; re-sent with the session after a reconnect. */
|
|
58
|
+
commands(frame: Omit<SessionCommandsFrame, "type">): void;
|
|
59
|
+
status(sessionId: string, status: SessionStatus): void;
|
|
60
|
+
closed(sessionId: string): void;
|
|
61
|
+
/** Announces every session closed and stops reconnecting. */
|
|
62
|
+
close(): void;
|
|
63
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import { packageVersion } from "../config.js";
|
|
3
|
+
import { readStoredAuth, updateStoredAuth } from "../store.js";
|
|
4
|
+
import { HOST_WS_PATH, isServerFrame, } from "./protocol.js";
|
|
5
|
+
const SEEN_TURNS_MAX = 1000;
|
|
6
|
+
function wsUrl(baseUrl) {
|
|
7
|
+
const u = new URL(baseUrl);
|
|
8
|
+
u.protocol = u.protocol === "https:" ? "wss:" : "ws:";
|
|
9
|
+
u.pathname = `${u.pathname.replace(/\/+$/, "")}${HOST_WS_PATH}`;
|
|
10
|
+
u.search = "";
|
|
11
|
+
return u.toString();
|
|
12
|
+
}
|
|
13
|
+
export class HostClient {
|
|
14
|
+
options;
|
|
15
|
+
hostId;
|
|
16
|
+
socket;
|
|
17
|
+
connected = false;
|
|
18
|
+
closing = false;
|
|
19
|
+
attempt = 0;
|
|
20
|
+
heartbeat;
|
|
21
|
+
missedPongs = 0;
|
|
22
|
+
sessions = new Map();
|
|
23
|
+
catalogs = new Map();
|
|
24
|
+
seenTurns = [];
|
|
25
|
+
seenSet = new Set();
|
|
26
|
+
welcomeWaiters = [];
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.options = options;
|
|
29
|
+
}
|
|
30
|
+
get isConnected() {
|
|
31
|
+
return this.connected;
|
|
32
|
+
}
|
|
33
|
+
/** Starts connecting; resolves after the first `welcome` or after `timeoutMs` (still retrying in the background). */
|
|
34
|
+
async start(timeoutMs = 4000) {
|
|
35
|
+
this.hostId = (await readStoredAuth())?.hostId;
|
|
36
|
+
void this.connectLoop();
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
const timer = setTimeout(() => resolve(false), timeoutMs);
|
|
39
|
+
this.welcomeWaiters.push(() => {
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
resolve(true);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async connectLoop() {
|
|
46
|
+
while (!this.closing) {
|
|
47
|
+
try {
|
|
48
|
+
await this.connectOnce();
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
this.options.log?.(`remote control: ${err instanceof Error ? err.message : String(err)}`);
|
|
52
|
+
}
|
|
53
|
+
if (this.closing)
|
|
54
|
+
return;
|
|
55
|
+
const base = this.options.reconnectBaseMs ?? 1000;
|
|
56
|
+
const max = this.options.reconnectMaxMs ?? 30_000;
|
|
57
|
+
const delay = Math.min(max, base * 2 ** Math.min(this.attempt, 6)) * (0.75 + Math.random() * 0.5);
|
|
58
|
+
this.attempt += 1;
|
|
59
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
connectOnce() {
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const socket = new WebSocket(wsUrl(this.options.baseUrl), {
|
|
65
|
+
// Node's WebSocket (undici) accepts request headers; browsers do not, but this is the CLI.
|
|
66
|
+
headers: { Authorization: `Bearer ${this.options.apiKey}` },
|
|
67
|
+
});
|
|
68
|
+
this.socket = socket;
|
|
69
|
+
// Resolves only once the socket closes after a successful welcome, so the
|
|
70
|
+
// loop never opens a second connection next to a live one.
|
|
71
|
+
let welcomed = false;
|
|
72
|
+
let settled = false;
|
|
73
|
+
const fail = (message) => {
|
|
74
|
+
if (settled)
|
|
75
|
+
return;
|
|
76
|
+
settled = true;
|
|
77
|
+
reject(new Error(message));
|
|
78
|
+
};
|
|
79
|
+
socket.addEventListener("open", () => {
|
|
80
|
+
this.send({
|
|
81
|
+
type: "hello",
|
|
82
|
+
kind: "cli",
|
|
83
|
+
name: this.options.name ?? os.hostname(),
|
|
84
|
+
platform: `${process.platform}-${process.arch}`,
|
|
85
|
+
version: packageVersion,
|
|
86
|
+
capabilities: this.options.capabilities,
|
|
87
|
+
...(this.hostId ? { hostId: this.hostId } : {}),
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
socket.addEventListener("message", (event) => {
|
|
91
|
+
let parsed;
|
|
92
|
+
try {
|
|
93
|
+
parsed = JSON.parse(typeof event.data === "string" ? event.data : String(event.data));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (!isServerFrame(parsed))
|
|
99
|
+
return;
|
|
100
|
+
if (parsed.type === "welcome") {
|
|
101
|
+
welcomed = true;
|
|
102
|
+
this.onWelcome(parsed);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.dispatch(parsed);
|
|
106
|
+
});
|
|
107
|
+
socket.addEventListener("error", () => fail("connection failed"));
|
|
108
|
+
socket.addEventListener("close", (event) => {
|
|
109
|
+
this.stopHeartbeat();
|
|
110
|
+
const wasConnected = this.connected;
|
|
111
|
+
this.connected = false;
|
|
112
|
+
if (wasConnected && !this.closing)
|
|
113
|
+
this.options.log?.(`remote control: disconnected (${event.code}); reconnecting`);
|
|
114
|
+
if (welcomed) {
|
|
115
|
+
if (!settled) {
|
|
116
|
+
settled = true;
|
|
117
|
+
resolve();
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
fail(`closed (${event.code}${event.reason ? ` ${event.reason}` : ""})`);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
onWelcome(frame) {
|
|
126
|
+
this.connected = true;
|
|
127
|
+
this.attempt = 0;
|
|
128
|
+
this.missedPongs = 0;
|
|
129
|
+
if (frame.hostId && frame.hostId !== this.hostId) {
|
|
130
|
+
this.hostId = frame.hostId;
|
|
131
|
+
void updateStoredAuth({ hostId: frame.hostId }).catch(() => undefined);
|
|
132
|
+
}
|
|
133
|
+
this.startHeartbeat(frame.heartbeatSeconds || this.options.heartbeatSeconds || 30);
|
|
134
|
+
for (const session of this.sessions.values()) {
|
|
135
|
+
this.send(session);
|
|
136
|
+
const catalog = this.catalogs.get(session.sessionId);
|
|
137
|
+
if (catalog)
|
|
138
|
+
this.send(catalog);
|
|
139
|
+
}
|
|
140
|
+
const waiters = this.welcomeWaiters;
|
|
141
|
+
this.welcomeWaiters = [];
|
|
142
|
+
for (const w of waiters)
|
|
143
|
+
w();
|
|
144
|
+
}
|
|
145
|
+
dispatch(frame) {
|
|
146
|
+
switch (frame.type) {
|
|
147
|
+
case "ping":
|
|
148
|
+
this.send({ type: "pong", t: frame.t });
|
|
149
|
+
return;
|
|
150
|
+
case "pong":
|
|
151
|
+
this.missedPongs = 0;
|
|
152
|
+
return;
|
|
153
|
+
case "turn.deliver":
|
|
154
|
+
if (this.seenSet.has(frame.turnId)) {
|
|
155
|
+
this.send({ type: "turn.ack", turnId: frame.turnId });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this.remember(frame.turnId);
|
|
159
|
+
this.options.handlers.onTurn(frame);
|
|
160
|
+
return;
|
|
161
|
+
case "turn.interrupt":
|
|
162
|
+
this.options.handlers.onInterrupt(frame.turnId);
|
|
163
|
+
return;
|
|
164
|
+
case "checkpoint.request":
|
|
165
|
+
this.options.handlers.onCheckpoint(frame);
|
|
166
|
+
return;
|
|
167
|
+
case "prompt.answer":
|
|
168
|
+
this.options.handlers.onPromptAnswer?.(frame);
|
|
169
|
+
return;
|
|
170
|
+
default:
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
remember(turnId) {
|
|
175
|
+
this.seenSet.add(turnId);
|
|
176
|
+
this.seenTurns.push(turnId);
|
|
177
|
+
if (this.seenTurns.length > SEEN_TURNS_MAX) {
|
|
178
|
+
const old = this.seenTurns.shift();
|
|
179
|
+
if (old)
|
|
180
|
+
this.seenSet.delete(old);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
startHeartbeat(seconds) {
|
|
184
|
+
this.stopHeartbeat();
|
|
185
|
+
this.heartbeat = setInterval(() => {
|
|
186
|
+
if (this.missedPongs >= 3) {
|
|
187
|
+
this.options.log?.("remote control: heartbeat lost; reconnecting");
|
|
188
|
+
this.socket?.close(4000, "heartbeat lost");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
this.missedPongs += 1;
|
|
192
|
+
this.send({ type: "ping", t: Date.now() });
|
|
193
|
+
}, Math.max(1, seconds) * 1000);
|
|
194
|
+
this.heartbeat.unref?.();
|
|
195
|
+
}
|
|
196
|
+
stopHeartbeat() {
|
|
197
|
+
if (this.heartbeat)
|
|
198
|
+
clearInterval(this.heartbeat);
|
|
199
|
+
this.heartbeat = undefined;
|
|
200
|
+
}
|
|
201
|
+
send(frame) {
|
|
202
|
+
const socket = this.socket;
|
|
203
|
+
if (!socket || socket.readyState !== WebSocket.OPEN)
|
|
204
|
+
return false;
|
|
205
|
+
socket.send(JSON.stringify(frame));
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
announce(session) {
|
|
209
|
+
const frame = { type: "session.announce", ...session };
|
|
210
|
+
this.sessions.set(session.sessionId, frame);
|
|
211
|
+
this.send(frame);
|
|
212
|
+
}
|
|
213
|
+
/** Announces a session's command catalog; re-sent with the session after a reconnect. */
|
|
214
|
+
commands(frame) {
|
|
215
|
+
const full = { type: "session.commands", ...frame };
|
|
216
|
+
this.catalogs.set(frame.sessionId, full);
|
|
217
|
+
this.send(full);
|
|
218
|
+
}
|
|
219
|
+
status(sessionId, status) {
|
|
220
|
+
const known = this.sessions.get(sessionId);
|
|
221
|
+
if (known)
|
|
222
|
+
known.status = status;
|
|
223
|
+
this.send({ type: "session.status", sessionId, status });
|
|
224
|
+
}
|
|
225
|
+
closed(sessionId) {
|
|
226
|
+
this.sessions.delete(sessionId);
|
|
227
|
+
this.catalogs.delete(sessionId);
|
|
228
|
+
this.send({ type: "session.closed", sessionId });
|
|
229
|
+
}
|
|
230
|
+
/** Announces every session closed and stops reconnecting. */
|
|
231
|
+
close() {
|
|
232
|
+
this.closing = true;
|
|
233
|
+
this.stopHeartbeat();
|
|
234
|
+
for (const id of [...this.sessions.keys()])
|
|
235
|
+
this.closed(id);
|
|
236
|
+
this.socket?.close(1000, "host exiting");
|
|
237
|
+
this.socket = undefined;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/remote/host.ts"],"names":[],"mappings":"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"}
|