svamp-cli 0.2.128 → 0.2.130
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/bin/skills/crew/SKILL.md +11 -10
- package/bin/skills/loop/bin/checklist.mjs +3 -2
- package/bin/skills/loop/bin/loop-init.mjs +2 -2
- package/dist/{agentCommands-jGCEdEYn.mjs → agentCommands-DOUG625_.mjs} +4 -4
- package/dist/{auth-DVa-sVa9.mjs → auth-UNUDBJKU.mjs} +1 -1
- package/dist/cli.mjs +85 -95
- package/dist/{commands-CHUE-0Mz.mjs → commands-B8vUCW50.mjs} +1 -1
- package/dist/{commands-deNTJ9jb.mjs → commands-BFGR6-V-.mjs} +9 -9
- package/dist/{commands-BLYvHcrD.mjs → commands-BeOI6l78.mjs} +19 -8
- package/dist/{commands-Dif088xw.mjs → commands-D830hGXM.mjs} +2 -2
- package/dist/{commands-CO-lf8m_.mjs → commands-MdYMcyaZ.mjs} +2 -2
- package/dist/{commands-BVx72l2K.mjs → commands-QGaI-ukW.mjs} +12 -46
- package/dist/{fleet-D3L05h5k.mjs → fleet-nj6bMyhh.mjs} +1 -1
- package/dist/{frpc-CWyoLax7.mjs → frpc-DrfDPPux.mjs} +147 -8
- package/dist/{headlessCli-CB9HN7zY.mjs → headlessCli-D8x-uGEN.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{package-C5owhm4c.mjs → package-BK6btwnG.mjs} +2 -2
- package/dist/{run-CkPzZuKK.mjs → run---5cgexR.mjs} +1 -1
- package/dist/{run-C23-A9KM.mjs → run-DMahGhJP.mjs} +166 -102
- package/dist/{serveCommands-BBIKhjxn.mjs → serveCommands-C8iIs7jb.mjs} +5 -5
- package/dist/{serveManager-CxbgXYEo.mjs → serveManager-Csqa6icR.mjs} +2 -2
- package/dist/{sideband-D5F6XGss.mjs → sideband-Bk7iN3dp.mjs} +1 -1
- package/package.json +2 -2
|
@@ -2677,7 +2677,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2677
2677
|
const tunnels = handlers.tunnels;
|
|
2678
2678
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2679
2679
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2680
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2680
|
+
const { FrpcTunnel } = await import('./frpc-DrfDPPux.mjs');
|
|
2681
2681
|
const tunnel = new FrpcTunnel({
|
|
2682
2682
|
name: params.name,
|
|
2683
2683
|
ports: params.ports,
|
|
@@ -2686,6 +2686,10 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2686
2686
|
healthCheckType: params.healthCheckType,
|
|
2687
2687
|
healthCheckPath: params.healthCheckPath,
|
|
2688
2688
|
healthCheckInterval: params.healthCheckInterval,
|
|
2689
|
+
// Poll frpc's loopback admin API for backend-agnostic
|
|
2690
|
+
// ghost/stuck-proxy detection (the daemon health loop recreates
|
|
2691
|
+
// on persistent failure).
|
|
2692
|
+
adminStatus: true,
|
|
2689
2693
|
onError: (err) => console.error(`[FRPC] ${params.name}: ${err.message}`),
|
|
2690
2694
|
onConnect: () => console.log(`[FRPC] ${params.name}: connected`),
|
|
2691
2695
|
onDisconnect: () => console.log(`[FRPC] ${params.name}: disconnected, will auto-reconnect`)
|
|
@@ -2718,14 +2722,47 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2718
2722
|
handlers.forgetExposedTunnel?.(params.name);
|
|
2719
2723
|
return { name: params.name, stopped: true };
|
|
2720
2724
|
},
|
|
2721
|
-
/**
|
|
2725
|
+
/**
|
|
2726
|
+
* List the full CONFIGURED set of daemon-managed tunnels with a
|
|
2727
|
+
* derived per-tunnel `state`, not just the ones currently live. The
|
|
2728
|
+
* configured set comes from the persisted specs (survives restarts +
|
|
2729
|
+
* mid-recreate gaps); each is joined with its live FrpcTunnel status
|
|
2730
|
+
* if present. A tunnel that's persisted but not live (failed restore,
|
|
2731
|
+
* recreating) shows up as `reconnecting` instead of silently vanishing.
|
|
2732
|
+
*/
|
|
2722
2733
|
tunnelList: async (context) => {
|
|
2723
2734
|
authorizeRequest(context, currentMetadata.sharing, "view");
|
|
2724
2735
|
const tunnels = handlers.tunnels;
|
|
2725
2736
|
if (!tunnels) return [];
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2737
|
+
const specs = handlers.listExposedTunnels?.() ?? [];
|
|
2738
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2739
|
+
const rows = [];
|
|
2740
|
+
const stateOf = (s) => {
|
|
2741
|
+
if (!s) return "reconnecting";
|
|
2742
|
+
if (s.probe && !s.probe.ok) return "failed";
|
|
2743
|
+
if (s.connected) return "connected";
|
|
2744
|
+
return s.restartAttempts > 0 ? "reconnecting" : "failed";
|
|
2745
|
+
};
|
|
2746
|
+
for (const spec of specs) {
|
|
2747
|
+
seen.add(spec.name);
|
|
2748
|
+
const tunnel = tunnels.get(spec.name);
|
|
2749
|
+
const status = tunnel ? tunnel.status : null;
|
|
2750
|
+
rows.push({
|
|
2751
|
+
name: spec.name,
|
|
2752
|
+
ports: spec.ports,
|
|
2753
|
+
group: spec.group,
|
|
2754
|
+
configured: true,
|
|
2755
|
+
live: !!tunnel,
|
|
2756
|
+
state: stateOf(status),
|
|
2757
|
+
...status ?? {}
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
for (const [name, tunnel] of tunnels) {
|
|
2761
|
+
if (seen.has(name)) continue;
|
|
2762
|
+
const status = tunnel.status;
|
|
2763
|
+
rows.push({ ...status, name, configured: false, live: true, state: stateOf(status) });
|
|
2764
|
+
}
|
|
2765
|
+
return rows;
|
|
2729
2766
|
},
|
|
2730
2767
|
// ── Shared static file server ────────────────────────────────────
|
|
2731
2768
|
/** Add a mount to the shared static file server. */
|
|
@@ -2938,7 +2975,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
2938
2975
|
}
|
|
2939
2976
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
2940
2977
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
2941
|
-
const { toolsForRole } = await import('./sideband-
|
|
2978
|
+
const { toolsForRole } = await import('./sideband-Bk7iN3dp.mjs');
|
|
2942
2979
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
2943
2980
|
return fmt(r2);
|
|
2944
2981
|
}
|
|
@@ -3037,7 +3074,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3037
3074
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3038
3075
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3039
3076
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3040
|
-
const { queryCore } = await import('./commands-
|
|
3077
|
+
const { queryCore } = await import('./commands-QGaI-ukW.mjs');
|
|
3041
3078
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3042
3079
|
let result;
|
|
3043
3080
|
try {
|
|
@@ -3519,9 +3556,20 @@ function patchStoredText(msg, newText) {
|
|
|
3519
3556
|
return true;
|
|
3520
3557
|
}
|
|
3521
3558
|
if (data?.type === "assistant" && Array.isArray(data?.message?.content)) {
|
|
3522
|
-
const
|
|
3523
|
-
|
|
3559
|
+
const content = data.message.content;
|
|
3560
|
+
const isText = (b) => b && b.type === "text" && typeof b.text === "string";
|
|
3561
|
+
const textBlocks = content.filter(isText);
|
|
3562
|
+
if (textBlocks.length >= 1) {
|
|
3524
3563
|
textBlocks[0].text = newText;
|
|
3564
|
+
let seen = false;
|
|
3565
|
+
data.message.content = content.filter((b) => {
|
|
3566
|
+
if (!isText(b)) return true;
|
|
3567
|
+
if (!seen) {
|
|
3568
|
+
seen = true;
|
|
3569
|
+
return true;
|
|
3570
|
+
}
|
|
3571
|
+
return false;
|
|
3572
|
+
});
|
|
3525
3573
|
return true;
|
|
3526
3574
|
}
|
|
3527
3575
|
}
|
|
@@ -7734,11 +7782,21 @@ function applyTranscriptEdit(file, index, newText) {
|
|
|
7734
7782
|
if (target.type === "assistant") {
|
|
7735
7783
|
const content = target?.message?.content;
|
|
7736
7784
|
if (!Array.isArray(content)) return { ok: false, reason: "assistant content is not a block array" };
|
|
7737
|
-
const
|
|
7738
|
-
|
|
7739
|
-
|
|
7785
|
+
const isText = (b) => b && b.type === "text" && typeof b.text === "string";
|
|
7786
|
+
const textBlocks = content.filter(isText);
|
|
7787
|
+
if (textBlocks.length === 0) {
|
|
7788
|
+
return { ok: false, reason: "expected at least 1 text block, found 0" };
|
|
7740
7789
|
}
|
|
7741
7790
|
textBlocks[0].text = newText;
|
|
7791
|
+
let seen = false;
|
|
7792
|
+
target.message.content = content.filter((b) => {
|
|
7793
|
+
if (!isText(b)) return true;
|
|
7794
|
+
if (!seen) {
|
|
7795
|
+
seen = true;
|
|
7796
|
+
return true;
|
|
7797
|
+
}
|
|
7798
|
+
return false;
|
|
7799
|
+
});
|
|
7742
7800
|
} else if (target.type === "user") {
|
|
7743
7801
|
if (typeof target?.message?.content !== "string") {
|
|
7744
7802
|
return { ok: false, reason: "user content is not a string" };
|
|
@@ -10804,15 +10862,32 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
10804
10862
|
loopChecker();
|
|
10805
10863
|
};
|
|
10806
10864
|
const writeConfig = (patch) => {
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
const
|
|
10812
|
-
const
|
|
10865
|
+
const _gateKey = "loop" in patch ? "loop" : "supervisor" in patch ? "supervisor" : null;
|
|
10866
|
+
if (_gateKey) {
|
|
10867
|
+
let cfg = patch[_gateKey];
|
|
10868
|
+
if (_gateKey === "supervisor" && cfg && typeof cfg === "object") {
|
|
10869
|
+
const j = Array.isArray(cfg.judges) ? cfg.judges : [];
|
|
10870
|
+
const oj = j.find((x) => x?.type === "oracle");
|
|
10871
|
+
const pj = j.find((x) => x?.type === "parent");
|
|
10872
|
+
cfg = {
|
|
10873
|
+
// legacy supervisor = hot-plug (no task); criteria → until.
|
|
10874
|
+
until: cfg.criteria,
|
|
10875
|
+
oracle: (oj && typeof oj.cmd === "string" ? oj.cmd : void 0) || cfg.oracle,
|
|
10876
|
+
evaluator: j.length === 0 || j.some((x) => x?.type === "agent"),
|
|
10877
|
+
parent: pj?.parent || cfg.parent,
|
|
10878
|
+
max_iterations: cfg.max_rounds
|
|
10879
|
+
};
|
|
10880
|
+
}
|
|
10881
|
+
const task = cfg && typeof cfg.task === "string" && cfg.task.trim() ? cfg.task.trim() : void 0;
|
|
10882
|
+
const until = cfg && typeof cfg.until === "string" && cfg.until.trim() ? cfg.until.trim() : cfg && typeof cfg.criteria === "string" && cfg.criteria.trim() ? cfg.criteria.trim() : void 0;
|
|
10883
|
+
if (cfg && typeof cfg === "object" && (task || until)) {
|
|
10884
|
+
const oracle = typeof cfg.oracle === "string" && cfg.oracle.trim() ? cfg.oracle.trim() : void 0;
|
|
10885
|
+
const evaluator = cfg.evaluator !== false;
|
|
10886
|
+
const maxIterations = typeof cfg.max_iterations === "number" ? cfg.max_iterations : typeof cfg.max_rounds === "number" ? cfg.max_rounds : 20;
|
|
10813
10887
|
const ok = initLoop(directory, {
|
|
10814
|
-
task:
|
|
10815
|
-
|
|
10888
|
+
task: task || until,
|
|
10889
|
+
// LOOP.md goal = the task, or the until-criteria when hot-plugging
|
|
10890
|
+
criteria: until,
|
|
10816
10891
|
oracle,
|
|
10817
10892
|
maxIterations,
|
|
10818
10893
|
evaluator,
|
|
@@ -10820,24 +10895,23 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
10820
10895
|
});
|
|
10821
10896
|
if (ok) {
|
|
10822
10897
|
const existingQueue = getMetadata().messageQueue || [];
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
...m,
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
displayText: `\u{1F501} Loop
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
10898
|
+
if (task) {
|
|
10899
|
+
const kickoff = "Begin the loop. Read LOOP.md and work on the task until the exit conditions are met. Do not stop early \u2014 an independent Stop gate will re-check before the loop can end.";
|
|
10900
|
+
setMetadata((m) => ({ ...m, messageQueue: [...existingQueue, { id: randomUUID$1(), text: kickoff, displayText: `\u{1F501} Loop started: ${task.slice(0, 100)}${task.length > 100 ? "\u2026" : ""}`, createdAt: Date.now() }] }));
|
|
10901
|
+
} else {
|
|
10902
|
+
const idle = getMetadata().lifecycleState === "idle";
|
|
10903
|
+
if (idle) {
|
|
10904
|
+
const nudge = `A loop gate is now active. Keep working until this is met: ${until}
|
|
10905
|
+
Or verify and finish \u2014 an independent Stop gate re-checks before you can stop.`;
|
|
10906
|
+
setMetadata((m) => ({ ...m, messageQueue: [...existingQueue, { id: randomUUID$1(), text: nudge, displayText: `\u{1F501} Loop until: ${until.slice(0, 100)}${until.length > 100 ? "\u2026" : ""}`, createdAt: Date.now() }] }));
|
|
10907
|
+
}
|
|
10908
|
+
}
|
|
10909
|
+
onLoopActivated?.();
|
|
10835
10910
|
sessionService.pushMessage(
|
|
10836
|
-
{ type: "message", message: `\u{1F501} Loop
|
|
10911
|
+
{ type: "message", message: `\u{1F501} Loop active \u2014 ${task ? "iterating on the task" : "gating current work"} (until: ${until || "done"}, oracle: ${oracle || "none"}, evaluator ${evaluator ? "on" : "off"}, max ${maxIterations}).` },
|
|
10837
10912
|
"event"
|
|
10838
10913
|
);
|
|
10839
|
-
logger.log(`[svampConfig] Loop
|
|
10840
|
-
onLoopActivated?.();
|
|
10914
|
+
logger.log(`[svampConfig] Loop active (${task ? "kickoff" : "hot-plug"}): until="${(until || task || "").slice(0, 50)}"`);
|
|
10841
10915
|
} else {
|
|
10842
10916
|
sessionService.pushMessage(
|
|
10843
10917
|
{ type: "message", message: "Failed to start loop \u2014 the loop skill could not be located. Reinstall with: svamp skills install loop --force", level: "error" },
|
|
@@ -10850,54 +10924,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
10850
10924
|
sessionService.pushMessage({ type: "message", message: "Loop cancelled." }, "event");
|
|
10851
10925
|
logger.log(`[svampConfig] Loop cancelled`);
|
|
10852
10926
|
}
|
|
10853
|
-
const {
|
|
10854
|
-
patch = restPatch;
|
|
10855
|
-
}
|
|
10856
|
-
if ("supervisor" in patch) {
|
|
10857
|
-
const sup = patch.supervisor;
|
|
10858
|
-
if (sup && typeof sup === "object" && typeof sup.criteria === "string" && sup.criteria.trim()) {
|
|
10859
|
-
const criteria = sup.criteria.trim();
|
|
10860
|
-
const judges = Array.isArray(sup.judges) ? sup.judges : [];
|
|
10861
|
-
const oracleJudge = judges.find((j) => j?.type === "oracle");
|
|
10862
|
-
const oracle = oracleJudge && typeof oracleJudge.cmd === "string" && oracleJudge.cmd.trim() ? oracleJudge.cmd.trim() : typeof sup.oracle === "string" && sup.oracle.trim() ? sup.oracle.trim() : void 0;
|
|
10863
|
-
const hasAgentJudge = judges.length === 0 || judges.some((j) => j?.type === "agent");
|
|
10864
|
-
const maxRounds = typeof sup.max_rounds === "number" ? sup.max_rounds : 20;
|
|
10865
|
-
const ok = initLoop(directory, {
|
|
10866
|
-
task: criteria,
|
|
10867
|
-
// P1: criteria seeds LOOP.md as the goal (skill carries it as config.criteria in #30)
|
|
10868
|
-
criteria,
|
|
10869
|
-
oracle,
|
|
10870
|
-
maxIterations: maxRounds,
|
|
10871
|
-
evaluator: hasAgentJudge,
|
|
10872
|
-
sessionId
|
|
10873
|
-
});
|
|
10874
|
-
if (ok) {
|
|
10875
|
-
const judgeLabel = judges.length ? judges.map((j) => j.type).join("\u2192") : "agent";
|
|
10876
|
-
const idle = getMetadata().lifecycleState === "idle";
|
|
10877
|
-
if (idle) {
|
|
10878
|
-
const existingQueue = getMetadata().messageQueue || [];
|
|
10879
|
-
const nudge = `You are now under supervision. Success criteria: ${criteria}
|
|
10880
|
-
Continue working until they are met, or verify and finish \u2014 an independent Stop gate will re-check before you can stop.`;
|
|
10881
|
-
setMetadata((m) => ({ ...m, messageQueue: [...existingQueue, { id: randomUUID$1(), text: nudge, displayText: `\u{1F441} Supervisor attached`, createdAt: Date.now() }] }));
|
|
10882
|
-
onLoopActivated?.();
|
|
10883
|
-
}
|
|
10884
|
-
sessionService.pushMessage(
|
|
10885
|
-
{ type: "message", message: `\u{1F441} Supervisor attached \u2014 judges: ${judgeLabel}, max ${maxRounds}. Criteria: ${criteria.slice(0, 120)}${criteria.length > 120 ? "\u2026" : ""}` },
|
|
10886
|
-
"event"
|
|
10887
|
-
);
|
|
10888
|
-
logger.log(`[svampConfig] Supervisor attached (judges: ${judgeLabel}, max ${maxRounds})`);
|
|
10889
|
-
} else {
|
|
10890
|
-
sessionService.pushMessage(
|
|
10891
|
-
{ type: "message", message: "Failed to attach supervisor \u2014 the loop skill could not be located. Reinstall with: svamp skills install loop --force", level: "error" },
|
|
10892
|
-
"event"
|
|
10893
|
-
);
|
|
10894
|
-
}
|
|
10895
|
-
} else {
|
|
10896
|
-
deactivateLoop(directory, sessionId);
|
|
10897
|
-
sessionService.pushMessage({ type: "message", message: "Supervisor detached." }, "event");
|
|
10898
|
-
logger.log(`[svampConfig] Supervisor detached`);
|
|
10899
|
-
}
|
|
10900
|
-
const { supervisor: _s, ...restPatch } = patch;
|
|
10927
|
+
const { [_gateKey]: _drop, ...restPatch } = patch;
|
|
10901
10928
|
patch = restPatch;
|
|
10902
10929
|
}
|
|
10903
10930
|
if ("checklist" in patch) {
|
|
@@ -11359,7 +11386,28 @@ async function startDaemon(options) {
|
|
|
11359
11386
|
const list = loadExposedTunnels().filter((t) => t.name !== name);
|
|
11360
11387
|
saveExposedTunnels(list);
|
|
11361
11388
|
}
|
|
11362
|
-
|
|
11389
|
+
async function createExposedTunnel(spec) {
|
|
11390
|
+
const { FrpcTunnel } = await import('./frpc-DrfDPPux.mjs');
|
|
11391
|
+
const tunnel = new FrpcTunnel({
|
|
11392
|
+
name: spec.name,
|
|
11393
|
+
ports: spec.ports,
|
|
11394
|
+
group: spec.group,
|
|
11395
|
+
groupKey: spec.groupKey,
|
|
11396
|
+
healthCheckType: spec.healthCheckType,
|
|
11397
|
+
healthCheckPath: spec.healthCheckPath,
|
|
11398
|
+
healthCheckInterval: spec.healthCheckInterval,
|
|
11399
|
+
// Backend-agnostic ghost/stuck-proxy detection via frpc's loopback
|
|
11400
|
+
// admin API — exposed backends rarely have an HTTP health endpoint.
|
|
11401
|
+
adminStatus: true,
|
|
11402
|
+
onError: (err) => logger.log(`[FRPC ${spec.name}] ${err.message}`),
|
|
11403
|
+
onConnect: () => logger.log(`[FRPC ${spec.name}] connected`),
|
|
11404
|
+
onDisconnect: () => logger.log(`[FRPC ${spec.name}] disconnected, will auto-reconnect`)
|
|
11405
|
+
});
|
|
11406
|
+
await tunnel.connect();
|
|
11407
|
+
return tunnel;
|
|
11408
|
+
}
|
|
11409
|
+
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
11410
|
+
const { ServeManager } = await import('./serveManager-Csqa6icR.mjs');
|
|
11363
11411
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
11364
11412
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
11365
11413
|
});
|
|
@@ -14019,7 +14067,8 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14019
14067
|
serveManager,
|
|
14020
14068
|
sharingNotificationSync,
|
|
14021
14069
|
persistExposedTunnel,
|
|
14022
|
-
forgetExposedTunnel
|
|
14070
|
+
forgetExposedTunnel,
|
|
14071
|
+
listExposedTunnels: () => loadExposedTunnels().map((t) => ({ name: t.name, ports: t.ports, group: t.group, addedAt: t.addedAt }))
|
|
14023
14072
|
}
|
|
14024
14073
|
);
|
|
14025
14074
|
logger.log(`Machine service registered: svamp-machine-${machineId}`);
|
|
@@ -14047,23 +14096,10 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14047
14096
|
const specs = loadExposedTunnels();
|
|
14048
14097
|
if (specs.length === 0) return;
|
|
14049
14098
|
logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
|
|
14050
|
-
const { FrpcTunnel } = await import('./frpc-CWyoLax7.mjs');
|
|
14051
14099
|
for (const spec of specs) {
|
|
14052
14100
|
if (tunnels.has(spec.name)) continue;
|
|
14053
14101
|
try {
|
|
14054
|
-
const tunnel =
|
|
14055
|
-
name: spec.name,
|
|
14056
|
-
ports: spec.ports,
|
|
14057
|
-
group: spec.group,
|
|
14058
|
-
groupKey: spec.groupKey,
|
|
14059
|
-
healthCheckType: spec.healthCheckType,
|
|
14060
|
-
healthCheckPath: spec.healthCheckPath,
|
|
14061
|
-
healthCheckInterval: spec.healthCheckInterval,
|
|
14062
|
-
onError: (err) => logger.log(`[FRPC ${spec.name}] ${err.message}`),
|
|
14063
|
-
onConnect: () => logger.log(`[FRPC ${spec.name}] connected (restored)`),
|
|
14064
|
-
onDisconnect: () => logger.log(`[FRPC ${spec.name}] disconnected, will auto-reconnect`)
|
|
14065
|
-
});
|
|
14066
|
-
await tunnel.connect();
|
|
14102
|
+
const tunnel = await createExposedTunnel(spec);
|
|
14067
14103
|
tunnels.set(spec.name, tunnel);
|
|
14068
14104
|
logger.log(`[exposed-tunnels] Restored: ${spec.name} \u2192 ports ${spec.ports.join(",")}`);
|
|
14069
14105
|
} catch (err) {
|
|
@@ -14421,11 +14457,39 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14421
14457
|
for (const [name, tunnel] of tunnels) {
|
|
14422
14458
|
const health = tunnel.status;
|
|
14423
14459
|
const reason = tunnelLooksDead(health);
|
|
14424
|
-
if (reason) {
|
|
14425
|
-
|
|
14426
|
-
|
|
14460
|
+
if (!reason) {
|
|
14461
|
+
tunnelRecreateState.delete(name);
|
|
14462
|
+
continue;
|
|
14463
|
+
}
|
|
14464
|
+
const spec = loadExposedTunnels().find((t) => t.name === name);
|
|
14465
|
+
if (!spec) {
|
|
14466
|
+
logger.log(`frpc tunnel '${name}' ${reason}, no persisted spec \u2014 destroying (cannot recreate)`);
|
|
14467
|
+
try {
|
|
14468
|
+
tunnel.destroy();
|
|
14469
|
+
} catch {
|
|
14470
|
+
}
|
|
14427
14471
|
tunnels.delete(name);
|
|
14472
|
+
tunnelRecreateState.delete(name);
|
|
14473
|
+
continue;
|
|
14428
14474
|
}
|
|
14475
|
+
const now = Date.now();
|
|
14476
|
+
const st = tunnelRecreateState.get(name) ?? { nextAttemptAt: 0, attempts: 0 };
|
|
14477
|
+
if (now < st.nextAttemptAt) continue;
|
|
14478
|
+
st.attempts++;
|
|
14479
|
+
st.nextAttemptAt = now + Math.min(15e3 * Math.pow(2, st.attempts - 1), 5 * 6e4);
|
|
14480
|
+
tunnelRecreateState.set(name, st);
|
|
14481
|
+
logger.log(`frpc tunnel '${name}' ${reason} \u2014 recreating (attempt ${st.attempts})`);
|
|
14482
|
+
try {
|
|
14483
|
+
tunnel.destroy();
|
|
14484
|
+
} catch {
|
|
14485
|
+
}
|
|
14486
|
+
tunnels.delete(name);
|
|
14487
|
+
createExposedTunnel(spec).then((fresh) => {
|
|
14488
|
+
tunnels.set(name, fresh);
|
|
14489
|
+
logger.log(`frpc tunnel '${name}' recreated`);
|
|
14490
|
+
}).catch((err) => {
|
|
14491
|
+
logger.log(`frpc tunnel '${name}' recreate failed: ${err.message} (will retry)`);
|
|
14492
|
+
});
|
|
14429
14493
|
}
|
|
14430
14494
|
} finally {
|
|
14431
14495
|
heartbeatRunning = false;
|
|
@@ -54,7 +54,7 @@ async function handleServeCommand() {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function serveAdd(args, machineId) {
|
|
57
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
57
|
+
const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
|
|
58
58
|
const pos = positionalArgs(args);
|
|
59
59
|
const name = pos[0];
|
|
60
60
|
if (!name) {
|
|
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
async function serveApply(args, machineId) {
|
|
96
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
96
|
+
const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
|
|
97
97
|
const fs = await import('fs');
|
|
98
98
|
const yaml = await import('yaml');
|
|
99
99
|
const file = positionalArgs(args)[0];
|
|
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
async function serveRemove(args, machineId) {
|
|
185
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
185
|
+
const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
|
|
186
186
|
const pos = positionalArgs(args);
|
|
187
187
|
const name = pos[0];
|
|
188
188
|
if (!name) {
|
|
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
async function serveList(args, machineId) {
|
|
205
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
205
|
+
const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
|
|
206
206
|
const all = hasFlag(args, "--all", "-a");
|
|
207
207
|
const json = hasFlag(args, "--json");
|
|
208
208
|
const sessionId = getFlag(args, "--session");
|
|
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
async function serveInfo(machineId) {
|
|
238
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
238
|
+
const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
|
|
239
239
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
240
240
|
try {
|
|
241
241
|
const info = await machine.serveInfo();
|
|
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as http from 'http';
|
|
5
5
|
import * as net from 'net';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
-
import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-
|
|
7
|
+
import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-DMahGhJP.mjs';
|
|
8
8
|
import 'os';
|
|
9
9
|
import 'fs/promises';
|
|
10
10
|
import 'url';
|
|
@@ -713,7 +713,7 @@ class ServeManager {
|
|
|
713
713
|
const mount = this.mounts.get(mountName);
|
|
714
714
|
const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, `static-${subdomainSafe}-${mount.linkToken}`]]) : void 0;
|
|
715
715
|
try {
|
|
716
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
716
|
+
const { FrpcTunnel } = await import('./frpc-DrfDPPux.mjs');
|
|
717
717
|
let tunnel;
|
|
718
718
|
tunnel = new FrpcTunnel({
|
|
719
719
|
name: tunnelName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as READ_ONLY_TOOLS, z as loadMachineContext, A as buildMachineInstructions, B as machineToolsForRole, C as buildMachineTools } from './run-
|
|
1
|
+
import { R as READ_ONLY_TOOLS, z as loadMachineContext, A as buildMachineInstructions, B as machineToolsForRole, C as buildMachineTools } from './run-DMahGhJP.mjs';
|
|
2
2
|
import 'node:child_process';
|
|
3
3
|
import 'os';
|
|
4
4
|
import 'fs/promises';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svamp-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.130",
|
|
4
4
|
"description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
|
|
5
5
|
"author": "Amun AI AB",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/loop bin/skills/loop && cp -r ../../skills/crew bin/skills/crew && tsc --noEmit && pkgroll",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
|
-
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-loop-activation.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-checklist.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-routine.mjs && npx tsx test/test-routine-rpc.mjs && npx tsx test/test-checklist-watchdog.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs",
|
|
23
|
+
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-loop-activation.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-checklist.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-routine.mjs && npx tsx test/test-routine-rpc.mjs && npx tsx test/test-checklist-watchdog.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs",
|
|
24
24
|
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
25
25
|
"dev": "tsx src/cli.ts",
|
|
26
26
|
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|