svamp-cli 0.2.36 → 0.2.38
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-oLVbSvBZ.mjs → agentCommands-Djr_xWX-.mjs} +2 -2
- package/dist/cli.mjs +50 -39
- package/dist/{commands-Cik0LIAl.mjs → commands-B8Q1ig7j.mjs} +1 -1
- package/dist/{commands-B4qmyXeM.mjs → commands-BpW-hioi.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/package-CcU8sNV_.mjs +63 -0
- package/dist/{run-sAfJWYld.mjs → run-BStlzTCG.mjs} +1 -1
- package/dist/{run-BKBv5P9v.mjs → run-D-1qvfLH.mjs} +48 -15
- package/dist/{serveCommands-Cgq6Vif4.mjs → serveCommands-B2vQJyUt.mjs} +4 -4
- package/dist/{serveManager-XPnOLqQ4.mjs → serveManager-MqY_0frw.mjs} +137 -53
- package/dist/supervisorLock-DwNAn0VN.mjs +157 -0
- package/package.json +2 -2
- package/dist/package-DDUrcHYU.mjs +0 -63
|
@@ -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-B8Q1ig7j.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-B8Q1ig7j.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-D-1qvfLH.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-D-1qvfLH.mjs').then(function (n) { return n.k; });
|
|
40
40
|
await restartDaemon();
|
|
41
41
|
process.exit(0);
|
|
42
42
|
}
|
|
@@ -82,10 +82,11 @@ async function main() {
|
|
|
82
82
|
process.exit(0);
|
|
83
83
|
} else if (daemonSubcommand === "start-supervised") {
|
|
84
84
|
const { spawn: spawnChild } = await import('child_process');
|
|
85
|
-
const { appendFileSync, mkdirSync
|
|
85
|
+
const { appendFileSync, mkdirSync } = await import('fs');
|
|
86
86
|
const { join: pathJoin } = await import('path');
|
|
87
87
|
const osModule = await import('os');
|
|
88
88
|
const svampHome = process.env.SVAMP_HOME || pathJoin(osModule.homedir(), ".svamp");
|
|
89
|
+
mkdirSync(svampHome, { recursive: true });
|
|
89
90
|
const logsDir = pathJoin(svampHome, "logs");
|
|
90
91
|
mkdirSync(logsDir, { recursive: true });
|
|
91
92
|
const logFile = pathJoin(logsDir, "daemon-supervised.log");
|
|
@@ -97,13 +98,27 @@ async function main() {
|
|
|
97
98
|
} catch {
|
|
98
99
|
}
|
|
99
100
|
};
|
|
101
|
+
const {
|
|
102
|
+
acquireSupervisorLockWithRetry,
|
|
103
|
+
releaseSupervisorLock,
|
|
104
|
+
findOrphanedSyncPids,
|
|
105
|
+
killOrphanedSyncs
|
|
106
|
+
} = await import('./supervisorLock-DwNAn0VN.mjs');
|
|
100
107
|
const supervisorPidFile = pathJoin(svampHome, "supervisor.pid");
|
|
108
|
+
const lock = acquireSupervisorLockWithRetry(supervisorPidFile, process.pid);
|
|
109
|
+
if (!lock.acquired) {
|
|
110
|
+
log(`Another supervisor is already running (PID ${lock.heldBy}) \u2014 exiting`);
|
|
111
|
+
console.error(`svamp daemon: another supervisor is already running (PID ${lock.heldBy}).`);
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
101
114
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
const orphans = findOrphanedSyncPids(process.pid);
|
|
116
|
+
if (orphans.length > 0) {
|
|
117
|
+
await killOrphanedSyncs(orphans, { log, gracePeriodMs: 3e3 });
|
|
118
|
+
}
|
|
119
|
+
} catch (err) {
|
|
120
|
+
log(`Orphan scan failed (non-fatal): ${err?.message || err}`);
|
|
104
121
|
}
|
|
105
|
-
const { writeFileSync: wfs } = await import('fs');
|
|
106
|
-
wfs(supervisorPidFile, String(process.pid), "utf-8");
|
|
107
122
|
const extraSyncArgs = [];
|
|
108
123
|
if (args.includes("--no-auto-continue")) extraSyncArgs.push("--no-auto-continue");
|
|
109
124
|
const BASE_DELAY_MS = 2e3;
|
|
@@ -188,11 +203,7 @@ async function main() {
|
|
|
188
203
|
setTimeout(() => clearInterval(checkStop), delay + 100);
|
|
189
204
|
});
|
|
190
205
|
}
|
|
191
|
-
|
|
192
|
-
const { unlinkSync: us } = await import('fs');
|
|
193
|
-
us(supervisorPidFile);
|
|
194
|
-
} catch {
|
|
195
|
-
}
|
|
206
|
+
releaseSupervisorLock(supervisorPidFile, process.pid);
|
|
196
207
|
process.exit(0);
|
|
197
208
|
} else if (daemonSubcommand === "start-sync") {
|
|
198
209
|
const noAutoContinue = args.includes("--no-auto-continue");
|
|
@@ -246,7 +257,7 @@ async function main() {
|
|
|
246
257
|
console.error("svamp serve: Serve commands are not available in sandboxed sessions.");
|
|
247
258
|
process.exit(1);
|
|
248
259
|
}
|
|
249
|
-
const { handleServeCommand } = await import('./serveCommands-
|
|
260
|
+
const { handleServeCommand } = await import('./serveCommands-B2vQJyUt.mjs');
|
|
250
261
|
await handleServeCommand();
|
|
251
262
|
process.exit(0);
|
|
252
263
|
} else if (subcommand === "process" || subcommand === "proc") {
|
|
@@ -255,7 +266,7 @@ async function main() {
|
|
|
255
266
|
console.error("svamp process: Process commands are not available in sandboxed sessions.");
|
|
256
267
|
process.exit(1);
|
|
257
268
|
}
|
|
258
|
-
const { processCommand } = await import('./commands-
|
|
269
|
+
const { processCommand } = await import('./commands-BpW-hioi.mjs');
|
|
259
270
|
let machineId;
|
|
260
271
|
const processArgs = args.slice(1);
|
|
261
272
|
const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
|
|
@@ -273,7 +284,7 @@ async function main() {
|
|
|
273
284
|
} else if (!subcommand || subcommand === "start") {
|
|
274
285
|
await handleInteractiveCommand();
|
|
275
286
|
} else if (subcommand === "--version" || subcommand === "-v") {
|
|
276
|
-
const pkg = await import('./package-
|
|
287
|
+
const pkg = await import('./package-CcU8sNV_.mjs').catch(() => ({ default: { version: "unknown" } }));
|
|
277
288
|
console.log(`svamp version: ${pkg.default.version}`);
|
|
278
289
|
} else {
|
|
279
290
|
console.error(`Unknown command: ${subcommand}`);
|
|
@@ -282,7 +293,7 @@ async function main() {
|
|
|
282
293
|
}
|
|
283
294
|
}
|
|
284
295
|
async function handleInteractiveCommand() {
|
|
285
|
-
const { runInteractive } = await import('./run-
|
|
296
|
+
const { runInteractive } = await import('./run-BStlzTCG.mjs');
|
|
286
297
|
const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
|
|
287
298
|
let directory = process.cwd();
|
|
288
299
|
let resumeSessionId;
|
|
@@ -327,7 +338,7 @@ async function handleAgentCommand() {
|
|
|
327
338
|
return;
|
|
328
339
|
}
|
|
329
340
|
if (agentArgs[0] === "list") {
|
|
330
|
-
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-
|
|
341
|
+
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-D-1qvfLH.mjs').then(function (n) { return n.i; });
|
|
331
342
|
console.log("Known agents:");
|
|
332
343
|
for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
|
|
333
344
|
console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
|
|
@@ -339,7 +350,7 @@ async function handleAgentCommand() {
|
|
|
339
350
|
console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
|
|
340
351
|
return;
|
|
341
352
|
}
|
|
342
|
-
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-
|
|
353
|
+
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-D-1qvfLH.mjs').then(function (n) { return n.i; });
|
|
343
354
|
let cwd = process.cwd();
|
|
344
355
|
const filteredArgs = [];
|
|
345
356
|
for (let i = 0; i < agentArgs.length; i++) {
|
|
@@ -363,12 +374,12 @@ async function handleAgentCommand() {
|
|
|
363
374
|
console.log(`Starting ${config.agentName} agent in ${cwd}...`);
|
|
364
375
|
let backend;
|
|
365
376
|
if (KNOWN_MCP_AGENTS[config.agentName]) {
|
|
366
|
-
const { CodexMcpBackend } = await import('./run-
|
|
377
|
+
const { CodexMcpBackend } = await import('./run-D-1qvfLH.mjs').then(function (n) { return n.j; });
|
|
367
378
|
backend = new CodexMcpBackend({ cwd, log: logFn });
|
|
368
379
|
} else {
|
|
369
|
-
const { AcpBackend } = await import('./run-
|
|
370
|
-
const { GeminiTransport } = await import('./run-
|
|
371
|
-
const { DefaultTransport } = await import('./run-
|
|
380
|
+
const { AcpBackend } = await import('./run-D-1qvfLH.mjs').then(function (n) { return n.h; });
|
|
381
|
+
const { GeminiTransport } = await import('./run-D-1qvfLH.mjs').then(function (n) { return n.G; });
|
|
382
|
+
const { DefaultTransport } = await import('./run-D-1qvfLH.mjs').then(function (n) { return n.D; });
|
|
372
383
|
const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
|
|
373
384
|
backend = new AcpBackend({
|
|
374
385
|
agentName: config.agentName,
|
|
@@ -495,7 +506,7 @@ async function handleSessionCommand() {
|
|
|
495
506
|
process.exit(1);
|
|
496
507
|
}
|
|
497
508
|
}
|
|
498
|
-
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-
|
|
509
|
+
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-B8Q1ig7j.mjs');
|
|
499
510
|
const parseFlagStr = (flag, shortFlag) => {
|
|
500
511
|
for (let i = 1; i < sessionArgs.length; i++) {
|
|
501
512
|
if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
|
|
@@ -555,7 +566,7 @@ async function handleSessionCommand() {
|
|
|
555
566
|
allowDomain.push(sessionArgs[++i]);
|
|
556
567
|
}
|
|
557
568
|
}
|
|
558
|
-
const { parseShareArg } = await import('./commands-
|
|
569
|
+
const { parseShareArg } = await import('./commands-B8Q1ig7j.mjs');
|
|
559
570
|
const shareEntries = share.map((s) => parseShareArg(s));
|
|
560
571
|
await sessionSpawn(agent, dir, targetMachineId, {
|
|
561
572
|
message,
|
|
@@ -641,7 +652,7 @@ async function handleSessionCommand() {
|
|
|
641
652
|
console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
|
|
642
653
|
process.exit(1);
|
|
643
654
|
}
|
|
644
|
-
const { sessionApprove } = await import('./commands-
|
|
655
|
+
const { sessionApprove } = await import('./commands-B8Q1ig7j.mjs');
|
|
645
656
|
const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
646
657
|
await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
|
|
647
658
|
json: hasFlag("--json")
|
|
@@ -651,7 +662,7 @@ async function handleSessionCommand() {
|
|
|
651
662
|
console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
|
|
652
663
|
process.exit(1);
|
|
653
664
|
}
|
|
654
|
-
const { sessionDeny } = await import('./commands-
|
|
665
|
+
const { sessionDeny } = await import('./commands-B8Q1ig7j.mjs');
|
|
655
666
|
const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
656
667
|
await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
|
|
657
668
|
json: hasFlag("--json")
|
|
@@ -687,7 +698,7 @@ async function handleSessionCommand() {
|
|
|
687
698
|
console.error("Usage: svamp session set-title <title>");
|
|
688
699
|
process.exit(1);
|
|
689
700
|
}
|
|
690
|
-
const { sessionSetTitle } = await import('./agentCommands-
|
|
701
|
+
const { sessionSetTitle } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
691
702
|
await sessionSetTitle(title);
|
|
692
703
|
} else if (sessionSubcommand === "set-link") {
|
|
693
704
|
const url = sessionArgs[1];
|
|
@@ -696,7 +707,7 @@ async function handleSessionCommand() {
|
|
|
696
707
|
process.exit(1);
|
|
697
708
|
}
|
|
698
709
|
const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
699
|
-
const { sessionSetLink } = await import('./agentCommands-
|
|
710
|
+
const { sessionSetLink } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
700
711
|
await sessionSetLink(url, label);
|
|
701
712
|
} else if (sessionSubcommand === "notify") {
|
|
702
713
|
const message = sessionArgs[1];
|
|
@@ -705,7 +716,7 @@ async function handleSessionCommand() {
|
|
|
705
716
|
process.exit(1);
|
|
706
717
|
}
|
|
707
718
|
const level = parseFlagStr("--level") || "info";
|
|
708
|
-
const { sessionNotify } = await import('./agentCommands-
|
|
719
|
+
const { sessionNotify } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
709
720
|
await sessionNotify(message, level);
|
|
710
721
|
} else if (sessionSubcommand === "broadcast") {
|
|
711
722
|
const action = sessionArgs[1];
|
|
@@ -713,7 +724,7 @@ async function handleSessionCommand() {
|
|
|
713
724
|
console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
|
|
714
725
|
process.exit(1);
|
|
715
726
|
}
|
|
716
|
-
const { sessionBroadcast } = await import('./agentCommands-
|
|
727
|
+
const { sessionBroadcast } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
717
728
|
await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
|
|
718
729
|
} else if (sessionSubcommand === "inbox") {
|
|
719
730
|
const inboxSubcmd = sessionArgs[1];
|
|
@@ -724,7 +735,7 @@ async function handleSessionCommand() {
|
|
|
724
735
|
process.exit(1);
|
|
725
736
|
}
|
|
726
737
|
if (agentSessionId) {
|
|
727
|
-
const { inboxSend } = await import('./agentCommands-
|
|
738
|
+
const { inboxSend } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
728
739
|
await inboxSend(sessionArgs[2], {
|
|
729
740
|
body: sessionArgs[3],
|
|
730
741
|
subject: parseFlagStr("--subject"),
|
|
@@ -739,7 +750,7 @@ async function handleSessionCommand() {
|
|
|
739
750
|
}
|
|
740
751
|
} else if (inboxSubcmd === "list" || inboxSubcmd === "ls") {
|
|
741
752
|
if (agentSessionId && !sessionArgs[2]) {
|
|
742
|
-
const { inboxList } = await import('./agentCommands-
|
|
753
|
+
const { inboxList } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
743
754
|
await inboxList({
|
|
744
755
|
unread: hasFlag("--unread"),
|
|
745
756
|
limit: parseFlagInt("--limit"),
|
|
@@ -761,7 +772,7 @@ async function handleSessionCommand() {
|
|
|
761
772
|
process.exit(1);
|
|
762
773
|
}
|
|
763
774
|
if (agentSessionId && !sessionArgs[3]) {
|
|
764
|
-
const { inboxList } = await import('./agentCommands-
|
|
775
|
+
const { inboxList } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
765
776
|
await sessionInboxRead(agentSessionId, sessionArgs[2], targetMachineId);
|
|
766
777
|
} else if (sessionArgs[3]) {
|
|
767
778
|
await sessionInboxRead(sessionArgs[2], sessionArgs[3], targetMachineId);
|
|
@@ -771,7 +782,7 @@ async function handleSessionCommand() {
|
|
|
771
782
|
}
|
|
772
783
|
} else if (inboxSubcmd === "reply") {
|
|
773
784
|
if (agentSessionId && sessionArgs[2] && sessionArgs[3] && !sessionArgs[4]) {
|
|
774
|
-
const { inboxReply } = await import('./agentCommands-
|
|
785
|
+
const { inboxReply } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
775
786
|
await inboxReply(sessionArgs[2], sessionArgs[3]);
|
|
776
787
|
} else if (sessionArgs[2] && sessionArgs[3] && sessionArgs[4]) {
|
|
777
788
|
await sessionInboxReply(sessionArgs[2], sessionArgs[3], sessionArgs[4], targetMachineId);
|
|
@@ -807,7 +818,7 @@ async function handleMachineCommand() {
|
|
|
807
818
|
return;
|
|
808
819
|
}
|
|
809
820
|
if (machineSubcommand === "share") {
|
|
810
|
-
const { machineShare } = await import('./commands-
|
|
821
|
+
const { machineShare } = await import('./commands-B8Q1ig7j.mjs');
|
|
811
822
|
let machineId;
|
|
812
823
|
const shareArgs = [];
|
|
813
824
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
@@ -837,7 +848,7 @@ async function handleMachineCommand() {
|
|
|
837
848
|
}
|
|
838
849
|
await machineShare(machineId, { add, remove, list, configPath, showConfig });
|
|
839
850
|
} else if (machineSubcommand === "exec") {
|
|
840
|
-
const { machineExec } = await import('./commands-
|
|
851
|
+
const { machineExec } = await import('./commands-B8Q1ig7j.mjs');
|
|
841
852
|
let machineId;
|
|
842
853
|
let cwd;
|
|
843
854
|
const cmdParts = [];
|
|
@@ -857,7 +868,7 @@ async function handleMachineCommand() {
|
|
|
857
868
|
}
|
|
858
869
|
await machineExec(machineId, command, cwd);
|
|
859
870
|
} else if (machineSubcommand === "info") {
|
|
860
|
-
const { machineInfo } = await import('./commands-
|
|
871
|
+
const { machineInfo } = await import('./commands-B8Q1ig7j.mjs');
|
|
861
872
|
let machineId;
|
|
862
873
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
863
874
|
if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
|
|
@@ -877,10 +888,10 @@ async function handleMachineCommand() {
|
|
|
877
888
|
level = machineArgs[++i];
|
|
878
889
|
}
|
|
879
890
|
}
|
|
880
|
-
const { machineNotify } = await import('./agentCommands-
|
|
891
|
+
const { machineNotify } = await import('./agentCommands-Djr_xWX-.mjs');
|
|
881
892
|
await machineNotify(message, level);
|
|
882
893
|
} else if (machineSubcommand === "ls") {
|
|
883
|
-
const { machineLs } = await import('./commands-
|
|
894
|
+
const { machineLs } = await import('./commands-B8Q1ig7j.mjs');
|
|
884
895
|
let machineId;
|
|
885
896
|
let showHidden = false;
|
|
886
897
|
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-D-1qvfLH.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
|
@@ -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-B8Q1ig7j.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-D-1qvfLH.mjs';
|
|
9
9
|
import 'os';
|
|
10
10
|
import 'fs/promises';
|
|
11
11
|
import 'url';
|
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-D-1qvfLH.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var name = "svamp-cli";
|
|
2
|
+
var version = "0.2.38";
|
|
3
|
+
var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
|
|
4
|
+
var author = "Amun AI AB";
|
|
5
|
+
var license = "SEE LICENSE IN LICENSE";
|
|
6
|
+
var type = "module";
|
|
7
|
+
var bin = {
|
|
8
|
+
svamp: "./bin/svamp.mjs"
|
|
9
|
+
};
|
|
10
|
+
var files = [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin"
|
|
13
|
+
];
|
|
14
|
+
var main = "./dist/index.mjs";
|
|
15
|
+
var exports$1 = {
|
|
16
|
+
".": "./dist/index.mjs",
|
|
17
|
+
"./cli": "./dist/cli.mjs"
|
|
18
|
+
};
|
|
19
|
+
var scripts = {
|
|
20
|
+
build: "rm -rf dist && tsc --noEmit && pkgroll",
|
|
21
|
+
typecheck: "tsc --noEmit",
|
|
22
|
+
test: "npx tsx test/test-authorize.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-ralph-loop.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-output-formatters.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-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.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 && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.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",
|
|
23
|
+
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
24
|
+
dev: "tsx src/cli.ts",
|
|
25
|
+
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
26
|
+
"test:e2e": "node --no-warnings test/e2e-session-tests.mjs",
|
|
27
|
+
"test:frpc": "npx tsx test/test-frpc-e2e.mjs"
|
|
28
|
+
};
|
|
29
|
+
var dependencies = {
|
|
30
|
+
"@agentclientprotocol/sdk": "^0.14.1",
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
32
|
+
"hypha-rpc": "0.21.36",
|
|
33
|
+
"node-pty": "1.2.0-beta.11",
|
|
34
|
+
ws: "^8.18.0",
|
|
35
|
+
yaml: "^2.8.2",
|
|
36
|
+
zod: "^3.24.4"
|
|
37
|
+
};
|
|
38
|
+
var devDependencies = {
|
|
39
|
+
"@types/node": ">=20",
|
|
40
|
+
"@types/ws": "^8.5.14",
|
|
41
|
+
pkgroll: "^2.14.2",
|
|
42
|
+
tsx: "^4.20.6",
|
|
43
|
+
typescript: "5.9.3"
|
|
44
|
+
};
|
|
45
|
+
var packageManager = "yarn@1.22.22";
|
|
46
|
+
var _package = {
|
|
47
|
+
name: name,
|
|
48
|
+
version: version,
|
|
49
|
+
description: description,
|
|
50
|
+
author: author,
|
|
51
|
+
license: license,
|
|
52
|
+
type: type,
|
|
53
|
+
bin: bin,
|
|
54
|
+
files: files,
|
|
55
|
+
main: main,
|
|
56
|
+
exports: exports$1,
|
|
57
|
+
scripts: scripts,
|
|
58
|
+
dependencies: dependencies,
|
|
59
|
+
devDependencies: devDependencies,
|
|
60
|
+
packageManager: packageManager
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export { author, bin, _package as default, dependencies, description, devDependencies, exports$1 as exports, files, license, main, name, packageManager, scripts, type, version };
|
|
@@ -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-D-1qvfLH.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
@@ -4922,6 +4922,14 @@ var credentialStaging = /*#__PURE__*/Object.freeze({
|
|
|
4922
4922
|
stageCredentialsForSharing: stageCredentialsForSharing
|
|
4923
4923
|
});
|
|
4924
4924
|
|
|
4925
|
+
function shouldIsolate(input) {
|
|
4926
|
+
if (input.forceIsolation) return true;
|
|
4927
|
+
const sessionCtx = input.sessionSecurityContext;
|
|
4928
|
+
if (sessionCtx === null) return false;
|
|
4929
|
+
if (sessionCtx !== void 0) return true;
|
|
4930
|
+
return input.optionsSecurityContext !== null && input.optionsSecurityContext !== void 0;
|
|
4931
|
+
}
|
|
4932
|
+
|
|
4925
4933
|
const DEFAULT_PROBE_INTERVAL_S = 10;
|
|
4926
4934
|
const DEFAULT_PROBE_TIMEOUT_S = 5;
|
|
4927
4935
|
const DEFAULT_PROBE_FAILURE_THRESHOLD = 3;
|
|
@@ -6300,6 +6308,20 @@ async function startDaemon(options) {
|
|
|
6300
6308
|
process.on("SIGINT", () => requestShutdown("os-signal"));
|
|
6301
6309
|
process.on("SIGTERM", () => requestShutdown("os-signal"));
|
|
6302
6310
|
process.on("SIGUSR1", () => requestShutdown("os-signal-cleanup"));
|
|
6311
|
+
const { watchParentLiveness } = await import('./supervisorLock-DwNAn0VN.mjs');
|
|
6312
|
+
const cancelParentWatchdog = watchParentLiveness({
|
|
6313
|
+
intervalMs: 5e3,
|
|
6314
|
+
onParentDeath: () => {
|
|
6315
|
+
logger.log("Supervisor process died \u2014 sync daemon shutting down to avoid orphan state");
|
|
6316
|
+
requestShutdown("supervisor-died");
|
|
6317
|
+
}
|
|
6318
|
+
});
|
|
6319
|
+
process.on("exit", () => {
|
|
6320
|
+
try {
|
|
6321
|
+
cancelParentWatchdog();
|
|
6322
|
+
} catch {
|
|
6323
|
+
}
|
|
6324
|
+
});
|
|
6303
6325
|
process.on("uncaughtException", (error) => {
|
|
6304
6326
|
if (shutdownRequested) return;
|
|
6305
6327
|
logger.error("Uncaught exception (non-fatal, daemon continues):", error);
|
|
@@ -6440,7 +6462,7 @@ async function startDaemon(options) {
|
|
|
6440
6462
|
const supervisor = new ProcessSupervisor(join(SVAMP_HOME, "processes"));
|
|
6441
6463
|
await supervisor.init();
|
|
6442
6464
|
const tunnels = /* @__PURE__ */ new Map();
|
|
6443
|
-
const { ServeManager } = await import('./serveManager-
|
|
6465
|
+
const { ServeManager } = await import('./serveManager-MqY_0frw.mjs');
|
|
6444
6466
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
6445
6467
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
6446
6468
|
});
|
|
@@ -6585,8 +6607,7 @@ async function startDaemon(options) {
|
|
|
6585
6607
|
}
|
|
6586
6608
|
});
|
|
6587
6609
|
}, buildIsolationConfig2 = function(dir) {
|
|
6588
|
-
if (!
|
|
6589
|
-
if (sessionMetadata.securityContext === null) return null;
|
|
6610
|
+
if (!shouldIsolateSession()) return null;
|
|
6590
6611
|
const method = isolationCapabilities.preferred;
|
|
6591
6612
|
if (!method) return null;
|
|
6592
6613
|
const detail = isolationCapabilities.details[method];
|
|
@@ -6603,10 +6624,10 @@ async function startDaemon(options) {
|
|
|
6603
6624
|
trustOverride: true
|
|
6604
6625
|
};
|
|
6605
6626
|
}
|
|
6606
|
-
if (
|
|
6627
|
+
if (stagedCredentials) {
|
|
6607
6628
|
config.credentialStagingPath = stagedCredentials.homePath;
|
|
6608
6629
|
}
|
|
6609
|
-
const activeSecurityContext =
|
|
6630
|
+
const activeSecurityContext = getActiveSecurityContext();
|
|
6610
6631
|
if (activeSecurityContext) {
|
|
6611
6632
|
config = applySecurityContext(config, activeSecurityContext);
|
|
6612
6633
|
}
|
|
@@ -6743,6 +6764,12 @@ async function startDaemon(options) {
|
|
|
6743
6764
|
"auto-approve-all": "bypassPermissions"
|
|
6744
6765
|
};
|
|
6745
6766
|
const toClaudePermissionMode = (mode) => CLAUDE_PERMISSION_MODE_MAP[mode] || mode;
|
|
6767
|
+
const getActiveSecurityContext = () => sessionMetadata.securityContext ?? options2.securityContext;
|
|
6768
|
+
const shouldIsolateSession = () => shouldIsolate({
|
|
6769
|
+
forceIsolation: options2.forceIsolation,
|
|
6770
|
+
sessionSecurityContext: sessionMetadata.securityContext,
|
|
6771
|
+
optionsSecurityContext: options2.securityContext
|
|
6772
|
+
});
|
|
6746
6773
|
let isolationCleanupFiles = [];
|
|
6747
6774
|
const spawnClaude = (initialMessage, meta) => {
|
|
6748
6775
|
const effectiveMeta = { ...lastSpawnMeta, ...meta };
|
|
@@ -6786,7 +6813,7 @@ async function startDaemon(options) {
|
|
|
6786
6813
|
if (wrapped.cleanupFiles) isolationCleanupFiles = wrapped.cleanupFiles;
|
|
6787
6814
|
sessionMetadata = { ...sessionMetadata, isolationMethod: isoConfig.method };
|
|
6788
6815
|
logger.log(`[Session ${sessionId}] Isolation: ${isoConfig.method} (binary: ${isoConfig.binaryPath})`);
|
|
6789
|
-
} else if (
|
|
6816
|
+
} else if (shouldIsolateSession()) {
|
|
6790
6817
|
logger.log(`[Session ${sessionId}] WARNING: No isolation runtime (nono/docker/podman) available. Session is NOT sandboxed.`);
|
|
6791
6818
|
sessionMetadata = { ...sessionMetadata, isolationMethod: void 0 };
|
|
6792
6819
|
} else {
|
|
@@ -6797,18 +6824,15 @@ async function startDaemon(options) {
|
|
|
6797
6824
|
delete spawnEnv.CLAUDECODE;
|
|
6798
6825
|
spawnEnv.SVAMP_SESSION_ID = sessionId;
|
|
6799
6826
|
delete spawnEnv.SVAMP_SANDBOXED;
|
|
6800
|
-
if (
|
|
6827
|
+
if (isoConfig && stagedCredentials) {
|
|
6801
6828
|
Object.assign(spawnEnv, stagedCredentials.env);
|
|
6802
6829
|
const filtered = {};
|
|
6803
6830
|
for (const [k, v] of Object.entries(spawnEnv)) {
|
|
6804
6831
|
if (v !== void 0) filtered[k] = v;
|
|
6805
6832
|
}
|
|
6806
6833
|
spawnEnv = sanitizeEnvForSharing(filtered);
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
spawnEnv.SVAMP_SANDBOXED = "1";
|
|
6810
|
-
logger.log(`[Session ${sessionId}] Sandbox mode: ON (securityContext present)`);
|
|
6811
|
-
}
|
|
6834
|
+
spawnEnv.SVAMP_SANDBOXED = "1";
|
|
6835
|
+
logger.log(`[Session ${sessionId}] Credential staging: HOME=${stagedCredentials.homePath}, SVAMP_SANDBOXED=1`);
|
|
6812
6836
|
}
|
|
6813
6837
|
const child = spawn$1(spawnCommand, spawnArgs, {
|
|
6814
6838
|
cwd: directory,
|
|
@@ -7419,6 +7443,14 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
7419
7443
|
return { success: false, message: "Session was stopped during restart." };
|
|
7420
7444
|
}
|
|
7421
7445
|
if (claudeResumeId) {
|
|
7446
|
+
if (!stagedCredentials && shouldIsolateSession()) {
|
|
7447
|
+
try {
|
|
7448
|
+
stagedCredentials = await stageCredentialsForSharing(sessionId);
|
|
7449
|
+
logger.log(`[Session ${sessionId}] Credentials staged at ${stagedCredentials.homePath} (runtime enable)`);
|
|
7450
|
+
} catch (err) {
|
|
7451
|
+
logger.log(`[Session ${sessionId}] WARNING: Runtime credential staging failed: ${err.message}.`);
|
|
7452
|
+
}
|
|
7453
|
+
}
|
|
7422
7454
|
spawnClaude(void 0, { permissionMode: currentPermissionMode });
|
|
7423
7455
|
sessionService.updateMetadata(sessionMetadata);
|
|
7424
7456
|
logger.log(`[Session ${sessionId}] Claude respawned with --resume ${claudeResumeId}`);
|
|
@@ -7435,12 +7467,12 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
7435
7467
|
isRestartingClaude = false;
|
|
7436
7468
|
}
|
|
7437
7469
|
};
|
|
7438
|
-
if (
|
|
7470
|
+
if (shouldIsolateSession()) {
|
|
7439
7471
|
try {
|
|
7440
7472
|
stagedCredentials = await stageCredentialsForSharing(sessionId);
|
|
7441
7473
|
logger.log(`[Session ${sessionId}] Credentials staged at ${stagedCredentials.homePath}`);
|
|
7442
7474
|
} catch (err) {
|
|
7443
|
-
logger.log(`[Session ${sessionId}] WARNING: Credential staging failed: ${err.message}
|
|
7475
|
+
logger.log(`[Session ${sessionId}] WARNING: Credential staging failed: ${err.message}.`);
|
|
7444
7476
|
}
|
|
7445
7477
|
}
|
|
7446
7478
|
let processMessageQueueRef;
|
|
@@ -8357,7 +8389,8 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8357
8389
|
const writeSvampConfigPatchAcp = svampConfigChecker.writeConfig;
|
|
8358
8390
|
const permissionHandler = new HyphaPermissionHandler(shouldAutoAllow2, logger.log);
|
|
8359
8391
|
let agentIsoConfig;
|
|
8360
|
-
|
|
8392
|
+
const agentShouldIsolate = Boolean(options2.forceIsolation) || Boolean(options2.securityContext);
|
|
8393
|
+
if (agentShouldIsolate && isolationCapabilities.preferred) {
|
|
8361
8394
|
const method = isolationCapabilities.preferred;
|
|
8362
8395
|
const detail = isolationCapabilities.details[method];
|
|
8363
8396
|
if (detail.found && detail.verified !== false) {
|
|
@@ -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-B8Q1ig7j.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-B8Q1ig7j.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-B8Q1ig7j.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-B8Q1ig7j.mjs');
|
|
141
141
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
142
142
|
try {
|
|
143
143
|
const info = await machine.serveInfo();
|
|
@@ -258,13 +258,14 @@ class ServeManager {
|
|
|
258
258
|
// auth proxy public port
|
|
259
259
|
caddyPort = 0;
|
|
260
260
|
// Caddy internal port
|
|
261
|
-
|
|
261
|
+
/** Per-mount frpc tunnel — each mount gets its own subdomain `static-<name>-<hash>`. */
|
|
262
|
+
mountTunnels = /* @__PURE__ */ new Map();
|
|
263
|
+
/** hostname (lowercased, no port) → mount name. Updated when tunnels connect. */
|
|
264
|
+
hostToMount = /* @__PURE__ */ new Map();
|
|
262
265
|
caddy = null;
|
|
263
266
|
proxyServer = null;
|
|
264
|
-
serviceUrl = null;
|
|
265
267
|
auth = null;
|
|
266
268
|
persistFile;
|
|
267
|
-
serviceName = "static-serve";
|
|
268
269
|
log;
|
|
269
270
|
hyphaServerUrl;
|
|
270
271
|
constructor(svampHome, logger, hyphaServerUrl) {
|
|
@@ -300,19 +301,31 @@ class ServeManager {
|
|
|
300
301
|
if (this.caddy?.isRunning) {
|
|
301
302
|
await this.caddy.addMount(name, resolvedDir);
|
|
302
303
|
}
|
|
304
|
+
await this.startMountTunnel(name);
|
|
303
305
|
this.persist();
|
|
304
306
|
const url = this.getMountUrl(name);
|
|
305
|
-
this.log(`Mount added: ${name} \u2192 ${resolvedDir} (${url})`);
|
|
306
|
-
return { url, mount };
|
|
307
|
+
this.log(`Mount added: ${name} \u2192 ${resolvedDir} (${url ?? "tunnel pending"})`);
|
|
308
|
+
return { url: url || "", mount };
|
|
307
309
|
}
|
|
308
310
|
/**
|
|
309
|
-
* Remove a mount. If no mounts remain, stop Caddy +
|
|
311
|
+
* Remove a mount. If no mounts remain, stop Caddy + auth proxy.
|
|
310
312
|
*/
|
|
311
313
|
async removeMount(name) {
|
|
312
314
|
if (!this.mounts.has(name)) {
|
|
313
315
|
throw new Error(`Mount '${name}' not found`);
|
|
314
316
|
}
|
|
315
317
|
this.mounts.delete(name);
|
|
318
|
+
const tunnel = this.mountTunnels.get(name);
|
|
319
|
+
if (tunnel) {
|
|
320
|
+
try {
|
|
321
|
+
tunnel.destroy();
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
this.mountTunnels.delete(name);
|
|
325
|
+
}
|
|
326
|
+
for (const [host, mountName] of this.hostToMount.entries()) {
|
|
327
|
+
if (mountName === name) this.hostToMount.delete(host);
|
|
328
|
+
}
|
|
316
329
|
if (this.caddy?.isRunning) {
|
|
317
330
|
try {
|
|
318
331
|
await this.caddy.removeMount(name);
|
|
@@ -337,16 +350,21 @@ class ServeManager {
|
|
|
337
350
|
return all;
|
|
338
351
|
}
|
|
339
352
|
/**
|
|
340
|
-
* Get server info
|
|
353
|
+
* Get server info — each mount has its own URL (per-mount subdomain).
|
|
341
354
|
*/
|
|
342
355
|
getInfo() {
|
|
343
356
|
const running = this.caddy?.isRunning ?? false;
|
|
357
|
+
const firstMount = this.mounts.values().next().value;
|
|
358
|
+
const firstUrl = firstMount ? this.getMountUrl(firstMount.name) : null;
|
|
344
359
|
return {
|
|
345
|
-
url:
|
|
360
|
+
url: firstUrl,
|
|
346
361
|
port: running ? this.caddy?.port ?? this.port : 0,
|
|
347
362
|
running,
|
|
348
363
|
mountCount: this.mounts.size,
|
|
349
|
-
mounts: Array.from(this.mounts.values())
|
|
364
|
+
mounts: Array.from(this.mounts.values()).map((m) => ({
|
|
365
|
+
...m,
|
|
366
|
+
url: this.getMountUrl(m.name) || void 0
|
|
367
|
+
}))
|
|
350
368
|
};
|
|
351
369
|
}
|
|
352
370
|
/**
|
|
@@ -370,6 +388,13 @@ class ServeManager {
|
|
|
370
388
|
if (restoredCount > 0) {
|
|
371
389
|
this.log(`Restoring ${restoredCount} mount(s)...`);
|
|
372
390
|
await this.ensureRunning();
|
|
391
|
+
for (const m of this.mounts.values()) {
|
|
392
|
+
try {
|
|
393
|
+
await this.startMountTunnel(m.name);
|
|
394
|
+
} catch (err) {
|
|
395
|
+
this.log(`Failed to start tunnel for restored mount '${m.name}': ${err.message}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
373
398
|
this.persist();
|
|
374
399
|
}
|
|
375
400
|
} catch (err) {
|
|
@@ -377,14 +402,18 @@ class ServeManager {
|
|
|
377
402
|
}
|
|
378
403
|
}
|
|
379
404
|
/**
|
|
380
|
-
* Shut down auth proxy + Caddy + frpc
|
|
405
|
+
* Shut down auth proxy + Caddy + all per-mount frpc tunnels.
|
|
381
406
|
*/
|
|
382
407
|
async shutdown() {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
408
|
+
for (const [name, tunnel] of this.mountTunnels.entries()) {
|
|
409
|
+
try {
|
|
410
|
+
tunnel.destroy();
|
|
411
|
+
} catch {
|
|
412
|
+
}
|
|
413
|
+
this.log(`frpc tunnel for '${name}' stopped`);
|
|
387
414
|
}
|
|
415
|
+
this.mountTunnels.clear();
|
|
416
|
+
this.hostToMount.clear();
|
|
388
417
|
if (this.proxyServer) {
|
|
389
418
|
await new Promise((resolve) => this.proxyServer.close(() => resolve()));
|
|
390
419
|
this.proxyServer = null;
|
|
@@ -395,18 +424,19 @@ class ServeManager {
|
|
|
395
424
|
this.log("Caddy stopped");
|
|
396
425
|
}
|
|
397
426
|
this.auth?.destroy();
|
|
398
|
-
this.serviceUrl = null;
|
|
399
427
|
}
|
|
400
428
|
// ── Internal ─────────────────────────────────────────────────────────
|
|
429
|
+
/** Get the public URL for a mount (mount-specific subdomain). */
|
|
401
430
|
getMountUrl(name) {
|
|
402
|
-
const
|
|
403
|
-
|
|
431
|
+
const tunnel = this.mountTunnels.get(name);
|
|
432
|
+
const url = tunnel?.getUrls().get(this.port);
|
|
433
|
+
if (url) return `${url}/`;
|
|
434
|
+
if (this.port) return `http://127.0.0.1:${this.port}/${name}/`;
|
|
435
|
+
return null;
|
|
404
436
|
}
|
|
405
437
|
persist() {
|
|
406
438
|
const state = {
|
|
407
|
-
mounts: Array.from(this.mounts.values())
|
|
408
|
-
serviceName: this.serviceName,
|
|
409
|
-
serviceUrl: this.serviceUrl
|
|
439
|
+
mounts: Array.from(this.mounts.values())
|
|
410
440
|
};
|
|
411
441
|
try {
|
|
412
442
|
fs.writeFileSync(this.persistFile, JSON.stringify(state, null, 2));
|
|
@@ -414,7 +444,7 @@ class ServeManager {
|
|
|
414
444
|
this.log(`Error persisting serve state: ${err.message}`);
|
|
415
445
|
}
|
|
416
446
|
}
|
|
417
|
-
/** Start auth proxy + Caddy
|
|
447
|
+
/** Start auth proxy + Caddy if not already running. Per-mount tunnels are started separately. */
|
|
418
448
|
async ensureRunning() {
|
|
419
449
|
if (this.caddy?.isRunning) return;
|
|
420
450
|
this.caddyPort = await findFreePort();
|
|
@@ -434,14 +464,27 @@ class ServeManager {
|
|
|
434
464
|
this.log(`Caddy file server started on 127.0.0.1:${this.caddyPort}`);
|
|
435
465
|
await this.startAuthProxy();
|
|
436
466
|
this.log(`Auth proxy started on 127.0.0.1:${this.port}`);
|
|
437
|
-
await this.ensureTunnel();
|
|
438
467
|
}
|
|
439
468
|
/** Start a lightweight Node.js HTTP proxy with auth + upload support. */
|
|
440
469
|
startAuthProxy() {
|
|
441
470
|
return new Promise((resolve, reject) => {
|
|
442
471
|
const server = http.createServer(async (req, res) => {
|
|
443
472
|
const url = new URL(req.url || "/", `http://127.0.0.1:${this.port}`);
|
|
444
|
-
|
|
473
|
+
const incomingHost = (req.headers.host || "").split(":")[0].toLowerCase();
|
|
474
|
+
const hostMount = this.hostToMount.get(incomingHost);
|
|
475
|
+
let mountName;
|
|
476
|
+
let mountResolvedByHost = false;
|
|
477
|
+
let basePath;
|
|
478
|
+
if (hostMount && this.mounts.has(hostMount)) {
|
|
479
|
+
mountName = hostMount;
|
|
480
|
+
mountResolvedByHost = true;
|
|
481
|
+
basePath = url.pathname;
|
|
482
|
+
} else {
|
|
483
|
+
mountName = url.pathname.split("/").filter(Boolean)[0];
|
|
484
|
+
basePath = mountName ? url.pathname.slice(`/${mountName}`.length) || "/" : url.pathname;
|
|
485
|
+
}
|
|
486
|
+
const mount = mountName ? this.mounts.get(mountName) : void 0;
|
|
487
|
+
if (basePath === "/__login__" || url.pathname === "/__login__") {
|
|
445
488
|
const returnUrl = url.searchParams.get("return") || "/";
|
|
446
489
|
const safeReturn = returnUrl.startsWith("/__login__") ? "/" : returnUrl;
|
|
447
490
|
const html = this.auth ? this.auth.getLoginPageHtml(safeReturn) : "<h1>Auth not configured</h1>";
|
|
@@ -452,8 +495,6 @@ class ServeManager {
|
|
|
452
495
|
res.end(html);
|
|
453
496
|
return;
|
|
454
497
|
}
|
|
455
|
-
const mountName = url.pathname.split("/").filter(Boolean)[0];
|
|
456
|
-
const mount = mountName ? this.mounts.get(mountName) : void 0;
|
|
457
498
|
if (mount && mount.access !== "public") {
|
|
458
499
|
const userEmail = this.auth ? await this.auth.authenticate(req).catch(() => null) : null;
|
|
459
500
|
const allowed = this.auth ? this.auth.isAuthorized(userEmail, mount.access, mount.ownerEmail) : false;
|
|
@@ -469,8 +510,7 @@ class ServeManager {
|
|
|
469
510
|
}
|
|
470
511
|
}
|
|
471
512
|
if (req.method === "PUT" && mount) {
|
|
472
|
-
const
|
|
473
|
-
const filePath = path.join(mount.directory, subPath);
|
|
513
|
+
const filePath = path.join(mount.directory, basePath);
|
|
474
514
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
475
515
|
const ws = fs.createWriteStream(filePath);
|
|
476
516
|
req.pipe(ws);
|
|
@@ -485,8 +525,7 @@ class ServeManager {
|
|
|
485
525
|
return;
|
|
486
526
|
}
|
|
487
527
|
if (req.method === "DELETE" && mount) {
|
|
488
|
-
const
|
|
489
|
-
const filePath = path.join(mount.directory, subPath);
|
|
528
|
+
const filePath = path.join(mount.directory, basePath);
|
|
490
529
|
try {
|
|
491
530
|
fs.unlinkSync(filePath);
|
|
492
531
|
res.writeHead(204);
|
|
@@ -497,10 +536,15 @@ class ServeManager {
|
|
|
497
536
|
}
|
|
498
537
|
return;
|
|
499
538
|
}
|
|
539
|
+
let proxyPath = req.url || "/";
|
|
540
|
+
if (mountResolvedByHost && mountName) {
|
|
541
|
+
const search = url.search || "";
|
|
542
|
+
proxyPath = `/${mountName}${basePath === "/" ? "/" : basePath}${search}`;
|
|
543
|
+
}
|
|
500
544
|
const proxyReq = http.request({
|
|
501
545
|
hostname: "127.0.0.1",
|
|
502
546
|
port: this.caddyPort,
|
|
503
|
-
path:
|
|
547
|
+
path: proxyPath,
|
|
504
548
|
method: req.method,
|
|
505
549
|
headers: req.headers
|
|
506
550
|
}, (proxyRes) => {
|
|
@@ -520,42 +564,82 @@ class ServeManager {
|
|
|
520
564
|
server.on("error", reject);
|
|
521
565
|
});
|
|
522
566
|
}
|
|
523
|
-
/** Get
|
|
567
|
+
/** Get aggregate health: returns the status of the worst-failing per-mount tunnel. */
|
|
524
568
|
getTunnelHealth() {
|
|
525
|
-
|
|
569
|
+
if (this.mountTunnels.size === 0) return null;
|
|
570
|
+
let worst = null;
|
|
571
|
+
for (const t of this.mountTunnels.values()) {
|
|
572
|
+
const s = t.status;
|
|
573
|
+
if (worst === null) {
|
|
574
|
+
worst = s;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (s.failingDurationMs > worst.failingDurationMs) worst = s;
|
|
578
|
+
}
|
|
579
|
+
return worst;
|
|
526
580
|
}
|
|
527
|
-
/** Destroy and recreate
|
|
581
|
+
/** Destroy and recreate all per-mount tunnels with fresh configs. */
|
|
528
582
|
async recreateTunnel() {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
583
|
+
this.log("Recreating all per-mount frpc tunnels (persistent failure detected)...");
|
|
584
|
+
const names = Array.from(this.mountTunnels.keys());
|
|
585
|
+
for (const name of names) {
|
|
586
|
+
const t = this.mountTunnels.get(name);
|
|
587
|
+
if (t) {
|
|
588
|
+
try {
|
|
589
|
+
t.destroy();
|
|
590
|
+
} catch {
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
this.mountTunnels.delete(name);
|
|
594
|
+
}
|
|
595
|
+
this.hostToMount.clear();
|
|
596
|
+
for (const name of names) {
|
|
597
|
+
try {
|
|
598
|
+
await this.startMountTunnel(name);
|
|
599
|
+
} catch (err) {
|
|
600
|
+
this.log(`Failed to restart tunnel for '${name}': ${err.message}`);
|
|
601
|
+
}
|
|
533
602
|
}
|
|
534
|
-
await this.ensureTunnel();
|
|
535
603
|
}
|
|
536
|
-
/** Start frpc tunnel
|
|
537
|
-
async
|
|
604
|
+
/** Start a per-mount frpc tunnel pointing the auth-proxy port to a dedicated subdomain. */
|
|
605
|
+
async startMountTunnel(mountName) {
|
|
606
|
+
if (this.mountTunnels.has(mountName)) return;
|
|
607
|
+
if (!this.port) throw new Error("Auth proxy not running \u2014 call ensureRunning() first");
|
|
608
|
+
const subdomainSafe = mountName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
609
|
+
const tunnelName = `static-${subdomainSafe}`;
|
|
538
610
|
try {
|
|
539
611
|
const { FrpcTunnel } = await import('./frpc-DzRFx60H.mjs');
|
|
540
|
-
|
|
541
|
-
name:
|
|
612
|
+
const tunnel = new FrpcTunnel({
|
|
613
|
+
name: tunnelName,
|
|
542
614
|
ports: [this.port],
|
|
543
|
-
onError: (err) => this.log(`frpc error: ${err.message}`),
|
|
615
|
+
onError: (err) => this.log(`frpc error [${mountName}]: ${err.message}`),
|
|
544
616
|
onConnect: () => {
|
|
545
|
-
const
|
|
546
|
-
if (
|
|
547
|
-
|
|
548
|
-
|
|
617
|
+
const url2 = tunnel.getUrls().get(this.port);
|
|
618
|
+
if (url2) {
|
|
619
|
+
try {
|
|
620
|
+
const host = new URL(url2).hostname.toLowerCase();
|
|
621
|
+
this.hostToMount.set(host, mountName);
|
|
622
|
+
} catch {
|
|
623
|
+
}
|
|
624
|
+
this.log(`frpc tunnel connected for '${mountName}'. URL: ${url2}/`);
|
|
549
625
|
}
|
|
550
626
|
},
|
|
551
|
-
onDisconnect: () => this.log(
|
|
627
|
+
onDisconnect: () => this.log(`frpc tunnel for '${mountName}' disconnected, will auto-reconnect...`)
|
|
552
628
|
});
|
|
553
|
-
await
|
|
554
|
-
this.
|
|
555
|
-
|
|
629
|
+
await tunnel.connect();
|
|
630
|
+
this.mountTunnels.set(mountName, tunnel);
|
|
631
|
+
const url = tunnel.getUrls().get(this.port);
|
|
632
|
+
if (url) {
|
|
633
|
+
try {
|
|
634
|
+
const host = new URL(url).hostname.toLowerCase();
|
|
635
|
+
this.hostToMount.set(host, mountName);
|
|
636
|
+
} catch {
|
|
637
|
+
}
|
|
638
|
+
this.log(`frpc tunnel started for '${mountName}'. URL: ${url}/`);
|
|
639
|
+
}
|
|
556
640
|
} catch (err) {
|
|
557
|
-
this.log(`Warning: could not expose
|
|
558
|
-
this.log(`
|
|
641
|
+
this.log(`Warning: could not expose mount '${mountName}' externally: ${err.message}`);
|
|
642
|
+
this.log(`Mount '${mountName}' available locally at http://127.0.0.1:${this.port}/${mountName}/`);
|
|
559
643
|
}
|
|
560
644
|
}
|
|
561
645
|
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync, openSync, writeSync, closeSync } from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
|
|
4
|
+
function acquireSupervisorLock(pidFile, pid = process.pid) {
|
|
5
|
+
try {
|
|
6
|
+
const fd = openSync(pidFile, "wx");
|
|
7
|
+
try {
|
|
8
|
+
writeSync(fd, String(pid));
|
|
9
|
+
} finally {
|
|
10
|
+
closeSync(fd);
|
|
11
|
+
}
|
|
12
|
+
return { acquired: true };
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (err?.code !== "EEXIST") throw err;
|
|
15
|
+
}
|
|
16
|
+
let existingPid = 0;
|
|
17
|
+
try {
|
|
18
|
+
existingPid = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
if (!existingPid || Number.isNaN(existingPid) || existingPid <= 0) {
|
|
22
|
+
try {
|
|
23
|
+
unlinkSync(pidFile);
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
return { acquired: false, staleCleaned: true };
|
|
27
|
+
}
|
|
28
|
+
if (isPidAlive(existingPid)) {
|
|
29
|
+
return { acquired: false, heldBy: existingPid };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
unlinkSync(pidFile);
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
return { acquired: false, staleCleaned: true };
|
|
36
|
+
}
|
|
37
|
+
function acquireSupervisorLockWithRetry(pidFile, pid = process.pid) {
|
|
38
|
+
let result = acquireSupervisorLock(pidFile, pid);
|
|
39
|
+
if (!result.acquired && result.staleCleaned) {
|
|
40
|
+
result = acquireSupervisorLock(pidFile, pid);
|
|
41
|
+
}
|
|
42
|
+
return { acquired: result.acquired, heldBy: result.heldBy };
|
|
43
|
+
}
|
|
44
|
+
function releaseSupervisorLock(pidFile, pid = process.pid) {
|
|
45
|
+
try {
|
|
46
|
+
if (!existsSync(pidFile)) return;
|
|
47
|
+
const content = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
|
|
48
|
+
if (content === pid) unlinkSync(pidFile);
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function isPidAlive(pid) {
|
|
53
|
+
if (!pid || Number.isNaN(pid) || pid <= 0) return false;
|
|
54
|
+
try {
|
|
55
|
+
process.kill(pid, 0);
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function findOrphanedSyncPids(currentSupervisorPid = process.pid) {
|
|
62
|
+
if (process.platform === "win32") return [];
|
|
63
|
+
const pids = [];
|
|
64
|
+
try {
|
|
65
|
+
const output = execFileSync("pgrep", ["-af", "svamp daemon start-sync"], {
|
|
66
|
+
encoding: "utf-8",
|
|
67
|
+
timeout: 5e3
|
|
68
|
+
});
|
|
69
|
+
for (const line of output.split("\n")) {
|
|
70
|
+
const trimmed = line.trim();
|
|
71
|
+
if (!trimmed) continue;
|
|
72
|
+
const match = trimmed.match(/^(\d+)\s/);
|
|
73
|
+
if (!match) continue;
|
|
74
|
+
const pid = parseInt(match[1], 10);
|
|
75
|
+
if (Number.isNaN(pid) || pid <= 0) continue;
|
|
76
|
+
if (pid === process.pid) continue;
|
|
77
|
+
const ppid = getPpid(pid);
|
|
78
|
+
if (ppid === currentSupervisorPid) continue;
|
|
79
|
+
pids.push(pid);
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if (err?.status !== 1) ;
|
|
83
|
+
}
|
|
84
|
+
return pids;
|
|
85
|
+
}
|
|
86
|
+
function getPpid(pid) {
|
|
87
|
+
if (process.platform === "win32") return 0;
|
|
88
|
+
try {
|
|
89
|
+
const out = execFileSync("ps", ["-o", "ppid=", "-p", String(pid)], {
|
|
90
|
+
encoding: "utf-8",
|
|
91
|
+
timeout: 2e3
|
|
92
|
+
}).trim();
|
|
93
|
+
const ppid = parseInt(out, 10);
|
|
94
|
+
return Number.isNaN(ppid) ? 0 : ppid;
|
|
95
|
+
} catch {
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function killOrphanedSyncs(pids, opts = {}) {
|
|
100
|
+
if (pids.length === 0) return;
|
|
101
|
+
const log = opts.log || (() => {
|
|
102
|
+
});
|
|
103
|
+
const gracePeriodMs = opts.gracePeriodMs ?? 3e3;
|
|
104
|
+
log(`Killing ${pids.length} orphan sync daemon(s): ${pids.join(", ")}`);
|
|
105
|
+
for (const pid of pids) {
|
|
106
|
+
try {
|
|
107
|
+
process.kill(pid, "SIGTERM");
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const pollStep = 100;
|
|
112
|
+
const steps = Math.max(1, Math.ceil(gracePeriodMs / pollStep));
|
|
113
|
+
for (let i = 0; i < steps; i++) {
|
|
114
|
+
await new Promise((r) => setTimeout(r, pollStep));
|
|
115
|
+
if (pids.every((p) => !isPidAlive(p))) {
|
|
116
|
+
log("All orphan daemons exited cleanly");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const survivors = pids.filter(isPidAlive);
|
|
121
|
+
if (survivors.length > 0) {
|
|
122
|
+
log(`Force-killing survivors: ${survivors.join(", ")}`);
|
|
123
|
+
for (const pid of survivors) {
|
|
124
|
+
try {
|
|
125
|
+
process.kill(pid, "SIGKILL");
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function watchParentLiveness(opts) {
|
|
132
|
+
if (process.env.SVAMP_SUPERVISED !== "1") {
|
|
133
|
+
return () => {
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const supervisorPid = process.ppid;
|
|
137
|
+
if (!supervisorPid || supervisorPid === 1) {
|
|
138
|
+
return () => {
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const intervalMs = opts.intervalMs ?? 5e3;
|
|
142
|
+
let fired = false;
|
|
143
|
+
const timer = setInterval(() => {
|
|
144
|
+
if (fired) return;
|
|
145
|
+
if (!isPidAlive(supervisorPid)) {
|
|
146
|
+
fired = true;
|
|
147
|
+
clearInterval(timer);
|
|
148
|
+
opts.onParentDeath();
|
|
149
|
+
}
|
|
150
|
+
}, intervalMs);
|
|
151
|
+
timer.unref?.();
|
|
152
|
+
return () => {
|
|
153
|
+
clearInterval(timer);
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { acquireSupervisorLock, acquireSupervisorLockWithRetry, findOrphanedSyncPids, getPpid, isPidAlive, killOrphanedSyncs, releaseSupervisorLock, watchParentLiveness };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svamp-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.38",
|
|
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 && tsc --noEmit && pkgroll",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
|
-
"test": "npx tsx test/test-authorize.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-ralph-loop.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-output-formatters.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-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.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-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.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",
|
|
23
|
+
"test": "npx tsx test/test-authorize.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-ralph-loop.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-output-formatters.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-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.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 && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.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",
|
|
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",
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
var name = "svamp-cli";
|
|
2
|
-
var version = "0.2.36";
|
|
3
|
-
var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
|
|
4
|
-
var author = "Amun AI AB";
|
|
5
|
-
var license = "SEE LICENSE IN LICENSE";
|
|
6
|
-
var type = "module";
|
|
7
|
-
var bin = {
|
|
8
|
-
svamp: "./bin/svamp.mjs"
|
|
9
|
-
};
|
|
10
|
-
var files = [
|
|
11
|
-
"dist",
|
|
12
|
-
"bin"
|
|
13
|
-
];
|
|
14
|
-
var main = "./dist/index.mjs";
|
|
15
|
-
var exports$1 = {
|
|
16
|
-
".": "./dist/index.mjs",
|
|
17
|
-
"./cli": "./dist/cli.mjs"
|
|
18
|
-
};
|
|
19
|
-
var scripts = {
|
|
20
|
-
build: "rm -rf dist && tsc --noEmit && pkgroll",
|
|
21
|
-
typecheck: "tsc --noEmit",
|
|
22
|
-
test: "npx tsx test/test-authorize.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-ralph-loop.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-output-formatters.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-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.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-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.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",
|
|
23
|
-
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
24
|
-
dev: "tsx src/cli.ts",
|
|
25
|
-
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
26
|
-
"test:e2e": "node --no-warnings test/e2e-session-tests.mjs",
|
|
27
|
-
"test:frpc": "npx tsx test/test-frpc-e2e.mjs"
|
|
28
|
-
};
|
|
29
|
-
var dependencies = {
|
|
30
|
-
"@agentclientprotocol/sdk": "^0.14.1",
|
|
31
|
-
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
32
|
-
"hypha-rpc": "0.21.36",
|
|
33
|
-
"node-pty": "1.2.0-beta.11",
|
|
34
|
-
ws: "^8.18.0",
|
|
35
|
-
yaml: "^2.8.2",
|
|
36
|
-
zod: "^3.24.4"
|
|
37
|
-
};
|
|
38
|
-
var devDependencies = {
|
|
39
|
-
"@types/node": ">=20",
|
|
40
|
-
"@types/ws": "^8.5.14",
|
|
41
|
-
pkgroll: "^2.14.2",
|
|
42
|
-
tsx: "^4.20.6",
|
|
43
|
-
typescript: "5.9.3"
|
|
44
|
-
};
|
|
45
|
-
var packageManager = "yarn@1.22.22";
|
|
46
|
-
var _package = {
|
|
47
|
-
name: name,
|
|
48
|
-
version: version,
|
|
49
|
-
description: description,
|
|
50
|
-
author: author,
|
|
51
|
-
license: license,
|
|
52
|
-
type: type,
|
|
53
|
-
bin: bin,
|
|
54
|
-
files: files,
|
|
55
|
-
main: main,
|
|
56
|
-
exports: exports$1,
|
|
57
|
-
scripts: scripts,
|
|
58
|
-
dependencies: dependencies,
|
|
59
|
-
devDependencies: devDependencies,
|
|
60
|
-
packageManager: packageManager
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export { author, bin, _package as default, dependencies, description, devDependencies, exports$1 as exports, files, license, main, name, packageManager, scripts, type, version };
|