svamp-cli 0.1.75 → 0.1.76

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/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { b as stopDaemon, s as startDaemon, d as daemonStatus } from './run-lhAjX4NB.mjs';
1
+ import { b as stopDaemon, s as startDaemon, d as daemonStatus } from './run-BnFGIK0c.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -47,7 +47,7 @@ async function main() {
47
47
  ...process.argv.slice(1, 2),
48
48
  // the script path
49
49
  "daemon",
50
- "start-sync",
50
+ "start-supervised",
51
51
  ...extraArgs
52
52
  ], {
53
53
  detached: true,
@@ -77,6 +77,94 @@ async function main() {
77
77
  process.exit(1);
78
78
  }
79
79
  process.exit(0);
80
+ } else if (daemonSubcommand === "start-supervised") {
81
+ const { spawn: spawnChild } = await import('child_process');
82
+ const { appendFileSync, mkdirSync, existsSync: fsExists } = await import('fs');
83
+ const { join: pathJoin } = await import('path');
84
+ const osModule = await import('os');
85
+ const svampHome = process.env.SVAMP_HOME || pathJoin(osModule.homedir(), ".svamp");
86
+ const logsDir = pathJoin(svampHome, "logs");
87
+ mkdirSync(logsDir, { recursive: true });
88
+ const logFile = pathJoin(logsDir, "daemon-supervised.log");
89
+ const log = (msg) => {
90
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [supervisor] ${msg}
91
+ `;
92
+ try {
93
+ appendFileSync(logFile, line);
94
+ } catch {
95
+ }
96
+ };
97
+ const extraSyncArgs = [];
98
+ if (args.includes("--no-auto-continue")) extraSyncArgs.push("--no-auto-continue");
99
+ const BASE_DELAY_MS = 2e3;
100
+ const MAX_DELAY_MS = 3e5;
101
+ const BACKOFF_RESET_UPTIME_MS = 6e4;
102
+ let consecutiveRapidCrashes = 0;
103
+ let currentChild = null;
104
+ let stopping = false;
105
+ const onSignal = (sig) => {
106
+ stopping = true;
107
+ if (currentChild && !currentChild.killed) {
108
+ currentChild.kill(sig);
109
+ }
110
+ };
111
+ process.on("SIGTERM", () => onSignal("SIGTERM"));
112
+ process.on("SIGINT", () => onSignal("SIGINT"));
113
+ process.on("SIGUSR1", () => onSignal("SIGUSR1"));
114
+ log("Supervisor started");
115
+ while (!stopping) {
116
+ const startTime = Date.now();
117
+ const exitCode = await new Promise((resolve) => {
118
+ const child = spawnChild(process.execPath, [
119
+ "--no-warnings",
120
+ "--no-deprecation",
121
+ ...process.argv.slice(1, 2),
122
+ "daemon",
123
+ "start-sync",
124
+ ...extraSyncArgs
125
+ ], {
126
+ stdio: ["ignore", "inherit", "inherit"],
127
+ env: { ...process.env, SVAMP_SUPERVISED: "1" }
128
+ });
129
+ currentChild = child;
130
+ child.on("exit", (code) => resolve(code));
131
+ child.on("error", (err) => {
132
+ log(`Failed to spawn daemon: ${err.message}`);
133
+ resolve(1);
134
+ });
135
+ });
136
+ currentChild = null;
137
+ const uptime = Date.now() - startTime;
138
+ if (stopping) {
139
+ log("Supervisor received stop signal, exiting");
140
+ break;
141
+ }
142
+ if (exitCode === 0) {
143
+ log("Daemon exited cleanly (exit 0), not restarting");
144
+ break;
145
+ }
146
+ if (uptime > BACKOFF_RESET_UPTIME_MS) {
147
+ consecutiveRapidCrashes = 0;
148
+ } else {
149
+ consecutiveRapidCrashes++;
150
+ }
151
+ const backoffExp = Math.min(consecutiveRapidCrashes, 10);
152
+ let delay = Math.min(BASE_DELAY_MS * Math.pow(2, backoffExp), MAX_DELAY_MS);
153
+ delay = Math.max(BASE_DELAY_MS, Math.round(delay + (Math.random() * 0.2 - 0.1) * delay));
154
+ log(`Daemon exited with code ${exitCode} (uptime=${Math.round(uptime / 1e3)}s). Restarting in ${Math.round(delay / 1e3)}s...`);
155
+ await new Promise((resolve) => {
156
+ const timer = setTimeout(resolve, delay);
157
+ const checkStop = setInterval(() => {
158
+ if (stopping) {
159
+ clearTimeout(timer);
160
+ clearInterval(checkStop);
161
+ resolve();
162
+ }
163
+ }, 500);
164
+ setTimeout(() => clearInterval(checkStop), delay + 100);
165
+ });
166
+ }
167
+ process.exit(0);
80
168
  } else if (daemonSubcommand === "start-sync") {
81
169
  const noAutoContinue = args.includes("--no-auto-continue");
82
170
  await startDaemon({ noAutoContinue });
@@ -106,10 +194,10 @@ async function main() {
106
194
  } else if (subcommand === "skills") {
107
195
  await handleSkillsCommand();
108
196
  } else if (subcommand === "service" || subcommand === "svc") {
109
- const { handleServiceCommand } = await import('./commands-BLjcT1Vl.mjs');
197
+ const { handleServiceCommand } = await import('./commands-CADr1mQg.mjs');
110
198
  await handleServiceCommand();
111
199
  } else if (subcommand === "process" || subcommand === "proc") {
112
- const { processCommand } = await import('./commands-Dq8WSqvt.mjs');
200
+ const { processCommand } = await import('./commands-7Iw1nFwf.mjs');
113
201
  let machineId;
114
202
  const processArgs = args.slice(1);
115
203
  const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
@@ -127,7 +215,7 @@ async function main() {
127
215
  } else if (!subcommand || subcommand === "start") {
128
216
  await handleInteractiveCommand();
129
217
  } else if (subcommand === "--version" || subcommand === "-v") {
130
- const pkg = await import('./package-BFnad6d1.mjs').catch(() => ({ default: { version: "unknown" } }));
218
+ const pkg = await import('./package-Dpz1MLO4.mjs').catch(() => ({ default: { version: "unknown" } }));
131
219
  console.log(`svamp version: ${pkg.default.version}`);
132
220
  } else {
133
221
  console.error(`Unknown command: ${subcommand}`);
@@ -136,7 +224,7 @@ async function main() {
136
224
  }
137
225
  }
138
226
  async function handleInteractiveCommand() {
139
- const { runInteractive } = await import('./run-Dy5lxT3M.mjs');
227
+ const { runInteractive } = await import('./run-B29grSMh.mjs');
140
228
  const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
141
229
  let directory = process.cwd();
142
230
  let resumeSessionId;
@@ -181,7 +269,7 @@ async function handleAgentCommand() {
181
269
  return;
182
270
  }
183
271
  if (agentArgs[0] === "list") {
184
- const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-lhAjX4NB.mjs').then(function (n) { return n.i; });
272
+ const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-BnFGIK0c.mjs').then(function (n) { return n.i; });
185
273
  console.log("Known agents:");
186
274
  for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
187
275
  console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
@@ -193,7 +281,7 @@ async function handleAgentCommand() {
193
281
  console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
194
282
  return;
195
283
  }
196
- const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-lhAjX4NB.mjs').then(function (n) { return n.i; });
284
+ const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-BnFGIK0c.mjs').then(function (n) { return n.i; });
197
285
  let cwd = process.cwd();
198
286
  const filteredArgs = [];
199
287
  for (let i = 0; i < agentArgs.length; i++) {
@@ -217,12 +305,12 @@ async function handleAgentCommand() {
217
305
  console.log(`Starting ${config.agentName} agent in ${cwd}...`);
218
306
  let backend;
219
307
  if (KNOWN_MCP_AGENTS[config.agentName]) {
220
- const { CodexMcpBackend } = await import('./run-lhAjX4NB.mjs').then(function (n) { return n.j; });
308
+ const { CodexMcpBackend } = await import('./run-BnFGIK0c.mjs').then(function (n) { return n.j; });
221
309
  backend = new CodexMcpBackend({ cwd, log: logFn });
222
310
  } else {
223
- const { AcpBackend } = await import('./run-lhAjX4NB.mjs').then(function (n) { return n.h; });
224
- const { GeminiTransport } = await import('./run-lhAjX4NB.mjs').then(function (n) { return n.G; });
225
- const { DefaultTransport } = await import('./run-lhAjX4NB.mjs').then(function (n) { return n.D; });
311
+ const { AcpBackend } = await import('./run-BnFGIK0c.mjs').then(function (n) { return n.h; });
312
+ const { GeminiTransport } = await import('./run-BnFGIK0c.mjs').then(function (n) { return n.G; });
313
+ const { DefaultTransport } = await import('./run-BnFGIK0c.mjs').then(function (n) { return n.D; });
226
314
  const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
227
315
  backend = new AcpBackend({
228
316
  agentName: config.agentName,
@@ -340,7 +428,7 @@ async function handleSessionCommand() {
340
428
  printSessionHelp();
341
429
  return;
342
430
  }
343
- const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionQueueAdd, sessionQueueList, sessionQueueClear } = await import('./commands-a7p1jW-t.mjs');
431
+ const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-lJ8V7MJE.mjs');
344
432
  const parseFlagStr = (flag, shortFlag) => {
345
433
  for (let i = 1; i < sessionArgs.length; i++) {
346
434
  if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
@@ -400,7 +488,7 @@ async function handleSessionCommand() {
400
488
  allowDomain.push(sessionArgs[++i]);
401
489
  }
402
490
  }
403
- const { parseShareArg } = await import('./commands-a7p1jW-t.mjs');
491
+ const { parseShareArg } = await import('./commands-lJ8V7MJE.mjs');
404
492
  const shareEntries = share.map((s) => parseShareArg(s));
405
493
  await sessionSpawn(agent, dir, targetMachineId, {
406
494
  message,
@@ -484,7 +572,7 @@ async function handleSessionCommand() {
484
572
  console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
485
573
  process.exit(1);
486
574
  }
487
- const { sessionApprove } = await import('./commands-a7p1jW-t.mjs');
575
+ const { sessionApprove } = await import('./commands-lJ8V7MJE.mjs');
488
576
  const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
489
577
  await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
490
578
  json: hasFlag("--json")
@@ -494,7 +582,7 @@ async function handleSessionCommand() {
494
582
  console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
495
583
  process.exit(1);
496
584
  }
497
- const { sessionDeny } = await import('./commands-a7p1jW-t.mjs');
585
+ const { sessionDeny } = await import('./commands-lJ8V7MJE.mjs');
498
586
  const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
499
587
  await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
500
588
  json: hasFlag("--json")
@@ -530,7 +618,7 @@ async function handleSessionCommand() {
530
618
  console.error("Usage: svamp session set-title <title>");
531
619
  process.exit(1);
532
620
  }
533
- const { sessionSetTitle } = await import('./agentCommands-C6iGblcL.mjs');
621
+ const { sessionSetTitle } = await import('./agentCommands-NVZzP_Vo.mjs');
534
622
  await sessionSetTitle(title);
535
623
  } else if (sessionSubcommand === "set-link") {
536
624
  const url = sessionArgs[1];
@@ -539,7 +627,7 @@ async function handleSessionCommand() {
539
627
  process.exit(1);
540
628
  }
541
629
  const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
542
- const { sessionSetLink } = await import('./agentCommands-C6iGblcL.mjs');
630
+ const { sessionSetLink } = await import('./agentCommands-NVZzP_Vo.mjs');
543
631
  await sessionSetLink(url, label);
544
632
  } else if (sessionSubcommand === "notify") {
545
633
  const message = sessionArgs[1];
@@ -548,7 +636,7 @@ async function handleSessionCommand() {
548
636
  process.exit(1);
549
637
  }
550
638
  const level = parseFlagStr("--level") || "info";
551
- const { sessionNotify } = await import('./agentCommands-C6iGblcL.mjs');
639
+ const { sessionNotify } = await import('./agentCommands-NVZzP_Vo.mjs');
552
640
  await sessionNotify(message, level);
553
641
  } else if (sessionSubcommand === "broadcast") {
554
642
  const action = sessionArgs[1];
@@ -556,30 +644,83 @@ async function handleSessionCommand() {
556
644
  console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
557
645
  process.exit(1);
558
646
  }
559
- const { sessionBroadcast } = await import('./agentCommands-C6iGblcL.mjs');
647
+ const { sessionBroadcast } = await import('./agentCommands-NVZzP_Vo.mjs');
560
648
  await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
561
- } else if (sessionSubcommand === "queue") {
562
- const queueSubcmd = sessionArgs[1];
563
- if (queueSubcmd === "add") {
649
+ } else if (sessionSubcommand === "inbox") {
650
+ const inboxSubcmd = sessionArgs[1];
651
+ const agentSessionId = process.env.SVAMP_SESSION_ID;
652
+ if (inboxSubcmd === "send") {
564
653
  if (!sessionArgs[2] || !sessionArgs[3]) {
565
- console.error('Usage: svamp session queue add <session-id> "<message>"');
654
+ console.error('Usage: svamp session inbox send <target-session-id> "<body>" [--subject "..."] [--urgency urgent|normal] [--json]');
566
655
  process.exit(1);
567
656
  }
568
- await sessionQueueAdd(sessionArgs[2], sessionArgs[3], targetMachineId);
569
- } else if (queueSubcmd === "list" || queueSubcmd === "ls") {
570
- if (!sessionArgs[2]) {
571
- console.error("Usage: svamp session queue list <session-id>");
657
+ if (agentSessionId) {
658
+ const { inboxSend } = await import('./agentCommands-NVZzP_Vo.mjs');
659
+ await inboxSend(sessionArgs[2], {
660
+ body: sessionArgs[3],
661
+ subject: parseFlagStr("--subject"),
662
+ urgency: parseFlagStr("--urgency")
663
+ });
664
+ } else {
665
+ await sessionInboxSend(sessionArgs[2], sessionArgs[3], targetMachineId, {
666
+ subject: parseFlagStr("--subject"),
667
+ urgency: parseFlagStr("--urgency"),
668
+ json: hasFlag("--json")
669
+ });
670
+ }
671
+ } else if (inboxSubcmd === "list" || inboxSubcmd === "ls") {
672
+ if (agentSessionId && !sessionArgs[2]) {
673
+ const { inboxList } = await import('./agentCommands-NVZzP_Vo.mjs');
674
+ await inboxList({
675
+ unread: hasFlag("--unread"),
676
+ limit: parseFlagInt("--limit"),
677
+ json: hasFlag("--json")
678
+ });
679
+ } else if (sessionArgs[2]) {
680
+ await sessionInboxList(sessionArgs[2], targetMachineId, {
681
+ unread: hasFlag("--unread"),
682
+ limit: parseFlagInt("--limit"),
683
+ json: hasFlag("--json")
684
+ });
685
+ } else {
686
+ console.error("Usage: svamp session inbox list [session-id] [--unread] [--limit N] [--json]");
572
687
  process.exit(1);
573
688
  }
574
- await sessionQueueList(sessionArgs[2], targetMachineId);
575
- } else if (queueSubcmd === "clear") {
689
+ } else if (inboxSubcmd === "read") {
576
690
  if (!sessionArgs[2]) {
577
- console.error("Usage: svamp session queue clear <session-id>");
691
+ console.error("Usage: svamp session inbox read <session-id|message-id> [message-id]");
692
+ process.exit(1);
693
+ }
694
+ if (agentSessionId && !sessionArgs[3]) {
695
+ const { inboxList } = await import('./agentCommands-NVZzP_Vo.mjs');
696
+ await sessionInboxRead(agentSessionId, sessionArgs[2], targetMachineId);
697
+ } else if (sessionArgs[3]) {
698
+ await sessionInboxRead(sessionArgs[2], sessionArgs[3], targetMachineId);
699
+ } else {
700
+ console.error("Usage: svamp session inbox read <session-id> <message-id>");
701
+ process.exit(1);
702
+ }
703
+ } else if (inboxSubcmd === "reply") {
704
+ if (agentSessionId && sessionArgs[2] && sessionArgs[3] && !sessionArgs[4]) {
705
+ const { inboxReply } = await import('./agentCommands-NVZzP_Vo.mjs');
706
+ await inboxReply(sessionArgs[2], sessionArgs[3]);
707
+ } else if (sessionArgs[2] && sessionArgs[3] && sessionArgs[4]) {
708
+ await sessionInboxReply(sessionArgs[2], sessionArgs[3], sessionArgs[4], targetMachineId);
709
+ } else {
710
+ console.error('Usage: svamp session inbox reply <session-id> <message-id> "<body>"');
711
+ process.exit(1);
712
+ }
713
+ } else if (inboxSubcmd === "clear") {
714
+ if (agentSessionId && !sessionArgs[2]) {
715
+ await sessionInboxClear(agentSessionId, targetMachineId, { all: hasFlag("--all") });
716
+ } else if (sessionArgs[2]) {
717
+ await sessionInboxClear(sessionArgs[2], targetMachineId, { all: hasFlag("--all") });
718
+ } else {
719
+ console.error("Usage: svamp session inbox clear [session-id] [--all]");
578
720
  process.exit(1);
579
721
  }
580
- await sessionQueueClear(sessionArgs[2], targetMachineId);
581
722
  } else {
582
- console.error("Usage: svamp session queue <add|list|clear> <session-id> [args]");
723
+ console.error("Usage: svamp session inbox <send|list|read|reply|clear> [session-id] [args]");
583
724
  process.exit(1);
584
725
  }
585
726
  } else {
@@ -597,7 +738,7 @@ async function handleMachineCommand() {
597
738
  return;
598
739
  }
599
740
  if (machineSubcommand === "share") {
600
- const { machineShare } = await import('./commands-a7p1jW-t.mjs');
741
+ const { machineShare } = await import('./commands-lJ8V7MJE.mjs');
601
742
  let machineId;
602
743
  const shareArgs = [];
603
744
  for (let i = 1; i < machineArgs.length; i++) {
@@ -627,7 +768,7 @@ async function handleMachineCommand() {
627
768
  }
628
769
  await machineShare(machineId, { add, remove, list, configPath, showConfig });
629
770
  } else if (machineSubcommand === "exec") {
630
- const { machineExec } = await import('./commands-a7p1jW-t.mjs');
771
+ const { machineExec } = await import('./commands-lJ8V7MJE.mjs');
631
772
  let machineId;
632
773
  let cwd;
633
774
  const cmdParts = [];
@@ -647,7 +788,7 @@ async function handleMachineCommand() {
647
788
  }
648
789
  await machineExec(machineId, command, cwd);
649
790
  } else if (machineSubcommand === "info") {
650
- const { machineInfo } = await import('./commands-a7p1jW-t.mjs');
791
+ const { machineInfo } = await import('./commands-lJ8V7MJE.mjs');
651
792
  let machineId;
652
793
  for (let i = 1; i < machineArgs.length; i++) {
653
794
  if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
@@ -667,10 +808,10 @@ async function handleMachineCommand() {
667
808
  level = machineArgs[++i];
668
809
  }
669
810
  }
670
- const { machineNotify } = await import('./agentCommands-C6iGblcL.mjs');
811
+ const { machineNotify } = await import('./agentCommands-NVZzP_Vo.mjs');
671
812
  await machineNotify(message, level);
672
813
  } else if (machineSubcommand === "ls") {
673
- const { machineLs } = await import('./commands-a7p1jW-t.mjs');
814
+ const { machineLs } = await import('./commands-lJ8V7MJE.mjs');
674
815
  let machineId;
675
816
  let showHidden = false;
676
817
  let path;
@@ -698,7 +839,7 @@ async function handleSkillsCommand() {
698
839
  printSkillsHelp();
699
840
  return;
700
841
  }
701
- const { skillsFind, skillsInstall, skillsList, skillsRemove, skillsPublish } = await import('./commands-UFi0_ESV.mjs');
842
+ const { skillsFind, skillsInstall, skillsList, skillsRemove, skillsPublish } = await import('./commands-DJoYOM_1.mjs');
702
843
  if (skillsSubcommand === "find" || skillsSubcommand === "search") {
703
844
  const query = skillsArgs.slice(1).filter((a) => !a.startsWith("--")).join(" ");
704
845
  if (!query) {
@@ -861,18 +1002,10 @@ async function installDaemonService() {
861
1002
  <array>
862
1003
  <string>${svampBinary}</string>
863
1004
  <string>daemon</string>
864
- <string>start-sync</string>
1005
+ <string>start-supervised</string>
865
1006
  </array>
866
1007
  <key>RunAtLoad</key>
867
1008
  <true/>
868
- <key>KeepAlive</key>
869
- <dict>
870
- <!-- Only restart on crash (non-zero exit). svamp daemon stop exits 0. -->
871
- <key>SuccessfulExit</key>
872
- <false/>
873
- </dict>
874
- <key>ThrottleInterval</key>
875
- <integer>10</integer>
876
1009
  <key>StandardOutPath</key>
877
1010
  <string>${logsDir}/daemon-supervised.log</string>
878
1011
  <key>StandardErrorPath</key>
@@ -883,8 +1016,6 @@ async function installDaemonService() {
883
1016
  <string>${supervisedPath}</string>
884
1017
  <key>SVAMP_HOME</key>
885
1018
  <string>${svampHome}</string>
886
- <key>SVAMP_SUPERVISED</key>
887
- <string>1</string>
888
1019
  </dict>
889
1020
  </dict>
890
1021
  </plist>
@@ -923,12 +1054,9 @@ After=network-online.target
923
1054
  Wants=network-online.target
924
1055
 
925
1056
  [Service]
926
- ExecStart=${svampBinary} daemon start-sync
927
- Restart=on-failure
928
- RestartSec=10
1057
+ ExecStart=${svampBinary} daemon start-supervised
929
1058
  Environment=PATH=${supervisedPath}
930
1059
  Environment=SVAMP_HOME=${svampHome}
931
- Environment=SVAMP_SUPERVISED=1
932
1060
  StandardOutput=append:${logsDir}/daemon-supervised.log
933
1061
  StandardError=append:${logsDir}/daemon-supervised.log
934
1062
 
@@ -948,47 +1076,24 @@ Svamp daemon service installed and started!`);
948
1076
  To stop the service: svamp daemon stop`);
949
1077
  console.log(`To uninstall: svamp daemon uninstall`);
950
1078
  } else {
951
- const wrapperPath = join(svampHome, "daemon-supervisor.sh");
952
- const wrapper = `#!/bin/sh
953
- # Svamp daemon supervisor \u2014 restarts on crash. Run this as your container CMD
954
- # or in a background process. Stop with: svamp daemon stop
955
- export PATH="${supervisedPath}"
956
- export SVAMP_HOME="${svampHome}"
957
- export SVAMP_SUPERVISED=1
958
-
959
- mkdir -p "${logsDir}"
960
- echo "[svamp-supervisor] Starting svamp daemon supervisor" >> "${logsDir}/daemon-supervised.log"
961
-
962
- while true; do
963
- "${svampBinary}" daemon start-sync >> "${logsDir}/daemon-supervised.log" 2>&1
964
- EXIT_CODE=$?
965
- # Exit code 0 = clean stop (svamp daemon stop). Do not restart.
966
- if [ "$EXIT_CODE" -eq 0 ]; then
967
- echo "[svamp-supervisor] Daemon stopped cleanly (exit 0). Not restarting." >> "${logsDir}/daemon-supervised.log"
968
- break
969
- fi
970
- echo "[svamp-supervisor] Daemon exited with code $EXIT_CODE. Restarting in 10s..." >> "${logsDir}/daemon-supervised.log"
971
- sleep 10
972
- done
973
- `;
974
- fs.writeFileSync(wrapperPath, wrapper, { mode: 493 });
975
1079
  console.log(`
976
- Svamp daemon supervisor script written!`);
977
- console.log(` Script: ${wrapperPath}`);
978
- console.log(` Log file: ${logsDir}/daemon-supervised.log`);
1080
+ No systemd detected. The built-in supervisor is cross-platform.`);
979
1081
  console.log(`
980
- To use in Docker, set your container CMD to:`);
981
- console.log(` ${wrapperPath}`);
1082
+ For Docker, set your container CMD to:`);
1083
+ console.log(` ${svampBinary} daemon start-supervised`);
982
1084
  console.log(`
983
1085
  Or run in the background:`);
984
- console.log(` nohup ${wrapperPath} &`);
1086
+ console.log(` svamp daemon start`);
985
1087
  console.log(`
986
1088
  To stop the daemon: svamp daemon stop`);
987
1089
  }
988
1090
  } else {
989
- console.error(`Daemon service install is not supported on ${process.platform}.`);
990
- console.error("Supported platforms: macOS (launchd), Linux (systemd or wrapper script).");
991
- process.exit(1);
1091
+ console.log(`
1092
+ The built-in supervisor is cross-platform.`);
1093
+ console.log(`
1094
+ Run: svamp daemon start`);
1095
+ console.log(`
1096
+ To stop: svamp daemon stop`);
992
1097
  }
993
1098
  }
994
1099
  async function uninstallDaemonService() {
@@ -1099,8 +1204,8 @@ Usage:
1099
1204
  svamp daemon stop --cleanup Stop and mark all sessions as stopped
1100
1205
  svamp daemon restart Restart daemon (sessions resume seamlessly)
1101
1206
  svamp daemon status Show daemon status
1102
- svamp daemon install Install as launchd service (macOS) \u2014 auto-restart on crash
1103
- svamp daemon uninstall Remove launchd service
1207
+ svamp daemon install Install as login service (macOS/Linux) \u2014 auto-start at login
1208
+ svamp daemon uninstall Remove login service
1104
1209
  `);
1105
1210
  }
1106
1211
  function printSessionHelp() {
@@ -1160,10 +1265,12 @@ COMMANDS:
1160
1265
  ralph-cancel <id> Cancel loop
1161
1266
  ralph-status <id> Show loop status
1162
1267
 
1163
- Message Queue:
1164
- queue add <id> "<message>" Queue a message for later
1165
- queue list <id> List queued messages
1166
- queue clear <id> Clear queue
1268
+ Inbox:
1269
+ inbox send <id> "<body>" [opts] Send a message to session inbox
1270
+ inbox list <id> [--unread] [--json] List inbox messages
1271
+ inbox read <id> <msg-id> Read and mark a message
1272
+ inbox reply <id> <msg-id> "<body>" Reply to sender's session
1273
+ inbox clear <id> [--all] Clear read (or all) messages
1167
1274
 
1168
1275
  SPAWN OPTIONS:
1169
1276
  -d, --directory <path> Working directory (REQUIRED for meaningful work)
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-a7p1jW-t.mjs';
3
+ import { connectAndGetMachine } from './commands-lJ8V7MJE.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-lhAjX4NB.mjs';
8
+ import './run-BnFGIK0c.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
@@ -1,4 +1,4 @@
1
- import { createServiceGroup, listServiceGroups, getServiceGroup, deleteServiceGroup, addBackend, removeBackend, addPort, removePort, renameSubdomain, getSandboxEnv } from './api-BRbsyqJ4.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { createServiceGroup, listServiceGroups, getServiceGroup, deleteServiceGroup, addBackend, removeBackend, addPort, removePort, renameSubdomain, getSandboxEnv } from './api-BRbsyqJ4.mjs';
2
2
 
3
3
  function getFlag(args, flag) {
4
4
  const idx = args.indexOf(flag);
@@ -304,6 +304,61 @@ Service is live:`);
304
304
  process.exit(1);
305
305
  }
306
306
  }
307
+ async function serviceServe(args) {
308
+ const positional = positionalArgs(args);
309
+ const name = positional[0];
310
+ const directory = positional[1] || ".";
311
+ const subdomain = getFlag(args, "--subdomain");
312
+ const healthPath = getFlag(args, "--health-path");
313
+ const healthIntervalStr = getFlag(args, "--health-interval");
314
+ const noListing = hasFlag(args, "--no-listing");
315
+ if (!name) {
316
+ console.error("Usage: svamp service serve <name> [directory] [--subdomain <sub>] [--no-listing]");
317
+ console.error(" directory defaults to current directory");
318
+ process.exit(1);
319
+ }
320
+ if (subdomain) validateSubdomain(subdomain);
321
+ const healthInterval = healthIntervalStr ? parseInt(healthIntervalStr, 10) : void 0;
322
+ try {
323
+ const { startStaticServer } = await import('./staticServer-B-S9sl6E.mjs');
324
+ const resolvedDir = require("path").resolve(directory);
325
+ console.log(`Serving ${resolvedDir}`);
326
+ const staticServer = await startStaticServer({
327
+ directory: resolvedDir,
328
+ listing: !noListing
329
+ });
330
+ console.log(`Static server listening on 127.0.0.1:${staticServer.port}`);
331
+ const ports = [staticServer.port];
332
+ const env = getSandboxEnv();
333
+ const group = await createServiceGroup(name, ports, {
334
+ subdomain,
335
+ healthPath,
336
+ healthInterval
337
+ });
338
+ if (env.sandboxId) {
339
+ const result = await addBackend(name);
340
+ console.log(`Backend added: ${result.sandbox_id} (${result.pod_ip})`);
341
+ console.log(`
342
+ Serving ${resolvedDir}:`);
343
+ for (const p of group.ports) {
344
+ console.log(` ${p.url}`);
345
+ }
346
+ } else {
347
+ console.log(`No SANDBOX_ID detected \u2014 starting reverse tunnel.`);
348
+ const { runTunnel } = await import('./tunnel-C2kqST5d.mjs');
349
+ await runTunnel(name, ports);
350
+ }
351
+ const cleanup = () => {
352
+ staticServer.close();
353
+ process.exit(0);
354
+ };
355
+ process.on("SIGINT", cleanup);
356
+ process.on("SIGTERM", cleanup);
357
+ } catch (err) {
358
+ console.error(`Error serving directory: ${err.message}`);
359
+ process.exit(1);
360
+ }
361
+ }
307
362
  async function serviceTunnel(args) {
308
363
  const positional = positionalArgs(args);
309
364
  const name = positional[0];
@@ -344,6 +399,8 @@ async function handleServiceCommand() {
344
399
  await serviceRename(commandArgs);
345
400
  } else if (sub === "expose") {
346
401
  await serviceExpose(commandArgs);
402
+ } else if (sub === "serve") {
403
+ await serviceServe(commandArgs);
347
404
  } else if (sub === "tunnel") {
348
405
  await serviceTunnel(commandArgs);
349
406
  } else {
@@ -367,6 +424,7 @@ Usage:
367
424
  svamp service remove-port <name> --port <port> Remove port from group
368
425
  svamp service rename <name> --port <port> --subdomain <sub> Rename subdomain of a port
369
426
  svamp service expose <name> --port <port> [--port <port2>] [options] Create + join (auto-detects tunnel)
427
+ svamp service serve <name> [directory] [options] Serve a directory as static files
370
428
  svamp service tunnel <name> --port <port> [--port <port2>] Tunnel local ports to service group
371
429
 
372
430
  Create/Expose options:
@@ -391,9 +449,15 @@ Environment variables (set by provisioner):
391
449
  SANDBOX_NAMESPACE User's sandbox namespace
392
450
  SANDBOX_ID This pod's sandbox ID
393
451
 
452
+ Serve options:
453
+ --no-listing Disable directory listing
454
+ --subdomain <sub> Custom subdomain (must contain at least 2 hyphens)
455
+
394
456
  Examples:
395
457
  svamp service expose my-api --port 8000
396
458
  svamp service expose my-api --port 8000 --port 3000 --health-path /health
459
+ svamp service serve my-site ./dist
460
+ svamp service serve my-site (serves current directory)
397
461
  svamp service add-port my-api --port 5000
398
462
  svamp service remove-port my-api --port 5000
399
463
  svamp service list
@@ -402,4 +466,4 @@ Examples:
402
466
  `.trim());
403
467
  }
404
468
 
405
- export { handleServiceCommand, serviceAddBackend, serviceAddPort, serviceCreate, serviceDelete, serviceExpose, serviceInfo, serviceList, serviceRemoveBackend, serviceRemovePort, serviceRename, serviceTunnel };
469
+ export { handleServiceCommand, serviceAddBackend, serviceAddPort, serviceCreate, serviceDelete, serviceExpose, serviceInfo, serviceList, serviceRemoveBackend, serviceRemovePort, serviceRename, serviceServe, serviceTunnel };