svamp-cli 0.1.65 → 0.1.67
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 +229 -172
- package/dist/{commands-Dc_6JdE_.mjs → commands-CdQ8qr6l.mjs} +2 -2
- package/dist/{commands-FAWhkKtz.mjs → commands-Dpc6QK0m.mjs} +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{package-CvCDrFPH.mjs → package-BeHXZuag.mjs} +2 -2
- package/dist/{run-DPhSmbr1.mjs → run-BDmLH9T8.mjs} +12 -2
- package/dist/{run-DTwMJ7dx.mjs → run-ez-QlRKy.mjs} +1 -1
- package/dist/storageCommands-CKhntx1P.mjs +154 -0
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as stopDaemon, s as startDaemon, d as daemonStatus } from './run-
|
|
1
|
+
import { b as stopDaemon, s as startDaemon, d as daemonStatus } from './run-BDmLH9T8.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -109,7 +109,7 @@ async function main() {
|
|
|
109
109
|
const { handleServiceCommand } = await import('./commands-CF32XIau.mjs');
|
|
110
110
|
await handleServiceCommand();
|
|
111
111
|
} else if (subcommand === "process" || subcommand === "proc") {
|
|
112
|
-
const { processCommand } = await import('./commands-
|
|
112
|
+
const { processCommand } = await import('./commands-CdQ8qr6l.mjs');
|
|
113
113
|
let machineId;
|
|
114
114
|
const processArgs = args.slice(1);
|
|
115
115
|
const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
|
|
@@ -127,7 +127,7 @@ async function main() {
|
|
|
127
127
|
} else if (!subcommand || subcommand === "start") {
|
|
128
128
|
await handleInteractiveCommand();
|
|
129
129
|
} else if (subcommand === "--version" || subcommand === "-v") {
|
|
130
|
-
const pkg = await import('./package-
|
|
130
|
+
const pkg = await import('./package-BeHXZuag.mjs').catch(() => ({ default: { version: "unknown" } }));
|
|
131
131
|
console.log(`svamp version: ${pkg.default.version}`);
|
|
132
132
|
} else {
|
|
133
133
|
console.error(`Unknown command: ${subcommand}`);
|
|
@@ -136,7 +136,7 @@ async function main() {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
async function handleInteractiveCommand() {
|
|
139
|
-
const { runInteractive } = await import('./run-
|
|
139
|
+
const { runInteractive } = await import('./run-ez-QlRKy.mjs');
|
|
140
140
|
const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
|
|
141
141
|
let directory = process.cwd();
|
|
142
142
|
let resumeSessionId;
|
|
@@ -181,7 +181,7 @@ async function handleAgentCommand() {
|
|
|
181
181
|
return;
|
|
182
182
|
}
|
|
183
183
|
if (agentArgs[0] === "list") {
|
|
184
|
-
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-
|
|
184
|
+
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-BDmLH9T8.mjs').then(function (n) { return n.i; });
|
|
185
185
|
console.log("Known agents:");
|
|
186
186
|
for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
|
|
187
187
|
console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
|
|
@@ -193,7 +193,7 @@ async function handleAgentCommand() {
|
|
|
193
193
|
console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
|
|
194
194
|
return;
|
|
195
195
|
}
|
|
196
|
-
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-
|
|
196
|
+
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-BDmLH9T8.mjs').then(function (n) { return n.i; });
|
|
197
197
|
let cwd = process.cwd();
|
|
198
198
|
const filteredArgs = [];
|
|
199
199
|
for (let i = 0; i < agentArgs.length; i++) {
|
|
@@ -217,12 +217,12 @@ async function handleAgentCommand() {
|
|
|
217
217
|
console.log(`Starting ${config.agentName} agent in ${cwd}...`);
|
|
218
218
|
let backend;
|
|
219
219
|
if (KNOWN_MCP_AGENTS[config.agentName]) {
|
|
220
|
-
const { CodexMcpBackend } = await import('./run-
|
|
220
|
+
const { CodexMcpBackend } = await import('./run-BDmLH9T8.mjs').then(function (n) { return n.j; });
|
|
221
221
|
backend = new CodexMcpBackend({ cwd, log: logFn });
|
|
222
222
|
} else {
|
|
223
|
-
const { AcpBackend } = await import('./run-
|
|
224
|
-
const { GeminiTransport } = await import('./run-
|
|
225
|
-
const { DefaultTransport } = await import('./run-
|
|
223
|
+
const { AcpBackend } = await import('./run-BDmLH9T8.mjs').then(function (n) { return n.h; });
|
|
224
|
+
const { GeminiTransport } = await import('./run-BDmLH9T8.mjs').then(function (n) { return n.G; });
|
|
225
|
+
const { DefaultTransport } = await import('./run-BDmLH9T8.mjs').then(function (n) { return n.D; });
|
|
226
226
|
const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
|
|
227
227
|
backend = new AcpBackend({
|
|
228
228
|
agentName: config.agentName,
|
|
@@ -340,7 +340,7 @@ async function handleSessionCommand() {
|
|
|
340
340
|
printSessionHelp();
|
|
341
341
|
return;
|
|
342
342
|
}
|
|
343
|
-
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionQueueAdd, sessionQueueList, sessionQueueClear } = await import('./commands-
|
|
343
|
+
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionQueueAdd, sessionQueueList, sessionQueueClear } = await import('./commands-Dpc6QK0m.mjs');
|
|
344
344
|
const parseFlagStr = (flag, shortFlag) => {
|
|
345
345
|
for (let i = 1; i < sessionArgs.length; i++) {
|
|
346
346
|
if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
|
|
@@ -400,7 +400,7 @@ async function handleSessionCommand() {
|
|
|
400
400
|
allowDomain.push(sessionArgs[++i]);
|
|
401
401
|
}
|
|
402
402
|
}
|
|
403
|
-
const { parseShareArg } = await import('./commands-
|
|
403
|
+
const { parseShareArg } = await import('./commands-Dpc6QK0m.mjs');
|
|
404
404
|
const shareEntries = share.map((s) => parseShareArg(s));
|
|
405
405
|
await sessionSpawn(agent, dir, targetMachineId, {
|
|
406
406
|
message,
|
|
@@ -484,7 +484,7 @@ async function handleSessionCommand() {
|
|
|
484
484
|
console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
|
|
485
485
|
process.exit(1);
|
|
486
486
|
}
|
|
487
|
-
const { sessionApprove } = await import('./commands-
|
|
487
|
+
const { sessionApprove } = await import('./commands-Dpc6QK0m.mjs');
|
|
488
488
|
const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
489
489
|
await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
|
|
490
490
|
json: hasFlag("--json")
|
|
@@ -494,7 +494,7 @@ async function handleSessionCommand() {
|
|
|
494
494
|
console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
|
|
495
495
|
process.exit(1);
|
|
496
496
|
}
|
|
497
|
-
const { sessionDeny } = await import('./commands-
|
|
497
|
+
const { sessionDeny } = await import('./commands-Dpc6QK0m.mjs');
|
|
498
498
|
const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
499
499
|
await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
|
|
500
500
|
json: hasFlag("--json")
|
|
@@ -597,7 +597,7 @@ async function handleMachineCommand() {
|
|
|
597
597
|
return;
|
|
598
598
|
}
|
|
599
599
|
if (machineSubcommand === "share") {
|
|
600
|
-
const { machineShare } = await import('./commands-
|
|
600
|
+
const { machineShare } = await import('./commands-Dpc6QK0m.mjs');
|
|
601
601
|
let machineId;
|
|
602
602
|
const shareArgs = [];
|
|
603
603
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
@@ -627,7 +627,7 @@ async function handleMachineCommand() {
|
|
|
627
627
|
}
|
|
628
628
|
await machineShare(machineId, { add, remove, list, configPath, showConfig });
|
|
629
629
|
} else if (machineSubcommand === "exec") {
|
|
630
|
-
const { machineExec } = await import('./commands-
|
|
630
|
+
const { machineExec } = await import('./commands-Dpc6QK0m.mjs');
|
|
631
631
|
let machineId;
|
|
632
632
|
let cwd;
|
|
633
633
|
const cmdParts = [];
|
|
@@ -647,7 +647,7 @@ async function handleMachineCommand() {
|
|
|
647
647
|
}
|
|
648
648
|
await machineExec(machineId, command, cwd);
|
|
649
649
|
} else if (machineSubcommand === "info") {
|
|
650
|
-
const { machineInfo } = await import('./commands-
|
|
650
|
+
const { machineInfo } = await import('./commands-Dpc6QK0m.mjs');
|
|
651
651
|
let machineId;
|
|
652
652
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
653
653
|
if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
|
|
@@ -670,7 +670,7 @@ async function handleMachineCommand() {
|
|
|
670
670
|
const { machineNotify } = await import('./agentCommands-C6iGblcL.mjs');
|
|
671
671
|
await machineNotify(message, level);
|
|
672
672
|
} else if (machineSubcommand === "ls") {
|
|
673
|
-
const { machineLs } = await import('./commands-
|
|
673
|
+
const { machineLs } = await import('./commands-Dpc6QK0m.mjs');
|
|
674
674
|
let machineId;
|
|
675
675
|
let showHidden = false;
|
|
676
676
|
let path;
|
|
@@ -684,6 +684,43 @@ async function handleMachineCommand() {
|
|
|
684
684
|
}
|
|
685
685
|
}
|
|
686
686
|
await machineLs(machineId, path, showHidden);
|
|
687
|
+
} else if (machineSubcommand === "storage") {
|
|
688
|
+
const { storageList, storageOrphans, storageMarkOrphan, storageUnmarkOrphan, storageDelete, printStorageHelp } = await import('./storageCommands-CKhntx1P.mjs');
|
|
689
|
+
const storageArgs = machineArgs.slice(1);
|
|
690
|
+
const storageSubcmd = storageArgs[0];
|
|
691
|
+
if (!storageSubcmd || storageSubcmd === "--help" || storageSubcmd === "-h") {
|
|
692
|
+
printStorageHelp();
|
|
693
|
+
} else if (storageSubcmd === "list") {
|
|
694
|
+
await storageList();
|
|
695
|
+
} else if (storageSubcmd === "orphans") {
|
|
696
|
+
const mark = storageArgs.includes("--mark");
|
|
697
|
+
await storageOrphans(mark);
|
|
698
|
+
} else if (storageSubcmd === "mark-orphan") {
|
|
699
|
+
const ns = storageArgs[1];
|
|
700
|
+
if (!ns) {
|
|
701
|
+
console.error("Usage: svamp machine storage mark-orphan <namespace>");
|
|
702
|
+
process.exit(1);
|
|
703
|
+
}
|
|
704
|
+
await storageMarkOrphan(ns);
|
|
705
|
+
} else if (storageSubcmd === "unmark-orphan") {
|
|
706
|
+
const ns = storageArgs[1];
|
|
707
|
+
if (!ns) {
|
|
708
|
+
console.error("Usage: svamp machine storage unmark-orphan <namespace>");
|
|
709
|
+
process.exit(1);
|
|
710
|
+
}
|
|
711
|
+
await storageUnmarkOrphan(ns);
|
|
712
|
+
} else if (storageSubcmd === "delete") {
|
|
713
|
+
const ns = storageArgs[1];
|
|
714
|
+
if (!ns) {
|
|
715
|
+
console.error("Usage: svamp machine storage delete <namespace>");
|
|
716
|
+
process.exit(1);
|
|
717
|
+
}
|
|
718
|
+
await storageDelete(ns);
|
|
719
|
+
} else {
|
|
720
|
+
console.error(`Unknown storage command: ${storageSubcmd}`);
|
|
721
|
+
printStorageHelp();
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
687
724
|
} else {
|
|
688
725
|
console.error(`Unknown machine command: ${machineSubcommand}`);
|
|
689
726
|
printMachineHelp();
|
|
@@ -1044,50 +1081,42 @@ function printHelp() {
|
|
|
1044
1081
|
console.log(`
|
|
1045
1082
|
svamp \u2014 AI workspace on Hypha Cloud
|
|
1046
1083
|
|
|
1047
|
-
|
|
1048
|
-
svamp
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
svamp
|
|
1056
|
-
svamp
|
|
1057
|
-
svamp
|
|
1058
|
-
svamp
|
|
1059
|
-
svamp
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
svamp session
|
|
1063
|
-
svamp session
|
|
1064
|
-
svamp session
|
|
1065
|
-
svamp session
|
|
1066
|
-
svamp session
|
|
1067
|
-
svamp
|
|
1068
|
-
svamp
|
|
1069
|
-
svamp
|
|
1070
|
-
svamp
|
|
1071
|
-
svamp
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
svamp
|
|
1075
|
-
svamp
|
|
1076
|
-
svamp
|
|
1077
|
-
svamp
|
|
1078
|
-
svamp
|
|
1079
|
-
svamp service --help Show all service commands
|
|
1080
|
-
svamp agent list List known agents
|
|
1081
|
-
svamp agent <name> Start local agent session (gemini, codex)
|
|
1082
|
-
svamp --version Show version
|
|
1083
|
-
svamp --help Show this help
|
|
1084
|
+
Quick start \u2014 spawn an agent to do a task (use -p bypassPermissions to run autonomously):
|
|
1085
|
+
svamp session spawn claude -d ./project -p bypassPermissions --message "fix tests" --wait
|
|
1086
|
+
|
|
1087
|
+
Note: without -p bypassPermissions the agent PAUSES waiting for human approval of each
|
|
1088
|
+
tool use. This will cause it to hang in automated/agent contexts. Use bypassPermissions
|
|
1089
|
+
for autonomous work, or "default" only when a human is actively watching.
|
|
1090
|
+
|
|
1091
|
+
Commands:
|
|
1092
|
+
svamp Start interactive Claude session (synced to cloud)
|
|
1093
|
+
svamp login [url] Login to Hypha (opens browser, stores token)
|
|
1094
|
+
svamp daemon start Start the background daemon (required for sessions)
|
|
1095
|
+
svamp daemon status Show daemon status
|
|
1096
|
+
svamp daemon --help Show all daemon commands
|
|
1097
|
+
|
|
1098
|
+
Session management (requires daemon running):
|
|
1099
|
+
svamp session spawn <agent> [opts] Spawn a new agent session (agents: claude, gemini, codex)
|
|
1100
|
+
svamp session send <id> <message> Send message to a session (--wait to block until done)
|
|
1101
|
+
svamp session wait <id> Wait for agent to become idle
|
|
1102
|
+
svamp session messages <id> Show message history (--last N, --json, --raw)
|
|
1103
|
+
svamp session info <id> Show session status & pending permissions
|
|
1104
|
+
svamp session list List sessions
|
|
1105
|
+
svamp session stop <id> Stop a session
|
|
1106
|
+
svamp session attach <id> Attach interactive terminal (stdin/stdout)
|
|
1107
|
+
svamp session approve/deny <id> Approve or deny pending permission requests
|
|
1108
|
+
svamp session --help Show ALL session commands and detailed options
|
|
1109
|
+
|
|
1110
|
+
Other:
|
|
1111
|
+
svamp machine --help Machine sharing & security contexts
|
|
1112
|
+
svamp skills --help Skills marketplace (find, install, publish)
|
|
1113
|
+
svamp service --help Service exposure (HTTP services from sandboxes)
|
|
1114
|
+
svamp agent <name> Start local agent session (no daemon needed)
|
|
1115
|
+
svamp --version Show version
|
|
1084
1116
|
|
|
1085
1117
|
Interactive mode:
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
it's visible in the web app. When a message arrives from the web app,
|
|
1089
|
-
svamp switches to remote mode to process it, then you can press
|
|
1090
|
-
Space-Space to return to local mode.
|
|
1118
|
+
Run 'svamp' with no arguments to start Claude in your terminal with cloud sync.
|
|
1119
|
+
Messages from the web app auto-switch to remote mode. Space-Space returns to local.
|
|
1091
1120
|
|
|
1092
1121
|
Environment variables:
|
|
1093
1122
|
HYPHA_SERVER_URL Hypha server URL (required for cloud sync)
|
|
@@ -1113,149 +1142,170 @@ Usage:
|
|
|
1113
1142
|
}
|
|
1114
1143
|
function printSessionHelp() {
|
|
1115
1144
|
console.log(`
|
|
1116
|
-
svamp session \u2014
|
|
1145
|
+
svamp session \u2014 Spawn and manage AI agent sessions
|
|
1117
1146
|
|
|
1118
|
-
|
|
1119
|
-
svamp session list [--active] [--json] List sessions (alias: ls)
|
|
1120
|
-
svamp session machines List discoverable machines
|
|
1121
|
-
svamp session spawn <agent> [-d <path>] [options] Spawn a new session
|
|
1122
|
-
svamp session stop <id> Stop a session
|
|
1123
|
-
svamp session info <id> [--json] Show status, activity & pending permissions
|
|
1124
|
-
svamp session send <id> <message> [--wait] [--timeout N] [--json]
|
|
1125
|
-
Send a message to a session
|
|
1126
|
-
svamp session wait <id> [--timeout N] [--json] Wait for agent to become idle
|
|
1127
|
-
svamp session messages <id> [--last N] [--json] [--raw] [--after N] [--limit N]
|
|
1128
|
-
Show messages (alias: msgs)
|
|
1129
|
-
svamp session attach <id> Attach to session (interactive terminal)
|
|
1130
|
-
svamp session approve <id> [request-id] [--json] Approve pending permission(s)
|
|
1131
|
-
svamp session deny <id> [request-id] [--json] Deny pending permission(s)
|
|
1147
|
+
QUICK START \u2014 Spawn a session, give it a task, get results:
|
|
1132
1148
|
|
|
1133
|
-
|
|
1134
|
-
-d
|
|
1135
|
-
|
|
1136
|
-
--message <msg> Send initial message after spawn
|
|
1137
|
-
--wait Wait for agent to become idle before returning
|
|
1138
|
-
--worktree Create a git worktree branch (at .dev/worktree/<name>)
|
|
1139
|
-
--isolate Force OS-level sandbox isolation
|
|
1140
|
-
--security-context <path> Apply security context config (JSON file)
|
|
1141
|
-
--share <email>[:<role>] Share with user (repeatable). Role: view, interact, admin
|
|
1142
|
-
--deny-network Block all network access
|
|
1143
|
-
--deny-read <path> Deny reading path (repeatable)
|
|
1144
|
-
--allow-write <path> Allow writing path (repeatable)
|
|
1145
|
-
--allow-domain <domain> Allow network domain (repeatable)
|
|
1149
|
+
# One-liner: spawn agent, send task, wait for completion
|
|
1150
|
+
svamp session spawn claude -d ./my-project -p bypassPermissions \\
|
|
1151
|
+
--message "fix the failing tests" --wait
|
|
1146
1152
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1153
|
+
# The command prints the session ID (e.g., "abc12345-..."). Use it to:
|
|
1154
|
+
svamp session messages abc1 --last 10 # read output (prefix match on ID)
|
|
1155
|
+
svamp session send abc1 "now run lint" --wait # send follow-up, wait for done
|
|
1156
|
+
svamp session stop abc1 # stop when finished
|
|
1151
1157
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
when it needs to use a tool that requires approval. Use these commands to manage:
|
|
1158
|
+
# Spawn on a remote/cloud machine:
|
|
1159
|
+
svamp session spawn claude -d /workspace -m cloud-box --message "deploy" --wait
|
|
1155
1160
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
3. Approve: svamp session approve <id> (approve all pending)
|
|
1161
|
-
svamp session approve <id> <rid> (approve specific request)
|
|
1162
|
-
4. Deny: svamp session deny <id> (deny all pending)
|
|
1163
|
-
svamp session deny <id> <rid> (deny specific request)
|
|
1161
|
+
AGENTS:
|
|
1162
|
+
claude Claude Code \u2014 full coding agent (default)
|
|
1163
|
+
gemini Google Gemini via ACP protocol
|
|
1164
|
+
codex OpenAI Codex via MCP protocol
|
|
1164
1165
|
|
|
1165
|
-
|
|
1166
|
+
Usage: svamp session spawn <agent> [options]
|
|
1167
|
+
The <agent> argument is required. Use "claude" if unsure.
|
|
1166
1168
|
|
|
1167
|
-
|
|
1168
|
-
--last N Show only the last N messages
|
|
1169
|
-
--after N Start after sequence number N
|
|
1170
|
-
--limit N Max messages to fetch from server (default: 1000, cap: 500 per page)
|
|
1171
|
-
--json Output as JSON (FormattedMessage: id, seq, role, text, createdAt)
|
|
1172
|
-
--raw With --json: output full raw message objects (includes tool_use args,
|
|
1173
|
-
tool_result content, thinking blocks \u2014 use for programmatic parsing)
|
|
1169
|
+
COMMANDS:
|
|
1174
1170
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1171
|
+
Lifecycle:
|
|
1172
|
+
spawn <agent> [-d <path>] [options] Spawn a new agent session
|
|
1173
|
+
stop <id> Stop a running session
|
|
1174
|
+
list [--active] [--json] List sessions (alias: ls)
|
|
1175
|
+
machines List discoverable machines
|
|
1176
|
+
info <id> [--json] Show status & pending permissions
|
|
1179
1177
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1178
|
+
Communicate:
|
|
1179
|
+
send <id> <message> [--wait] [--timeout N] [--json]
|
|
1180
|
+
Send a message to a running session
|
|
1181
|
+
messages <id> [--last N] [--json] Read message history (alias: msgs)
|
|
1182
|
+
wait <id> [--timeout N] [--json] Wait for agent to become idle
|
|
1183
|
+
attach <id> Attach interactive terminal (stdin/stdout)
|
|
1182
1184
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
+
Permissions (when agent pauses for approval):
|
|
1186
|
+
approve <id> [request-id] [--json] Approve pending tool permission(s)
|
|
1187
|
+
deny <id> [request-id] [--json] Deny pending tool permission(s)
|
|
1185
1188
|
|
|
1186
|
-
Sharing:
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1189
|
+
Sharing:
|
|
1190
|
+
share <id> --list List shared users
|
|
1191
|
+
share <id> --add <email>[:<role>] Share with user (role: view|interact|admin)
|
|
1192
|
+
share <id> --remove <email> Remove shared user
|
|
1193
|
+
share <id> --public <view|interact|off> Set public link access
|
|
1191
1194
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1195
|
+
Ralph Loop (iterative automation):
|
|
1196
|
+
ralph-start <id> "<task>" [options] Start iterative loop
|
|
1197
|
+
ralph-cancel <id> Cancel loop
|
|
1198
|
+
ralph-status <id> Show loop status
|
|
1194
1199
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1200
|
+
Message Queue:
|
|
1201
|
+
queue add <id> "<message>" Queue a message for later
|
|
1202
|
+
queue list <id> List queued messages
|
|
1203
|
+
queue clear <id> Clear queue
|
|
1198
1204
|
|
|
1199
|
-
|
|
1200
|
-
--
|
|
1201
|
-
--
|
|
1202
|
-
--
|
|
1203
|
-
--
|
|
1204
|
-
|
|
1205
|
-
--
|
|
1206
|
-
--
|
|
1205
|
+
SPAWN OPTIONS:
|
|
1206
|
+
-d, --directory <path> Working directory (REQUIRED for meaningful work)
|
|
1207
|
+
-p, --permission-mode <mode> Permission mode (see below). Default: "default"
|
|
1208
|
+
--message <msg> Send this message to the agent immediately after spawn
|
|
1209
|
+
--wait Block until agent finishes (idle). Combine with --message
|
|
1210
|
+
for fire-and-forget: spawn \u2192 task \u2192 wait \u2192 done.
|
|
1211
|
+
--timeout <sec> Timeout for --wait (default: no timeout)
|
|
1212
|
+
--worktree Create isolated git worktree branch (.dev/worktree/<name>)
|
|
1213
|
+
--tags <tag1,tag2> Comma-separated tags for session grouping
|
|
1214
|
+
--parent <sessionId> Set parent session (auto-set from SVAMP_SESSION_ID env)
|
|
1207
1215
|
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1216
|
+
PERMISSION MODES:
|
|
1217
|
+
default \u26A0 Agent PAUSES and waits for human approval before each tool use.
|
|
1218
|
+
The agent will be stuck (exit code 2) until you run approve/deny.
|
|
1219
|
+
DO NOT use default mode in automated pipelines \u2014 the agent will hang.
|
|
1220
|
+
acceptEdits Auto-approve file edits. Still pauses for bash & dangerous tools.
|
|
1221
|
+
bypassPermissions Auto-approve everything. No prompts, no pauses. Fully autonomous.
|
|
1222
|
+
Use this for automation, CI, or when spawning from another agent.
|
|
1211
1223
|
|
|
1212
|
-
|
|
1224
|
+
\u26A1 For agents/scripts: ALWAYS pass -p bypassPermissions unless you have a human
|
|
1225
|
+
monitoring the session who can approve/deny tool requests in real time.
|
|
1213
1226
|
|
|
1214
|
-
|
|
1227
|
+
PERMISSION WORKFLOW (only relevant for "default" and "acceptEdits" modes):
|
|
1228
|
+
1. svamp session wait <id> --json \u2192 exit code 2 means permission pending
|
|
1229
|
+
2. svamp session info <id> --json \u2192 shows pendingPermissions array
|
|
1230
|
+
3. svamp session approve <id> \u2192 approve all pending (or approve <id> <rid>)
|
|
1231
|
+
4. svamp session deny <id> \u2192 deny all pending (or deny <id> <rid>)
|
|
1215
1232
|
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1233
|
+
ISOLATION & SHARING OPTIONS (for spawn):
|
|
1234
|
+
--isolate Force OS-level sandbox (even without sharing)
|
|
1235
|
+
--security-context <path> Apply security context config (JSON file)
|
|
1236
|
+
--share <email>[:<role>] Share session (repeatable). Role: view|interact|admin
|
|
1237
|
+
--deny-network Block all network access
|
|
1238
|
+
--deny-read <path> Deny reading path (repeatable)
|
|
1239
|
+
--allow-write <path> Allow writing path (repeatable)
|
|
1240
|
+
--allow-domain <domain> Allow network domain (repeatable)
|
|
1241
|
+
|
|
1242
|
+
MESSAGES OPTIONS:
|
|
1243
|
+
--last N Show last N messages only
|
|
1244
|
+
--after N Start after sequence number N (for pagination)
|
|
1245
|
+
--limit N Max messages to fetch (default: 1000)
|
|
1246
|
+
--json Output as JSON array of {id, seq, role, text, createdAt}
|
|
1247
|
+
--raw With --json: include full raw objects (tool_use, tool_result, thinking)
|
|
1248
|
+
|
|
1249
|
+
RALPH LOOP OPTIONS (for ralph-start):
|
|
1250
|
+
--context-mode <mode> fresh (new process each iteration) or continue (same process)
|
|
1251
|
+
--completion-promise <text> Completion signal text (default: DONE)
|
|
1252
|
+
--max-iterations <n> Max iterations (default: 10)
|
|
1253
|
+
--cooldown <sec> Delay between iterations (default: 1)
|
|
1254
|
+
|
|
1255
|
+
EXIT CODES (for wait, send --wait, spawn --wait):
|
|
1256
|
+
0 Agent is idle (task completed successfully)
|
|
1257
|
+
1 Error (timeout, connection failure, session not found)
|
|
1258
|
+
2 Agent is waiting for permission approval \u2014 use approve/deny to unblock
|
|
1219
1259
|
|
|
1220
|
-
|
|
1260
|
+
ATTACH COMMANDS (inside an attached session):
|
|
1221
1261
|
/quit, /detach Detach (session keeps running)
|
|
1222
1262
|
/abort, /cancel Cancel current agent turn
|
|
1223
|
-
/kill Stop the session
|
|
1263
|
+
/kill Stop the session entirely
|
|
1224
1264
|
/info Show session status
|
|
1225
1265
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
svamp session ralph-cancel <id>
|
|
1229
|
-
svamp session ralph-status <id>
|
|
1230
|
-
(alias: svamp session ralph <id> "<task>" ...)
|
|
1266
|
+
GLOBAL OPTIONS:
|
|
1267
|
+
--machine <id>, -m <id> Target a specific machine (prefix match supported)
|
|
1231
1268
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
svamp session queue clear <id>
|
|
1269
|
+
ID MATCHING:
|
|
1270
|
+
Session and machine IDs support prefix matching.
|
|
1271
|
+
"abc1" matches "abc12345-6789-..." \u2014 you only need enough characters to be unique.
|
|
1236
1272
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1273
|
+
EXAMPLES:
|
|
1274
|
+
|
|
1275
|
+
# === Common: Spawn agent to do a task and wait ===
|
|
1276
|
+
svamp session spawn claude -d ./my-project -p bypassPermissions \\
|
|
1277
|
+
--message "fix all TypeScript errors, run tsc to verify" --wait
|
|
1240
1278
|
|
|
1241
|
-
#
|
|
1242
|
-
svamp session spawn claude -d ./proj
|
|
1243
|
-
svamp session
|
|
1244
|
-
svamp session
|
|
1245
|
-
svamp session
|
|
1279
|
+
# === Multi-step: Spawn, then send messages interactively ===
|
|
1280
|
+
ID=$(svamp session spawn claude -d ./proj -p bypassPermissions)
|
|
1281
|
+
svamp session send $ID "first, read the README" --wait
|
|
1282
|
+
svamp session send $ID "now implement the feature described there" --wait
|
|
1283
|
+
svamp session messages $ID --last 20
|
|
1246
1284
|
|
|
1247
|
-
#
|
|
1248
|
-
|
|
1285
|
+
# === Spawn child session from another agent (e.g., in a bash tool) ===
|
|
1286
|
+
# Use -p bypassPermissions so the child doesn't get stuck on approvals.
|
|
1287
|
+
# Use --wait so your script blocks until the child finishes.
|
|
1288
|
+
svamp session spawn claude -d /tmp/test-project -p bypassPermissions \\
|
|
1289
|
+
--message "run the test suite and report results" --wait
|
|
1290
|
+
# Then read the child's output:
|
|
1291
|
+
svamp session messages <child-id> --last 5
|
|
1249
1292
|
|
|
1250
|
-
#
|
|
1251
|
-
svamp session
|
|
1293
|
+
# === Monitor with permissions (default mode) ===
|
|
1294
|
+
svamp session spawn claude -d ./proj --message "refactor auth module"
|
|
1295
|
+
svamp session wait abc1 --json # exit code 2 = permission pending
|
|
1296
|
+
svamp session approve abc1 # approve all pending
|
|
1297
|
+
svamp session wait abc1 # wait for completion
|
|
1252
1298
|
|
|
1253
|
-
#
|
|
1299
|
+
# === Spawn on a remote machine ===
|
|
1300
|
+
svamp session spawn claude -d /workspace -m my-cloud-box \\
|
|
1301
|
+
-p bypassPermissions --message "deploy to staging" --wait
|
|
1302
|
+
|
|
1303
|
+
# === Isolated session with network restrictions ===
|
|
1254
1304
|
svamp session spawn claude -d /tmp/sandbox --isolate --deny-network
|
|
1255
|
-
svamp session spawn claude -d /tmp/proj --share alice@example.com:admin
|
|
1256
1305
|
|
|
1257
|
-
# Ralph Loop
|
|
1258
|
-
svamp session ralph-start abc1 "Fix all linting errors"
|
|
1306
|
+
# === Ralph Loop: iterative task automation ===
|
|
1307
|
+
svamp session ralph-start abc1 "Fix all linting errors" \\
|
|
1308
|
+
--context-mode fresh --max-iterations 10
|
|
1259
1309
|
`);
|
|
1260
1310
|
}
|
|
1261
1311
|
function printMachineHelp() {
|
|
@@ -1272,6 +1322,13 @@ Usage:
|
|
|
1272
1322
|
svamp machine share --config <path> Apply security context config
|
|
1273
1323
|
svamp machine share --show-config Show current security context config
|
|
1274
1324
|
|
|
1325
|
+
Storage (admin):
|
|
1326
|
+
svamp machine storage list List all user PVCs with status
|
|
1327
|
+
svamp machine storage orphans [--mark] Show PVCs with no active namespace
|
|
1328
|
+
svamp machine storage mark-orphan <namespace> Flag a PVC as orphaned
|
|
1329
|
+
svamp machine storage unmark-orphan <namespace> Remove orphan flag
|
|
1330
|
+
svamp machine storage delete <namespace> Permanently delete a PVC (double confirm)
|
|
1331
|
+
|
|
1275
1332
|
Options:
|
|
1276
1333
|
--machine <id>, -m <id> Target a specific machine (prefix match supported)
|
|
1277
1334
|
--cwd <path> Working directory for exec (default: home dir)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { writeFileSync, readFileSync } from 'fs';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
|
-
import { connectAndGetMachine } from './commands-
|
|
3
|
+
import { connectAndGetMachine } from './commands-Dpc6QK0m.mjs';
|
|
4
4
|
import 'node:fs';
|
|
5
5
|
import 'node:child_process';
|
|
6
6
|
import 'node:path';
|
|
7
7
|
import 'node:os';
|
|
8
|
-
import './run-
|
|
8
|
+
import './run-BDmLH9T8.mjs';
|
|
9
9
|
import 'os';
|
|
10
10
|
import 'fs/promises';
|
|
11
11
|
import 'url';
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import os from 'node:os';
|
|
5
|
-
import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-
|
|
5
|
+
import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-BDmLH9T8.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-
|
|
1
|
+
export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-BDmLH9T8.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.1.
|
|
2
|
+
var version = "0.1.67";
|
|
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 && tsc --noEmit && pkgroll",
|
|
21
21
|
typecheck: "tsc --noEmit",
|
|
22
|
-
test: "npx tsx test/test-authorize.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-ralph-loop.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs",
|
|
22
|
+
test: "npx tsx test/test-authorize.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-ralph-loop.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-clear-detection.mjs",
|
|
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",
|
|
@@ -5411,6 +5411,10 @@ async function startDaemon(options) {
|
|
|
5411
5411
|
securityContext: options2.securityContext,
|
|
5412
5412
|
tags: options2.tags,
|
|
5413
5413
|
parentSessionId: options2.parentSessionId,
|
|
5414
|
+
...options2.parentSessionId && (() => {
|
|
5415
|
+
const parentTracked = Array.from(pidToTrackedSession.values()).find((t) => t.svampSessionId === options2.parentSessionId);
|
|
5416
|
+
return parentTracked?.directory ? { parentSessionPath: parentTracked.directory } : {};
|
|
5417
|
+
})(),
|
|
5414
5418
|
...options2.injectPlatformGuidance !== void 0 && { injectPlatformGuidance: options2.injectPlatformGuidance }
|
|
5415
5419
|
};
|
|
5416
5420
|
let claudeProcess = null;
|
|
@@ -5421,6 +5425,7 @@ async function startDaemon(options) {
|
|
|
5421
5425
|
let lastSpawnMeta = persisted?.spawnMeta || {};
|
|
5422
5426
|
let sessionWasProcessing = !!options2.wasProcessing;
|
|
5423
5427
|
let lastAssistantText = "";
|
|
5428
|
+
let spawnHasReceivedInit = false;
|
|
5424
5429
|
const signalProcessing = (processing) => {
|
|
5425
5430
|
sessionService.sendKeepAlive(processing);
|
|
5426
5431
|
const newState = processing ? "running" : "idle";
|
|
@@ -5535,6 +5540,7 @@ async function startDaemon(options) {
|
|
|
5535
5540
|
shell: process.platform === "win32"
|
|
5536
5541
|
});
|
|
5537
5542
|
claudeProcess = child;
|
|
5543
|
+
spawnHasReceivedInit = false;
|
|
5538
5544
|
logger.log(`[Session ${sessionId}] Claude PID: ${child.pid}, stdin: ${!!child.stdin}, stdout: ${!!child.stdout}, stderr: ${!!child.stderr}`);
|
|
5539
5545
|
child.stdin?.on("error", (err) => {
|
|
5540
5546
|
logger.log(`[Session ${sessionId}] Claude stdin error: ${err.message}`);
|
|
@@ -5893,7 +5899,9 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
5893
5899
|
}
|
|
5894
5900
|
userMessagePending = false;
|
|
5895
5901
|
if (msg.session_id) {
|
|
5896
|
-
const
|
|
5902
|
+
const isResumeFailure = !spawnHasReceivedInit && claudeResumeId && msg.session_id !== claudeResumeId;
|
|
5903
|
+
const isConversationClear = spawnHasReceivedInit && claudeResumeId && msg.session_id !== claudeResumeId;
|
|
5904
|
+
spawnHasReceivedInit = true;
|
|
5897
5905
|
claudeResumeId = msg.session_id;
|
|
5898
5906
|
sessionMetadata = { ...sessionMetadata, claudeSessionId: msg.session_id };
|
|
5899
5907
|
sessionService.updateMetadata(sessionMetadata);
|
|
@@ -5911,7 +5919,9 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
5911
5919
|
});
|
|
5912
5920
|
artifactSync.scheduleDebouncedSync(sessionId, getSessionDir(directory, sessionId), sessionMetadata, machineId);
|
|
5913
5921
|
}
|
|
5914
|
-
if (
|
|
5922
|
+
if (isResumeFailure) {
|
|
5923
|
+
logger.log(`[Session ${sessionId}] Resume failed \u2014 Claude started fresh session (tried: ${persisted?.claudeResumeId ?? "unknown"}, got: ${msg.session_id})`);
|
|
5924
|
+
} else if (isConversationClear) {
|
|
5915
5925
|
logger.log(`[Session ${sessionId}] Conversation cleared (/clear) \u2014 new Claude session: ${msg.session_id}`);
|
|
5916
5926
|
sessionService.clearMessages();
|
|
5917
5927
|
sessionService.pushMessage(
|
|
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { join, resolve } from 'node:path';
|
|
4
4
|
import { mkdirSync, writeFileSync, existsSync, unlinkSync, readFileSync, watch } from 'node:fs';
|
|
5
|
-
import { c as connectToHypha, a as registerSessionService } from './run-
|
|
5
|
+
import { c as connectToHypha, a as registerSessionService } from './run-BDmLH9T8.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { getSandboxEnv } from './api-BRbsyqJ4.mjs';
|
|
2
|
+
|
|
3
|
+
async function adminFetch(path, method = "GET", body) {
|
|
4
|
+
const env = getSandboxEnv();
|
|
5
|
+
if (!env.apiKey) {
|
|
6
|
+
throw new Error('No API credentials. Run "svamp login" or set SANDBOX_API_KEY.');
|
|
7
|
+
}
|
|
8
|
+
const url = `${env.apiUrl.replace(/\/+$/, "")}${path}`;
|
|
9
|
+
const res = await fetch(url, {
|
|
10
|
+
method,
|
|
11
|
+
headers: {
|
|
12
|
+
"Authorization": `Bearer ${env.apiKey}`,
|
|
13
|
+
"Content-Type": "application/json"
|
|
14
|
+
},
|
|
15
|
+
...{}
|
|
16
|
+
});
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
const text = await res.text().catch(() => "");
|
|
19
|
+
throw new Error(`HTTP ${res.status} ${method} ${path}: ${text}`);
|
|
20
|
+
}
|
|
21
|
+
return res.json();
|
|
22
|
+
}
|
|
23
|
+
function fmtAge(ts) {
|
|
24
|
+
if (!ts) return "?";
|
|
25
|
+
try {
|
|
26
|
+
const dt = /^\d+$/.test(ts) ? new Date(parseInt(ts, 10) * 1e3) : new Date(ts);
|
|
27
|
+
const secs = (Date.now() - dt.getTime()) / 1e3;
|
|
28
|
+
const d = Math.floor(secs / 86400);
|
|
29
|
+
const h = Math.floor(secs % 86400 / 3600);
|
|
30
|
+
return d > 0 ? `${d}d ${h}h ago` : `${h}h ago`;
|
|
31
|
+
} catch {
|
|
32
|
+
return ts;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function pad(s, n) {
|
|
36
|
+
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
37
|
+
}
|
|
38
|
+
async function storageList() {
|
|
39
|
+
const data = await adminFetch("/admin/storage/pvcs");
|
|
40
|
+
const pvcs = data.pvcs ?? [];
|
|
41
|
+
if (!pvcs.length) {
|
|
42
|
+
console.log("No managed PVCs found.");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
console.log("");
|
|
46
|
+
console.log(`${pad("NAMESPACE", 36)}${pad("SIZE", 9)}${pad("STATUS", 9)}${pad("CREATED", 20)}ORPHANED`);
|
|
47
|
+
console.log("\u2500".repeat(90));
|
|
48
|
+
for (const p of pvcs.sort((a, b) => a.namespace.localeCompare(b.namespace))) {
|
|
49
|
+
const orphan = p.orphaned_at ? `\u26A0 ${fmtAge(p.orphaned_at)}` : "\u2014";
|
|
50
|
+
console.log(`${pad(p.namespace, 36)}${pad(p.capacity ?? "?", 9)}${pad(p.status ?? "?", 9)}${pad(fmtAge(p.created_at), 20)}${orphan}`);
|
|
51
|
+
}
|
|
52
|
+
console.log(`
|
|
53
|
+
Total: ${data.count} PVC(s)`);
|
|
54
|
+
}
|
|
55
|
+
async function storageOrphans(mark = false) {
|
|
56
|
+
const data = await adminFetch(`/admin/orphaned-pvcs?dry_run=${mark ? "false" : "true"}`);
|
|
57
|
+
const orphans = data.orphaned_pvcs ?? [];
|
|
58
|
+
if (!orphans.length) {
|
|
59
|
+
console.log("\u2705 No orphaned PVCs found.");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console.log(`
|
|
63
|
+
\u26A0 ${orphans.length} orphaned PVC(s) (namespace has no running pods):
|
|
64
|
+
`);
|
|
65
|
+
console.log(`${pad("NAMESPACE", 36)}${pad("SIZE", 9)}${pad("MARKED", 9)}ORPHANED FOR`);
|
|
66
|
+
console.log("\u2500".repeat(72));
|
|
67
|
+
for (const o of orphans) {
|
|
68
|
+
const age = o.orphaned_for || (o.marked ? "?" : "not marked");
|
|
69
|
+
console.log(`${pad(o.namespace, 36)}${pad(o.storage ?? "?", 9)}${pad(o.marked ? "yes" : "no", 9)}${age}`);
|
|
70
|
+
}
|
|
71
|
+
if (!mark) {
|
|
72
|
+
console.log("\nTip: pass --mark to start the retention clock on unmarked orphans.");
|
|
73
|
+
}
|
|
74
|
+
console.log("\nTo delete: svamp machine storage delete <namespace>");
|
|
75
|
+
}
|
|
76
|
+
async function storageMarkOrphan(namespace) {
|
|
77
|
+
const data = await adminFetch(`/admin/storage/mark-orphan/${namespace}`, "POST");
|
|
78
|
+
if (data.marked) {
|
|
79
|
+
console.log(`\u2705 PVC for '${namespace}' marked as orphaned.`);
|
|
80
|
+
console.log(` To delete: svamp machine storage delete ${namespace}`);
|
|
81
|
+
} else {
|
|
82
|
+
console.log(`\u2139 PVC for '${namespace}' was already marked or not found.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function storageUnmarkOrphan(namespace) {
|
|
86
|
+
const data = await adminFetch(`/admin/storage/unmark-orphan/${namespace}`, "POST");
|
|
87
|
+
if (data.unmarked) {
|
|
88
|
+
console.log(`\u2705 Orphan mark removed from '${namespace}'.`);
|
|
89
|
+
} else {
|
|
90
|
+
console.log(`\u2139 PVC for '${namespace}' was not marked or not found.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function storageDelete(namespace) {
|
|
94
|
+
const readline = await import('readline');
|
|
95
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
96
|
+
const ask = (q) => new Promise((res) => rl.question(q, res));
|
|
97
|
+
try {
|
|
98
|
+
const data2 = await adminFetch("/admin/storage/pvcs");
|
|
99
|
+
const pvc = (data2.pvcs ?? []).find((p) => p.namespace === namespace);
|
|
100
|
+
if (!pvc) {
|
|
101
|
+
console.error(`No PVC found for namespace '${namespace}'.`);
|
|
102
|
+
rl.close();
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
console.log(`
|
|
106
|
+
\u26A0 Namespace: ${pvc.namespace}`);
|
|
107
|
+
console.log(` Size: ${pvc.capacity ?? "?"}`);
|
|
108
|
+
console.log(` Created: ${fmtAge(pvc.created_at)}`);
|
|
109
|
+
if (pvc.orphaned_at) console.log(` Orphaned: ${fmtAge(pvc.orphaned_at)}`);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
console.log("\n\u26A0 PERMANENTLY deleting this PVC will IRREVERSIBLY destroy all user data.\n");
|
|
113
|
+
const a1 = (await ask(`Type the namespace name to confirm: `)).trim();
|
|
114
|
+
if (a1 !== namespace) {
|
|
115
|
+
console.log("Confirmation mismatch \u2014 aborted.");
|
|
116
|
+
rl.close();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const a2 = (await ask(`Type it again to double-confirm: `)).trim();
|
|
120
|
+
rl.close();
|
|
121
|
+
if (a2 !== namespace) {
|
|
122
|
+
console.log("Confirmation mismatch \u2014 aborted.");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const data = await adminFetch(`/admin/storage/pvc/${namespace}`, "DELETE");
|
|
126
|
+
if (data.deleted) {
|
|
127
|
+
console.log(`\u2705 PVC for '${namespace}' permanently deleted.`);
|
|
128
|
+
} else {
|
|
129
|
+
console.log(`\u2139 PVC for '${namespace}' was not found or already deleted.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function printStorageHelp() {
|
|
133
|
+
console.log(`
|
|
134
|
+
svamp machine storage \u2014 sandbox PVC lifecycle management (admin only)
|
|
135
|
+
|
|
136
|
+
Usage:
|
|
137
|
+
svamp machine storage list List all PVCs with status
|
|
138
|
+
svamp machine storage orphans [--mark] Show PVCs with no active namespace
|
|
139
|
+
svamp machine storage mark-orphan <ns> Flag a PVC as orphaned (no deletion)
|
|
140
|
+
svamp machine storage unmark-orphan <ns> Remove orphan flag
|
|
141
|
+
svamp machine storage delete <ns> Permanently delete a PVC (double confirmation)
|
|
142
|
+
|
|
143
|
+
Environment:
|
|
144
|
+
SANDBOX_API_URL Sandbox API base URL (default: https://agent-sandbox.aicell.io)
|
|
145
|
+
SANDBOX_API_KEY Admin API key (or use HYPHA_TOKEN from svamp login)
|
|
146
|
+
|
|
147
|
+
Safety rules:
|
|
148
|
+
- Automated systems NEVER delete PVCs
|
|
149
|
+
- delete requires typing the namespace name twice
|
|
150
|
+
- delete checks for running pods and refuses if any are active
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export { printStorageHelp, storageDelete, storageList, storageMarkOrphan, storageOrphans, storageUnmarkOrphan };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svamp-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Svamp CLI
|
|
3
|
+
"version": "0.1.67",
|
|
4
|
+
"description": "Svamp CLI \u2014 AI workspace daemon on Hypha Cloud",
|
|
5
5
|
"author": "Amun AI AB",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
7
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "rm -rf dist && tsc --noEmit && pkgroll",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
|
-
"test": "npx tsx test/test-authorize.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-ralph-loop.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs",
|
|
23
|
+
"test": "npx tsx test/test-authorize.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-ralph-loop.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-clear-detection.mjs",
|
|
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",
|