svamp-cli 0.2.24 → 0.2.25
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/{agentCommands-XQqXEOXO.mjs → agentCommands-MiAH1S9e.mjs} +2 -2
- package/dist/cli.mjs +30 -30
- package/dist/{commands-CEiWVChH.mjs → commands-DRpGTxYD.mjs} +1 -1
- package/dist/{commands-ybRzIOIu.mjs → commands-DV2tFSXm.mjs} +8 -18
- package/dist/{commands-BjHspJLZ.mjs → commands-ibBP43SF.mjs} +2 -2
- package/dist/{frpc-C9Qs-5jQ.mjs → frpc-BhS2e6r-.mjs} +45 -5
- package/dist/index.mjs +1 -1
- package/dist/{package-BPrzOhTb.mjs → package-BKGqkCGK.mjs} +1 -1
- package/dist/{run-Cq-vqhYX.mjs → run-BTlUAfV3.mjs} +86 -9
- package/dist/{run-BZDgOpT5.mjs → run-DlcgeE63.mjs} +1 -1
- package/dist/{serveCommands-Q6dCUe4U.mjs → serveCommands-Ci4DE-iQ.mjs} +4 -4
- package/dist/{serveManager-BiyUnYbW.mjs → serveManager-BTbXyL0i.mjs} +14 -1
- package/package.json +1 -1
|
@@ -148,7 +148,7 @@ async function sessionBroadcast(action, args) {
|
|
|
148
148
|
console.log(`Broadcast sent: ${action}`);
|
|
149
149
|
}
|
|
150
150
|
async function connectToMachineService() {
|
|
151
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
151
|
+
const { connectAndGetMachine } = await import('./commands-DRpGTxYD.mjs');
|
|
152
152
|
return connectAndGetMachine();
|
|
153
153
|
}
|
|
154
154
|
async function inboxSend(targetSessionId, opts) {
|
|
@@ -165,7 +165,7 @@ async function inboxSend(targetSessionId, opts) {
|
|
|
165
165
|
}
|
|
166
166
|
const { server, machine } = await connectToMachineService();
|
|
167
167
|
try {
|
|
168
|
-
const { resolveSessionId } = await import('./commands-
|
|
168
|
+
const { resolveSessionId } = await import('./commands-DRpGTxYD.mjs');
|
|
169
169
|
const sessions = await machine.listSessions();
|
|
170
170
|
const match = resolveSessionId(sessions, targetSessionId);
|
|
171
171
|
const fullTargetId = match.sessionId;
|
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-
|
|
1
|
+
import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-BTlUAfV3.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -36,7 +36,7 @@ async function main() {
|
|
|
36
36
|
await logoutFromHypha();
|
|
37
37
|
} else if (subcommand === "daemon") {
|
|
38
38
|
if (daemonSubcommand === "restart") {
|
|
39
|
-
const { restartDaemon } = await import('./run-
|
|
39
|
+
const { restartDaemon } = await import('./run-BTlUAfV3.mjs').then(function (n) { return n.k; });
|
|
40
40
|
await restartDaemon();
|
|
41
41
|
process.exit(0);
|
|
42
42
|
}
|
|
@@ -238,7 +238,7 @@ async function main() {
|
|
|
238
238
|
console.error("svamp service: Service commands are not available in sandboxed sessions.");
|
|
239
239
|
process.exit(1);
|
|
240
240
|
}
|
|
241
|
-
const { handleServiceCommand } = await import('./commands-
|
|
241
|
+
const { handleServiceCommand } = await import('./commands-DV2tFSXm.mjs');
|
|
242
242
|
await handleServiceCommand();
|
|
243
243
|
} else if (subcommand === "serve") {
|
|
244
244
|
const { isSandboxed: isSandboxedServe } = await import('./sandboxDetect-DNTcbgWD.mjs');
|
|
@@ -246,7 +246,7 @@ async function main() {
|
|
|
246
246
|
console.error("svamp serve: Serve commands are not available in sandboxed sessions.");
|
|
247
247
|
process.exit(1);
|
|
248
248
|
}
|
|
249
|
-
const { handleServeCommand } = await import('./serveCommands-
|
|
249
|
+
const { handleServeCommand } = await import('./serveCommands-Ci4DE-iQ.mjs');
|
|
250
250
|
await handleServeCommand();
|
|
251
251
|
process.exit(0);
|
|
252
252
|
} else if (subcommand === "process" || subcommand === "proc") {
|
|
@@ -255,7 +255,7 @@ async function main() {
|
|
|
255
255
|
console.error("svamp process: Process commands are not available in sandboxed sessions.");
|
|
256
256
|
process.exit(1);
|
|
257
257
|
}
|
|
258
|
-
const { processCommand } = await import('./commands-
|
|
258
|
+
const { processCommand } = await import('./commands-ibBP43SF.mjs');
|
|
259
259
|
let machineId;
|
|
260
260
|
const processArgs = args.slice(1);
|
|
261
261
|
const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
|
|
@@ -273,7 +273,7 @@ async function main() {
|
|
|
273
273
|
} else if (!subcommand || subcommand === "start") {
|
|
274
274
|
await handleInteractiveCommand();
|
|
275
275
|
} else if (subcommand === "--version" || subcommand === "-v") {
|
|
276
|
-
const pkg = await import('./package-
|
|
276
|
+
const pkg = await import('./package-BKGqkCGK.mjs').catch(() => ({ default: { version: "unknown" } }));
|
|
277
277
|
console.log(`svamp version: ${pkg.default.version}`);
|
|
278
278
|
} else {
|
|
279
279
|
console.error(`Unknown command: ${subcommand}`);
|
|
@@ -282,7 +282,7 @@ async function main() {
|
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
async function handleInteractiveCommand() {
|
|
285
|
-
const { runInteractive } = await import('./run-
|
|
285
|
+
const { runInteractive } = await import('./run-DlcgeE63.mjs');
|
|
286
286
|
const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
|
|
287
287
|
let directory = process.cwd();
|
|
288
288
|
let resumeSessionId;
|
|
@@ -327,7 +327,7 @@ async function handleAgentCommand() {
|
|
|
327
327
|
return;
|
|
328
328
|
}
|
|
329
329
|
if (agentArgs[0] === "list") {
|
|
330
|
-
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-
|
|
330
|
+
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-BTlUAfV3.mjs').then(function (n) { return n.i; });
|
|
331
331
|
console.log("Known agents:");
|
|
332
332
|
for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
|
|
333
333
|
console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
|
|
@@ -339,7 +339,7 @@ async function handleAgentCommand() {
|
|
|
339
339
|
console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
|
|
340
340
|
return;
|
|
341
341
|
}
|
|
342
|
-
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-
|
|
342
|
+
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-BTlUAfV3.mjs').then(function (n) { return n.i; });
|
|
343
343
|
let cwd = process.cwd();
|
|
344
344
|
const filteredArgs = [];
|
|
345
345
|
for (let i = 0; i < agentArgs.length; i++) {
|
|
@@ -363,12 +363,12 @@ async function handleAgentCommand() {
|
|
|
363
363
|
console.log(`Starting ${config.agentName} agent in ${cwd}...`);
|
|
364
364
|
let backend;
|
|
365
365
|
if (KNOWN_MCP_AGENTS[config.agentName]) {
|
|
366
|
-
const { CodexMcpBackend } = await import('./run-
|
|
366
|
+
const { CodexMcpBackend } = await import('./run-BTlUAfV3.mjs').then(function (n) { return n.j; });
|
|
367
367
|
backend = new CodexMcpBackend({ cwd, log: logFn });
|
|
368
368
|
} else {
|
|
369
|
-
const { AcpBackend } = await import('./run-
|
|
370
|
-
const { GeminiTransport } = await import('./run-
|
|
371
|
-
const { DefaultTransport } = await import('./run-
|
|
369
|
+
const { AcpBackend } = await import('./run-BTlUAfV3.mjs').then(function (n) { return n.h; });
|
|
370
|
+
const { GeminiTransport } = await import('./run-BTlUAfV3.mjs').then(function (n) { return n.G; });
|
|
371
|
+
const { DefaultTransport } = await import('./run-BTlUAfV3.mjs').then(function (n) { return n.D; });
|
|
372
372
|
const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
|
|
373
373
|
backend = new AcpBackend({
|
|
374
374
|
agentName: config.agentName,
|
|
@@ -495,7 +495,7 @@ async function handleSessionCommand() {
|
|
|
495
495
|
process.exit(1);
|
|
496
496
|
}
|
|
497
497
|
}
|
|
498
|
-
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-
|
|
498
|
+
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-DRpGTxYD.mjs');
|
|
499
499
|
const parseFlagStr = (flag, shortFlag) => {
|
|
500
500
|
for (let i = 1; i < sessionArgs.length; i++) {
|
|
501
501
|
if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
|
|
@@ -555,7 +555,7 @@ async function handleSessionCommand() {
|
|
|
555
555
|
allowDomain.push(sessionArgs[++i]);
|
|
556
556
|
}
|
|
557
557
|
}
|
|
558
|
-
const { parseShareArg } = await import('./commands-
|
|
558
|
+
const { parseShareArg } = await import('./commands-DRpGTxYD.mjs');
|
|
559
559
|
const shareEntries = share.map((s) => parseShareArg(s));
|
|
560
560
|
await sessionSpawn(agent, dir, targetMachineId, {
|
|
561
561
|
message,
|
|
@@ -641,7 +641,7 @@ async function handleSessionCommand() {
|
|
|
641
641
|
console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
|
|
642
642
|
process.exit(1);
|
|
643
643
|
}
|
|
644
|
-
const { sessionApprove } = await import('./commands-
|
|
644
|
+
const { sessionApprove } = await import('./commands-DRpGTxYD.mjs');
|
|
645
645
|
const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
646
646
|
await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
|
|
647
647
|
json: hasFlag("--json")
|
|
@@ -651,7 +651,7 @@ async function handleSessionCommand() {
|
|
|
651
651
|
console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
|
|
652
652
|
process.exit(1);
|
|
653
653
|
}
|
|
654
|
-
const { sessionDeny } = await import('./commands-
|
|
654
|
+
const { sessionDeny } = await import('./commands-DRpGTxYD.mjs');
|
|
655
655
|
const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
656
656
|
await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
|
|
657
657
|
json: hasFlag("--json")
|
|
@@ -687,7 +687,7 @@ async function handleSessionCommand() {
|
|
|
687
687
|
console.error("Usage: svamp session set-title <title>");
|
|
688
688
|
process.exit(1);
|
|
689
689
|
}
|
|
690
|
-
const { sessionSetTitle } = await import('./agentCommands-
|
|
690
|
+
const { sessionSetTitle } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
691
691
|
await sessionSetTitle(title);
|
|
692
692
|
} else if (sessionSubcommand === "set-link") {
|
|
693
693
|
const url = sessionArgs[1];
|
|
@@ -696,7 +696,7 @@ async function handleSessionCommand() {
|
|
|
696
696
|
process.exit(1);
|
|
697
697
|
}
|
|
698
698
|
const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
699
|
-
const { sessionSetLink } = await import('./agentCommands-
|
|
699
|
+
const { sessionSetLink } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
700
700
|
await sessionSetLink(url, label);
|
|
701
701
|
} else if (sessionSubcommand === "notify") {
|
|
702
702
|
const message = sessionArgs[1];
|
|
@@ -705,7 +705,7 @@ async function handleSessionCommand() {
|
|
|
705
705
|
process.exit(1);
|
|
706
706
|
}
|
|
707
707
|
const level = parseFlagStr("--level") || "info";
|
|
708
|
-
const { sessionNotify } = await import('./agentCommands-
|
|
708
|
+
const { sessionNotify } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
709
709
|
await sessionNotify(message, level);
|
|
710
710
|
} else if (sessionSubcommand === "broadcast") {
|
|
711
711
|
const action = sessionArgs[1];
|
|
@@ -713,7 +713,7 @@ async function handleSessionCommand() {
|
|
|
713
713
|
console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
|
|
714
714
|
process.exit(1);
|
|
715
715
|
}
|
|
716
|
-
const { sessionBroadcast } = await import('./agentCommands-
|
|
716
|
+
const { sessionBroadcast } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
717
717
|
await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
|
|
718
718
|
} else if (sessionSubcommand === "inbox") {
|
|
719
719
|
const inboxSubcmd = sessionArgs[1];
|
|
@@ -724,7 +724,7 @@ async function handleSessionCommand() {
|
|
|
724
724
|
process.exit(1);
|
|
725
725
|
}
|
|
726
726
|
if (agentSessionId) {
|
|
727
|
-
const { inboxSend } = await import('./agentCommands-
|
|
727
|
+
const { inboxSend } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
728
728
|
await inboxSend(sessionArgs[2], {
|
|
729
729
|
body: sessionArgs[3],
|
|
730
730
|
subject: parseFlagStr("--subject"),
|
|
@@ -739,7 +739,7 @@ async function handleSessionCommand() {
|
|
|
739
739
|
}
|
|
740
740
|
} else if (inboxSubcmd === "list" || inboxSubcmd === "ls") {
|
|
741
741
|
if (agentSessionId && !sessionArgs[2]) {
|
|
742
|
-
const { inboxList } = await import('./agentCommands-
|
|
742
|
+
const { inboxList } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
743
743
|
await inboxList({
|
|
744
744
|
unread: hasFlag("--unread"),
|
|
745
745
|
limit: parseFlagInt("--limit"),
|
|
@@ -761,7 +761,7 @@ async function handleSessionCommand() {
|
|
|
761
761
|
process.exit(1);
|
|
762
762
|
}
|
|
763
763
|
if (agentSessionId && !sessionArgs[3]) {
|
|
764
|
-
const { inboxList } = await import('./agentCommands-
|
|
764
|
+
const { inboxList } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
765
765
|
await sessionInboxRead(agentSessionId, sessionArgs[2], targetMachineId);
|
|
766
766
|
} else if (sessionArgs[3]) {
|
|
767
767
|
await sessionInboxRead(sessionArgs[2], sessionArgs[3], targetMachineId);
|
|
@@ -771,7 +771,7 @@ async function handleSessionCommand() {
|
|
|
771
771
|
}
|
|
772
772
|
} else if (inboxSubcmd === "reply") {
|
|
773
773
|
if (agentSessionId && sessionArgs[2] && sessionArgs[3] && !sessionArgs[4]) {
|
|
774
|
-
const { inboxReply } = await import('./agentCommands-
|
|
774
|
+
const { inboxReply } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
775
775
|
await inboxReply(sessionArgs[2], sessionArgs[3]);
|
|
776
776
|
} else if (sessionArgs[2] && sessionArgs[3] && sessionArgs[4]) {
|
|
777
777
|
await sessionInboxReply(sessionArgs[2], sessionArgs[3], sessionArgs[4], targetMachineId);
|
|
@@ -807,7 +807,7 @@ async function handleMachineCommand() {
|
|
|
807
807
|
return;
|
|
808
808
|
}
|
|
809
809
|
if (machineSubcommand === "share") {
|
|
810
|
-
const { machineShare } = await import('./commands-
|
|
810
|
+
const { machineShare } = await import('./commands-DRpGTxYD.mjs');
|
|
811
811
|
let machineId;
|
|
812
812
|
const shareArgs = [];
|
|
813
813
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
@@ -837,7 +837,7 @@ async function handleMachineCommand() {
|
|
|
837
837
|
}
|
|
838
838
|
await machineShare(machineId, { add, remove, list, configPath, showConfig });
|
|
839
839
|
} else if (machineSubcommand === "exec") {
|
|
840
|
-
const { machineExec } = await import('./commands-
|
|
840
|
+
const { machineExec } = await import('./commands-DRpGTxYD.mjs');
|
|
841
841
|
let machineId;
|
|
842
842
|
let cwd;
|
|
843
843
|
const cmdParts = [];
|
|
@@ -857,7 +857,7 @@ async function handleMachineCommand() {
|
|
|
857
857
|
}
|
|
858
858
|
await machineExec(machineId, command, cwd);
|
|
859
859
|
} else if (machineSubcommand === "info") {
|
|
860
|
-
const { machineInfo } = await import('./commands-
|
|
860
|
+
const { machineInfo } = await import('./commands-DRpGTxYD.mjs');
|
|
861
861
|
let machineId;
|
|
862
862
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
863
863
|
if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
|
|
@@ -877,10 +877,10 @@ async function handleMachineCommand() {
|
|
|
877
877
|
level = machineArgs[++i];
|
|
878
878
|
}
|
|
879
879
|
}
|
|
880
|
-
const { machineNotify } = await import('./agentCommands-
|
|
880
|
+
const { machineNotify } = await import('./agentCommands-MiAH1S9e.mjs');
|
|
881
881
|
await machineNotify(message, level);
|
|
882
882
|
} else if (machineSubcommand === "ls") {
|
|
883
|
-
const { machineLs } = await import('./commands-
|
|
883
|
+
const { machineLs } = await import('./commands-DRpGTxYD.mjs');
|
|
884
884
|
let machineId;
|
|
885
885
|
let showHidden = false;
|
|
886
886
|
let path;
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import os from 'node:os';
|
|
5
|
-
import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-
|
|
5
|
+
import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-BTlUAfV3.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
|
@@ -21,9 +21,7 @@ function positionalArgs(args) {
|
|
|
21
21
|
const result = [];
|
|
22
22
|
for (let i = 0; i < args.length; i++) {
|
|
23
23
|
if (args[i].startsWith("--")) {
|
|
24
|
-
if (i + 1 < args.length && !args[i + 1].startsWith("--"))
|
|
25
|
-
i++;
|
|
26
|
-
}
|
|
24
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("--")) i++;
|
|
27
25
|
continue;
|
|
28
26
|
}
|
|
29
27
|
result.push(args[i]);
|
|
@@ -57,7 +55,7 @@ async function serviceExpose(args) {
|
|
|
57
55
|
console.error("Usage: svamp service expose <name> --port <port> [--port <port2>] [options]");
|
|
58
56
|
process.exit(1);
|
|
59
57
|
}
|
|
60
|
-
const { runFrpcTunnel } = await import('./frpc-
|
|
58
|
+
const { runFrpcTunnel } = await import('./frpc-BhS2e6r-.mjs');
|
|
61
59
|
await runFrpcTunnel(name, ports, void 0, {
|
|
62
60
|
group,
|
|
63
61
|
groupKey,
|
|
@@ -90,17 +88,14 @@ async function serviceServe(args) {
|
|
|
90
88
|
};
|
|
91
89
|
process.on("SIGINT", cleanup);
|
|
92
90
|
process.on("SIGTERM", cleanup);
|
|
93
|
-
const { runFrpcTunnel } = await import('./frpc-
|
|
91
|
+
const { runFrpcTunnel } = await import('./frpc-BhS2e6r-.mjs');
|
|
94
92
|
await runFrpcTunnel(name, [caddyPort]);
|
|
95
93
|
} catch (err) {
|
|
96
94
|
console.error(`Error serving directory: ${err.message}`);
|
|
97
95
|
process.exit(1);
|
|
98
96
|
}
|
|
99
97
|
}
|
|
100
|
-
async function
|
|
101
|
-
await serviceExpose(args);
|
|
102
|
-
}
|
|
103
|
-
async function serviceList(args) {
|
|
98
|
+
async function serviceList(_args) {
|
|
104
99
|
console.log("Active tunnels are managed by the svamp daemon.");
|
|
105
100
|
console.log('Use "svamp service expose" to start a tunnel in the foreground.');
|
|
106
101
|
}
|
|
@@ -122,12 +117,10 @@ async function handleServiceCommand() {
|
|
|
122
117
|
return;
|
|
123
118
|
}
|
|
124
119
|
const commandArgs = serviceArgs.slice(1);
|
|
125
|
-
if (sub === "expose") {
|
|
120
|
+
if (sub === "expose" || sub === "tunnel") {
|
|
126
121
|
await serviceExpose(commandArgs);
|
|
127
122
|
} else if (sub === "serve") {
|
|
128
123
|
await serviceServe(commandArgs);
|
|
129
|
-
} else if (sub === "tunnel") {
|
|
130
|
-
await serviceTunnel(commandArgs);
|
|
131
124
|
} else if (sub === "list" || sub === "ls") {
|
|
132
125
|
await serviceList();
|
|
133
126
|
} else if (sub === "delete" || sub === "rm") {
|
|
@@ -145,7 +138,6 @@ svamp service \u2014 Expose local services via frpc tunnels
|
|
|
145
138
|
Usage:
|
|
146
139
|
svamp service expose <name> --port <port> [options] Expose local ports via tunnel
|
|
147
140
|
svamp service serve <name> [directory] [--no-listing] Serve static files via tunnel
|
|
148
|
-
svamp service tunnel <name> --port <port> [options] Alias for expose
|
|
149
141
|
svamp service list List active tunnels (daemon)
|
|
150
142
|
svamp service delete <name> Stop a tunnel (daemon)
|
|
151
143
|
|
|
@@ -160,10 +152,9 @@ Options:
|
|
|
160
152
|
Service Groups:
|
|
161
153
|
Run the same command on different machines with --group to create a
|
|
162
154
|
load-balanced service. frps round-robins traffic across all group members.
|
|
163
|
-
Unhealthy backends are auto-removed when --health-check is enabled.
|
|
164
155
|
|
|
165
|
-
Machine A: svamp service expose my-api --port 8000 --group my-api --group-key
|
|
166
|
-
Machine B: svamp service expose my-api --port 8000 --group my-api --group-key
|
|
156
|
+
Machine A: svamp service expose my-api --port 8000 --group my-api --group-key s3cret
|
|
157
|
+
Machine B: svamp service expose my-api --port 8000 --group my-api --group-key s3cret
|
|
167
158
|
\u2192 Both serve at the same URL, frps load-balances between them
|
|
168
159
|
|
|
169
160
|
Examples:
|
|
@@ -172,8 +163,7 @@ Examples:
|
|
|
172
163
|
svamp service expose my-api --port 8000 --port 3000
|
|
173
164
|
svamp service expose my-api --port 8000 --group my-api --group-key s3cret
|
|
174
165
|
svamp service serve my-site ./dist
|
|
175
|
-
svamp service serve my-site
|
|
176
166
|
`.trim());
|
|
177
167
|
}
|
|
178
168
|
|
|
179
|
-
export { handleServiceCommand, serviceDelete, serviceExpose, serviceList, serviceServe
|
|
169
|
+
export { handleServiceCommand, serviceDelete, serviceExpose, serviceList, serviceServe };
|
|
@@ -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-DRpGTxYD.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-BTlUAfV3.mjs';
|
|
9
9
|
import 'os';
|
|
10
10
|
import 'fs/promises';
|
|
11
11
|
import 'url';
|
|
@@ -4,7 +4,7 @@ import { join } from 'path';
|
|
|
4
4
|
import { homedir, platform, arch } from 'os';
|
|
5
5
|
import { createHash } from 'crypto';
|
|
6
6
|
|
|
7
|
-
const FRP_VERSION = "0.
|
|
7
|
+
const FRP_VERSION = "0.68.0";
|
|
8
8
|
const BIN_DIR = join(homedir(), ".svamp", "bin");
|
|
9
9
|
const FRPC_BIN = join(BIN_DIR, platform() === "win32" ? "frpc.exe" : "frpc");
|
|
10
10
|
function isInCluster() {
|
|
@@ -62,7 +62,19 @@ function getFrpcPath() {
|
|
|
62
62
|
return "frpc";
|
|
63
63
|
}
|
|
64
64
|
async function ensureFrpc(log) {
|
|
65
|
-
if (existsSync(FRPC_BIN))
|
|
65
|
+
if (existsSync(FRPC_BIN)) {
|
|
66
|
+
try {
|
|
67
|
+
const out = execSync(`"${FRPC_BIN}" --version`, { stdio: "pipe", timeout: 5e3 }).toString().trim();
|
|
68
|
+
if (out === FRP_VERSION) return FRPC_BIN;
|
|
69
|
+
(log || console.log)(`frpc version mismatch: ${out} \u2260 ${FRP_VERSION}, re-downloading...`);
|
|
70
|
+
} catch {
|
|
71
|
+
(log || console.log)("frpc binary broken or invalid signature, re-downloading...");
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
unlinkSync(FRPC_BIN);
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
}
|
|
66
78
|
const logger = log || console.log;
|
|
67
79
|
mkdirSync(BIN_DIR, { recursive: true });
|
|
68
80
|
const url = getFrpcDownloadUrl();
|
|
@@ -165,6 +177,12 @@ class FrpcTunnel {
|
|
|
165
177
|
logBuffer = [];
|
|
166
178
|
maxLogLines = 200;
|
|
167
179
|
proxies = [];
|
|
180
|
+
// Health tracking
|
|
181
|
+
_consecutiveErrors = 0;
|
|
182
|
+
_lastConnectedAt = 0;
|
|
183
|
+
_lastErrorAt = 0;
|
|
184
|
+
_firstErrorAt = 0;
|
|
185
|
+
_restartAttempts = 0;
|
|
168
186
|
constructor(options) {
|
|
169
187
|
this.options = options;
|
|
170
188
|
this.log = options.log || ((msg) => console.log(`[FRPC] ${msg}`));
|
|
@@ -230,6 +248,10 @@ class FrpcTunnel {
|
|
|
230
248
|
if (text.includes("start proxy success") || text.includes("login to server success")) {
|
|
231
249
|
if (!this._connected) {
|
|
232
250
|
this._connected = true;
|
|
251
|
+
this._lastConnectedAt = Date.now();
|
|
252
|
+
this._restartAttempts = 0;
|
|
253
|
+
this._consecutiveErrors = 0;
|
|
254
|
+
this._firstErrorAt = 0;
|
|
233
255
|
this.options.onConnect?.();
|
|
234
256
|
if (!resolved) {
|
|
235
257
|
resolved = true;
|
|
@@ -238,6 +260,9 @@ class FrpcTunnel {
|
|
|
238
260
|
}
|
|
239
261
|
}
|
|
240
262
|
if (text.includes("[E]") || text.includes("error")) {
|
|
263
|
+
this._consecutiveErrors++;
|
|
264
|
+
this._lastErrorAt = Date.now();
|
|
265
|
+
if (this._firstErrorAt === 0) this._firstErrorAt = Date.now();
|
|
241
266
|
this.options.onError?.(new Error(text));
|
|
242
267
|
}
|
|
243
268
|
};
|
|
@@ -259,14 +284,16 @@ class FrpcTunnel {
|
|
|
259
284
|
reject(new Error(`frpc exited with code ${code}`));
|
|
260
285
|
}
|
|
261
286
|
if (!this._destroyed && code !== 0) {
|
|
262
|
-
this.
|
|
287
|
+
this._restartAttempts++;
|
|
288
|
+
const delay = Math.min(3e3 * Math.pow(2, this._restartAttempts - 1), 6e4);
|
|
289
|
+
this.log(`frpc exited with code ${code}, restarting in ${Math.round(delay / 1e3)}s (attempt ${this._restartAttempts})...`);
|
|
263
290
|
setTimeout(() => {
|
|
264
291
|
if (!this._destroyed) {
|
|
265
292
|
this.connect().catch((err) => {
|
|
266
293
|
this.options.onError?.(err);
|
|
267
294
|
});
|
|
268
295
|
}
|
|
269
|
-
},
|
|
296
|
+
}, delay);
|
|
270
297
|
}
|
|
271
298
|
});
|
|
272
299
|
setTimeout(() => {
|
|
@@ -310,9 +337,22 @@ class FrpcTunnel {
|
|
|
310
337
|
name: this.options.name,
|
|
311
338
|
ports: this.options.ports,
|
|
312
339
|
serverAddr: this.serverConfig.serverAddr,
|
|
313
|
-
recentLogs: this.logBuffer.slice(-10)
|
|
340
|
+
recentLogs: this.logBuffer.slice(-10),
|
|
341
|
+
consecutiveErrors: this._consecutiveErrors,
|
|
342
|
+
lastConnectedAt: this._lastConnectedAt,
|
|
343
|
+
lastErrorAt: this._lastErrorAt,
|
|
344
|
+
firstErrorAt: this._firstErrorAt,
|
|
345
|
+
restartAttempts: this._restartAttempts,
|
|
346
|
+
failingDurationMs: this._firstErrorAt > 0 ? Date.now() - this._firstErrorAt : 0
|
|
314
347
|
};
|
|
315
348
|
}
|
|
349
|
+
/** Update the Hypha token. Rewrites config; takes effect on next frpc restart. */
|
|
350
|
+
updateToken(newToken) {
|
|
351
|
+
this.serverConfig.hyphaToken = newToken;
|
|
352
|
+
const configContent = generateFrpcConfig(this.serverConfig, this.proxies);
|
|
353
|
+
writeFileSync(this.configPath, configContent);
|
|
354
|
+
this.log("Config updated with fresh token");
|
|
355
|
+
}
|
|
316
356
|
/** Get the public URLs for each port. */
|
|
317
357
|
getUrls() {
|
|
318
358
|
const urls = /* @__PURE__ */ new Map();
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-
|
|
1
|
+
export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-BTlUAfV3.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -8,9 +8,9 @@ import { randomUUID as randomUUID$1 } from 'crypto';
|
|
|
8
8
|
import { existsSync, readFileSync, writeFileSync as writeFileSync$1, mkdirSync as mkdirSync$1, appendFileSync } from 'node:fs';
|
|
9
9
|
import { randomUUID, createHash } from 'node:crypto';
|
|
10
10
|
import { join as join$1 } from 'node:path';
|
|
11
|
-
import { spawn, execSync, execFile } from 'node:child_process';
|
|
11
|
+
import { spawn, execSync, execFile, execFileSync } from 'node:child_process';
|
|
12
12
|
import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
|
|
13
|
-
import { homedir } from 'node:os';
|
|
13
|
+
import { homedir, platform } from 'node:os';
|
|
14
14
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
15
15
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
16
16
|
import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -350,6 +350,10 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
350
350
|
let currentDaemonState = { ...daemonState };
|
|
351
351
|
let metadataVersion = 1;
|
|
352
352
|
let daemonStateVersion = 1;
|
|
353
|
+
let lastInboundRpcAt = Date.now();
|
|
354
|
+
const trackInbound = () => {
|
|
355
|
+
lastInboundRpcAt = Date.now();
|
|
356
|
+
};
|
|
353
357
|
const listeners = [];
|
|
354
358
|
const removeListener = (listener, reason) => {
|
|
355
359
|
const idx = listeners.indexOf(listener);
|
|
@@ -394,6 +398,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
394
398
|
config: { visibility: "unlisted", require_context: true },
|
|
395
399
|
// Machine info
|
|
396
400
|
getMachineInfo: async (context) => {
|
|
401
|
+
trackInbound();
|
|
397
402
|
let hasMachineAccess = false;
|
|
398
403
|
try {
|
|
399
404
|
authorizeRequest(context, currentMetadata.sharing, "view");
|
|
@@ -435,6 +440,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
435
440
|
},
|
|
436
441
|
// Heartbeat
|
|
437
442
|
heartbeat: async (context) => {
|
|
443
|
+
trackInbound();
|
|
438
444
|
authorizeRequest(context, currentMetadata.sharing, "view");
|
|
439
445
|
return {
|
|
440
446
|
time: Date.now(),
|
|
@@ -446,6 +452,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
446
452
|
// If machine sharing grants access, return all. Otherwise return only
|
|
447
453
|
// sessions whose own sharing config grants access to this user.
|
|
448
454
|
listSessions: async (context) => {
|
|
455
|
+
trackInbound();
|
|
449
456
|
let hasMachineAccess = false;
|
|
450
457
|
try {
|
|
451
458
|
authorizeRequest(context, currentMetadata.sharing, "view");
|
|
@@ -473,6 +480,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
473
480
|
* sessions whose sharing config grants them access.
|
|
474
481
|
*/
|
|
475
482
|
getSessions: async (context) => {
|
|
483
|
+
trackInbound();
|
|
476
484
|
let hasMachineAccess = false;
|
|
477
485
|
try {
|
|
478
486
|
authorizeRequest(context, currentMetadata.sharing, "view");
|
|
@@ -513,8 +521,12 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
513
521
|
* Dispatch an RPC call to a specific session's handler.
|
|
514
522
|
* This consolidates all session RPCs through the machine service,
|
|
515
523
|
* eliminating the need for per-session Hypha service registration.
|
|
524
|
+
*
|
|
525
|
+
* `kwargs` is an object whose keys map to handler parameter names.
|
|
526
|
+
* Context is injected automatically — callers must NOT include it.
|
|
516
527
|
*/
|
|
517
528
|
sessionRPC: async (sessionId, method, kwargs, context) => {
|
|
529
|
+
trackInbound();
|
|
518
530
|
const rpc = handlers.getSessionRPCHandlers?.(sessionId);
|
|
519
531
|
if (!rpc) {
|
|
520
532
|
throw new Error(`Session ${sessionId} not found on this machine`);
|
|
@@ -545,6 +557,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
545
557
|
* Delegates to the session store's registerListener.
|
|
546
558
|
*/
|
|
547
559
|
registerSessionListener: async (sessionId, callback, context) => {
|
|
560
|
+
trackInbound();
|
|
548
561
|
const rpc = handlers.getSessionRPCHandlers?.(sessionId);
|
|
549
562
|
if (!rpc) {
|
|
550
563
|
throw new Error(`Session ${sessionId} not found on this machine`);
|
|
@@ -564,6 +577,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
564
577
|
},
|
|
565
578
|
// Spawn a new session
|
|
566
579
|
spawnSession: async (options, context) => {
|
|
580
|
+
trackInbound();
|
|
567
581
|
authorizeRequest(context, currentMetadata.sharing, "interact");
|
|
568
582
|
const callerEmail = context?.user?.email;
|
|
569
583
|
const machineOwner = currentMetadata.sharing?.owner;
|
|
@@ -776,6 +790,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
776
790
|
},
|
|
777
791
|
// Register a listener for real-time updates (app calls this with _rintf callback)
|
|
778
792
|
registerListener: async (callback, context) => {
|
|
793
|
+
trackInbound();
|
|
779
794
|
authorizeRequest(context, currentMetadata.sharing, "view");
|
|
780
795
|
listeners.push(callback);
|
|
781
796
|
return { success: true, listenerId: listeners.length - 1 };
|
|
@@ -1092,7 +1107,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
1092
1107
|
const tunnels = handlers.tunnels;
|
|
1093
1108
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
1094
1109
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
1095
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
1110
|
+
const { FrpcTunnel } = await import('./frpc-BhS2e6r-.mjs');
|
|
1096
1111
|
const tunnel = new FrpcTunnel({
|
|
1097
1112
|
name: params.name,
|
|
1098
1113
|
ports: params.ports,
|
|
@@ -1222,6 +1237,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
1222
1237
|
daemonState: { value: currentDaemonState, version: daemonStateVersion }
|
|
1223
1238
|
});
|
|
1224
1239
|
},
|
|
1240
|
+
getLastInboundRpcAt: () => lastInboundRpcAt,
|
|
1225
1241
|
disconnect: async () => {
|
|
1226
1242
|
const toRemove = [...listeners];
|
|
1227
1243
|
for (const listener of toRemove) {
|
|
@@ -4780,12 +4796,32 @@ async function stageCredentialsForSharing(sessionId) {
|
|
|
4780
4796
|
const stagedClaudeDir = join$1(tmpHome, ".claude");
|
|
4781
4797
|
await mkdir(stagedClaudeDir, { recursive: true });
|
|
4782
4798
|
const credentialFiles = ["credentials.json", ".credentials.json"];
|
|
4799
|
+
let credentialsCopied = false;
|
|
4783
4800
|
for (const file of credentialFiles) {
|
|
4784
4801
|
try {
|
|
4785
4802
|
await copyFile(join$1(realClaudeDir, file), join$1(stagedClaudeDir, file));
|
|
4803
|
+
credentialsCopied = true;
|
|
4786
4804
|
} catch {
|
|
4787
4805
|
}
|
|
4788
4806
|
}
|
|
4807
|
+
if (!credentialsCopied && platform() === "darwin") {
|
|
4808
|
+
const stagedCredFile = join$1(stagedClaudeDir, ".credentials.json");
|
|
4809
|
+
const hasExistingCredentials = existsSync(stagedCredFile);
|
|
4810
|
+
if (!hasExistingCredentials) {
|
|
4811
|
+
try {
|
|
4812
|
+
const keychainData = execFileSync("security", [
|
|
4813
|
+
"find-generic-password",
|
|
4814
|
+
"-s",
|
|
4815
|
+
"Claude Code-credentials",
|
|
4816
|
+
"-w"
|
|
4817
|
+
], { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
4818
|
+
if (keychainData.startsWith("{") || keychainData.startsWith("[")) {
|
|
4819
|
+
await writeFile(stagedCredFile, keychainData);
|
|
4820
|
+
}
|
|
4821
|
+
} catch {
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4789
4825
|
try {
|
|
4790
4826
|
await copyFile(join$1(realHome, ".gitconfig"), join$1(tmpHome, ".gitconfig"));
|
|
4791
4827
|
} catch {
|
|
@@ -6174,6 +6210,11 @@ async function startDaemon(options) {
|
|
|
6174
6210
|
const msg = String(reason);
|
|
6175
6211
|
logger.error("Unhandled rejection:", reason);
|
|
6176
6212
|
const isTransient = TRANSIENT_REJECTION_PATTERNS.some((p) => msg.toLowerCase().includes(p.toLowerCase()));
|
|
6213
|
+
const isSessionLoss = msg.toLowerCase().includes("session does not exist");
|
|
6214
|
+
if (isSessionLoss && consecutiveHeartbeatFailures === 0) {
|
|
6215
|
+
logger.log(`Session loss detected with healthy heartbeat \u2014 triggering immediate service probe`);
|
|
6216
|
+
consecutiveHeartbeatFailures = 1;
|
|
6217
|
+
}
|
|
6177
6218
|
if (isTransient || consecutiveHeartbeatFailures > 0) {
|
|
6178
6219
|
logger.log(`Ignoring transient rejection (heartbeatFailures=${consecutiveHeartbeatFailures}): ${msg.slice(0, 200)}`);
|
|
6179
6220
|
return;
|
|
@@ -6256,7 +6297,7 @@ async function startDaemon(options) {
|
|
|
6256
6297
|
const supervisor = new ProcessSupervisor(join(SVAMP_HOME, "processes"));
|
|
6257
6298
|
await supervisor.init();
|
|
6258
6299
|
const tunnels = /* @__PURE__ */ new Map();
|
|
6259
|
-
const { ServeManager } = await import('./serveManager-
|
|
6300
|
+
const { ServeManager } = await import('./serveManager-BTbXyL0i.mjs');
|
|
6260
6301
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
6261
6302
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
6262
6303
|
});
|
|
@@ -8695,13 +8736,15 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8695
8736
|
console.log(` Service: svamp-machine-${machineId}`);
|
|
8696
8737
|
console.log(` Log file: ${logger.logFilePath}`);
|
|
8697
8738
|
const HEARTBEAT_INTERVAL_MS = 1e4;
|
|
8698
|
-
const PING_TIMEOUT_MS =
|
|
8739
|
+
const PING_TIMEOUT_MS = 15e3;
|
|
8699
8740
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
8700
8741
|
let heartbeatRunning = false;
|
|
8701
8742
|
let lastReconnectAt = 0;
|
|
8743
|
+
let heartbeatCycle = 0;
|
|
8702
8744
|
const heartbeatInterval = setInterval(async () => {
|
|
8703
8745
|
if (heartbeatRunning) return;
|
|
8704
8746
|
heartbeatRunning = true;
|
|
8747
|
+
heartbeatCycle++;
|
|
8705
8748
|
try {
|
|
8706
8749
|
try {
|
|
8707
8750
|
const state = readDaemonStateFile();
|
|
@@ -8731,6 +8774,22 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8731
8774
|
}
|
|
8732
8775
|
}
|
|
8733
8776
|
}
|
|
8777
|
+
const INBOUND_SILENCE_THRESHOLD_MS = 12e4;
|
|
8778
|
+
const inboundSilenceMs = Date.now() - machineService.getLastInboundRpcAt();
|
|
8779
|
+
const hasActiveSessions = pidToTrackedSession.size > 0;
|
|
8780
|
+
if (hasActiveSessions && inboundSilenceMs > INBOUND_SILENCE_THRESHOLD_MS && consecutiveHeartbeatFailures === 0) {
|
|
8781
|
+
logger.log(`No inbound RPC for ${Math.round(inboundSilenceMs / 1e3)}s with ${pidToTrackedSession.size} active session(s) \u2014 zombie probe`);
|
|
8782
|
+
try {
|
|
8783
|
+
const machineServiceId = `${server.config.client_id}:default`;
|
|
8784
|
+
await Promise.race([
|
|
8785
|
+
server.getService(machineServiceId),
|
|
8786
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Zombie probe timed out")), PING_TIMEOUT_MS))
|
|
8787
|
+
]);
|
|
8788
|
+
} catch (err) {
|
|
8789
|
+
logger.log(`Zombie detection probe failed: ${err.message} \u2014 forcing reconnection`);
|
|
8790
|
+
consecutiveHeartbeatFailures = 2;
|
|
8791
|
+
}
|
|
8792
|
+
}
|
|
8734
8793
|
const inGrace = lastReconnectAt > 0 && Date.now() - lastReconnectAt < POST_RECONNECT_GRACE_MS;
|
|
8735
8794
|
if (!inGrace) {
|
|
8736
8795
|
try {
|
|
@@ -8756,7 +8815,7 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8756
8815
|
} else if (consecutiveHeartbeatFailures % 6 === 0) {
|
|
8757
8816
|
logger.log(`Connection down for ${consecutiveHeartbeatFailures * HEARTBEAT_INTERVAL_MS / 1e3}s (${consecutiveHeartbeatFailures} failures, retrying indefinitely)`);
|
|
8758
8817
|
}
|
|
8759
|
-
if (consecutiveHeartbeatFailures ===
|
|
8818
|
+
if (consecutiveHeartbeatFailures === 2 || consecutiveHeartbeatFailures % 3 === 0) {
|
|
8760
8819
|
const conn = server.rpc?._connection;
|
|
8761
8820
|
const ws = conn?._websocket;
|
|
8762
8821
|
if (ws?.readyState === 1) {
|
|
@@ -8782,6 +8841,22 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8782
8841
|
}
|
|
8783
8842
|
}
|
|
8784
8843
|
}
|
|
8844
|
+
const FRPC_FAILING_THRESHOLD_MS = 5 * 6e4;
|
|
8845
|
+
const serveHealth = serveManager.getTunnelHealth();
|
|
8846
|
+
if (serveHealth && serveHealth.failingDurationMs > FRPC_FAILING_THRESHOLD_MS) {
|
|
8847
|
+
logger.log(`Serve manager tunnel failing for ${Math.round(serveHealth.failingDurationMs / 1e3)}s (${serveHealth.consecutiveErrors} errors, ${serveHealth.restartAttempts} restarts) \u2014 recreating`);
|
|
8848
|
+
serveManager.recreateTunnel().catch((err) => {
|
|
8849
|
+
logger.log(`Failed to recreate serve tunnel: ${err.message}`);
|
|
8850
|
+
});
|
|
8851
|
+
}
|
|
8852
|
+
for (const [name, tunnel] of tunnels) {
|
|
8853
|
+
const health = tunnel.status;
|
|
8854
|
+
if (health.failingDurationMs > FRPC_FAILING_THRESHOLD_MS) {
|
|
8855
|
+
logger.log(`frpc tunnel '${name}' failing for ${Math.round(health.failingDurationMs / 1e3)}s \u2014 destroying stale tunnel`);
|
|
8856
|
+
tunnel.destroy();
|
|
8857
|
+
tunnels.delete(name);
|
|
8858
|
+
}
|
|
8859
|
+
}
|
|
8785
8860
|
} finally {
|
|
8786
8861
|
heartbeatRunning = false;
|
|
8787
8862
|
}
|
|
@@ -8817,6 +8892,7 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8817
8892
|
shutdownSource: source
|
|
8818
8893
|
});
|
|
8819
8894
|
await new Promise((r) => setTimeout(r, 200));
|
|
8895
|
+
const shouldMarkStopped = source === "os-signal-cleanup";
|
|
8820
8896
|
for (const [, session] of pidToTrackedSession) {
|
|
8821
8897
|
session.hyphaService?.disconnect().catch(() => {
|
|
8822
8898
|
});
|
|
@@ -8826,11 +8902,12 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8826
8902
|
} catch {
|
|
8827
8903
|
}
|
|
8828
8904
|
}
|
|
8829
|
-
|
|
8830
|
-
|
|
8905
|
+
if (shouldMarkStopped) {
|
|
8906
|
+
session.cleanupCredentials?.().catch(() => {
|
|
8907
|
+
});
|
|
8908
|
+
}
|
|
8831
8909
|
session.cleanupSvampConfig?.();
|
|
8832
8910
|
}
|
|
8833
|
-
const shouldMarkStopped = source === "os-signal-cleanup";
|
|
8834
8911
|
if (shouldMarkStopped) {
|
|
8835
8912
|
try {
|
|
8836
8913
|
const index = loadSessionIndex();
|
|
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { join, resolve } from 'node:path';
|
|
4
4
|
import { mkdirSync, writeFileSync, existsSync, unlinkSync, readFileSync, watch } from 'node:fs';
|
|
5
|
-
import { c as connectToHypha, a as registerSessionService } from './run-
|
|
5
|
+
import { c as connectToHypha, a as registerSessionService } from './run-BTlUAfV3.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
@@ -52,7 +52,7 @@ async function handleServeCommand() {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
async function serveAdd(args, machineId) {
|
|
55
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
55
|
+
const { connectAndGetMachine } = await import('./commands-DRpGTxYD.mjs');
|
|
56
56
|
const pos = positionalArgs(args);
|
|
57
57
|
const name = pos[0];
|
|
58
58
|
if (!name) {
|
|
@@ -84,7 +84,7 @@ async function serveAdd(args, machineId) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
async function serveRemove(args, machineId) {
|
|
87
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
87
|
+
const { connectAndGetMachine } = await import('./commands-DRpGTxYD.mjs');
|
|
88
88
|
const pos = positionalArgs(args);
|
|
89
89
|
const name = pos[0];
|
|
90
90
|
if (!name) {
|
|
@@ -104,7 +104,7 @@ async function serveRemove(args, machineId) {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
async function serveList(args, machineId) {
|
|
107
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
107
|
+
const { connectAndGetMachine } = await import('./commands-DRpGTxYD.mjs');
|
|
108
108
|
const all = hasFlag(args, "--all", "-a");
|
|
109
109
|
const json = hasFlag(args, "--json");
|
|
110
110
|
const sessionId = getFlag(args, "--session");
|
|
@@ -137,7 +137,7 @@ async function serveList(args, machineId) {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
async function serveInfo(machineId) {
|
|
140
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
140
|
+
const { connectAndGetMachine } = await import('./commands-DRpGTxYD.mjs');
|
|
141
141
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
142
142
|
try {
|
|
143
143
|
const info = await machine.serveInfo();
|
|
@@ -530,10 +530,23 @@ button{padding:10px 24px;background:#0969da;color:#fff;border:none;border-radius
|
|
|
530
530
|
server.on("error", reject);
|
|
531
531
|
});
|
|
532
532
|
}
|
|
533
|
+
/** Get frpc tunnel health status, or null if no tunnel. */
|
|
534
|
+
getTunnelHealth() {
|
|
535
|
+
return this.frpcTunnel?.status ?? null;
|
|
536
|
+
}
|
|
537
|
+
/** Destroy and recreate the frpc tunnel with fresh config. */
|
|
538
|
+
async recreateTunnel() {
|
|
539
|
+
if (this.frpcTunnel) {
|
|
540
|
+
this.log("Recreating frpc tunnel (persistent failure detected)...");
|
|
541
|
+
this.frpcTunnel.destroy();
|
|
542
|
+
this.frpcTunnel = null;
|
|
543
|
+
}
|
|
544
|
+
await this.ensureTunnel();
|
|
545
|
+
}
|
|
533
546
|
/** Start frpc tunnel for the Caddy port. */
|
|
534
547
|
async ensureTunnel() {
|
|
535
548
|
try {
|
|
536
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
549
|
+
const { FrpcTunnel } = await import('./frpc-BhS2e6r-.mjs');
|
|
537
550
|
this.frpcTunnel = new FrpcTunnel({
|
|
538
551
|
name: this.serviceName,
|
|
539
552
|
ports: [this.port],
|