svamp-cli 0.2.71 → 0.2.73

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.
@@ -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-B0zqVia0.mjs');
151
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.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-B0zqVia0.mjs');
168
+ const { resolveSessionId } = await import('./commands-DV0URNXH.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-h-QVSVFd.mjs';
1
+ import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-CC-0bNTe.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -44,7 +44,7 @@ async function main() {
44
44
  console.error(`svamp daemon restart: ${err.message || err}`);
45
45
  process.exit(1);
46
46
  }
47
- const { restartDaemon } = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.u; });
47
+ const { restartDaemon } = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.u; });
48
48
  await restartDaemon();
49
49
  process.exit(0);
50
50
  }
@@ -267,6 +267,13 @@ async function main() {
267
267
  process.exit(1);
268
268
  }
269
269
  await handleMachineCommand();
270
+ } else if (subcommand === "fleet") {
271
+ const { isSandboxed } = await import('./sandboxDetect-DNTcbgWD.mjs');
272
+ if (isSandboxed()) {
273
+ console.error("svamp fleet: Fleet commands are not available in sandboxed sessions.");
274
+ process.exit(1);
275
+ }
276
+ await handleFleetCommand();
270
277
  } else if (subcommand === "skills") {
271
278
  const { isSandboxed } = await import('./sandboxDetect-DNTcbgWD.mjs');
272
279
  if (isSandboxed()) {
@@ -280,7 +287,7 @@ async function main() {
280
287
  console.error("svamp service: Service commands are not available in sandboxed sessions.");
281
288
  process.exit(1);
282
289
  }
283
- const { handleServiceCommand } = await import('./commands-Cc8AE2jl.mjs');
290
+ const { handleServiceCommand } = await import('./commands-DJVdDzTe.mjs');
284
291
  await handleServiceCommand();
285
292
  } else if (subcommand === "serve") {
286
293
  const { isSandboxed: isSandboxedServe } = await import('./sandboxDetect-DNTcbgWD.mjs');
@@ -288,7 +295,7 @@ async function main() {
288
295
  console.error("svamp serve: Serve commands are not available in sandboxed sessions.");
289
296
  process.exit(1);
290
297
  }
291
- const { handleServeCommand } = await import('./serveCommands-CM17DByZ.mjs');
298
+ const { handleServeCommand } = await import('./serveCommands-BeQKrUIn.mjs');
292
299
  await handleServeCommand();
293
300
  process.exit(0);
294
301
  } else if (subcommand === "process" || subcommand === "proc") {
@@ -297,7 +304,7 @@ async function main() {
297
304
  console.error("svamp process: Process commands are not available in sandboxed sessions.");
298
305
  process.exit(1);
299
306
  }
300
- const { processCommand } = await import('./commands-Kztc20Nx.mjs');
307
+ const { processCommand } = await import('./commands-Cyp2k10B.mjs');
301
308
  let machineId;
302
309
  const processArgs = args.slice(1);
303
310
  const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
@@ -315,7 +322,7 @@ async function main() {
315
322
  } else if (!subcommand || subcommand === "start") {
316
323
  await handleInteractiveCommand();
317
324
  } else if (subcommand === "--version" || subcommand === "-v") {
318
- const pkg = await import('./package-CDSBvCp_.mjs').catch(() => ({ default: { version: "unknown" } }));
325
+ const pkg = await import('./package-O54lUaPi.mjs').catch(() => ({ default: { version: "unknown" } }));
319
326
  console.log(`svamp version: ${pkg.default.version}`);
320
327
  } else {
321
328
  console.error(`Unknown command: ${subcommand}`);
@@ -324,7 +331,7 @@ async function main() {
324
331
  }
325
332
  }
326
333
  async function handleInteractiveCommand() {
327
- const { runInteractive } = await import('./run-B_bKTQ8M.mjs');
334
+ const { runInteractive } = await import('./run-D4yrAi1I.mjs');
328
335
  const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
329
336
  let directory = process.cwd();
330
337
  let resumeSessionId;
@@ -369,7 +376,7 @@ async function handleAgentCommand() {
369
376
  return;
370
377
  }
371
378
  if (agentArgs[0] === "list") {
372
- const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.p; });
379
+ const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.p; });
373
380
  console.log("Known agents:");
374
381
  for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
375
382
  console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
@@ -381,7 +388,7 @@ async function handleAgentCommand() {
381
388
  console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
382
389
  return;
383
390
  }
384
- const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.p; });
391
+ const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.p; });
385
392
  let cwd = process.cwd();
386
393
  const filteredArgs = [];
387
394
  for (let i = 0; i < agentArgs.length; i++) {
@@ -405,12 +412,12 @@ async function handleAgentCommand() {
405
412
  console.log(`Starting ${config.agentName} agent in ${cwd}...`);
406
413
  let backend;
407
414
  if (KNOWN_MCP_AGENTS[config.agentName]) {
408
- const { CodexMcpBackend } = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.q; });
415
+ const { CodexMcpBackend } = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.q; });
409
416
  backend = new CodexMcpBackend({ cwd, log: logFn });
410
417
  } else {
411
- const { AcpBackend } = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.o; });
412
- const { GeminiTransport } = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.G; });
413
- const { DefaultTransport } = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.D; });
418
+ const { AcpBackend } = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.o; });
419
+ const { GeminiTransport } = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.G; });
420
+ const { DefaultTransport } = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.D; });
414
421
  const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
415
422
  backend = new AcpBackend({
416
423
  agentName: config.agentName,
@@ -537,7 +544,7 @@ async function handleSessionCommand() {
537
544
  process.exit(1);
538
545
  }
539
546
  }
540
- const { sessionList, sessionSpawn, sessionArchive, sessionResume, sessionDelete, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-B0zqVia0.mjs');
547
+ const { sessionList, sessionSpawn, sessionArchive, sessionResume, sessionDelete, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-DV0URNXH.mjs');
541
548
  const parseFlagStr = (flag, shortFlag) => {
542
549
  for (let i = 1; i < sessionArgs.length; i++) {
543
550
  if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
@@ -603,7 +610,7 @@ async function handleSessionCommand() {
603
610
  allowDomain.push(sessionArgs[++i]);
604
611
  }
605
612
  }
606
- const { parseShareArg } = await import('./commands-B0zqVia0.mjs');
613
+ const { parseShareArg } = await import('./commands-DV0URNXH.mjs');
607
614
  const shareEntries = share.map((s) => parseShareArg(s));
608
615
  await sessionSpawn(agent, dir, targetMachineId, {
609
616
  message,
@@ -687,7 +694,7 @@ async function handleSessionCommand() {
687
694
  console.error(" Spawns a stateless Claude session in <directory>, sends <prompt>, prints the answer, then deletes the session.");
688
695
  process.exit(1);
689
696
  }
690
- const { sessionQuery } = await import('./commands-B0zqVia0.mjs');
697
+ const { sessionQuery } = await import('./commands-DV0URNXH.mjs');
691
698
  await sessionQuery(dir, prompt, targetMachineId, {
692
699
  timeout: parseFlagInt("--timeout"),
693
700
  json: hasFlag("--json"),
@@ -720,7 +727,7 @@ async function handleSessionCommand() {
720
727
  console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
721
728
  process.exit(1);
722
729
  }
723
- const { sessionApprove } = await import('./commands-B0zqVia0.mjs');
730
+ const { sessionApprove } = await import('./commands-DV0URNXH.mjs');
724
731
  const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
725
732
  await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
726
733
  json: hasFlag("--json")
@@ -730,7 +737,7 @@ async function handleSessionCommand() {
730
737
  console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
731
738
  process.exit(1);
732
739
  }
733
- const { sessionDeny } = await import('./commands-B0zqVia0.mjs');
740
+ const { sessionDeny } = await import('./commands-DV0URNXH.mjs');
734
741
  const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
735
742
  await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
736
743
  json: hasFlag("--json")
@@ -766,7 +773,7 @@ async function handleSessionCommand() {
766
773
  console.error("Usage: svamp session set-title <title>");
767
774
  process.exit(1);
768
775
  }
769
- const { sessionSetTitle } = await import('./agentCommands-COEbsMuw.mjs');
776
+ const { sessionSetTitle } = await import('./agentCommands-C1Q4jqOT.mjs');
770
777
  await sessionSetTitle(title);
771
778
  } else if (sessionSubcommand === "set-link") {
772
779
  const url = sessionArgs[1];
@@ -775,7 +782,7 @@ async function handleSessionCommand() {
775
782
  process.exit(1);
776
783
  }
777
784
  const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
778
- const { sessionSetLink } = await import('./agentCommands-COEbsMuw.mjs');
785
+ const { sessionSetLink } = await import('./agentCommands-C1Q4jqOT.mjs');
779
786
  await sessionSetLink(url, label);
780
787
  } else if (sessionSubcommand === "notify") {
781
788
  const message = sessionArgs[1];
@@ -784,7 +791,7 @@ async function handleSessionCommand() {
784
791
  process.exit(1);
785
792
  }
786
793
  const level = parseFlagStr("--level") || "info";
787
- const { sessionNotify } = await import('./agentCommands-COEbsMuw.mjs');
794
+ const { sessionNotify } = await import('./agentCommands-C1Q4jqOT.mjs');
788
795
  await sessionNotify(message, level);
789
796
  } else if (sessionSubcommand === "broadcast") {
790
797
  const action = sessionArgs[1];
@@ -792,7 +799,7 @@ async function handleSessionCommand() {
792
799
  console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
793
800
  process.exit(1);
794
801
  }
795
- const { sessionBroadcast } = await import('./agentCommands-COEbsMuw.mjs');
802
+ const { sessionBroadcast } = await import('./agentCommands-C1Q4jqOT.mjs');
796
803
  await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
797
804
  } else if (sessionSubcommand === "inbox") {
798
805
  const inboxSubcmd = sessionArgs[1];
@@ -803,7 +810,7 @@ async function handleSessionCommand() {
803
810
  process.exit(1);
804
811
  }
805
812
  if (agentSessionId) {
806
- const { inboxSend } = await import('./agentCommands-COEbsMuw.mjs');
813
+ const { inboxSend } = await import('./agentCommands-C1Q4jqOT.mjs');
807
814
  await inboxSend(sessionArgs[2], {
808
815
  body: sessionArgs[3],
809
816
  subject: parseFlagStr("--subject"),
@@ -818,7 +825,7 @@ async function handleSessionCommand() {
818
825
  }
819
826
  } else if (inboxSubcmd === "list" || inboxSubcmd === "ls") {
820
827
  if (agentSessionId && !sessionArgs[2]) {
821
- const { inboxList } = await import('./agentCommands-COEbsMuw.mjs');
828
+ const { inboxList } = await import('./agentCommands-C1Q4jqOT.mjs');
822
829
  await inboxList({
823
830
  unread: hasFlag("--unread"),
824
831
  limit: parseFlagInt("--limit"),
@@ -840,7 +847,7 @@ async function handleSessionCommand() {
840
847
  process.exit(1);
841
848
  }
842
849
  if (agentSessionId && !sessionArgs[3]) {
843
- const { inboxList } = await import('./agentCommands-COEbsMuw.mjs');
850
+ const { inboxList } = await import('./agentCommands-C1Q4jqOT.mjs');
844
851
  await sessionInboxRead(agentSessionId, sessionArgs[2], targetMachineId);
845
852
  } else if (sessionArgs[3]) {
846
853
  await sessionInboxRead(sessionArgs[2], sessionArgs[3], targetMachineId);
@@ -850,7 +857,7 @@ async function handleSessionCommand() {
850
857
  }
851
858
  } else if (inboxSubcmd === "reply") {
852
859
  if (agentSessionId && sessionArgs[2] && sessionArgs[3] && !sessionArgs[4]) {
853
- const { inboxReply } = await import('./agentCommands-COEbsMuw.mjs');
860
+ const { inboxReply } = await import('./agentCommands-C1Q4jqOT.mjs');
854
861
  await inboxReply(sessionArgs[2], sessionArgs[3]);
855
862
  } else if (sessionArgs[2] && sessionArgs[3] && sessionArgs[4]) {
856
863
  await sessionInboxReply(sessionArgs[2], sessionArgs[3], sessionArgs[4], targetMachineId);
@@ -886,7 +893,7 @@ async function handleMachineCommand() {
886
893
  return;
887
894
  }
888
895
  if (machineSubcommand === "share") {
889
- const { machineShare } = await import('./commands-B0zqVia0.mjs');
896
+ const { machineShare } = await import('./commands-DV0URNXH.mjs');
890
897
  let machineId;
891
898
  const shareArgs = [];
892
899
  for (let i = 1; i < machineArgs.length; i++) {
@@ -916,7 +923,7 @@ async function handleMachineCommand() {
916
923
  }
917
924
  await machineShare(machineId, { add, remove, list, configPath, showConfig });
918
925
  } else if (machineSubcommand === "exec") {
919
- const { machineExec } = await import('./commands-B0zqVia0.mjs');
926
+ const { machineExec } = await import('./commands-DV0URNXH.mjs');
920
927
  let machineId;
921
928
  let cwd;
922
929
  const cmdParts = [];
@@ -936,7 +943,7 @@ async function handleMachineCommand() {
936
943
  }
937
944
  await machineExec(machineId, command, cwd);
938
945
  } else if (machineSubcommand === "info") {
939
- const { machineInfo } = await import('./commands-B0zqVia0.mjs');
946
+ const { machineInfo } = await import('./commands-DV0URNXH.mjs');
940
947
  let machineId;
941
948
  for (let i = 1; i < machineArgs.length; i++) {
942
949
  if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
@@ -956,10 +963,10 @@ async function handleMachineCommand() {
956
963
  level = machineArgs[++i];
957
964
  }
958
965
  }
959
- const { machineNotify } = await import('./agentCommands-COEbsMuw.mjs');
966
+ const { machineNotify } = await import('./agentCommands-C1Q4jqOT.mjs');
960
967
  await machineNotify(message, level);
961
968
  } else if (machineSubcommand === "ls") {
962
- const { machineLs } = await import('./commands-B0zqVia0.mjs');
969
+ const { machineLs } = await import('./commands-DV0URNXH.mjs');
963
970
  let machineId;
964
971
  let showHidden = false;
965
972
  let path;
@@ -980,6 +987,65 @@ async function handleMachineCommand() {
980
987
  }
981
988
  process.exit(0);
982
989
  }
990
+ async function handleFleetCommand() {
991
+ const fleetArgs = args.slice(1);
992
+ const sub = fleetArgs[0];
993
+ if (!sub || sub === "--help" || sub === "-h") {
994
+ console.log(`Usage: svamp fleet <subcommand>
995
+
996
+ Subcommands (fan out across every machine in your workspace):
997
+ status Show svamp + claude-code version per machine
998
+ exec "<command>" Run a shell command on every machine
999
+ upgrade-claude [-v X] Install Claude Code (defaults to svamp-cli's pinned version)
1000
+ upgrade-svamp [-v X] npm install -g svamp-cli@X + svamp daemon restart
1001
+ daemon-restart [--cleanup] Restart daemons (default graceful)
1002
+ push-skill <name> Install/force-refresh a skill on all machines
1003
+
1004
+ Each row reports OK / FAIL / SKIP. Failures don't sink the batch; exit code 1
1005
+ only if at least one machine failed.
1006
+
1007
+ Examples:
1008
+ svamp fleet status
1009
+ svamp fleet exec "uptime"
1010
+ svamp fleet upgrade-claude
1011
+ svamp fleet upgrade-svamp -v 0.2.73
1012
+ svamp fleet push-skill hypha`);
1013
+ process.exit(0);
1014
+ }
1015
+ const flag = (name, short) => {
1016
+ for (let i = 1; i < fleetArgs.length; i++) {
1017
+ if (fleetArgs[i] === `--${name}` || short && fleetArgs[i] === short) return fleetArgs[i + 1];
1018
+ }
1019
+ return void 0;
1020
+ };
1021
+ const hasFlag = (name) => fleetArgs.includes(`--${name}`);
1022
+ if (sub === "status") {
1023
+ const { fleetStatus } = await import('./fleet-B9CWHUv1.mjs');
1024
+ await fleetStatus();
1025
+ } else if (sub === "exec") {
1026
+ const command = fleetArgs.slice(1).filter((a) => !a.startsWith("--")).join(" ");
1027
+ const { fleetExec } = await import('./fleet-B9CWHUv1.mjs');
1028
+ await fleetExec(command, { cwd: flag("cwd") });
1029
+ } else if (sub === "upgrade-claude") {
1030
+ const { fleetUpgradeClaude } = await import('./fleet-B9CWHUv1.mjs');
1031
+ await fleetUpgradeClaude({ version: flag("version", "-v") });
1032
+ } else if (sub === "upgrade-svamp") {
1033
+ const { fleetUpgradeSvamp } = await import('./fleet-B9CWHUv1.mjs');
1034
+ await fleetUpgradeSvamp({ version: flag("version", "-v") });
1035
+ } else if (sub === "daemon-restart") {
1036
+ const { fleetDaemonRestart } = await import('./fleet-B9CWHUv1.mjs');
1037
+ await fleetDaemonRestart({ graceful: !hasFlag("cleanup") });
1038
+ } else if (sub === "push-skill") {
1039
+ const name = fleetArgs[1];
1040
+ const { fleetPushSkill } = await import('./fleet-B9CWHUv1.mjs');
1041
+ await fleetPushSkill(name);
1042
+ } else {
1043
+ console.error(`Unknown fleet subcommand: ${sub}`);
1044
+ console.error('Run "svamp fleet --help" to see available subcommands.');
1045
+ process.exit(1);
1046
+ }
1047
+ process.exit(process.exitCode || 0);
1048
+ }
983
1049
  async function handleSkillsCommand() {
984
1050
  const skillsArgs = args.slice(1);
985
1051
  const skillsSubcommand = skillsArgs[0];
@@ -1371,6 +1437,7 @@ Commands:
1371
1437
 
1372
1438
  Other:
1373
1439
  svamp machine --help Machine sharing & security contexts
1440
+ svamp fleet --help Fan-out commands across all your machines (status, upgrade, exec, push-skill)
1374
1441
  svamp skills --help Skills marketplace (find, install, publish)
1375
1442
  svamp service --help Service exposure (HTTP services from sandboxes)
1376
1443
  svamp agent <name> Start local agent session (no daemon needed)
@@ -1429,7 +1496,7 @@ async function applyClaudeAuthFlags(argv) {
1429
1496
  "--use-hypha-proxy, --use-claude-login, and --anthropic-base-url/--anthropic-api-key are mutually exclusive"
1430
1497
  );
1431
1498
  }
1432
- const mod = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.t; });
1499
+ const mod = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.t; });
1433
1500
  if (hasHypha) {
1434
1501
  mod.setClaudeAuthHyphaProxy();
1435
1502
  console.log("Claude auth configured: hypha-proxy (uses HYPHA_TOKEN live at each spawn).");
@@ -1467,7 +1534,7 @@ async function applyDaemonShareFlag(argv) {
1467
1534
  }
1468
1535
  }
1469
1536
  if (collected.length === 0) return;
1470
- const { updateEnvFile } = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.t; });
1537
+ const { updateEnvFile } = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.t; });
1471
1538
  const seen = /* @__PURE__ */ new Set();
1472
1539
  const deduped = collected.filter((e) => {
1473
1540
  const k = e.toLowerCase();
@@ -1480,7 +1547,7 @@ async function applyDaemonShareFlag(argv) {
1480
1547
  }
1481
1548
  async function handleDaemonAuthCommand(argv) {
1482
1549
  const sub = (argv[0] || "status").toLowerCase();
1483
- const mod = await import('./run-h-QVSVFd.mjs').then(function (n) { return n.t; });
1550
+ const mod = await import('./run-CC-0bNTe.mjs').then(function (n) { return n.t; });
1484
1551
  if (sub === "--help" || sub === "-h" || sub === "help") {
1485
1552
  console.log(`
1486
1553
  svamp daemon auth \u2014 Configure how Claude subprocesses authenticate
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-B0zqVia0.mjs';
3
+ import { connectAndGetMachine } from './commands-DV0URNXH.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-h-QVSVFd.mjs';
8
+ import './run-CC-0bNTe.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
@@ -68,7 +68,7 @@ async function serviceExpose(args) {
68
68
  });
69
69
  return;
70
70
  }
71
- const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
71
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.mjs');
72
72
  const { server, machine } = await connectAndGetMachine();
73
73
  try {
74
74
  const status = await machine.tunnelStart({
@@ -132,7 +132,7 @@ async function serviceServe(args) {
132
132
  }
133
133
  async function serviceList(_args) {
134
134
  try {
135
- const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
135
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.mjs');
136
136
  const { server, machine } = await connectAndGetMachine();
137
137
  try {
138
138
  const tunnels = await machine.tunnelList({});
@@ -161,7 +161,7 @@ async function serviceDelete(args) {
161
161
  process.exit(1);
162
162
  }
163
163
  try {
164
- const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
164
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.mjs');
165
165
  const { server, machine } = await connectAndGetMachine();
166
166
  try {
167
167
  await machine.tunnelStop({ name });
@@ -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 { n as normalizeAllowedUser, l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha, i as buildSessionShareUrl, j as buildMachineShareUrl } from './run-h-QVSVFd.mjs';
5
+ import { n as normalizeAllowedUser, l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha, i as buildSessionShareUrl, j as buildMachineShareUrl } from './run-CC-0bNTe.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -0,0 +1,339 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import os from 'node:os';
4
+ import { c as connectToHypha } from './run-CC-0bNTe.mjs';
5
+ import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-HydRNEt7.mjs';
6
+ import 'os';
7
+ import 'fs/promises';
8
+ import 'fs';
9
+ import 'path';
10
+ import 'url';
11
+ import 'child_process';
12
+ import 'crypto';
13
+ import 'util';
14
+ import 'node:crypto';
15
+ import 'node:child_process';
16
+ import '@agentclientprotocol/sdk';
17
+ import '@modelcontextprotocol/sdk/client/index.js';
18
+ import '@modelcontextprotocol/sdk/client/stdio.js';
19
+ import '@modelcontextprotocol/sdk/types.js';
20
+ import 'zod';
21
+ import 'node:fs/promises';
22
+ import 'node:util';
23
+
24
+ const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
25
+ const DAEMON_STATE_FILE = join(SVAMP_HOME, "daemon.state.json");
26
+ const ENV_FILE = join(SVAMP_HOME, ".env");
27
+ function loadDotEnv() {
28
+ if (!existsSync(ENV_FILE)) return;
29
+ for (const raw of readFileSync(ENV_FILE, "utf-8").split("\n")) {
30
+ const line = raw.trim();
31
+ if (!line || line.startsWith("#")) continue;
32
+ const eq = line.indexOf("=");
33
+ if (eq === -1) continue;
34
+ const k = line.slice(0, eq).trim();
35
+ const v = line.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
36
+ if (!process.env[k]) process.env[k] = v;
37
+ }
38
+ }
39
+ function readDaemonState() {
40
+ if (!existsSync(DAEMON_STATE_FILE)) return null;
41
+ try {
42
+ return JSON.parse(readFileSync(DAEMON_STATE_FILE, "utf-8"));
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+ function suppressHyphaLogs() {
48
+ const ol = console.log, ow = console.warn, oi = console.info, oe = console.error;
49
+ const sw = process.stdout.write.bind(process.stdout);
50
+ const ew = process.stderr.write.bind(process.stderr);
51
+ const isLog = (chunk) => typeof chunk === "string" && (chunk.includes("WebSocket connection") || chunk.includes("Connection established") || chunk.includes("registering service built-in") || chunk.includes("registered service") || chunk.includes("registered all") || chunk.includes("Subscribing to client_") || chunk.includes("subscribed to client_") || chunk.includes("subscribe to client_") || chunk.includes("Cleaning up all sessions") || chunk.includes("WebSocket connection disconnected") || chunk.includes("local RPC disconnection"));
52
+ console.log = () => {
53
+ };
54
+ console.warn = () => {
55
+ };
56
+ console.info = () => {
57
+ };
58
+ console.error = (...args) => {
59
+ if (args.some((a) => isLog(a))) return;
60
+ oe(...args);
61
+ };
62
+ process.stdout.write = (c, ...a) => isLog(c) ? true : sw(c, ...a);
63
+ process.stderr.write = (c, ...a) => isLog(c) ? true : ew(c, ...a);
64
+ return () => {
65
+ console.log = ol;
66
+ console.warn = ow;
67
+ console.info = oi;
68
+ console.error = oe;
69
+ process.stdout.write = sw;
70
+ process.stderr.write = ew;
71
+ };
72
+ }
73
+ function truncate(s, n) {
74
+ if (!s) return s;
75
+ return s.length <= n ? s : s.slice(0, Math.max(1, n - 1)) + "\u2026";
76
+ }
77
+ async function discoverMachines() {
78
+ loadDotEnv();
79
+ const state = readDaemonState();
80
+ const serverUrl = process.env.HYPHA_SERVER_URL || state?.hyphaServerUrl;
81
+ const token = process.env.HYPHA_TOKEN;
82
+ if (!serverUrl) {
83
+ console.error('No Hypha server URL. Run "svamp login <url>" first.');
84
+ process.exit(1);
85
+ }
86
+ const restore = suppressHyphaLogs();
87
+ let server;
88
+ try {
89
+ server = await connectToHypha({ serverUrl, token, name: "svamp-fleet-cli" });
90
+ } catch (err) {
91
+ restore();
92
+ console.error(`Failed to connect to Hypha: ${err.message}`);
93
+ process.exit(1);
94
+ }
95
+ const services = await server.listServices({ query: { type: "svamp-machine" }, include_unlisted: true, _rkwargs: true });
96
+ restore();
97
+ const machines = await Promise.all(services.map(async (svc) => {
98
+ const serviceId = svc.id || svc.name;
99
+ try {
100
+ const rpc = await server.getService(serviceId);
101
+ const info = await rpc.getMachineInfo();
102
+ const label = info.metadata?.displayName || info.metadata?.host || info.metadata?.hostname || serviceId;
103
+ return { serviceId, machineId: info.machineId || serviceId, label, rpc };
104
+ } catch {
105
+ return { serviceId, machineId: serviceId, label: "-", rpc: null };
106
+ }
107
+ }));
108
+ return { server, machines };
109
+ }
110
+ async function fanOut(machines, fn) {
111
+ return Promise.all(machines.map(async (m) => {
112
+ if (!m.rpc) return { machine: m, ok: false, error: "unreachable" };
113
+ try {
114
+ const result = await fn(m);
115
+ return { machine: m, ok: true, result };
116
+ } catch (e) {
117
+ return { machine: m, ok: false, error: e?.message || String(e) };
118
+ }
119
+ }));
120
+ }
121
+ function printResultRow(m, status, detail) {
122
+ const id = truncate(m.machineId, 18).padEnd(20);
123
+ const label = truncate(m.label, 22).padEnd(24);
124
+ const color = status === "OK" ? "\x1B[32m" : status === "SKIP" ? "\x1B[33m" : "\x1B[31m";
125
+ console.log(`${id} ${label} ${color}${status.padEnd(5)}\x1B[0m ${truncate(detail, 60)}`);
126
+ }
127
+ function printHeader(headers, widths) {
128
+ const line = headers.map((h, i) => h.padEnd(widths[i])).join(" ");
129
+ console.log(line);
130
+ console.log("-".repeat(line.length));
131
+ }
132
+ async function fleetStatus() {
133
+ const { server, machines } = await discoverMachines();
134
+ if (machines.length === 0) {
135
+ console.log("No machines found.");
136
+ await server.disconnect();
137
+ return;
138
+ }
139
+ try {
140
+ printHeader(["MACHINE ID", "LABEL", "CLAUDE", "SVAMP", "STATUS"], [20, 24, 12, 10, 12]);
141
+ const rows = await fanOut(machines, async (m) => {
142
+ const [claudeRes, svampRes] = await Promise.all([
143
+ m.rpc.bash("claude --version 2>/dev/null || echo unknown").catch(() => null),
144
+ m.rpc.bash("svamp --version 2>/dev/null || echo unknown").catch(() => null)
145
+ ]);
146
+ const claude = (claudeRes?.stdout || "unknown").trim().split(/\s+/)[0] || "unknown";
147
+ const svamp = (svampRes?.stdout || "unknown").trim().split(/\s+/)[0] || "unknown";
148
+ const info = await m.rpc.getMachineInfo();
149
+ return { claude, svamp, status: info.daemonState?.status || "unknown" };
150
+ });
151
+ for (const r of rows) {
152
+ const id = truncate(r.machine.machineId, 18).padEnd(20);
153
+ const label = truncate(r.machine.label, 22).padEnd(24);
154
+ const claude = (r.ok ? r.result.claude : "-").padEnd(12);
155
+ const svamp = (r.ok ? r.result.svamp : "-").padEnd(10);
156
+ const status = r.ok ? r.result.status : r.error || "fail";
157
+ const color = r.ok && r.result.status === "running" ? "\x1B[32m" : "\x1B[31m";
158
+ console.log(`${id} ${label} ${claude} ${svamp} ${color}${status}\x1B[0m`);
159
+ }
160
+ console.log(`
161
+ ${machines.length} machine(s).`);
162
+ } finally {
163
+ await server.disconnect();
164
+ }
165
+ }
166
+ async function fleetExec(command, opts) {
167
+ if (!command) {
168
+ console.error('Usage: svamp fleet exec "<command>"');
169
+ process.exit(1);
170
+ }
171
+ const { server, machines } = await discoverMachines();
172
+ if (machines.length === 0) {
173
+ console.log("No machines found.");
174
+ await server.disconnect();
175
+ return;
176
+ }
177
+ try {
178
+ printHeader(["MACHINE ID", "LABEL", "EXIT", "OUTPUT"], [20, 24, 6, 60]);
179
+ const rows = await fanOut(machines, async (m) => {
180
+ return await m.rpc.bash(command, opts?.cwd || void 0);
181
+ });
182
+ let failures = 0;
183
+ for (const r of rows) {
184
+ const id = truncate(r.machine.machineId, 18).padEnd(20);
185
+ const label = truncate(r.machine.label, 22).padEnd(24);
186
+ if (!r.ok) {
187
+ console.log(`${id} ${label} ${"-".padEnd(6)} \x1B[31m${truncate(r.error || "error", 60)}\x1B[0m`);
188
+ failures += 1;
189
+ continue;
190
+ }
191
+ const exit = String(r.result.exitCode).padEnd(6);
192
+ const out = (r.result.stdout?.trim() || r.result.stderr?.trim() || "").replace(/\s+/g, " ");
193
+ const color = r.result.exitCode === 0 ? "" : "\x1B[31m";
194
+ const reset = color ? "\x1B[0m" : "";
195
+ console.log(`${id} ${label} ${exit} ${color}${truncate(out, 60)}${reset}`);
196
+ if (r.result.exitCode !== 0) failures += 1;
197
+ }
198
+ console.log(`
199
+ ${rows.length - failures}/${rows.length} succeeded.`);
200
+ if (failures > 0) process.exitCode = 1;
201
+ } finally {
202
+ await server.disconnect();
203
+ }
204
+ }
205
+ async function fleetUpgradeClaude(opts) {
206
+ const version = opts?.version || PINNED_CLAUDE_CODE_VERSION;
207
+ const { server, machines } = await discoverMachines();
208
+ if (machines.length === 0) {
209
+ console.log("No machines found.");
210
+ await server.disconnect();
211
+ return;
212
+ }
213
+ console.log(`Upgrading Claude Code to ${version} on ${machines.length} machine(s)...
214
+ `);
215
+ try {
216
+ printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
217
+ const cmd = `claude install ${version} 2>&1 || claude update 2>&1`;
218
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
219
+ let failures = 0;
220
+ for (const r of rows) {
221
+ if (!r.ok) {
222
+ printResultRow(r.machine, "FAIL", r.error || "");
223
+ failures += 1;
224
+ continue;
225
+ }
226
+ const ok = r.result.exitCode === 0;
227
+ const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
228
+ printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
229
+ if (!ok) failures += 1;
230
+ }
231
+ console.log(`
232
+ ${rows.length - failures}/${rows.length} machines on ${version}.`);
233
+ if (failures > 0) process.exitCode = 1;
234
+ } finally {
235
+ await server.disconnect();
236
+ }
237
+ }
238
+ async function fleetUpgradeSvamp(opts) {
239
+ const version = opts?.version || "latest";
240
+ const { server, machines } = await discoverMachines();
241
+ if (machines.length === 0) {
242
+ console.log("No machines found.");
243
+ await server.disconnect();
244
+ return;
245
+ }
246
+ console.log(`Upgrading svamp-cli to ${version} on ${machines.length} machine(s)...
247
+ `);
248
+ try {
249
+ printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
250
+ const cmd = `bash -lc 'npm install -g svamp-cli@${version} 2>&1 | tail -5 && svamp daemon restart 2>&1 | tail -5'`;
251
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
252
+ let failures = 0;
253
+ for (const r of rows) {
254
+ if (!r.ok) {
255
+ printResultRow(r.machine, "FAIL", r.error || "");
256
+ failures += 1;
257
+ continue;
258
+ }
259
+ const ok = r.result.exitCode === 0;
260
+ const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
261
+ printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
262
+ if (!ok) failures += 1;
263
+ }
264
+ console.log(`
265
+ ${rows.length - failures}/${rows.length} machines upgraded.`);
266
+ console.log("Note: daemons will auto-converge Claude Code to the pinned version on restart.");
267
+ if (failures > 0) process.exitCode = 1;
268
+ } finally {
269
+ await server.disconnect();
270
+ }
271
+ }
272
+ async function fleetDaemonRestart(opts) {
273
+ const { server, machines } = await discoverMachines();
274
+ if (machines.length === 0) {
275
+ console.log("No machines found.");
276
+ await server.disconnect();
277
+ return;
278
+ }
279
+ try {
280
+ printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
281
+ const cmd = opts?.graceful === false ? `svamp daemon stop --cleanup 2>&1 && svamp daemon start 2>&1` : `svamp daemon restart 2>&1`;
282
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
283
+ let failures = 0;
284
+ for (const r of rows) {
285
+ if (!r.ok) {
286
+ printResultRow(r.machine, "FAIL", r.error || "");
287
+ failures += 1;
288
+ continue;
289
+ }
290
+ const ok = r.result.exitCode === 0;
291
+ const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
292
+ printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
293
+ if (!ok) failures += 1;
294
+ }
295
+ console.log(`
296
+ ${rows.length - failures}/${rows.length} restarted.`);
297
+ if (failures > 0) process.exitCode = 1;
298
+ } finally {
299
+ await server.disconnect();
300
+ }
301
+ }
302
+ async function fleetPushSkill(name) {
303
+ if (!name) {
304
+ console.error("Usage: svamp fleet push-skill <name>");
305
+ process.exit(1);
306
+ }
307
+ const { server, machines } = await discoverMachines();
308
+ if (machines.length === 0) {
309
+ console.log("No machines found.");
310
+ await server.disconnect();
311
+ return;
312
+ }
313
+ console.log(`Pushing skill "${name}" to ${machines.length} machine(s)...
314
+ `);
315
+ try {
316
+ printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
317
+ const cmd = `svamp skills install ${name} --force 2>&1`;
318
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
319
+ let failures = 0;
320
+ for (const r of rows) {
321
+ if (!r.ok) {
322
+ printResultRow(r.machine, "FAIL", r.error || "");
323
+ failures += 1;
324
+ continue;
325
+ }
326
+ const ok = r.result.exitCode === 0;
327
+ const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
328
+ printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
329
+ if (!ok) failures += 1;
330
+ }
331
+ console.log(`
332
+ ${rows.length - failures}/${rows.length} updated.`);
333
+ if (failures > 0) process.exitCode = 1;
334
+ } finally {
335
+ await server.disconnect();
336
+ }
337
+ }
338
+
339
+ export { fleetDaemonRestart, fleetExec, fleetPushSkill, fleetStatus, fleetUpgradeClaude, fleetUpgradeSvamp };
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-h-QVSVFd.mjs';
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-CC-0bNTe.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.2.71";
2
+ var version = "0.2.73";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -19,7 +19,7 @@ var exports$1 = {
19
19
  var scripts = {
20
20
  build: "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && tsc --noEmit && pkgroll",
21
21
  typecheck: "tsc --noEmit",
22
- test: "npx tsx test/test-context-window.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-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-claude-auth.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-session-send-query.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",
22
+ test: "npx tsx test/test-context-window.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-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-claude-auth.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-session-send-query.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 && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs",
23
23
  "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
24
24
  dev: "tsx src/cli.ts",
25
25
  "dev:daemon": "tsx src/cli.ts daemon start-sync",
@@ -0,0 +1,58 @@
1
+ import { spawn, exec as exec$1 } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+
4
+ const PINNED_CLAUDE_CODE_VERSION = "2.1.168";
5
+ const exec = promisify(exec$1);
6
+ async function readClaudeVersion() {
7
+ try {
8
+ const { stdout } = await exec("claude --version", { timeout: 1e4 });
9
+ const m = stdout.trim().match(/(\d+\.\d+\.\d+(?:[-.][\w]+)?)/);
10
+ return m ? m[1] : null;
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+ function runInstaller(version, log) {
16
+ return new Promise((resolve) => {
17
+ const proc = spawn("claude", ["install", version], { stdio: ["ignore", "pipe", "pipe"] });
18
+ let stderr = "";
19
+ proc.stdout?.on("data", (b) => log(`[claude install] ${b.toString().trimEnd()}`));
20
+ proc.stderr?.on("data", (b) => {
21
+ const s = b.toString();
22
+ stderr += s;
23
+ log(`[claude install] ${s.trimEnd()}`);
24
+ });
25
+ proc.on("error", (e) => {
26
+ stderr += String(e);
27
+ resolve({ ok: false, stderr });
28
+ });
29
+ proc.on("exit", (code) => resolve({ ok: code === 0, stderr }));
30
+ });
31
+ }
32
+ async function ensureClaudeCodeVersion(log, pinned = PINNED_CLAUDE_CODE_VERSION, opts) {
33
+ const autoInstall = opts?.allowAutoInstall !== false;
34
+ const current = await readClaudeVersion();
35
+ if (!current) {
36
+ log(`[claude-version] 'claude' not on PATH \u2014 please run the Anthropic installer once: curl -fsSL https://claude.ai/install.sh | sh`);
37
+ return { kind: "not-installed", want: pinned };
38
+ }
39
+ if (current === pinned) {
40
+ log(`[claude-version] OK \u2014 claude ${current} matches pinned ${pinned}`);
41
+ return { kind: "ok", version: current };
42
+ }
43
+ if (!autoInstall) {
44
+ log(`[claude-version] DRIFT \u2014 installed ${current} \u2260 pinned ${pinned} (auto-install disabled)`);
45
+ return { kind: "skipped", reason: `installed=${current} pinned=${pinned}` };
46
+ }
47
+ log(`[claude-version] DRIFT \u2014 installed ${current} \u2260 pinned ${pinned}; running 'claude install ${pinned}'`);
48
+ const { ok, stderr } = await runInstaller(pinned, log);
49
+ if (!ok) {
50
+ log(`[claude-version] install failed for ${pinned}: ${stderr.trim().slice(0, 400)}`);
51
+ return { kind: "install-failed", from: current, want: pinned, error: stderr.slice(0, 400) };
52
+ }
53
+ const after = await readClaudeVersion();
54
+ log(`[claude-version] installer finished; now reports ${after ?? "unknown"}`);
55
+ return { kind: "installed", from: current, to: after ?? pinned };
56
+ }
57
+
58
+ export { PINNED_CLAUDE_CODE_VERSION, ensureClaudeCodeVersion };
@@ -6477,6 +6477,8 @@ You are running inside a Svamp session (id: ${sessionId}) on Hypha Cloud. Use th
6477
6477
  ## Parallel Agents
6478
6478
 
6479
6479
  You may be running in parallel with other agents \u2014 possibly sharing the same working directory. Stay aware of peers: if files change unexpectedly, or you want to initiate collaboration or coordinate to avoid edit conflicts, discover peers with \`svamp session list\` and inspect their activity with \`svamp session info <id>\` / \`svamp session messages <id>\`. Reach out via \`svamp session send <id> "<msg>"\` when coordination is genuinely useful. Keep cross-agent chatter minimal and purposeful: never reply reflexively, avoid ping-pong loops (especially across restarts where queued messages may re-fire), and only initiate contact with a concrete reason. Treat peer messages as informational unless they clearly require action.
6480
+
6481
+ **Inbox messages between agents** arrive wrapped as \`<svamp-message message-id="\u2026" from="agent:\u2026" from-session="\u2026" \u2026>BODY</svamp-message>\`. A plain user turn has no such wrapper \u2014 that's how you tell them apart. To reply to the sender, use \`svamp session inbox reply <message-id> "<body>"\` (auto-routes to \`from-session\`, preserves \`thread-id\`). Replying is optional \u2014 only when it's useful for the work.
6480
6482
  `;
6481
6483
  }
6482
6484
 
@@ -7867,10 +7869,18 @@ async function startDaemon(options) {
7867
7869
  const list = loadExposedTunnels().filter((t) => t.name !== name);
7868
7870
  saveExposedTunnels(list);
7869
7871
  }
7870
- const { ServeManager } = await import('./serveManager-Bzjw2bO6.mjs');
7872
+ const { ServeManager } = await import('./serveManager-B9cemdRt.mjs');
7871
7873
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
7872
7874
  ensureAutoInstalledSkills(logger).catch(() => {
7873
7875
  });
7876
+ (async () => {
7877
+ try {
7878
+ const { ensureClaudeCodeVersion } = await import('./pinnedClaudeCode-HydRNEt7.mjs');
7879
+ await ensureClaudeCodeVersion((msg) => logger.log(msg));
7880
+ } catch (e) {
7881
+ logger.log(`[claude-version] check failed: ${e?.message || e}`);
7882
+ }
7883
+ })();
7874
7884
  preventMachineSleep(logger);
7875
7885
  try {
7876
7886
  logger.log("Connecting to Hypha server...");
@@ -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 { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';
5
- import { c as connectToHypha, a as registerSessionService, k as generateHookSettings } from './run-h-QVSVFd.mjs';
5
+ import { c as connectToHypha, a as registerSessionService, k as generateHookSettings } from './run-CC-0bNTe.mjs';
6
6
  import { createServer } from 'node:http';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { createInterface } from 'node:readline';
@@ -54,7 +54,7 @@ async function handleServeCommand() {
54
54
  }
55
55
  }
56
56
  async function serveAdd(args, machineId) {
57
- const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
57
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.mjs');
58
58
  const pos = positionalArgs(args);
59
59
  const name = pos[0];
60
60
  if (!name) {
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
93
93
  }
94
94
  }
95
95
  async function serveApply(args, machineId) {
96
- const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
96
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.mjs');
97
97
  const fs = await import('fs');
98
98
  const yaml = await import('yaml');
99
99
  const file = positionalArgs(args)[0];
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
182
182
  }
183
183
  }
184
184
  async function serveRemove(args, machineId) {
185
- const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
185
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.mjs');
186
186
  const pos = positionalArgs(args);
187
187
  const name = pos[0];
188
188
  if (!name) {
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
202
202
  }
203
203
  }
204
204
  async function serveList(args, machineId) {
205
- const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
205
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.mjs');
206
206
  const all = hasFlag(args, "--all", "-a");
207
207
  const json = hasFlag(args, "--json");
208
208
  const sessionId = getFlag(args, "--session");
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
235
235
  }
236
236
  }
237
237
  async function serveInfo(machineId) {
238
- const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
238
+ const { connectAndGetMachine } = await import('./commands-DV0URNXH.mjs');
239
239
  const { machine, server } = await connectAndGetMachine(machineId);
240
240
  try {
241
241
  const info = await machine.serveInfo();
@@ -4,7 +4,7 @@ import * as fs from 'fs';
4
4
  import * as http from 'http';
5
5
  import * as net from 'net';
6
6
  import * as path from 'path';
7
- import { S as ServeAuth, h as hasCookieToken } from './run-h-QVSVFd.mjs';
7
+ import { S as ServeAuth, h as hasCookieToken } from './run-CC-0bNTe.mjs';
8
8
  import 'os';
9
9
  import 'fs/promises';
10
10
  import 'url';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.2.71",
3
+ "version": "0.2.73",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -20,7 +20,7 @@
20
20
  "scripts": {
21
21
  "build": "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && tsc --noEmit && pkgroll",
22
22
  "typecheck": "tsc --noEmit",
23
- "test": "npx tsx test/test-context-window.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-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-claude-auth.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-session-send-query.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": "npx tsx test/test-context-window.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-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-claude-auth.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-session-send-query.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 && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs",
24
24
  "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
25
25
  "dev": "tsx src/cli.ts",
26
26
  "dev:daemon": "tsx src/cli.ts daemon start-sync",