@prohost/cli 0.13.2 → 0.14.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 (61) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +129 -0
  3. package/dist/agent/claude.d.ts +28 -0
  4. package/dist/agent/claude.js +83 -4
  5. package/dist/agent/command.d.ts +8 -0
  6. package/dist/agent/command.js +40 -7
  7. package/dist/agent/daemon.d.ts +3 -1
  8. package/dist/agent/daemon.js +1 -2
  9. package/dist/agent/run.d.ts +30 -1
  10. package/dist/agent/run.js +311 -16
  11. package/dist/agent/runtime_status.d.ts +99 -3
  12. package/dist/agent/runtime_status.js +173 -5
  13. package/dist/agent/worktrees.d.ts +2 -0
  14. package/dist/agent/worktrees.js +3 -2
  15. package/dist/codehost/activity.d.ts +64 -0
  16. package/dist/codehost/activity.js +135 -0
  17. package/dist/codehost/agents.d.ts +31 -0
  18. package/dist/codehost/agents.js +57 -0
  19. package/dist/codehost/checkouts.d.ts +130 -0
  20. package/dist/codehost/checkouts.js +270 -0
  21. package/dist/codehost/claude_trust.d.ts +40 -0
  22. package/dist/codehost/claude_trust.js +129 -0
  23. package/dist/codehost/command.d.ts +25 -0
  24. package/dist/codehost/command.js +136 -0
  25. package/dist/codehost/decision.d.ts +156 -0
  26. package/dist/codehost/decision.js +553 -0
  27. package/dist/codehost/events.d.ts +49 -0
  28. package/dist/codehost/events.js +87 -0
  29. package/dist/codehost/folders.d.ts +122 -0
  30. package/dist/codehost/folders.js +317 -0
  31. package/dist/codehost/hooks.d.ts +178 -0
  32. package/dist/codehost/hooks.js +726 -0
  33. package/dist/codehost/host.d.ts +290 -0
  34. package/dist/codehost/host.js +625 -0
  35. package/dist/codehost/host_stats.d.ts +71 -0
  36. package/dist/codehost/host_stats.js +166 -0
  37. package/dist/codehost/invocations.d.ts +12 -0
  38. package/dist/codehost/invocations.js +82 -0
  39. package/dist/codehost/ledger.d.ts +35 -0
  40. package/dist/codehost/ledger.js +91 -0
  41. package/dist/codehost/local_server.d.ts +106 -0
  42. package/dist/codehost/local_server.js +339 -0
  43. package/dist/codehost/pty.d.ts +38 -0
  44. package/dist/codehost/pty.js +47 -0
  45. package/dist/codehost/ready.d.ts +31 -0
  46. package/dist/codehost/ready.js +109 -0
  47. package/dist/codehost/relay.d.ts +170 -0
  48. package/dist/codehost/relay.js +548 -0
  49. package/dist/codehost/settings.d.ts +52 -0
  50. package/dist/codehost/settings.js +114 -0
  51. package/dist/codehost/sizing.d.ts +163 -0
  52. package/dist/codehost/sizing.js +394 -0
  53. package/dist/codehost/terminals.d.ts +390 -0
  54. package/dist/codehost/terminals.js +965 -0
  55. package/dist/codehost/tmux.d.ts +196 -0
  56. package/dist/codehost/tmux.js +391 -0
  57. package/dist/index.d.ts +5 -0
  58. package/dist/index.js +21 -0
  59. package/dist/version.d.ts +2 -2
  60. package/dist/version.js +1 -1
  61. package/package.json +4 -1
@@ -0,0 +1,170 @@
1
+ /**
2
+ * This Mac's side of the code-host relay (`api_public/websockets/code_host_relay.py`).
3
+ *
4
+ * One **control** socket to `/v1/code-hosts/control`, authenticated with the
5
+ * paired agent's API key, open while Code-thread hosting is on. It says the
6
+ * machine is online, reports its stats and its `allowRemoteTerminal` switch,
7
+ * and receives:
8
+ *
9
+ * - `host_token` — the narrow `host_events` credential the hooks post with,
10
+ * written to `hook.json` (see `events.ts`);
11
+ * - `dial` — a viewer wants a terminal. Each dial opens a **fresh stream
12
+ * socket** to `/v1/code-hosts/stream` for that one viewer: terminal bytes are
13
+ * never multiplexed on the control socket, so one slow viewer can't stall
14
+ * another or the control channel.
15
+ *
16
+ * The relay refuses viewers unless this machine last said
17
+ * `allow_remote_terminal: true`; this side checks the switch again, on disk,
18
+ * at every dial, so turning it off takes effect for the next attach even
19
+ * before the relay has heard.
20
+ */
21
+ import WebSocket from 'ws';
22
+ import type { HostStats } from './host_stats.js';
23
+ import type { CodeHostSettings } from './settings.js';
24
+ import type { CodeTerminals, TerminalStream } from './terminals.js';
25
+ import type { TerminalWindow } from './tmux.js';
26
+ export declare const CONTROL_PATH = "/v1/code-hosts/control";
27
+ export declare const STREAM_PATH = "/v1/code-hosts/stream";
28
+ /** How often a status frame refreshes the relay's view of this machine. */
29
+ export declare const STATUS_INTERVAL_MS = 30000;
30
+ /**
31
+ * A flipped switch is sent at once and again this long after: the relay takes a
32
+ * flip inside its status window but at most once a second
33
+ * (`STATUS_SWITCH_MIN_INTERVAL_SECONDS`), so one that landed within a second of
34
+ * another status is dropped, and the repeat carries it.
35
+ */
36
+ export declare const STATUS_SWITCH_RESEND_MS = 1200;
37
+ /** No frame from the relay for this long means the control socket is dead. */
38
+ export declare const CONTROL_SILENCE_MS = 75000;
39
+ /** A relay that upgraded but never answered the auth frame is a dead socket. */
40
+ export declare const CONTROL_AUTH_TIMEOUT_MS = 20000;
41
+ /** Output buffered toward a slow relay before the stream is dropped (tmux redraws on reconnect). */
42
+ export declare const MAX_STREAM_BUFFERED_BYTES: number;
43
+ /** `ws(s)://host/prefix<route>` from the API base URL; `undefined` when unusable. */
44
+ export declare function relayUrl(baseUrl: string, route: string): string | undefined;
45
+ export interface DialFrame {
46
+ streamId: string;
47
+ streamToken: string;
48
+ terminalId: string;
49
+ window: TerminalWindow;
50
+ cols?: number;
51
+ rows?: number;
52
+ mode: 'type' | 'watch';
53
+ }
54
+ /** A `dial` frame, or `null` when it is not one this host can act on. */
55
+ export declare function parseDial(frame: Record<string, unknown>): DialFrame | null;
56
+ /** Pipe one terminal stream over one open socket until either side ends. */
57
+ export declare function pipeStream(ws: WebSocket, stream: TerminalStream, onClosed: (reason: string) => void): void;
58
+ /** Whose pairing this is, as the relay's `auth_ok` names it. */
59
+ export interface PairingIdentity {
60
+ accountId: string;
61
+ ownerUserId: string;
62
+ }
63
+ /** The pairing identity in an `auth_ok`, or `undefined` from a relay that sends none. */
64
+ export declare function parseIdentity(frame: Record<string, unknown>): PairingIdentity | undefined;
65
+ /** The relay's answer to `verify_viewer`: who the token's bearer is, and what it may attach to. */
66
+ export type ViewerVerdict = {
67
+ ok: true;
68
+ userId: string;
69
+ codeThreadId: string | null;
70
+ terminalId: string;
71
+ window: TerminalWindow | null;
72
+ mode: 'type' | 'watch';
73
+ /**
74
+ * The token was minted for this Mac's own desktop app (`loc`): the desktop socket admits it
75
+ * with the remote switch off. Only a literal `true` counts; an older server sends none.
76
+ */
77
+ local: boolean;
78
+ } | {
79
+ ok: false;
80
+ reason: string;
81
+ };
82
+ /** How long the relay gets to answer a `verify_viewer`. */
83
+ export declare const VERIFY_VIEWER_TIMEOUT_MS = 5000;
84
+ /** A `viewer_verified` frame, or `null` when it is malformed (treated as a refusal). */
85
+ export declare function parseViewerVerdict(frame: Record<string, unknown>): ViewerVerdict | null;
86
+ export interface CodeHostRelayOptions {
87
+ baseUrl: string;
88
+ apiKey: string;
89
+ machineId: string;
90
+ /**
91
+ * This home's CLI instance (see `codeHostInstanceId`): its presence and control
92
+ * channel on the relay are its own, so a thread's terminal is dialed only
93
+ * through the instance the thread is bound to.
94
+ */
95
+ instanceId?: string;
96
+ machine: string;
97
+ clientVersion: string;
98
+ settings: () => CodeHostSettings;
99
+ stats: () => Promise<HostStats | undefined>;
100
+ terminals: Pick<CodeTerminals, 'attach'>;
101
+ onHostToken: (token: string, expiresAt: number) => void;
102
+ /**
103
+ * The relay authorized this machine (`auth_ok`), or refused it (`auth_error`:
104
+ * a revoked key, a pairing no longer active). The host closes every other
105
+ * way into its terminals — the desktop socket — on a refusal.
106
+ */
107
+ onAuthorization?: (authorized: boolean, reason?: string, identity?: PairingIdentity) => void;
108
+ /**
109
+ * The current control socket closed. Authorization is unknown until the
110
+ * next `auth_ok`: the host fails closed on its other ways in (the desktop
111
+ * socket) for the gap, since a revocation during an outage can't be heard.
112
+ */
113
+ onDisconnected?: () => void;
114
+ log?: (line: string) => void;
115
+ WebSocketCtor?: typeof WebSocket;
116
+ setTimer?: (fn: () => void, ms: number) => () => void;
117
+ random?: () => number;
118
+ }
119
+ export declare class CodeHostRelay {
120
+ private readonly options;
121
+ private ws;
122
+ private stopped;
123
+ private attempt;
124
+ private readonly timers;
125
+ private readonly streams;
126
+ private lastFrameAt;
127
+ private authed;
128
+ /** `verify_viewer` requests awaiting the relay's answer, by request id. */
129
+ private readonly verifying;
130
+ /** Bumped by {@link dropStreams}: a dial begun before a drop never opens. */
131
+ private generation;
132
+ private refused;
133
+ private readonly log;
134
+ private readonly Ctor;
135
+ private readonly setTimer;
136
+ constructor(options: CodeHostRelayOptions);
137
+ /** Whether the control socket is authenticated right now. Exposed for assertions. */
138
+ connected(): boolean;
139
+ /** Open streams. Exposed for assertions. */
140
+ openStreams(): number;
141
+ start(): void;
142
+ /**
143
+ * End every attached viewer now. A terminal is attached only while this
144
+ * machine is authorized: losing the control socket, the relay refusing the
145
+ * pairing (revoked key, `pairing_revoked`), or the owner turning the remote
146
+ * terminal off all drop what is open — viewers reconnect through a fresh,
147
+ * fully checked attach.
148
+ */
149
+ /**
150
+ * Ask the relay who a viewer token's bearer is (the desktop fast path's only
151
+ * source of identity: the app page's own word is never taken). Resolves a
152
+ * refusal when the relay is unreachable, slow, or says no. Never rejects.
153
+ */
154
+ verifyViewer(token: string): Promise<ViewerVerdict>;
155
+ private refuseVerifications;
156
+ dropStreams(reason: string): void;
157
+ /** The owner's switch changed: tell the relay now, and once more a moment later, not at the next 30 s status. */
158
+ statusChanged(): void;
159
+ stop(): void;
160
+ private later;
161
+ private reconnect;
162
+ private send;
163
+ private sendStatus;
164
+ private statusLoop;
165
+ private connect;
166
+ private onFrame;
167
+ private acceptToken;
168
+ private dialError;
169
+ private onDial;
170
+ }