svamp-cli 0.2.289 → 0.2.291
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/dist/{adminCommands-C7-LgldE.mjs → adminCommands-DuA0IQ8B.mjs} +1 -1
- package/dist/{agentCommands-B7HQJo3X.mjs → agentCommands-CoPsnzwK.mjs} +5 -5
- package/dist/{auth-BB_4_6P7.mjs → auth-Dj_Pcii4.mjs} +1 -1
- package/dist/{cli-BH5LIMPI.mjs → cli-CuzIOUig.mjs} +72 -68
- package/dist/cli.mjs +2 -2
- package/dist/{commands-BaVcBOKA.mjs → commands-B-VYM4sA.mjs} +1 -1
- package/dist/commands-B9puiDBS.mjs +329 -0
- package/dist/{commands-Cf9KT-Ob.mjs → commands-Bfgme-kK.mjs} +2 -2
- package/dist/{commands-BNxsrqSl.mjs → commands-Cx8mEjjt.mjs} +2 -2
- package/dist/{commands-CZSPOqtd.mjs → commands-DSI9ZUTG.mjs} +1 -1
- package/dist/{commands-BsiQdwUs.mjs → commands-DwQIShcm.mjs} +2 -2
- package/dist/{commands-Bb-RzoKB.mjs → commands-eBqn9KgI.mjs} +1 -1
- package/dist/{commands-DFSD7jBH.mjs → commands-kahqMkYE.mjs} +8 -8
- package/dist/{fleet-2c-iNucb.mjs → fleet-CqbZhBG2.mjs} +1 -1
- package/dist/{frpc-CuvdlU0g.mjs → frpc-BeGCyERl.mjs} +45 -27
- package/dist/{headlessCli-CftEZbxm.mjs → headlessCli-CJiHG0gu.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{notifyCommands-1q8COUDd.mjs → notifyCommands-CXjWSZHe.mjs} +1 -1
- package/dist/package-C0aVCX6O.mjs +64 -0
- package/dist/{rpc-DMpJef5v.mjs → rpc-1Ic_yDzC.mjs} +1 -1
- package/dist/{rpc-C9U0Ho27.mjs → rpc-DiG_DaMK.mjs} +1 -1
- package/dist/{run-BNeZiySO.mjs → run-CKLU1B02.mjs} +119 -26
- package/dist/{run-CCENV09o.mjs → run-DZJuuuAu.mjs} +1 -1
- package/dist/{scheduler-CAUwniWA.mjs → scheduler-C5Gk_hYP.mjs} +1 -1
- package/dist/{serveCommands-XJVDS4zB.mjs → serveCommands-Dipn0QHs.mjs} +5 -5
- package/dist/{sideband-DB4Uipz9.mjs → sideband-BCMGW33d.mjs} +1 -1
- package/package.json +46 -46
- package/dist/package-DrTlgNWY.mjs +0 -64
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { a3 as ChannelStore, a4 as gatewayBase, a5 as genKey } from './run-CKLU1B02.mjs';
|
|
6
|
+
import 'os';
|
|
7
|
+
import 'fs/promises';
|
|
8
|
+
import 'fs';
|
|
9
|
+
import 'path';
|
|
10
|
+
import 'url';
|
|
11
|
+
import 'child_process';
|
|
12
|
+
import 'crypto';
|
|
13
|
+
import 'node:crypto';
|
|
14
|
+
import 'util';
|
|
15
|
+
import 'http';
|
|
16
|
+
import 'net';
|
|
17
|
+
import 'node:events';
|
|
18
|
+
import '@agentclientprotocol/sdk';
|
|
19
|
+
import 'node:readline';
|
|
20
|
+
import 'node:fs/promises';
|
|
21
|
+
import 'node:util';
|
|
22
|
+
import 'yaml';
|
|
23
|
+
|
|
24
|
+
const DEFAULT_DOWNLOAD_BASE = "https://github.com/amun-ai/hypha-cloud/releases/latest/download";
|
|
25
|
+
function shq(v) {
|
|
26
|
+
return `'${String(v).replace(/'/g, `'\\''`)}'`;
|
|
27
|
+
}
|
|
28
|
+
function psq(v) {
|
|
29
|
+
return `'${String(v).replace(/'/g, `''`)}'`;
|
|
30
|
+
}
|
|
31
|
+
function buildOutpostInstall(params) {
|
|
32
|
+
const base = params.serverBase.replace(/\/$/, "");
|
|
33
|
+
const dl = (params.downloadBase || DEFAULT_DOWNLOAD_BASE).replace(/\/$/, "");
|
|
34
|
+
const session = params.session || params.channel;
|
|
35
|
+
const q = `channel=${encodeURIComponent(params.channel)}&token=${encodeURIComponent(params.token)}&session=${encodeURIComponent(session)}&dl=${encodeURIComponent(dl)}`;
|
|
36
|
+
const unix = `curl -fsSL ${shq(`${base}/outpost/install.sh?${q}`)} | SVAMP_OUTPOST_SERVER=${shq(base)} SVAMP_OUTPOST_CHANNEL=${shq(params.channel)} SVAMP_OUTPOST_TOKEN=${shq(params.token)} SVAMP_OUTPOST_SESSION=${shq(session)} sh`;
|
|
37
|
+
const windows = `$env:SVAMP_OUTPOST_SERVER=${psq(base)}; $env:SVAMP_OUTPOST_CHANNEL=${psq(params.channel)}; $env:SVAMP_OUTPOST_TOKEN=${psq(params.token)}; $env:SVAMP_OUTPOST_SESSION=${psq(session)}; irm ${psq(`${base}/outpost/install.ps1?${q}`)} | iex`;
|
|
38
|
+
const direct = `svamp-outpost --server ${shq(base)} --channel ${shq(params.channel)} --token ${shq(params.token)} --session ${shq(session)}`;
|
|
39
|
+
return { unix, windows, direct };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function loadConfig() {
|
|
43
|
+
const p = join(os.homedir(), ".svamp", "outpost.json");
|
|
44
|
+
if (!existsSync(p)) return null;
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(readFileSync(p, "utf8"));
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function saveConfig(cfg) {
|
|
52
|
+
writeFileSync(join(os.homedir(), ".svamp", "outpost.json"), JSON.stringify(cfg, null, 2));
|
|
53
|
+
}
|
|
54
|
+
function envVar(name) {
|
|
55
|
+
if (process.env[name]) return process.env[name];
|
|
56
|
+
try {
|
|
57
|
+
for (const line of readFileSync(join(os.homedir(), ".svamp", ".env"), "utf8").split("\n")) {
|
|
58
|
+
const m = line.match(/^\s*([A-Za-z0-9_]+)\s*=\s*(.*)\s*$/);
|
|
59
|
+
if (m && m[1] === name) return m[2].replace(/^["']|["']$/g, "");
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
function machineId() {
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(readFileSync(join(os.homedir(), ".svamp", "daemon.state.json"), "utf8")).machineId;
|
|
68
|
+
} catch {
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function projectDir() {
|
|
73
|
+
try {
|
|
74
|
+
return execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).trim();
|
|
75
|
+
} catch {
|
|
76
|
+
return process.cwd();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function remotesPath() {
|
|
80
|
+
return join(os.homedir(), ".svamp", "outpost-remotes.json");
|
|
81
|
+
}
|
|
82
|
+
function loadRemotes() {
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(readFileSync(remotesPath(), "utf8"));
|
|
85
|
+
} catch {
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function rememberRemote(channel, id, targeted = false) {
|
|
90
|
+
const all = loadRemotes();
|
|
91
|
+
const ch = all[channel] ||= {};
|
|
92
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
93
|
+
ch[id] = { firstSeen: ch[id]?.firstSeen || now, ...targeted ? { lastTargeted: now } : ch[id]?.lastTargeted ? { lastTargeted: ch[id].lastTargeted } : {} };
|
|
94
|
+
try {
|
|
95
|
+
writeFileSync(remotesPath(), JSON.stringify(all, null, 2));
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function forgetRemote(channel, id) {
|
|
100
|
+
const all = loadRemotes();
|
|
101
|
+
if (!all[channel]?.[id]) return false;
|
|
102
|
+
delete all[channel][id];
|
|
103
|
+
try {
|
|
104
|
+
writeFileSync(remotesPath(), JSON.stringify(all, null, 2));
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
async function outpostUp(args) {
|
|
110
|
+
const session = process.env.SVAMP_SESSION_ID;
|
|
111
|
+
if (!session) {
|
|
112
|
+
console.error("SVAMP_SESSION_ID not set \u2014 run this inside a Svamp session.");
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const workspace = envVar("HYPHA_WORKSPACE");
|
|
116
|
+
const serverUrl = envVar("HYPHA_SERVER_URL") || "https://hypha.aicell.io";
|
|
117
|
+
const mid = machineId();
|
|
118
|
+
if (!workspace || !mid) {
|
|
119
|
+
console.error("Could not resolve workspace/machine (HYPHA_WORKSPACE / daemon.state.json).");
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
const channelsServiceId = `${workspace}/${mid}:channels`;
|
|
123
|
+
const gw = gatewayBase(channelsServiceId, serverUrl);
|
|
124
|
+
const channelId = `outpost-${session}`;
|
|
125
|
+
const store = new ChannelStore(projectDir());
|
|
126
|
+
const existing = store.get(channelId);
|
|
127
|
+
let token = existing?.identity?.callers?.find((c) => c.name === "outpost")?.key;
|
|
128
|
+
if (!token) token = genKey();
|
|
129
|
+
const channel = {
|
|
130
|
+
id: channelId,
|
|
131
|
+
name: `Remote outpost \u2014 ${session}`,
|
|
132
|
+
description: "FDE remote machine: a svamp-outpost binary connects here and the agent runs commands on it.",
|
|
133
|
+
enabled: true,
|
|
134
|
+
bind: { session },
|
|
135
|
+
identity: { mode: "per-key", callers: [{ name: "outpost", kind: "agent", key: token }] },
|
|
136
|
+
action: { kind: "message" },
|
|
137
|
+
reply: { mode: "queue" },
|
|
138
|
+
skill: { name: "remote-outpost", description: "Run commands on the connected remote machine." }
|
|
139
|
+
};
|
|
140
|
+
store.save(channel);
|
|
141
|
+
const dl = argVal(args, "--download-base") || envVar("SVAMP_OUTPOST_DL") || "https://static-outpost-bin-08c08c38.svc.hypha.aicell.io";
|
|
142
|
+
saveConfig({ control: "", serverBase: gw, serveBase: dl, token, channel: channelId, session });
|
|
143
|
+
const gen = buildOutpostInstall({ serverBase: gw, channel: channelId, token, session, downloadBase: dl });
|
|
144
|
+
console.log(`\u2713 outpost channel ready for session ${session} (channel "${channelId}", gateway transport)
|
|
145
|
+
`);
|
|
146
|
+
console.log("Give this to the person at the remote machine:\n");
|
|
147
|
+
console.log(" macOS / Linux:\n " + gen.unix + "\n");
|
|
148
|
+
console.log(" Windows (PowerShell):\n " + gen.windows + "\n");
|
|
149
|
+
console.log(" Direct binaries: " + dl);
|
|
150
|
+
console.log('\nThen drive it from this session: svamp outpost exec "<command>" \xB7 svamp outpost status');
|
|
151
|
+
}
|
|
152
|
+
function argVal(args, name) {
|
|
153
|
+
const i = args.indexOf(name);
|
|
154
|
+
return i >= 0 && i + 1 < args.length ? args[i + 1] : void 0;
|
|
155
|
+
}
|
|
156
|
+
async function http(method, url, body) {
|
|
157
|
+
const resp = await fetch(url, {
|
|
158
|
+
method,
|
|
159
|
+
...body !== void 0 ? { headers: { "content-type": "application/json" }, body: JSON.stringify(body) } : {}
|
|
160
|
+
});
|
|
161
|
+
const text = await resp.text();
|
|
162
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${text.slice(0, 200)}`);
|
|
163
|
+
try {
|
|
164
|
+
return JSON.parse(text);
|
|
165
|
+
} catch {
|
|
166
|
+
return text;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function usage() {
|
|
170
|
+
console.log(`svamp outpost \u2014 drive a connected remote machine (FDE outpost)
|
|
171
|
+
|
|
172
|
+
svamp outpost up Create this session's outpost channel + print the install command
|
|
173
|
+
|
|
174
|
+
svamp outpost list List the outposts (machines) connected to this session
|
|
175
|
+
svamp outpost status Show connected remotes + counts
|
|
176
|
+
svamp outpost exec [--machine <id>] "<command>"
|
|
177
|
+
Run a shell command on one outpost (--machine <id>) or, with no
|
|
178
|
+
--machine, broadcast to every connected outpost. Output arrives
|
|
179
|
+
as chat messages. Each outpost's id is shown in its connect banner.
|
|
180
|
+
svamp outpost forget <id> Drop a machine id from the local list
|
|
181
|
+
svamp outpost logs [--json] Show everything the remote(s) have sent back
|
|
182
|
+
svamp outpost install [--os <os>] Print the install one-liner(s) for the current outpost setup
|
|
183
|
+
|
|
184
|
+
With several machines connected, target each by id, e.g.:
|
|
185
|
+
svamp outpost exec --machine build-mac "uname -a"
|
|
186
|
+
svamp outpost exec "hostname" # runs on ALL connected outposts
|
|
187
|
+
|
|
188
|
+
Reads the relay control endpoint from ~/.svamp/outpost.json (or pass --control <url>).`);
|
|
189
|
+
}
|
|
190
|
+
async function outpostCommand(args) {
|
|
191
|
+
const sub = args[0];
|
|
192
|
+
const cfg = loadConfig();
|
|
193
|
+
const control = argVal(args, "--control") || cfg?.control;
|
|
194
|
+
if (!sub || sub === "help" || sub === "--help") {
|
|
195
|
+
usage();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (sub === "up") {
|
|
199
|
+
await outpostUp(args.slice(1));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (sub === "install") {
|
|
203
|
+
if (!cfg) {
|
|
204
|
+
console.error("No outpost configured (~/.svamp/outpost.json missing).");
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
const gen = buildOutpostInstall({ serverBase: cfg.serverBase, channel: cfg.channel, token: cfg.token, session: cfg.session, downloadBase: cfg.serveBase });
|
|
208
|
+
const only = argVal(args, "--os");
|
|
209
|
+
if (only === "windows") console.log(gen.windows);
|
|
210
|
+
else if (only === "unix" || only === "linux" || only === "darwin" || only === "macos") console.log(gen.unix);
|
|
211
|
+
else {
|
|
212
|
+
console.log("macOS / Linux:\n " + gen.unix + "\n");
|
|
213
|
+
console.log("Windows (PowerShell):\n " + gen.windows + "\n");
|
|
214
|
+
if (cfg.serveBase) console.log("Direct download: " + cfg.serveBase);
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const gateway = !!cfg?.serverBase && /\/services\/[^/]*:channels/.test(cfg.serverBase);
|
|
219
|
+
if (sub === "exec" && gateway) {
|
|
220
|
+
const target = argVal(args, "--machine") || argVal(args, "-m");
|
|
221
|
+
const cmd = args.slice(1).filter((a, i, arr) => a !== "--machine" && a !== "-m" && arr[i - 1] !== "--machine" && arr[i - 1] !== "-m").join(" ").trim();
|
|
222
|
+
if (!cmd) {
|
|
223
|
+
console.error('usage: svamp outpost exec [--machine <id>] "<command>"');
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
const { ChannelOutbox } = await import('./run-CKLU1B02.mjs').then(function (n) { return n.as; });
|
|
227
|
+
const outbox = new ChannelOutbox(projectDir());
|
|
228
|
+
const id = "x" + Date.now().toString(36);
|
|
229
|
+
outbox.append(cfg.channel, { to: "outpost", body: JSON.stringify({ type: "exec", id, cmd, target: target || "" }) });
|
|
230
|
+
if (target) rememberRemote(cfg.channel, target, true);
|
|
231
|
+
console.log(target ? `Sent to outpost "${target}" (channel ${cfg.channel}). Its output arrives as a message in this session shortly.` : `Broadcast to all connected outposts (channel ${cfg.channel}). Output from each arrives as a message shortly. Use --machine <id> to target one.`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (sub === "list" && gateway) {
|
|
235
|
+
const ch = new ChannelStore(projectDir()).get(cfg.channel);
|
|
236
|
+
const calls = (ch?.last_calls || []).filter((c) => c.sender === "outpost");
|
|
237
|
+
const known = loadRemotes()[cfg.channel] || {};
|
|
238
|
+
const ids = Object.keys(known);
|
|
239
|
+
if (!ids.length && !calls.length) {
|
|
240
|
+
console.log(`No outposts known yet on ${cfg.channel}. When one connects it announces itself in this session's chat as \u{1F5A5}\uFE0F outpost "<id>" connected \u2014 then target it with: svamp outpost exec --machine <id> "\u2026"`);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
console.log(`Outposts addressed from session ${cfg.session || cfg.channel} (each connect banner in chat confirms which are live):`);
|
|
244
|
+
for (const id of ids) {
|
|
245
|
+
const r = known[id];
|
|
246
|
+
console.log(` \u2022 ${id}${r.lastTargeted ? ` (last targeted ${r.lastTargeted})` : " (seen)"}`);
|
|
247
|
+
}
|
|
248
|
+
if (calls.length) console.log(` channel activity: ${calls.length} recent message(s); last at ${calls[0]?.at}`);
|
|
249
|
+
if (!ids.length) console.log(" (no machine ids recorded yet \u2014 the connect banner in chat shows each outpost's id)");
|
|
250
|
+
console.log(" Drop a stale id with: svamp outpost forget <id>");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if ((sub === "forget" || sub === "remove") && gateway) {
|
|
254
|
+
const id = args[1];
|
|
255
|
+
if (!id) {
|
|
256
|
+
console.error("usage: svamp outpost forget <machine-id>");
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
console.log(forgetRemote(cfg.channel, id) ? `Forgot outpost "${id}".` : `No such outpost "${id}" recorded.`);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (sub === "status" && gateway) {
|
|
263
|
+
const ch = new ChannelStore(projectDir()).get(cfg.channel);
|
|
264
|
+
const calls = (ch?.last_calls || []).filter((c) => c.sender === "outpost");
|
|
265
|
+
const ids = Object.keys(loadRemotes()[cfg.channel] || {});
|
|
266
|
+
const who = ids.length ? ` \u2014 known machines: ${ids.join(", ")}` : "";
|
|
267
|
+
console.log(calls.length ? `outpost active on ${cfg.channel} \u2014 ${calls.length} recent message(s); last at ${calls[0]?.at}${who}` : `no outpost has connected to ${cfg.channel} yet`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (sub === "logs" && gateway) {
|
|
271
|
+
console.log(`Gateway mode: the remote's host info + command output arrive as messages in this session's chat (channel ${cfg.channel}).`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (!control) {
|
|
275
|
+
console.error("No relay control endpoint (~/.svamp/outpost.json missing; run `svamp outpost up`, or pass --control <url>).");
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
if (sub === "status") {
|
|
279
|
+
const s = await http("GET", `${control}/state`);
|
|
280
|
+
console.log(`connected remotes: ${s.connectedOutposts} commands sent: ${s.commands} results: ${s.results}`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (sub === "logs") {
|
|
284
|
+
const r = await http("GET", `${control}/results`);
|
|
285
|
+
if (args.includes("--json")) {
|
|
286
|
+
console.log(JSON.stringify(r, null, 2));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
for (const m of r) {
|
|
290
|
+
if (m.type === "outpost-hello") console.log(`\u2022 connected: ${m.host?.user}@${m.host?.hostname} (${m.host?.os}/${m.host?.arch}) cwd=${m.host?.cwd}`);
|
|
291
|
+
else if (m.type === "exec-result") console.log(`\u2022 [${m.id}] exit=${m.exitCode}
|
|
292
|
+
${m.output}`);
|
|
293
|
+
}
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (sub === "exec") {
|
|
297
|
+
const cmd = args.slice(1).filter((a) => a !== "--control" && a !== control).join(" ").trim();
|
|
298
|
+
if (!cmd) {
|
|
299
|
+
console.error('usage: svamp outpost exec "<command>"');
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
const { enqueued } = await http("POST", `${control}/enqueue?cmd=${encodeURIComponent(cmd)}`, {});
|
|
303
|
+
if (!enqueued?.id) {
|
|
304
|
+
console.error("failed to enqueue");
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
const deadline = Date.now() + 6e5;
|
|
308
|
+
for (; ; ) {
|
|
309
|
+
const results = await http("GET", `${control}/results`);
|
|
310
|
+
const hit = results.find((m) => m.type === "exec-result" && m.id === enqueued.id);
|
|
311
|
+
if (hit) {
|
|
312
|
+
process.stdout.write(hit.output || "");
|
|
313
|
+
if (typeof hit.exitCode === "number" && hit.exitCode !== 0) console.error(`
|
|
314
|
+
[exit ${hit.exitCode}]`);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (Date.now() > deadline) {
|
|
318
|
+
console.error("timed out waiting for the remote to respond (is it connected?)");
|
|
319
|
+
process.exit(1);
|
|
320
|
+
}
|
|
321
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
console.error(`unknown outpost command: ${sub}`);
|
|
325
|
+
usage();
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export { outpostCommand };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { f as flushAndExit } from './cli-
|
|
5
|
-
import { j as resolveProjectRoot, q as searchIssues, o as listIssues, l as resumeIssue, p as pauseIssue, m as addComment, u as updateIssue, k as getIssue, t as isVisibleTo, H as summarize, n as addIssue } from './run-
|
|
4
|
+
import { f as flushAndExit } from './cli-CuzIOUig.mjs';
|
|
5
|
+
import { j as resolveProjectRoot, q as searchIssues, o as listIssues, l as resumeIssue, p as pauseIssue, m as addComment, u as updateIssue, k as getIssue, t as isVisibleTo, H as summarize, n as addIssue } from './run-CKLU1B02.mjs';
|
|
6
6
|
import './serviceManager-hlOVxkhW.mjs';
|
|
7
7
|
import 'os';
|
|
8
8
|
import 'fs/promises';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-
|
|
3
|
+
import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-B-VYM4sA.mjs';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
|
-
import { u as updateIssue, m as addComment, n as addIssue, i as shortId } from './run-
|
|
5
|
+
import { u as updateIssue, m as addComment, n as addIssue, i as shortId } from './run-CKLU1B02.mjs';
|
|
6
6
|
import 'node:os';
|
|
7
7
|
import 'os';
|
|
8
8
|
import 'fs/promises';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
2
|
import fs__default from 'fs';
|
|
3
3
|
import { resolve, join, relative } from 'path';
|
|
4
|
-
import { Q as parseFrontmatter, T as getSkillsServer, U as getSkillsWorkspaceName, V as getSkillsCollectionName, W as fetchWithTimeout, X as searchSkills, Y as SKILLS_DIR, Z as getSkillInfo, _ as downloadSkillFile, $ as listSkillFiles } from './run-
|
|
4
|
+
import { Q as parseFrontmatter, T as getSkillsServer, U as getSkillsWorkspaceName, V as getSkillsCollectionName, W as fetchWithTimeout, X as searchSkills, Y as SKILLS_DIR, Z as getSkillInfo, _ as downloadSkillFile, $ as listSkillFiles } from './run-CKLU1B02.mjs';
|
|
5
5
|
import 'fs/promises';
|
|
6
6
|
import 'url';
|
|
7
7
|
import 'child_process';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { writeFileSync, readFileSync } from 'fs';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
|
-
import { connectAndGetMachine } from './commands-
|
|
3
|
+
import { connectAndGetMachine } from './commands-B-VYM4sA.mjs';
|
|
4
4
|
import 'node:fs';
|
|
5
5
|
import 'node:child_process';
|
|
6
6
|
import 'node:path';
|
|
7
7
|
import 'node:os';
|
|
8
|
-
import './run-
|
|
8
|
+
import './run-CKLU1B02.mjs';
|
|
9
9
|
import 'os';
|
|
10
10
|
import 'fs/promises';
|
|
11
11
|
import 'url';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { j as resolveProjectRoot, I as workflowSteps, z as setWorkflowEnabled, E as isWorkflowEnabled, A as removeWorkflow, x as getWorkflow, D as listWorkflows, B as saveWorkflow, C as rawWorkflow } from './run-
|
|
2
|
+
import { j as resolveProjectRoot, I as workflowSteps, z as setWorkflowEnabled, E as isWorkflowEnabled, A as removeWorkflow, x as getWorkflow, D as listWorkflows, B as saveWorkflow, C as rawWorkflow } from './run-CKLU1B02.mjs';
|
|
3
3
|
import 'os';
|
|
4
4
|
import 'fs/promises';
|
|
5
5
|
import 'fs';
|
|
@@ -60,7 +60,7 @@ async function serviceExpose(args) {
|
|
|
60
60
|
process.exit(1);
|
|
61
61
|
}
|
|
62
62
|
if (foreground) {
|
|
63
|
-
const { runFrpcTunnel } = await import('./frpc-
|
|
63
|
+
const { runFrpcTunnel } = await import('./frpc-BeGCyERl.mjs');
|
|
64
64
|
await runFrpcTunnel(name, ports, void 0, {
|
|
65
65
|
group,
|
|
66
66
|
groupKey,
|
|
@@ -70,7 +70,7 @@ async function serviceExpose(args) {
|
|
|
70
70
|
});
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
73
|
+
const { connectAndGetMachine } = await import('./commands-B-VYM4sA.mjs');
|
|
74
74
|
const { server, machine } = await connectAndGetMachine();
|
|
75
75
|
try {
|
|
76
76
|
const status = await machine.tunnelStart({
|
|
@@ -92,7 +92,7 @@ async function serviceExpose(args) {
|
|
|
92
92
|
console.log(` port ${port}: ${url}`);
|
|
93
93
|
}
|
|
94
94
|
if (process.env.SVAMP_SESSION_ID) {
|
|
95
|
-
const { autoAddSessionLink } = await import('./agentCommands-
|
|
95
|
+
const { autoAddSessionLink } = await import('./agentCommands-CoPsnzwK.mjs');
|
|
96
96
|
let added = 0;
|
|
97
97
|
for (const [port, url] of urlEntries) {
|
|
98
98
|
const label = urlEntries.length > 1 ? `${name}:${port}` : name;
|
|
@@ -101,7 +101,7 @@ async function serviceExpose(args) {
|
|
|
101
101
|
if (added) console.log(` \u21B3 added to this session's Launch Pad`);
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
console.log(`To stop: svamp service delete ${name}
|
|
104
|
+
console.log(`To stop: svamp service delete ${name}`);
|
|
105
105
|
} catch (err) {
|
|
106
106
|
if (/already running/i.test(err?.message || "")) {
|
|
107
107
|
console.error(`Tunnel '${name}' is already running. Use \`svamp service delete ${name}\` first.`);
|
|
@@ -129,7 +129,7 @@ async function serviceServe(args) {
|
|
|
129
129
|
console.log(`Serving ${resolvedDir}`);
|
|
130
130
|
const servePort = 18080;
|
|
131
131
|
const http = await import('http');
|
|
132
|
-
const { serveStaticMount } = await import('./run-
|
|
132
|
+
const { serveStaticMount } = await import('./run-CKLU1B02.mjs').then(function (n) { return n.ao; });
|
|
133
133
|
const server = http.createServer((req, res) => {
|
|
134
134
|
const u = new URL(req.url || "/", "http://127.0.0.1");
|
|
135
135
|
let rel = u.pathname;
|
|
@@ -140,7 +140,7 @@ async function serviceServe(args) {
|
|
|
140
140
|
server.once("error", reject);
|
|
141
141
|
server.listen(servePort, "127.0.0.1", () => resolve());
|
|
142
142
|
});
|
|
143
|
-
const { runFrpcTunnel } = await import('./frpc-
|
|
143
|
+
const { runFrpcTunnel } = await import('./frpc-BeGCyERl.mjs');
|
|
144
144
|
void server;
|
|
145
145
|
await runFrpcTunnel(name, [servePort]);
|
|
146
146
|
} catch (err) {
|
|
@@ -150,7 +150,7 @@ async function serviceServe(args) {
|
|
|
150
150
|
}
|
|
151
151
|
async function serviceList(_args) {
|
|
152
152
|
try {
|
|
153
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
153
|
+
const { connectAndGetMachine } = await import('./commands-B-VYM4sA.mjs');
|
|
154
154
|
const { server, machine } = await connectAndGetMachine();
|
|
155
155
|
try {
|
|
156
156
|
const tunnels = await machine.tunnelList({});
|
|
@@ -190,7 +190,7 @@ async function serviceDelete(args) {
|
|
|
190
190
|
process.exit(1);
|
|
191
191
|
}
|
|
192
192
|
try {
|
|
193
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
193
|
+
const { connectAndGetMachine } = await import('./commands-B-VYM4sA.mjs');
|
|
194
194
|
const { server, machine } = await connectAndGetMachine();
|
|
195
195
|
try {
|
|
196
196
|
await machine.tunnelStop({ name });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { c as connectToHypha } from './run-
|
|
4
|
+
import { c as connectToHypha } from './run-CKLU1B02.mjs';
|
|
5
5
|
import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-BaMR97BE.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
@@ -4,7 +4,7 @@ import { mkdirSync, chmodSync, writeFileSync, unlinkSync, existsSync, readFileSy
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { homedir, platform, arch } from 'os';
|
|
6
6
|
import { randomUUID, createHash } from 'crypto';
|
|
7
|
-
import { e as getFrpsSubdomainHost, f as getFrpsServerPort, h as getFrpsServerAddr } from './run-
|
|
7
|
+
import { e as getFrpsSubdomainHost, f as getFrpsServerPort, h as getFrpsServerAddr } from './run-CKLU1B02.mjs';
|
|
8
8
|
import 'fs/promises';
|
|
9
9
|
import 'url';
|
|
10
10
|
import 'node:crypto';
|
|
@@ -278,6 +278,8 @@ class FrpcTunnel {
|
|
|
278
278
|
_lastErrorAt = 0;
|
|
279
279
|
_firstErrorAt = 0;
|
|
280
280
|
_restartAttempts = 0;
|
|
281
|
+
_restartTimer = null;
|
|
282
|
+
// #0583: pending reconnect timer (cancelled on destroy)
|
|
281
283
|
// End-to-end probe state (set when options.probeUrl is provided).
|
|
282
284
|
_probeTimer = null;
|
|
283
285
|
_lastProbeOkAt = 0;
|
|
@@ -309,7 +311,8 @@ class FrpcTunnel {
|
|
|
309
311
|
const multiPort = options.ports.length > 1;
|
|
310
312
|
this.proxies = options.ports.map((port) => {
|
|
311
313
|
const standaloneHash = createHash("sha256").update(multiPort ? `${options.name}|${port}|${machineFingerprint}` : `${options.name}|${machineFingerprint}`).digest("hex").slice(0, 8);
|
|
312
|
-
const
|
|
314
|
+
const groupSubdomainKey = multiPort ? `${options.group}|${port}` : `${options.group}`;
|
|
315
|
+
const subdomain = options.subdomains?.get(port) || (options.group ? `${options.group}-${createHash("sha256").update(groupSubdomainKey).digest("hex").slice(0, 8)}` : `${options.name}-${standaloneHash}`);
|
|
313
316
|
const proxyName = `${options.name}-${port}-${machineHash}`;
|
|
314
317
|
return {
|
|
315
318
|
name: proxyName,
|
|
@@ -332,6 +335,29 @@ class FrpcTunnel {
|
|
|
332
335
|
}
|
|
333
336
|
this.configPath = join(configDir, `${options.name}.toml`);
|
|
334
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* #0583: schedule a backoff reconnect after the child went down (spawn error OR exit).
|
|
340
|
+
* Extracted from the exit handler so BOTH the spawn-'error' and 'exit' paths share it, and
|
|
341
|
+
* so it is unit-testable without a real spawn. No-op once destroyed.
|
|
342
|
+
*/
|
|
343
|
+
_reconnectAfterDown(reason) {
|
|
344
|
+
if (this._destroyed) return;
|
|
345
|
+
const STABILITY_WINDOW_MS = 3e4;
|
|
346
|
+
if (this._lastConnectedAt > 0 && Date.now() - this._lastConnectedAt >= STABILITY_WINDOW_MS) {
|
|
347
|
+
this._restartAttempts = 0;
|
|
348
|
+
}
|
|
349
|
+
this._restartAttempts++;
|
|
350
|
+
const delay = Math.min(3e3 * Math.pow(2, this._restartAttempts - 1), 6e4);
|
|
351
|
+
this.log(`frpc down (${reason}), restarting in ${Math.round(delay / 1e3)}s (attempt ${this._restartAttempts})...`);
|
|
352
|
+
this._restartTimer = setTimeout(() => {
|
|
353
|
+
this._restartTimer = null;
|
|
354
|
+
if (!this._destroyed) {
|
|
355
|
+
this.connect().catch((err) => {
|
|
356
|
+
this.options.onError?.(err);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}, delay);
|
|
360
|
+
}
|
|
335
361
|
/** Connect to frps by starting the frpc process. */
|
|
336
362
|
async connect() {
|
|
337
363
|
if (this._destroyed) return;
|
|
@@ -404,37 +430,25 @@ class FrpcTunnel {
|
|
|
404
430
|
};
|
|
405
431
|
child.stdout?.on("data", onOutput);
|
|
406
432
|
child.stderr?.on("data", onOutput);
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
reject(err);
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
child.on("exit", (code) => {
|
|
433
|
+
let downHandled = false;
|
|
434
|
+
const onChildDown = (rejectErr) => {
|
|
435
|
+
if (downHandled) return;
|
|
436
|
+
downHandled = true;
|
|
415
437
|
this._connected = false;
|
|
416
438
|
this.process = null;
|
|
417
439
|
this.options.onDisconnect?.();
|
|
418
440
|
if (!resolved) {
|
|
419
441
|
resolved = true;
|
|
420
|
-
reject(
|
|
421
|
-
}
|
|
422
|
-
if (!this._destroyed) {
|
|
423
|
-
const STABILITY_WINDOW_MS = 3e4;
|
|
424
|
-
if (this._lastConnectedAt > 0 && Date.now() - this._lastConnectedAt >= STABILITY_WINDOW_MS) {
|
|
425
|
-
this._restartAttempts = 0;
|
|
426
|
-
}
|
|
427
|
-
this._restartAttempts++;
|
|
428
|
-
const delay = Math.min(3e3 * Math.pow(2, this._restartAttempts - 1), 6e4);
|
|
429
|
-
this.log(`frpc exited with code ${code}, restarting in ${Math.round(delay / 1e3)}s (attempt ${this._restartAttempts})...`);
|
|
430
|
-
setTimeout(() => {
|
|
431
|
-
if (!this._destroyed) {
|
|
432
|
-
this.connect().catch((err) => {
|
|
433
|
-
this.options.onError?.(err);
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
}, delay);
|
|
442
|
+
reject(rejectErr);
|
|
437
443
|
}
|
|
444
|
+
this._reconnectAfterDown(rejectErr.message);
|
|
445
|
+
};
|
|
446
|
+
child.on("error", (err) => {
|
|
447
|
+
this.options.onError?.(err);
|
|
448
|
+
onChildDown(err instanceof Error ? err : new Error(String(err)));
|
|
449
|
+
});
|
|
450
|
+
child.on("exit", (code) => {
|
|
451
|
+
onChildDown(new Error(`frpc exited with code ${code}`));
|
|
438
452
|
});
|
|
439
453
|
if (this.options.probeUrl || this.options.probePath) {
|
|
440
454
|
this.startProbeLoop({
|
|
@@ -625,6 +639,10 @@ class FrpcTunnel {
|
|
|
625
639
|
this._connected = false;
|
|
626
640
|
this.stopProbeLoop();
|
|
627
641
|
this.stopAdminStatusLoop();
|
|
642
|
+
if (this._restartTimer) {
|
|
643
|
+
clearTimeout(this._restartTimer);
|
|
644
|
+
this._restartTimer = null;
|
|
645
|
+
}
|
|
628
646
|
if (this.process) {
|
|
629
647
|
this.process.kill("SIGTERM");
|
|
630
648
|
const p = this.process;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a0 as resolveModel,
|
|
2
|
-
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-
|
|
1
|
+
import { a0 as resolveModel, ai as describeMisconfiguration, aj as buildMachineDeps } from './run-CKLU1B02.mjs';
|
|
2
|
+
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-BCMGW33d.mjs';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
4
|
import { execSync, spawn } from 'child_process';
|
|
5
5
|
import 'os';
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-
|
|
1
|
+
export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-CKLU1B02.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as connectToHypha, S as SharingNotificationSync, J as parseJwtEmail } from './run-
|
|
1
|
+
import { c as connectToHypha, S as SharingNotificationSync, J as parseJwtEmail } from './run-CKLU1B02.mjs';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { existsSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|