svamp-cli 0.1.75 → 0.1.78

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-DsXDjwLW.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -32,6 +32,8 @@ let daemonSubcommand = args[1];
32
32
  async function main() {
33
33
  if (subcommand === "login") {
34
34
  await loginToHypha();
35
+ } else if (subcommand === "logout") {
36
+ await logoutFromHypha();
35
37
  } else if (subcommand === "daemon") {
36
38
  if (daemonSubcommand === "restart") {
37
39
  await stopDaemon();
@@ -47,7 +49,7 @@ async function main() {
47
49
  ...process.argv.slice(1, 2),
48
50
  // the script path
49
51
  "daemon",
50
- "start-sync",
52
+ "start-supervised",
51
53
  ...extraArgs
52
54
  ], {
53
55
  detached: true,
@@ -77,6 +79,106 @@ async function main() {
77
79
  process.exit(1);
78
80
  }
79
81
  process.exit(0);
82
+ } else if (daemonSubcommand === "start-supervised") {
83
+ const { spawn: spawnChild } = await import('child_process');
84
+ const { appendFileSync, mkdirSync, existsSync: fsExists } = await import('fs');
85
+ const { join: pathJoin } = await import('path');
86
+ const osModule = await import('os');
87
+ const svampHome = process.env.SVAMP_HOME || pathJoin(osModule.homedir(), ".svamp");
88
+ const logsDir = pathJoin(svampHome, "logs");
89
+ mkdirSync(logsDir, { recursive: true });
90
+ const logFile = pathJoin(logsDir, "daemon-supervised.log");
91
+ const log = (msg) => {
92
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [supervisor] ${msg}
93
+ `;
94
+ try {
95
+ appendFileSync(logFile, line);
96
+ } catch {
97
+ }
98
+ };
99
+ const supervisorPidFile = pathJoin(svampHome, "supervisor.pid");
100
+ try {
101
+ appendFileSync(supervisorPidFile, "");
102
+ } catch {
103
+ }
104
+ const { writeFileSync: wfs } = await import('fs');
105
+ wfs(supervisorPidFile, String(process.pid), "utf-8");
106
+ const extraSyncArgs = [];
107
+ if (args.includes("--no-auto-continue")) extraSyncArgs.push("--no-auto-continue");
108
+ const BASE_DELAY_MS = 2e3;
109
+ const MAX_DELAY_MS = 3e5;
110
+ const BACKOFF_RESET_UPTIME_MS = 6e4;
111
+ let consecutiveRapidCrashes = 0;
112
+ let currentChild = null;
113
+ let stopping = false;
114
+ const onSignal = (sig) => {
115
+ stopping = true;
116
+ if (currentChild && !currentChild.killed) {
117
+ currentChild.kill(sig);
118
+ }
119
+ };
120
+ process.on("SIGTERM", () => onSignal("SIGTERM"));
121
+ process.on("SIGINT", () => onSignal("SIGINT"));
122
+ process.on("SIGUSR1", () => onSignal("SIGUSR1"));
123
+ log("Supervisor started");
124
+ while (!stopping) {
125
+ const startTime = Date.now();
126
+ const exitCode = await new Promise((resolve) => {
127
+ const child = spawnChild(process.execPath, [
128
+ "--no-warnings",
129
+ "--no-deprecation",
130
+ ...process.argv.slice(1, 2),
131
+ "daemon",
132
+ "start-sync",
133
+ ...extraSyncArgs
134
+ ], {
135
+ stdio: ["ignore", "inherit", "inherit"],
136
+ env: { ...process.env, SVAMP_SUPERVISED: "1" }
137
+ });
138
+ currentChild = child;
139
+ child.on("exit", (code) => resolve(code));
140
+ child.on("error", (err) => {
141
+ log(`Failed to spawn daemon: ${err.message}`);
142
+ resolve(1);
143
+ });
144
+ });
145
+ currentChild = null;
146
+ const uptime = Date.now() - startTime;
147
+ if (stopping) {
148
+ log("Supervisor received stop signal, exiting");
149
+ break;
150
+ }
151
+ if (exitCode === 0) {
152
+ log("Daemon exited cleanly (exit 0), not restarting");
153
+ break;
154
+ }
155
+ if (uptime > BACKOFF_RESET_UPTIME_MS) {
156
+ consecutiveRapidCrashes = 0;
157
+ } else {
158
+ consecutiveRapidCrashes++;
159
+ }
160
+ const backoffExp = Math.min(consecutiveRapidCrashes, 10);
161
+ let delay = Math.min(BASE_DELAY_MS * Math.pow(2, backoffExp), MAX_DELAY_MS);
162
+ delay = Math.max(BASE_DELAY_MS, Math.round(delay + (Math.random() * 0.2 - 0.1) * delay));
163
+ log(`Daemon exited with code ${exitCode} (uptime=${Math.round(uptime / 1e3)}s). Restarting in ${Math.round(delay / 1e3)}s...`);
164
+ await new Promise((resolve) => {
165
+ const timer = setTimeout(resolve, delay);
166
+ const checkStop = setInterval(() => {
167
+ if (stopping) {
168
+ clearTimeout(timer);
169
+ clearInterval(checkStop);
170
+ resolve();
171
+ }
172
+ }, 500);
173
+ setTimeout(() => clearInterval(checkStop), delay + 100);
174
+ });
175
+ }
176
+ try {
177
+ const { unlinkSync: us } = await import('fs');
178
+ us(supervisorPidFile);
179
+ } catch {
180
+ }
181
+ process.exit(0);
80
182
  } else if (daemonSubcommand === "start-sync") {
81
183
  const noAutoContinue = args.includes("--no-auto-continue");
82
184
  await startDaemon({ noAutoContinue });
@@ -106,10 +208,10 @@ async function main() {
106
208
  } else if (subcommand === "skills") {
107
209
  await handleSkillsCommand();
108
210
  } else if (subcommand === "service" || subcommand === "svc") {
109
- const { handleServiceCommand } = await import('./commands-BLjcT1Vl.mjs');
211
+ const { handleServiceCommand } = await import('./commands-BYbuedOK.mjs');
110
212
  await handleServiceCommand();
111
213
  } else if (subcommand === "process" || subcommand === "proc") {
112
- const { processCommand } = await import('./commands-Dq8WSqvt.mjs');
214
+ const { processCommand } = await import('./commands-Cf3mXxPZ.mjs');
113
215
  let machineId;
114
216
  const processArgs = args.slice(1);
115
217
  const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
@@ -127,7 +229,7 @@ async function main() {
127
229
  } else if (!subcommand || subcommand === "start") {
128
230
  await handleInteractiveCommand();
129
231
  } else if (subcommand === "--version" || subcommand === "-v") {
130
- const pkg = await import('./package-BFnad6d1.mjs').catch(() => ({ default: { version: "unknown" } }));
232
+ const pkg = await import('./package-DTOqWYBv.mjs').catch(() => ({ default: { version: "unknown" } }));
131
233
  console.log(`svamp version: ${pkg.default.version}`);
132
234
  } else {
133
235
  console.error(`Unknown command: ${subcommand}`);
@@ -136,7 +238,7 @@ async function main() {
136
238
  }
137
239
  }
138
240
  async function handleInteractiveCommand() {
139
- const { runInteractive } = await import('./run-Dy5lxT3M.mjs');
241
+ const { runInteractive } = await import('./run-DqvxMsWh.mjs');
140
242
  const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
141
243
  let directory = process.cwd();
142
244
  let resumeSessionId;
@@ -181,7 +283,7 @@ async function handleAgentCommand() {
181
283
  return;
182
284
  }
183
285
  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; });
286
+ const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-DsXDjwLW.mjs').then(function (n) { return n.i; });
185
287
  console.log("Known agents:");
186
288
  for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
187
289
  console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
@@ -193,7 +295,7 @@ async function handleAgentCommand() {
193
295
  console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
194
296
  return;
195
297
  }
196
- const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-lhAjX4NB.mjs').then(function (n) { return n.i; });
298
+ const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-DsXDjwLW.mjs').then(function (n) { return n.i; });
197
299
  let cwd = process.cwd();
198
300
  const filteredArgs = [];
199
301
  for (let i = 0; i < agentArgs.length; i++) {
@@ -217,12 +319,12 @@ async function handleAgentCommand() {
217
319
  console.log(`Starting ${config.agentName} agent in ${cwd}...`);
218
320
  let backend;
219
321
  if (KNOWN_MCP_AGENTS[config.agentName]) {
220
- const { CodexMcpBackend } = await import('./run-lhAjX4NB.mjs').then(function (n) { return n.j; });
322
+ const { CodexMcpBackend } = await import('./run-DsXDjwLW.mjs').then(function (n) { return n.j; });
221
323
  backend = new CodexMcpBackend({ cwd, log: logFn });
222
324
  } 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; });
325
+ const { AcpBackend } = await import('./run-DsXDjwLW.mjs').then(function (n) { return n.h; });
326
+ const { GeminiTransport } = await import('./run-DsXDjwLW.mjs').then(function (n) { return n.G; });
327
+ const { DefaultTransport } = await import('./run-DsXDjwLW.mjs').then(function (n) { return n.D; });
226
328
  const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
227
329
  backend = new AcpBackend({
228
330
  agentName: config.agentName,
@@ -340,7 +442,7 @@ async function handleSessionCommand() {
340
442
  printSessionHelp();
341
443
  return;
342
444
  }
343
- const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionQueueAdd, sessionQueueList, sessionQueueClear } = await import('./commands-a7p1jW-t.mjs');
445
+ const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-B6FEeZeP.mjs');
344
446
  const parseFlagStr = (flag, shortFlag) => {
345
447
  for (let i = 1; i < sessionArgs.length; i++) {
346
448
  if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
@@ -400,7 +502,7 @@ async function handleSessionCommand() {
400
502
  allowDomain.push(sessionArgs[++i]);
401
503
  }
402
504
  }
403
- const { parseShareArg } = await import('./commands-a7p1jW-t.mjs');
505
+ const { parseShareArg } = await import('./commands-B6FEeZeP.mjs');
404
506
  const shareEntries = share.map((s) => parseShareArg(s));
405
507
  await sessionSpawn(agent, dir, targetMachineId, {
406
508
  message,
@@ -451,13 +553,15 @@ async function handleSessionCommand() {
451
553
  await sessionAttach(sessionArgs[1], targetMachineId);
452
554
  } else if (sessionSubcommand === "send") {
453
555
  if (!sessionArgs[1] || !sessionArgs[2]) {
454
- console.error("Usage: svamp session send <session-id> <message> [--wait] [--timeout N] [--json]");
556
+ console.error('Usage: svamp session send <session-id> <message> [--subject "..."] [--urgency urgent|normal] [--wait] [--timeout N] [--json]');
455
557
  process.exit(1);
456
558
  }
457
559
  await sessionSend(sessionArgs[1], sessionArgs[2], targetMachineId, {
458
560
  wait: hasFlag("--wait"),
459
561
  timeout: parseFlagInt("--timeout"),
460
- json: hasFlag("--json")
562
+ json: hasFlag("--json"),
563
+ subject: parseFlagStr("--subject"),
564
+ urgency: parseFlagStr("--urgency")
461
565
  });
462
566
  } else if (sessionSubcommand === "wait") {
463
567
  if (!sessionArgs[1]) {
@@ -484,7 +588,7 @@ async function handleSessionCommand() {
484
588
  console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
485
589
  process.exit(1);
486
590
  }
487
- const { sessionApprove } = await import('./commands-a7p1jW-t.mjs');
591
+ const { sessionApprove } = await import('./commands-B6FEeZeP.mjs');
488
592
  const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
489
593
  await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
490
594
  json: hasFlag("--json")
@@ -494,7 +598,7 @@ async function handleSessionCommand() {
494
598
  console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
495
599
  process.exit(1);
496
600
  }
497
- const { sessionDeny } = await import('./commands-a7p1jW-t.mjs');
601
+ const { sessionDeny } = await import('./commands-B6FEeZeP.mjs');
498
602
  const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
499
603
  await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
500
604
  json: hasFlag("--json")
@@ -530,7 +634,7 @@ async function handleSessionCommand() {
530
634
  console.error("Usage: svamp session set-title <title>");
531
635
  process.exit(1);
532
636
  }
533
- const { sessionSetTitle } = await import('./agentCommands-C6iGblcL.mjs');
637
+ const { sessionSetTitle } = await import('./agentCommands-uNFhhdN1.mjs');
534
638
  await sessionSetTitle(title);
535
639
  } else if (sessionSubcommand === "set-link") {
536
640
  const url = sessionArgs[1];
@@ -539,7 +643,7 @@ async function handleSessionCommand() {
539
643
  process.exit(1);
540
644
  }
541
645
  const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
542
- const { sessionSetLink } = await import('./agentCommands-C6iGblcL.mjs');
646
+ const { sessionSetLink } = await import('./agentCommands-uNFhhdN1.mjs');
543
647
  await sessionSetLink(url, label);
544
648
  } else if (sessionSubcommand === "notify") {
545
649
  const message = sessionArgs[1];
@@ -548,7 +652,7 @@ async function handleSessionCommand() {
548
652
  process.exit(1);
549
653
  }
550
654
  const level = parseFlagStr("--level") || "info";
551
- const { sessionNotify } = await import('./agentCommands-C6iGblcL.mjs');
655
+ const { sessionNotify } = await import('./agentCommands-uNFhhdN1.mjs');
552
656
  await sessionNotify(message, level);
553
657
  } else if (sessionSubcommand === "broadcast") {
554
658
  const action = sessionArgs[1];
@@ -556,30 +660,83 @@ async function handleSessionCommand() {
556
660
  console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
557
661
  process.exit(1);
558
662
  }
559
- const { sessionBroadcast } = await import('./agentCommands-C6iGblcL.mjs');
663
+ const { sessionBroadcast } = await import('./agentCommands-uNFhhdN1.mjs');
560
664
  await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
561
- } else if (sessionSubcommand === "queue") {
562
- const queueSubcmd = sessionArgs[1];
563
- if (queueSubcmd === "add") {
665
+ } else if (sessionSubcommand === "inbox") {
666
+ const inboxSubcmd = sessionArgs[1];
667
+ const agentSessionId = process.env.SVAMP_SESSION_ID;
668
+ if (inboxSubcmd === "send") {
564
669
  if (!sessionArgs[2] || !sessionArgs[3]) {
565
- console.error('Usage: svamp session queue add <session-id> "<message>"');
670
+ console.error('Usage: svamp session inbox send <target-session-id> "<body>" [--subject "..."] [--urgency urgent|normal] [--json]');
566
671
  process.exit(1);
567
672
  }
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>");
673
+ if (agentSessionId) {
674
+ const { inboxSend } = await import('./agentCommands-uNFhhdN1.mjs');
675
+ await inboxSend(sessionArgs[2], {
676
+ body: sessionArgs[3],
677
+ subject: parseFlagStr("--subject"),
678
+ urgency: parseFlagStr("--urgency")
679
+ });
680
+ } else {
681
+ await sessionInboxSend(sessionArgs[2], sessionArgs[3], targetMachineId, {
682
+ subject: parseFlagStr("--subject"),
683
+ urgency: parseFlagStr("--urgency"),
684
+ json: hasFlag("--json")
685
+ });
686
+ }
687
+ } else if (inboxSubcmd === "list" || inboxSubcmd === "ls") {
688
+ if (agentSessionId && !sessionArgs[2]) {
689
+ const { inboxList } = await import('./agentCommands-uNFhhdN1.mjs');
690
+ await inboxList({
691
+ unread: hasFlag("--unread"),
692
+ limit: parseFlagInt("--limit"),
693
+ json: hasFlag("--json")
694
+ });
695
+ } else if (sessionArgs[2]) {
696
+ await sessionInboxList(sessionArgs[2], targetMachineId, {
697
+ unread: hasFlag("--unread"),
698
+ limit: parseFlagInt("--limit"),
699
+ json: hasFlag("--json")
700
+ });
701
+ } else {
702
+ console.error("Usage: svamp session inbox list [session-id] [--unread] [--limit N] [--json]");
572
703
  process.exit(1);
573
704
  }
574
- await sessionQueueList(sessionArgs[2], targetMachineId);
575
- } else if (queueSubcmd === "clear") {
705
+ } else if (inboxSubcmd === "read") {
576
706
  if (!sessionArgs[2]) {
577
- console.error("Usage: svamp session queue clear <session-id>");
707
+ console.error("Usage: svamp session inbox read <session-id|message-id> [message-id]");
708
+ process.exit(1);
709
+ }
710
+ if (agentSessionId && !sessionArgs[3]) {
711
+ const { inboxList } = await import('./agentCommands-uNFhhdN1.mjs');
712
+ await sessionInboxRead(agentSessionId, sessionArgs[2], targetMachineId);
713
+ } else if (sessionArgs[3]) {
714
+ await sessionInboxRead(sessionArgs[2], sessionArgs[3], targetMachineId);
715
+ } else {
716
+ console.error("Usage: svamp session inbox read <session-id> <message-id>");
717
+ process.exit(1);
718
+ }
719
+ } else if (inboxSubcmd === "reply") {
720
+ if (agentSessionId && sessionArgs[2] && sessionArgs[3] && !sessionArgs[4]) {
721
+ const { inboxReply } = await import('./agentCommands-uNFhhdN1.mjs');
722
+ await inboxReply(sessionArgs[2], sessionArgs[3]);
723
+ } else if (sessionArgs[2] && sessionArgs[3] && sessionArgs[4]) {
724
+ await sessionInboxReply(sessionArgs[2], sessionArgs[3], sessionArgs[4], targetMachineId);
725
+ } else {
726
+ console.error('Usage: svamp session inbox reply <session-id> <message-id> "<body>"');
727
+ process.exit(1);
728
+ }
729
+ } else if (inboxSubcmd === "clear") {
730
+ if (agentSessionId && !sessionArgs[2]) {
731
+ await sessionInboxClear(agentSessionId, targetMachineId, { all: hasFlag("--all") });
732
+ } else if (sessionArgs[2]) {
733
+ await sessionInboxClear(sessionArgs[2], targetMachineId, { all: hasFlag("--all") });
734
+ } else {
735
+ console.error("Usage: svamp session inbox clear [session-id] [--all]");
578
736
  process.exit(1);
579
737
  }
580
- await sessionQueueClear(sessionArgs[2], targetMachineId);
581
738
  } else {
582
- console.error("Usage: svamp session queue <add|list|clear> <session-id> [args]");
739
+ console.error("Usage: svamp session inbox <send|list|read|reply|clear> [session-id] [args]");
583
740
  process.exit(1);
584
741
  }
585
742
  } else {
@@ -597,7 +754,7 @@ async function handleMachineCommand() {
597
754
  return;
598
755
  }
599
756
  if (machineSubcommand === "share") {
600
- const { machineShare } = await import('./commands-a7p1jW-t.mjs');
757
+ const { machineShare } = await import('./commands-B6FEeZeP.mjs');
601
758
  let machineId;
602
759
  const shareArgs = [];
603
760
  for (let i = 1; i < machineArgs.length; i++) {
@@ -627,7 +784,7 @@ async function handleMachineCommand() {
627
784
  }
628
785
  await machineShare(machineId, { add, remove, list, configPath, showConfig });
629
786
  } else if (machineSubcommand === "exec") {
630
- const { machineExec } = await import('./commands-a7p1jW-t.mjs');
787
+ const { machineExec } = await import('./commands-B6FEeZeP.mjs');
631
788
  let machineId;
632
789
  let cwd;
633
790
  const cmdParts = [];
@@ -647,7 +804,7 @@ async function handleMachineCommand() {
647
804
  }
648
805
  await machineExec(machineId, command, cwd);
649
806
  } else if (machineSubcommand === "info") {
650
- const { machineInfo } = await import('./commands-a7p1jW-t.mjs');
807
+ const { machineInfo } = await import('./commands-B6FEeZeP.mjs');
651
808
  let machineId;
652
809
  for (let i = 1; i < machineArgs.length; i++) {
653
810
  if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
@@ -667,10 +824,10 @@ async function handleMachineCommand() {
667
824
  level = machineArgs[++i];
668
825
  }
669
826
  }
670
- const { machineNotify } = await import('./agentCommands-C6iGblcL.mjs');
827
+ const { machineNotify } = await import('./agentCommands-uNFhhdN1.mjs');
671
828
  await machineNotify(message, level);
672
829
  } else if (machineSubcommand === "ls") {
673
- const { machineLs } = await import('./commands-a7p1jW-t.mjs');
830
+ const { machineLs } = await import('./commands-B6FEeZeP.mjs');
674
831
  let machineId;
675
832
  let showHidden = false;
676
833
  let path;
@@ -698,7 +855,7 @@ async function handleSkillsCommand() {
698
855
  printSkillsHelp();
699
856
  return;
700
857
  }
701
- const { skillsFind, skillsInstall, skillsList, skillsRemove, skillsPublish } = await import('./commands-UFi0_ESV.mjs');
858
+ const { skillsFind, skillsInstall, skillsList, skillsRemove, skillsPublish } = await import('./commands-DJoYOM_1.mjs');
702
859
  if (skillsSubcommand === "find" || skillsSubcommand === "search") {
703
860
  const query = skillsArgs.slice(1).filter((a) => !a.startsWith("--")).join(" ");
704
861
  if (!query) {
@@ -824,6 +981,41 @@ You can now start the daemon: svamp daemon start`);
824
981
  process.exit(1);
825
982
  }
826
983
  }
984
+ async function logoutFromHypha() {
985
+ const os = await import('os');
986
+ const { join } = await import('path');
987
+ const fs = await import('fs');
988
+ const homeDir = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
989
+ const envFile = join(homeDir, ".env");
990
+ console.log("Stopping daemon...");
991
+ try {
992
+ await stopDaemon();
993
+ } catch {
994
+ }
995
+ if (fs.existsSync(envFile)) {
996
+ const existing = fs.readFileSync(envFile, "utf-8").split("\n");
997
+ const kept = [];
998
+ for (const line of existing) {
999
+ const trimmed = line.trim();
1000
+ if (trimmed.startsWith("HYPHA_TOKEN=") || trimmed.startsWith("HYPHA_WORKSPACE=") || trimmed.startsWith("HYPHA_SERVER_URL=")) {
1001
+ continue;
1002
+ }
1003
+ kept.push(line);
1004
+ }
1005
+ while (kept.length > 0 && kept[kept.length - 1].trim() === "") {
1006
+ kept.pop();
1007
+ }
1008
+ if (kept.length > 0) {
1009
+ fs.writeFileSync(envFile, kept.join("\n") + "\n", "utf-8");
1010
+ } else {
1011
+ fs.unlinkSync(envFile);
1012
+ }
1013
+ console.log("Credentials removed");
1014
+ } else {
1015
+ console.log("No credentials found");
1016
+ }
1017
+ console.log("Logged out successfully");
1018
+ }
827
1019
  const LAUNCHD_LABEL = "io.hypha.svamp.daemon";
828
1020
  async function installDaemonService() {
829
1021
  const os = await import('os');
@@ -861,18 +1053,10 @@ async function installDaemonService() {
861
1053
  <array>
862
1054
  <string>${svampBinary}</string>
863
1055
  <string>daemon</string>
864
- <string>start-sync</string>
1056
+ <string>start-supervised</string>
865
1057
  </array>
866
1058
  <key>RunAtLoad</key>
867
1059
  <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
1060
  <key>StandardOutPath</key>
877
1061
  <string>${logsDir}/daemon-supervised.log</string>
878
1062
  <key>StandardErrorPath</key>
@@ -883,8 +1067,6 @@ async function installDaemonService() {
883
1067
  <string>${supervisedPath}</string>
884
1068
  <key>SVAMP_HOME</key>
885
1069
  <string>${svampHome}</string>
886
- <key>SVAMP_SUPERVISED</key>
887
- <string>1</string>
888
1070
  </dict>
889
1071
  </dict>
890
1072
  </plist>
@@ -923,12 +1105,9 @@ After=network-online.target
923
1105
  Wants=network-online.target
924
1106
 
925
1107
  [Service]
926
- ExecStart=${svampBinary} daemon start-sync
927
- Restart=on-failure
928
- RestartSec=10
1108
+ ExecStart=${svampBinary} daemon start-supervised
929
1109
  Environment=PATH=${supervisedPath}
930
1110
  Environment=SVAMP_HOME=${svampHome}
931
- Environment=SVAMP_SUPERVISED=1
932
1111
  StandardOutput=append:${logsDir}/daemon-supervised.log
933
1112
  StandardError=append:${logsDir}/daemon-supervised.log
934
1113
 
@@ -948,47 +1127,24 @@ Svamp daemon service installed and started!`);
948
1127
  To stop the service: svamp daemon stop`);
949
1128
  console.log(`To uninstall: svamp daemon uninstall`);
950
1129
  } 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
1130
  console.log(`
976
- Svamp daemon supervisor script written!`);
977
- console.log(` Script: ${wrapperPath}`);
978
- console.log(` Log file: ${logsDir}/daemon-supervised.log`);
1131
+ No systemd detected. The built-in supervisor is cross-platform.`);
979
1132
  console.log(`
980
- To use in Docker, set your container CMD to:`);
981
- console.log(` ${wrapperPath}`);
1133
+ For Docker, set your container CMD to:`);
1134
+ console.log(` ${svampBinary} daemon start-supervised`);
982
1135
  console.log(`
983
1136
  Or run in the background:`);
984
- console.log(` nohup ${wrapperPath} &`);
1137
+ console.log(` svamp daemon start`);
985
1138
  console.log(`
986
1139
  To stop the daemon: svamp daemon stop`);
987
1140
  }
988
1141
  } 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);
1142
+ console.log(`
1143
+ The built-in supervisor is cross-platform.`);
1144
+ console.log(`
1145
+ Run: svamp daemon start`);
1146
+ console.log(`
1147
+ To stop: svamp daemon stop`);
992
1148
  }
993
1149
  }
994
1150
  async function uninstallDaemonService() {
@@ -1054,6 +1210,7 @@ Quick start \u2014 spawn an agent to do a task (use -p bypassPermissions to run
1054
1210
  Commands:
1055
1211
  svamp Start interactive Claude session (synced to cloud)
1056
1212
  svamp login [url] Login to Hypha (opens browser, stores token)
1213
+ svamp logout Logout (stop daemon, remove credentials)
1057
1214
  svamp daemon start Start the background daemon (required for sessions)
1058
1215
  svamp daemon status Show daemon status
1059
1216
  svamp daemon --help Show all daemon commands
@@ -1099,8 +1256,8 @@ Usage:
1099
1256
  svamp daemon stop --cleanup Stop and mark all sessions as stopped
1100
1257
  svamp daemon restart Restart daemon (sessions resume seamlessly)
1101
1258
  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
1259
+ svamp daemon install Install as login service (macOS/Linux) \u2014 auto-start at login
1260
+ svamp daemon uninstall Remove login service
1104
1261
  `);
1105
1262
  }
1106
1263
  function printSessionHelp() {
@@ -1160,10 +1317,12 @@ COMMANDS:
1160
1317
  ralph-cancel <id> Cancel loop
1161
1318
  ralph-status <id> Show loop status
1162
1319
 
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
1320
+ Inbox:
1321
+ inbox send <id> "<body>" [opts] Send a message to session inbox
1322
+ inbox list <id> [--unread] [--json] List inbox messages
1323
+ inbox read <id> <msg-id> Read and mark a message
1324
+ inbox reply <id> <msg-id> "<body>" Reply to sender's session
1325
+ inbox clear <id> [--all] Clear read (or all) messages
1167
1326
 
1168
1327
  SPAWN OPTIONS:
1169
1328
  -d, --directory <path> Working directory (REQUIRED for meaningful work)