svamp-cli 0.1.66 → 0.1.68
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 +172 -224
- package/dist/{commands-CdQ8qr6l.mjs → commands-BwT-PFl4.mjs} +2 -2
- package/dist/{commands-Dpc6QK0m.mjs → commands-D4_QgXrS.mjs} +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{package-CSFQKq7F.mjs → package-CHfluhvW.mjs} +1 -1
- package/dist/{run-ez-QlRKy.mjs → run-BYtrsPjt.mjs} +1 -1
- package/dist/{run-BDmLH9T8.mjs → run-DWFTrXVF.mjs} +22 -4
- package/package.json +2 -2
- package/dist/storageCommands-CKhntx1P.mjs +0 -154
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-DWFTrXVF.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-BwT-PFl4.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-CHfluhvW.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-BYtrsPjt.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-DWFTrXVF.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-DWFTrXVF.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-DWFTrXVF.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-DWFTrXVF.mjs').then(function (n) { return n.h; });
|
|
224
|
+
const { GeminiTransport } = await import('./run-DWFTrXVF.mjs').then(function (n) { return n.G; });
|
|
225
|
+
const { DefaultTransport } = await import('./run-DWFTrXVF.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-D4_QgXrS.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-D4_QgXrS.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-D4_QgXrS.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-D4_QgXrS.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-D4_QgXrS.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-D4_QgXrS.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-D4_QgXrS.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-D4_QgXrS.mjs');
|
|
674
674
|
let machineId;
|
|
675
675
|
let showHidden = false;
|
|
676
676
|
let path;
|
|
@@ -684,43 +684,6 @@ 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
|
-
}
|
|
724
687
|
} else {
|
|
725
688
|
console.error(`Unknown machine command: ${machineSubcommand}`);
|
|
726
689
|
printMachineHelp();
|
|
@@ -1081,38 +1044,50 @@ function printHelp() {
|
|
|
1081
1044
|
console.log(`
|
|
1082
1045
|
svamp \u2014 AI workspace on Hypha Cloud
|
|
1083
1046
|
|
|
1084
|
-
|
|
1085
|
-
svamp
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
svamp
|
|
1089
|
-
svamp
|
|
1090
|
-
svamp daemon
|
|
1091
|
-
svamp daemon status
|
|
1092
|
-
svamp daemon
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
svamp session
|
|
1096
|
-
svamp session
|
|
1097
|
-
svamp session
|
|
1098
|
-
svamp session messages <id>
|
|
1099
|
-
svamp session
|
|
1100
|
-
svamp session
|
|
1101
|
-
svamp session
|
|
1102
|
-
svamp session
|
|
1103
|
-
svamp session
|
|
1104
|
-
svamp
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
svamp
|
|
1108
|
-
svamp skills --help
|
|
1109
|
-
svamp
|
|
1110
|
-
svamp
|
|
1111
|
-
svamp
|
|
1047
|
+
Usage:
|
|
1048
|
+
svamp Start interactive Claude session (synced to cloud)
|
|
1049
|
+
svamp start [-d <path>] Same as above, with explicit directory
|
|
1050
|
+
svamp login [url] Login to Hypha (opens browser, stores token)
|
|
1051
|
+
svamp daemon start Start the daemon (detached)
|
|
1052
|
+
svamp daemon stop Stop the daemon (sessions preserved for restart)
|
|
1053
|
+
svamp daemon restart Restart the daemon (sessions resume seamlessly)
|
|
1054
|
+
svamp daemon status Show daemon status
|
|
1055
|
+
svamp daemon install Install as system service (launchd/systemd/wrapper)
|
|
1056
|
+
svamp session list List active sessions
|
|
1057
|
+
svamp session spawn Spawn a new session on the daemon
|
|
1058
|
+
svamp session send <id> <m> Send message to a session
|
|
1059
|
+
svamp session wait <id> Wait for agent to become idle
|
|
1060
|
+
svamp session info <id> Show session status & pending permissions
|
|
1061
|
+
svamp session messages <id> Show message history
|
|
1062
|
+
svamp session approve <id> Approve pending permission request
|
|
1063
|
+
svamp session deny <id> Deny pending permission request
|
|
1064
|
+
svamp session attach <id> Attach to a session (interactive terminal)
|
|
1065
|
+
svamp session share <id> Manage session sharing
|
|
1066
|
+
svamp session --help Show all session commands
|
|
1067
|
+
svamp machine share Manage machine sharing & security contexts
|
|
1068
|
+
svamp machine --help Show all machine commands
|
|
1069
|
+
svamp skills find <query> Search the skills marketplace
|
|
1070
|
+
svamp skills install <n> Install a skill from the marketplace
|
|
1071
|
+
svamp skills --help Show all skill commands
|
|
1072
|
+
svamp process apply <f.yaml> Create/update a supervised process (idempotent)
|
|
1073
|
+
svamp process list List supervised processes
|
|
1074
|
+
svamp process start <name> Start or create a process
|
|
1075
|
+
svamp process logs <name> Show process log output
|
|
1076
|
+
svamp process --help Show all process commands
|
|
1077
|
+
svamp service expose <name> Expose a service from this sandbox
|
|
1078
|
+
svamp service list List service groups
|
|
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
|
|
1112
1084
|
|
|
1113
1085
|
Interactive mode:
|
|
1114
|
-
|
|
1115
|
-
|
|
1086
|
+
When you run 'svamp' with no arguments, Claude starts in your terminal
|
|
1087
|
+
with full interactive access. Your session is synced to Hypha Cloud so
|
|
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.
|
|
1116
1091
|
|
|
1117
1092
|
Environment variables:
|
|
1118
1093
|
HYPHA_SERVER_URL Hypha server URL (required for cloud sync)
|
|
@@ -1138,169 +1113,149 @@ Usage:
|
|
|
1138
1113
|
}
|
|
1139
1114
|
function printSessionHelp() {
|
|
1140
1115
|
console.log(`
|
|
1141
|
-
svamp session \u2014
|
|
1142
|
-
|
|
1143
|
-
QUICK START \u2014 Spawn a session, give it a task, get results:
|
|
1116
|
+
svamp session \u2014 Manage daemon sessions (Claude, Gemini, Codex)
|
|
1144
1117
|
|
|
1145
|
-
|
|
1146
|
-
svamp session
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
svamp session
|
|
1151
|
-
svamp session send
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1118
|
+
Commands:
|
|
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)
|
|
1156
1132
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1133
|
+
Spawn options:
|
|
1134
|
+
-d, --directory <path> Working directory (default: cwd)
|
|
1135
|
+
-p, --permission-mode <mode> Agent permission mode (see "Permission modes" below)
|
|
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)
|
|
1161
1146
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1147
|
+
Permission modes (-p, --permission-mode):
|
|
1148
|
+
default Prompt for each tool use (default)
|
|
1149
|
+
acceptEdits Auto-approve file edits, prompt for bash/dangerous tools
|
|
1150
|
+
bypassPermissions Auto-approve all tools (no prompts)
|
|
1164
1151
|
|
|
1165
|
-
|
|
1152
|
+
Permission workflow:
|
|
1153
|
+
When an agent runs with 'default' or 'acceptEdits' permission mode, it pauses
|
|
1154
|
+
when it needs to use a tool that requires approval. Use these commands to manage:
|
|
1166
1155
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1156
|
+
1. Check status: svamp session info <id> --json
|
|
1157
|
+
\u2192 shows pendingPermissions array when agent is blocked
|
|
1158
|
+
2. Wait & detect: svamp session wait <id> --json
|
|
1159
|
+
\u2192 exits with code 2 if permission pending (see exit codes)
|
|
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)
|
|
1173
1164
|
|
|
1174
|
-
|
|
1175
|
-
send <id> <message> [--wait] [--timeout N] [--json]
|
|
1176
|
-
Send a message to a running session
|
|
1177
|
-
messages <id> [--last N] [--json] Read message history (alias: msgs)
|
|
1178
|
-
wait <id> [--timeout N] [--json] Wait for agent to become idle
|
|
1179
|
-
attach <id> Attach interactive terminal (stdin/stdout)
|
|
1165
|
+
Request IDs support prefix matching (e.g., "abc1" matches "abc12345-...").
|
|
1180
1166
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1167
|
+
Messages options:
|
|
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)
|
|
1184
1174
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
share <id> --public <view|interact|off> Set public link access
|
|
1175
|
+
Exit codes (wait, send --wait, spawn --wait):
|
|
1176
|
+
0 Agent is idle (task completed)
|
|
1177
|
+
1 Error (timeout, connection failure, session not found)
|
|
1178
|
+
2 Agent is waiting for permission approval \u2014 use approve/deny to continue
|
|
1190
1179
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
ralph-cancel <id> Cancel loop
|
|
1194
|
-
ralph-status <id> Show loop status
|
|
1180
|
+
Global options:
|
|
1181
|
+
--machine <id>, -m <id> Target a specific machine (prefix match supported)
|
|
1195
1182
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
queue list <id> List queued messages
|
|
1199
|
-
queue clear <id> Clear queue
|
|
1183
|
+
Agents: claude (default), gemini, codex
|
|
1184
|
+
Session and machine IDs support prefix matching (e.g., "abc1" matches "abc12345-...").
|
|
1200
1185
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
for fire-and-forget: spawn \u2192 task \u2192 wait \u2192 done.
|
|
1207
|
-
--timeout <sec> Timeout for --wait (default: no timeout)
|
|
1208
|
-
--worktree Create isolated git worktree branch (.dev/worktree/<name>)
|
|
1209
|
-
--tags <tag1,tag2> Comma-separated tags for session grouping
|
|
1210
|
-
--parent <sessionId> Set parent session (auto-set from SVAMP_SESSION_ID env)
|
|
1186
|
+
Sharing:
|
|
1187
|
+
svamp session share <id> --list List shared users
|
|
1188
|
+
svamp session share <id> --add <email>[:<role>] Share with user
|
|
1189
|
+
svamp session share <id> --remove <email> Remove shared user
|
|
1190
|
+
svamp session share <id> --public <view|interact|off> Set public link access
|
|
1211
1191
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
You must use "approve" / "deny" commands to unblock it.
|
|
1215
|
-
acceptEdits Auto-approve file edits. Pauses for bash & dangerous tools.
|
|
1216
|
-
bypassPermissions Auto-approve everything. No prompts, no pauses.
|
|
1217
|
-
\u26A0 Use this for automation / when you trust the agent fully.
|
|
1192
|
+
Options:
|
|
1193
|
+
--machine <id>, -m <id> Target a specific machine (prefix match supported)
|
|
1218
1194
|
|
|
1219
|
-
|
|
1220
|
-
-p
|
|
1195
|
+
Spawn options:
|
|
1196
|
+
-p, --permission-mode <mode> Agent permission mode: default, acceptEdits, bypassPermissions
|
|
1197
|
+
--worktree Create a git worktree branch as working directory
|
|
1221
1198
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1199
|
+
Spawn isolation options:
|
|
1200
|
+
--share <email>[:<role>] Share session (repeatable). Role: view, interact, admin
|
|
1201
|
+
--security-context <path> Path to security context JSON config file
|
|
1202
|
+
--isolate Force OS-level isolation even without sharing
|
|
1203
|
+
--deny-read <path> Deny read access to path (repeatable)
|
|
1204
|
+
--allow-write <path> Allow write access to path (repeatable)
|
|
1205
|
+
--deny-network Deny all network access
|
|
1206
|
+
--allow-domain <domain> Allow network access to domain (repeatable)
|
|
1227
1207
|
|
|
1228
|
-
|
|
1229
|
-
--
|
|
1230
|
-
--
|
|
1231
|
-
--share <email>[:<role>] Share session (repeatable). Role: view|interact|admin
|
|
1232
|
-
--deny-network Block all network access
|
|
1233
|
-
--deny-read <path> Deny reading path (repeatable)
|
|
1234
|
-
--allow-write <path> Allow writing path (repeatable)
|
|
1235
|
-
--allow-domain <domain> Allow network domain (repeatable)
|
|
1208
|
+
Spawn grouping options:
|
|
1209
|
+
--tags <tag1,tag2> Comma-separated tags
|
|
1210
|
+
--parent <sessionId> Set parent session (auto-detected from SVAMP_SESSION_ID)
|
|
1236
1211
|
|
|
1237
|
-
|
|
1238
|
-
--last N Show last N messages only
|
|
1239
|
-
--after N Start after sequence number N (for pagination)
|
|
1240
|
-
--limit N Max messages to fetch (default: 1000)
|
|
1241
|
-
--json Output as JSON array of {id, seq, role, text, createdAt}
|
|
1242
|
-
--raw With --json: include full raw objects (tool_use, tool_result, thinking)
|
|
1212
|
+
Agents: claude (default), gemini, codex
|
|
1243
1213
|
|
|
1244
|
-
|
|
1245
|
-
--context-mode <mode> fresh (new process each iteration) or continue (same process)
|
|
1246
|
-
--completion-promise <text> Completion signal text (default: DONE)
|
|
1247
|
-
--max-iterations <n> Max iterations (default: 10)
|
|
1248
|
-
--cooldown <sec> Delay between iterations (default: 1)
|
|
1214
|
+
Session and machine IDs can be abbreviated (prefix match, like Docker).
|
|
1249
1215
|
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
2 Agent is waiting for permission approval \u2014 use approve/deny to unblock
|
|
1216
|
+
NOTE: By default, spawned agents run with 'default' permission mode, which means
|
|
1217
|
+
the agent may pause to request permission for tool use (file edits, bash, etc.).
|
|
1218
|
+
Use -p bypassPermissions to run without approval prompts.
|
|
1254
1219
|
|
|
1255
|
-
|
|
1220
|
+
Attach commands:
|
|
1256
1221
|
/quit, /detach Detach (session keeps running)
|
|
1257
1222
|
/abort, /cancel Cancel current agent turn
|
|
1258
|
-
/kill Stop the session
|
|
1223
|
+
/kill Stop the session
|
|
1259
1224
|
/info Show session status
|
|
1260
1225
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1226
|
+
Ralph Loop (iterative task automation):
|
|
1227
|
+
svamp session ralph-start <id> "<task>" [--context-mode fresh|continue] [--completion-promise TEXT] [--max-iterations N] [--cooldown N]
|
|
1228
|
+
svamp session ralph-cancel <id>
|
|
1229
|
+
svamp session ralph-status <id>
|
|
1230
|
+
(alias: svamp session ralph <id> "<task>" ...)
|
|
1263
1231
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1232
|
+
Message Queue:
|
|
1233
|
+
svamp session queue add <id> "<message>"
|
|
1234
|
+
svamp session queue list <id>
|
|
1235
|
+
svamp session queue clear <id>
|
|
1267
1236
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
svamp session spawn claude -d ./my-project -p bypassPermissions \\
|
|
1272
|
-
--message "fix all TypeScript errors, run tsc to verify" --wait
|
|
1273
|
-
|
|
1274
|
-
# === Multi-step: Spawn, then send messages interactively ===
|
|
1275
|
-
ID=$(svamp session spawn claude -d ./proj -p bypassPermissions)
|
|
1276
|
-
svamp session send $ID "first, read the README" --wait
|
|
1277
|
-
svamp session send $ID "now implement the feature described there" --wait
|
|
1278
|
-
svamp session messages $ID --last 20
|
|
1237
|
+
Examples:
|
|
1238
|
+
# Spawn with bypassed permissions (no prompts)
|
|
1239
|
+
svamp session spawn claude -d ./proj -p bypassPermissions --message "fix tests" --wait
|
|
1279
1240
|
|
|
1280
|
-
#
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
svamp session
|
|
1284
|
-
|
|
1285
|
-
# Then read the child's output:
|
|
1286
|
-
svamp session messages <child-id> --last 5
|
|
1241
|
+
# Spawn with default permissions, handle approval via CLI
|
|
1242
|
+
svamp session spawn claude -d ./proj --message "refactor auth"
|
|
1243
|
+
svamp session wait abc1 --json # exits with code 2 if permission pending
|
|
1244
|
+
svamp session approve abc1 # approve all pending permissions
|
|
1245
|
+
svamp session wait abc1 # wait for completion
|
|
1287
1246
|
|
|
1288
|
-
#
|
|
1289
|
-
svamp session
|
|
1290
|
-
svamp session wait abc1 --json # exit code 2 = permission pending
|
|
1291
|
-
svamp session approve abc1 # approve all pending
|
|
1292
|
-
svamp session wait abc1 # wait for completion
|
|
1247
|
+
# Monitor session messages (raw for full tool details)
|
|
1248
|
+
svamp session messages abc1 --last 10 --json --raw
|
|
1293
1249
|
|
|
1294
|
-
#
|
|
1295
|
-
svamp session
|
|
1296
|
-
-p bypassPermissions --message "deploy to staging" --wait
|
|
1250
|
+
# Send message and wait for completion
|
|
1251
|
+
svamp session send abc1 "run the tests" --wait --json
|
|
1297
1252
|
|
|
1298
|
-
#
|
|
1253
|
+
# Isolation & sharing
|
|
1299
1254
|
svamp session spawn claude -d /tmp/sandbox --isolate --deny-network
|
|
1255
|
+
svamp session spawn claude -d /tmp/proj --share alice@example.com:admin
|
|
1300
1256
|
|
|
1301
|
-
#
|
|
1302
|
-
svamp session ralph-start abc1 "Fix all linting errors"
|
|
1303
|
-
--context-mode fresh --max-iterations 10
|
|
1257
|
+
# Ralph Loop
|
|
1258
|
+
svamp session ralph-start abc1 "Fix all linting errors" --context-mode fresh --max-iterations 10
|
|
1304
1259
|
`);
|
|
1305
1260
|
}
|
|
1306
1261
|
function printMachineHelp() {
|
|
@@ -1317,13 +1272,6 @@ Usage:
|
|
|
1317
1272
|
svamp machine share --config <path> Apply security context config
|
|
1318
1273
|
svamp machine share --show-config Show current security context config
|
|
1319
1274
|
|
|
1320
|
-
Storage (admin):
|
|
1321
|
-
svamp machine storage list List all user PVCs with status
|
|
1322
|
-
svamp machine storage orphans [--mark] Show PVCs with no active namespace
|
|
1323
|
-
svamp machine storage mark-orphan <namespace> Flag a PVC as orphaned
|
|
1324
|
-
svamp machine storage unmark-orphan <namespace> Remove orphan flag
|
|
1325
|
-
svamp machine storage delete <namespace> Permanently delete a PVC (double confirm)
|
|
1326
|
-
|
|
1327
1275
|
Options:
|
|
1328
1276
|
--machine <id>, -m <id> Target a specific machine (prefix match supported)
|
|
1329
1277
|
--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-D4_QgXrS.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-DWFTrXVF.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-DWFTrXVF.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-DWFTrXVF.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -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-DWFTrXVF.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
@@ -1158,6 +1158,7 @@ async function registerSessionService(server, sessionId, initialMetadata, initia
|
|
|
1158
1158
|
sessionId,
|
|
1159
1159
|
metadata: { value: metadata, version: metadataVersion }
|
|
1160
1160
|
});
|
|
1161
|
+
callbacks.onSharingUpdate?.(newSharing);
|
|
1161
1162
|
return { success: true, sharing: newSharing };
|
|
1162
1163
|
},
|
|
1163
1164
|
/** Update security context and restart the agent process with new rules */
|
|
@@ -5411,10 +5412,6 @@ async function startDaemon(options) {
|
|
|
5411
5412
|
securityContext: options2.securityContext,
|
|
5412
5413
|
tags: options2.tags,
|
|
5413
5414
|
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
|
-
})(),
|
|
5418
5415
|
...options2.injectPlatformGuidance !== void 0 && { injectPlatformGuidance: options2.injectPlatformGuidance }
|
|
5419
5416
|
};
|
|
5420
5417
|
let claudeProcess = null;
|
|
@@ -6218,6 +6215,23 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
6218
6215
|
sessionMetadata = { ...sessionMetadata, securityContext: newSecurityContext };
|
|
6219
6216
|
return await restartClaudeHandler();
|
|
6220
6217
|
},
|
|
6218
|
+
onSharingUpdate: (newSharing) => {
|
|
6219
|
+
logger.log(`[Session ${sessionId}] Sharing config updated \u2014 persisting to disk`);
|
|
6220
|
+
sessionMetadata = { ...sessionMetadata, sharing: newSharing };
|
|
6221
|
+
if (!trackedSession.stopped) {
|
|
6222
|
+
saveSession({
|
|
6223
|
+
sessionId,
|
|
6224
|
+
directory,
|
|
6225
|
+
claudeResumeId,
|
|
6226
|
+
permissionMode: currentPermissionMode,
|
|
6227
|
+
spawnMeta: lastSpawnMeta,
|
|
6228
|
+
metadata: sessionMetadata,
|
|
6229
|
+
createdAt: Date.now(),
|
|
6230
|
+
machineId,
|
|
6231
|
+
wasProcessing: sessionWasProcessing
|
|
6232
|
+
});
|
|
6233
|
+
}
|
|
6234
|
+
},
|
|
6221
6235
|
onApplySystemPrompt: async (prompt) => {
|
|
6222
6236
|
logger.log(`[Session ${sessionId}] System prompt update requested \u2014 restarting agent`);
|
|
6223
6237
|
lastSpawnMeta = { ...lastSpawnMeta, appendSystemPrompt: prompt };
|
|
@@ -6609,6 +6623,10 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
6609
6623
|
sessionMetadata = { ...sessionMetadata, securityContext: newSecurityContext };
|
|
6610
6624
|
return { success: false, message: "Security context updates with restart are not yet supported for this agent type." };
|
|
6611
6625
|
},
|
|
6626
|
+
onSharingUpdate: (newSharing) => {
|
|
6627
|
+
logger.log(`[${agentName} Session ${sessionId}] Sharing config updated \u2014 persisting in-memory`);
|
|
6628
|
+
sessionMetadata = { ...sessionMetadata, sharing: newSharing };
|
|
6629
|
+
},
|
|
6612
6630
|
onApplySystemPrompt: async () => {
|
|
6613
6631
|
return { success: false, message: "System prompt updates with restart are not yet supported for this agent type." };
|
|
6614
6632
|
},
|
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.68",
|
|
4
|
+
"description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
|
|
5
5
|
"author": "Amun AI AB",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
7
|
"type": "module",
|
|
@@ -1,154 +0,0 @@
|
|
|
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 };
|