agent-embassy 4.0.0 → 4.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.
@@ -0,0 +1,199 @@
1
+ import { spawn as nodeSpawn } from "node:child_process";
2
+ import { TextDecoder } from "node:util";
3
+ import { isBrokerResult } from "./broker-control.js";
4
+ const STDOUT_LIMIT = 256 * 1024;
5
+ const STDERR_LIMIT = 64 * 1024;
6
+ const HOST = /^[a-z0-9](?:[a-z0-9.-]{0,61}[a-z0-9])?$/;
7
+ const ENDPOINT = /^reg_[A-Za-z0-9_-]{1,252}$/;
8
+ const DELIVERY = /^dlv_[A-Za-z0-9_-]{24}$/;
9
+ const UNCERTAIN = new Set(["HANDLER_FAILURE", "INVALID_HANDLER_RESPONSE", "GATEWAY_STATE_COMMIT_OUTCOME_UNKNOWN"]);
10
+ const object = (value) => !!value && typeof value === "object" && !Array.isArray(value);
11
+ const exact = (value, keys) => object(value) &&
12
+ Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
13
+ const safeCode = (value) => typeof value === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(value);
14
+ const cleanEnvironment = (env) => Object.fromEntries(["HOME", "USER", "LOGNAME", "SSH_AUTH_SOCK"].flatMap((key) => env[key] === undefined ? [] : [[key, env[key]]]));
15
+ export class TuiSshError extends Error {
16
+ code;
17
+ detail;
18
+ constructor(code, detail) {
19
+ super(code);
20
+ this.code = code;
21
+ this.name = "TuiSshError";
22
+ this.detail = detail === undefined ? undefined : { detail };
23
+ }
24
+ }
25
+ function invocation(command) {
26
+ const empty = (value) => exact(value, []);
27
+ switch (command.method) {
28
+ case "list_snapshot":
29
+ if (empty(command.params))
30
+ return { cli: "status", args: ["--json"], timeout: 8_000, mutating: false };
31
+ break;
32
+ case "refresh_discovery":
33
+ if (empty(command.params))
34
+ return { cli: "refresh", args: [], timeout: 15_000, mutating: true };
35
+ break;
36
+ case "check":
37
+ if (empty(command.params))
38
+ return { cli: "check", args: [], timeout: 30_000, mutating: true };
39
+ break;
40
+ case "delivery_status":
41
+ if (exact(command.params, ["token"]) && DELIVERY.test(command.params.token))
42
+ return { cli: "delivery-status", args: ["--token", command.params.token], timeout: 8_000, mutating: false };
43
+ break;
44
+ case "retire_route":
45
+ if ("endpoint" in command.params && exact(command.params, ["endpoint"]) && ENDPOINT.test(command.params.endpoint))
46
+ return { cli: "retire", args: ["--endpoint", command.params.endpoint], timeout: 15_000, mutating: true };
47
+ break;
48
+ }
49
+ throw new TuiSshError("INVALID_REQUEST");
50
+ }
51
+ export function createTuiSshClient(options) {
52
+ if (options.nodes.length > 32 || new Set(options.nodes).size !== options.nodes.length || options.nodes.some((node) => !HOST.test(node)))
53
+ throw new TypeError("Invalid TUI SSH nodes");
54
+ const nodes = new Set(options.nodes), active = new Map();
55
+ const env = cleanEnvironment(options.env ?? process.env);
56
+ const spawn = options.spawn ?? ((command, args, spawnOptions) => nodeSpawn(command, [...args], spawnOptions));
57
+ const timers = options.timers ?? { setTimeout, clearTimeout };
58
+ let closing = false;
59
+ const stop = (entry) => {
60
+ if (entry.stopping)
61
+ return;
62
+ entry.stopping = true;
63
+ entry.child.kill("SIGTERM");
64
+ entry.killTimer = timers.setTimeout(() => entry.child.kill("SIGKILL"), 1_000);
65
+ entry.killTimer.unref?.();
66
+ };
67
+ const run = (host, args, timeout, mutating) => {
68
+ if (closing)
69
+ return Promise.reject(new TuiSshError("CONTROL_UNAVAILABLE"));
70
+ if (!nodes.has(host))
71
+ return Promise.reject(new TuiSshError("PEER_NOT_CONFIGURED"));
72
+ if (active.has(host))
73
+ return Promise.reject(new TuiSshError("ROUTE_BUSY"));
74
+ let child;
75
+ try {
76
+ child = spawn("/usr/bin/ssh", ["-T", "-x", "-o", "BatchMode=yes", "-o", "ClearAllForwardings=yes",
77
+ "-o", "ForwardAgent=no", "-o", "PermitLocalCommand=no", "-o", "SendEnv=-*", "-o", "Tunnel=no",
78
+ host, "embassy", ...args], { env, shell: false, stdio: ["ignore", "pipe", "pipe"] });
79
+ }
80
+ catch {
81
+ return Promise.reject(new TuiSshError("PEER_TUNNEL_UNAVAILABLE"));
82
+ }
83
+ const entry = { child };
84
+ active.set(host, entry);
85
+ return new Promise((resolve, reject) => {
86
+ const stdout = [];
87
+ let stdoutBytes = 0, stderrBytes = 0, settled = false, invalidOutput = false;
88
+ const finish = (error, value) => {
89
+ if (settled)
90
+ return;
91
+ settled = true;
92
+ timers.clearTimeout(wall);
93
+ error ? reject(error) : resolve(value);
94
+ };
95
+ const failed = () => new TuiSshError(mutating ? "CONTROL_WRITE_OUTCOME_AMBIGUOUS" : "PEER_TUNNEL_UNAVAILABLE");
96
+ child.stdout.on("data", (chunk) => {
97
+ stdoutBytes += chunk.length;
98
+ if (stdoutBytes > STDOUT_LIMIT) {
99
+ invalidOutput = true;
100
+ stop(entry);
101
+ }
102
+ else
103
+ stdout.push(Buffer.from(chunk));
104
+ });
105
+ child.stderr.on("data", (chunk) => {
106
+ stderrBytes += chunk.length;
107
+ if (stderrBytes > STDERR_LIMIT) {
108
+ finish(failed());
109
+ stop(entry);
110
+ }
111
+ });
112
+ child.once("error", () => finish(failed()));
113
+ child.once("close", (code) => {
114
+ if (entry.killTimer)
115
+ timers.clearTimeout(entry.killTimer);
116
+ if (active.get(host) === entry)
117
+ active.delete(host);
118
+ if (settled)
119
+ return;
120
+ if (stdoutBytes === 0 || code === null)
121
+ return finish(failed());
122
+ if (invalidOutput)
123
+ return finish(new TuiSshError(mutating ? "CONTROL_WRITE_OUTCOME_AMBIGUOUS" : "CONTROL_INVALID_RESPONSE"));
124
+ try {
125
+ finish(undefined, new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(stdout)));
126
+ }
127
+ catch {
128
+ finish(new TuiSshError(mutating ? "CONTROL_WRITE_OUTCOME_AMBIGUOUS" : "CONTROL_INVALID_RESPONSE"));
129
+ }
130
+ });
131
+ const wall = timers.setTimeout(() => { finish(failed()); stop(entry); }, timeout);
132
+ wall.unref?.();
133
+ });
134
+ };
135
+ const version = async (host) => {
136
+ try {
137
+ const output = await run(host, ["--version"], 8_000, false);
138
+ if (Buffer.byteLength(output) > 128)
139
+ return "unknown";
140
+ const match = /^embassy ([0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.-]+)?)\n?$/.exec(output);
141
+ return match?.[1] ?? "unknown";
142
+ }
143
+ catch {
144
+ return "unknown";
145
+ }
146
+ };
147
+ const call = async (host, command) => {
148
+ const request = invocation(command);
149
+ let output;
150
+ try {
151
+ output = await run(host, [request.cli, ...request.args], request.timeout, request.mutating);
152
+ }
153
+ catch (error) {
154
+ if (!request.mutating && error instanceof TuiSshError && error.code === "CONTROL_INVALID_RESPONSE") {
155
+ const observed = await version(host);
156
+ throw new TuiSshError("CONTROL_INVALID_RESPONSE", `unsupported response — remote CLI version ${observed}`);
157
+ }
158
+ throw error;
159
+ }
160
+ const unsupported = async () => {
161
+ if (request.mutating)
162
+ throw new TuiSshError("CONTROL_WRITE_OUTCOME_AMBIGUOUS");
163
+ const observed = await version(host);
164
+ throw new TuiSshError("CONTROL_INVALID_RESPONSE", `unsupported response — remote CLI version ${observed}`);
165
+ };
166
+ let envelope;
167
+ if (output.indexOf("\n") !== output.length - 1)
168
+ return await unsupported();
169
+ try {
170
+ envelope = JSON.parse(output.slice(0, -1));
171
+ }
172
+ catch {
173
+ return await unsupported();
174
+ }
175
+ if (!object(envelope) || envelope.command !== request.cli || typeof envelope.ok !== "boolean")
176
+ return await unsupported();
177
+ if (envelope.ok === false) {
178
+ if (!exact(envelope, ["ok", "command", "error"]) || !exact(envelope.error, ["code"]) || !safeCode(envelope.error.code))
179
+ return await unsupported();
180
+ if (request.mutating && UNCERTAIN.has(envelope.error.code))
181
+ throw new TuiSshError("CONTROL_WRITE_OUTCOME_AMBIGUOUS");
182
+ throw new TuiSshError(envelope.error.code);
183
+ }
184
+ if (!exact(envelope, ["ok", "command", "result"]) || !isBrokerResult(command.method, envelope.result))
185
+ return await unsupported();
186
+ if ((command.method === "list_snapshot" || command.method === "refresh_discovery") &&
187
+ envelope.result.routes.some((row) => row.host !== host))
188
+ throw new TuiSshError(request.mutating ? "CONTROL_WRITE_OUTCOME_AMBIGUOUS" : "CONTROL_INVALID_RESPONSE", request.mutating ? undefined : "host mismatch");
189
+ return envelope.result;
190
+ };
191
+ return { call, close: () => {
192
+ if (closing)
193
+ return;
194
+ closing = true;
195
+ for (const entry of active.values())
196
+ stop(entry);
197
+ } };
198
+ }
199
+ //# sourceMappingURL=tui-ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui-ssh.js","sourceRoot":"","sources":["../../../src/gateway/tui-ssh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { type Readable, type Writable } from "node:stream";
2
+ import type { BrokerCommand } from "./broker-control.js";
3
+ type Input = Pick<Readable, "on" | "off"> & {
4
+ isTTY?: boolean;
5
+ isRaw?: boolean;
6
+ readableFlowing?: boolean | null;
7
+ pause?: Readable["pause"];
8
+ resume?: Readable["resume"];
9
+ setRawMode?: (enabled: boolean) => unknown;
10
+ };
11
+ type Output = Pick<Writable, "write"> & {
12
+ isTTY?: boolean;
13
+ columns?: number;
14
+ rows?: number;
15
+ on?: Writable["on"];
16
+ off?: Writable["off"];
17
+ };
18
+ export type TuiDependencies = Readonly<{
19
+ input: Input;
20
+ output: Output;
21
+ call: (command: BrokerCommand) => Promise<unknown>;
22
+ renderStatus: (snapshot: unknown) => string;
23
+ host?: string;
24
+ hint?: (code: string, host?: string) => string;
25
+ remote?: Readonly<{
26
+ hosts: readonly string[];
27
+ call: (host: string, command: BrokerCommand) => Promise<unknown>;
28
+ close: () => void;
29
+ }>;
30
+ signal?: AbortSignal;
31
+ }>;
32
+ type Obj = Record<string, unknown>;
33
+ type Section = "endpoints" | "deliveries" | "retirements" | "result";
34
+ type Mode = "browse" | "token" | "confirm";
35
+ type EndpointRow = Readonly<{
36
+ id: string;
37
+ alias: string;
38
+ provider: string;
39
+ host: string;
40
+ local: boolean;
41
+ queueDepth?: number;
42
+ lastOperation?: Obj;
43
+ observedAt?: string;
44
+ safeErrorCode?: string;
45
+ placeholder?: boolean;
46
+ }>;
47
+ export type TuiModel = {
48
+ snapshot?: Obj;
49
+ snapshotAt?: number;
50
+ staleSince?: number;
51
+ error?: string;
52
+ errorDetail?: string;
53
+ action?: string;
54
+ actionRunning?: boolean;
55
+ host?: string;
56
+ restarted?: boolean;
57
+ section: Section;
58
+ previousSection?: Exclude<Section, "result">;
59
+ selected: Record<Exclude<Section, "result">, number> & Partial<Record<"result", number>>;
60
+ selectedEndpoint?: string;
61
+ mode: Mode;
62
+ token: string;
63
+ retiring?: EndpointRow;
64
+ result?: Readonly<{
65
+ label: string;
66
+ value: string;
67
+ }>;
68
+ };
69
+ /** Pure screen renderer. It emits no ANSI so all dynamic values remain inert. */
70
+ export declare function renderTui(model: Readonly<TuiModel>, columns?: number, rows?: number, now?: number): string;
71
+ /** Interactive, dependency-free client over the existing broker commands. */
72
+ export declare function runTui(dependencies: TuiDependencies): Promise<void>;
73
+ export {};