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.
- package/CHANGELOG.md +18 -0
- package/README.md +127 -23
- package/dist/src/gateway/codex-stateless-transport.js +2 -1
- package/dist/src/gateway/codex-stateless-transport.js.map +1 -1
- package/dist/src/gateway/core-cli.d.ts +1 -2
- package/dist/src/gateway/core-cli.js +18 -2
- package/dist/src/gateway/core-cli.js.map +1 -1
- package/dist/src/gateway/core-version.d.ts +1 -0
- package/dist/src/gateway/core-version.js +2 -0
- package/dist/src/gateway/core-version.js.map +1 -0
- package/dist/src/gateway/tui-ssh.d.ts +36 -0
- package/dist/src/gateway/tui-ssh.js +199 -0
- package/dist/src/gateway/tui-ssh.js.map +1 -0
- package/dist/src/gateway/tui.d.ts +73 -0
- package/dist/src/gateway/tui.js +563 -0
- package/dist/src/gateway/tui.js.map +1 -0
- package/docs/CONFIGURATION.md +66 -14
- package/docs/GATEWAY-ARCHITECTURE.md +9 -1
- package/package.json +1 -1
- package/skills/embassy-peer/SKILL.md +24 -6
|
@@ -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 {};
|