svamp-cli 0.1.63 → 0.1.65
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/agentCommands-C6iGblcL.mjs +156 -0
- package/dist/{api-Cegey1dh.mjs → api-BRbsyqJ4.mjs} +9 -2
- package/dist/cli.mjs +73 -20
- package/dist/{commands-dm3PSNsk.mjs → commands-CF32XIau.mjs} +3 -3
- package/dist/{commands-3Zu9sCxa.mjs → commands-Dc_6JdE_.mjs} +2 -2
- package/dist/{commands-BRow9cGp.mjs → commands-FAWhkKtz.mjs} +1 -1
- package/dist/{commands-6EyqaoCp.mjs → commands-UFi0_ESV.mjs} +48 -24
- package/dist/index.mjs +1 -1
- package/dist/{package-7U32bRBY.mjs → package-CvCDrFPH.mjs} +2 -2
- package/dist/{run-BaMf-bAo.mjs → run-DPhSmbr1.mjs} +942 -387
- package/dist/{run-COqTRwXb.mjs → run-DTwMJ7dx.mjs} +80 -28
- package/dist/{tunnel-dl6vFKgd.mjs → tunnel-C3UsqTxi.mjs} +53 -65
- package/package.json +2 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
|
|
5
|
+
const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
|
|
6
|
+
function getConfigPath(sessionId) {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
return join(cwd, ".svamp", sessionId, "config.json");
|
|
9
|
+
}
|
|
10
|
+
function readConfig(configPath) {
|
|
11
|
+
try {
|
|
12
|
+
if (existsSync(configPath)) return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
13
|
+
} catch {
|
|
14
|
+
}
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
function writeConfig(configPath, config) {
|
|
18
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
19
|
+
const tmp = configPath + ".tmp";
|
|
20
|
+
writeFileSync(tmp, JSON.stringify(config, null, 2));
|
|
21
|
+
renameSync(tmp, configPath);
|
|
22
|
+
}
|
|
23
|
+
async function connectAndEmit(event) {
|
|
24
|
+
const ENV_FILE = join(SVAMP_HOME, ".env");
|
|
25
|
+
if (existsSync(ENV_FILE)) {
|
|
26
|
+
const lines = readFileSync(ENV_FILE, "utf-8").split("\n");
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
const m = line.match(/^([A-Z_]+)=(.*)/);
|
|
29
|
+
if (m && !process.env[m[1]]) process.env[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const serverUrl = process.env.HYPHA_SERVER_URL;
|
|
33
|
+
const token = process.env.HYPHA_TOKEN;
|
|
34
|
+
if (!serverUrl || !token) {
|
|
35
|
+
console.error('No Hypha credentials. Run "svamp login" first.');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const origLog = console.log;
|
|
39
|
+
const origWarn = console.warn;
|
|
40
|
+
const origInfo = console.info;
|
|
41
|
+
console.log = () => {
|
|
42
|
+
};
|
|
43
|
+
console.warn = () => {
|
|
44
|
+
};
|
|
45
|
+
console.info = () => {
|
|
46
|
+
};
|
|
47
|
+
let server;
|
|
48
|
+
try {
|
|
49
|
+
const mod = await import('hypha-rpc');
|
|
50
|
+
const connectToServer = mod.connectToServer || mod.default?.connectToServer;
|
|
51
|
+
server = await connectToServer({ server_url: serverUrl, token, name: "svamp-agent-emit" });
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.log = origLog;
|
|
54
|
+
console.warn = origWarn;
|
|
55
|
+
console.info = origInfo;
|
|
56
|
+
console.error(`Failed to connect to Hypha: ${err.message}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
console.log = origLog;
|
|
60
|
+
console.warn = origWarn;
|
|
61
|
+
console.info = origInfo;
|
|
62
|
+
await server.emit({ ...event, to: "*" });
|
|
63
|
+
await server.disconnect();
|
|
64
|
+
}
|
|
65
|
+
async function sessionSetTitle(title) {
|
|
66
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
67
|
+
if (!sessionId) {
|
|
68
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const configPath = getConfigPath(sessionId);
|
|
72
|
+
const config = readConfig(configPath);
|
|
73
|
+
config.title = title.trim();
|
|
74
|
+
writeConfig(configPath, config);
|
|
75
|
+
console.log(`Session title set: "${title.trim()}"`);
|
|
76
|
+
}
|
|
77
|
+
async function sessionSetLink(url, label) {
|
|
78
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
79
|
+
if (!sessionId) {
|
|
80
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
const resolvedLabel = label?.trim() || (() => {
|
|
84
|
+
try {
|
|
85
|
+
return new URL(url).hostname;
|
|
86
|
+
} catch {
|
|
87
|
+
return "View";
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
const configPath = getConfigPath(sessionId);
|
|
91
|
+
const config = readConfig(configPath);
|
|
92
|
+
config.session_link = { url: url.trim(), label: resolvedLabel };
|
|
93
|
+
writeConfig(configPath, config);
|
|
94
|
+
console.log(`Session link set: "${resolvedLabel}" \u2192 ${url.trim()}`);
|
|
95
|
+
}
|
|
96
|
+
async function sessionNotify(message, level = "info") {
|
|
97
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
98
|
+
if (!sessionId) {
|
|
99
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
await connectAndEmit({
|
|
103
|
+
type: "svamp:session-notify",
|
|
104
|
+
data: { sessionId, message, level, timestamp: Date.now() }
|
|
105
|
+
});
|
|
106
|
+
console.log(`Notification sent [${level}]: ${message}`);
|
|
107
|
+
}
|
|
108
|
+
async function sessionBroadcast(action, args) {
|
|
109
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
110
|
+
if (!sessionId) {
|
|
111
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
let payload = { action };
|
|
115
|
+
if (action === "open-canvas") {
|
|
116
|
+
const url = args[0];
|
|
117
|
+
if (!url) {
|
|
118
|
+
console.error("Usage: svamp session broadcast open-canvas <url> [label]");
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const label = args[1] || (() => {
|
|
122
|
+
try {
|
|
123
|
+
return new URL(url).hostname;
|
|
124
|
+
} catch {
|
|
125
|
+
return "View";
|
|
126
|
+
}
|
|
127
|
+
})();
|
|
128
|
+
payload = { action, url, label };
|
|
129
|
+
} else if (action === "close-canvas") ; else if (action === "toast") {
|
|
130
|
+
const message = args[0];
|
|
131
|
+
if (!message) {
|
|
132
|
+
console.error("Usage: svamp session broadcast toast <message>");
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
payload = { action, message };
|
|
136
|
+
} else {
|
|
137
|
+
console.error(`Unknown broadcast action: ${action}`);
|
|
138
|
+
console.error("Available actions: open-canvas, close-canvas, toast");
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
await connectAndEmit({
|
|
142
|
+
type: "svamp:session-broadcast",
|
|
143
|
+
data: { sessionId, ...payload }
|
|
144
|
+
});
|
|
145
|
+
console.log(`Broadcast sent: ${action}`);
|
|
146
|
+
}
|
|
147
|
+
async function machineNotify(message, level = "info") {
|
|
148
|
+
const machineId = process.env.SVAMP_MACHINE_ID;
|
|
149
|
+
await connectAndEmit({
|
|
150
|
+
type: "svamp:machine-notify",
|
|
151
|
+
data: { machineId, message, level, timestamp: Date.now() }
|
|
152
|
+
});
|
|
153
|
+
console.log(`Machine notification sent [${level}]: ${message}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { machineNotify, sessionBroadcast, sessionNotify, sessionSetLink, sessionSetTitle };
|
|
@@ -29,14 +29,21 @@ function requireSandboxApiEnv() {
|
|
|
29
29
|
}
|
|
30
30
|
return env;
|
|
31
31
|
}
|
|
32
|
-
async function sandboxFetch(env, path, init) {
|
|
32
|
+
async function sandboxFetch(env, path, init, timeoutMs = 3e4) {
|
|
33
33
|
const url = `${env.apiUrl.replace(/\/+$/, "")}${path}`;
|
|
34
34
|
const headers = {
|
|
35
35
|
"Authorization": `Bearer ${env.apiKey}`,
|
|
36
36
|
"Content-Type": "application/json",
|
|
37
37
|
...init?.headers || {}
|
|
38
38
|
};
|
|
39
|
-
const
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
41
|
+
let res;
|
|
42
|
+
try {
|
|
43
|
+
res = await fetch(url, { ...init, headers, signal: controller.signal });
|
|
44
|
+
} finally {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
}
|
|
40
47
|
if (!res.ok) {
|
|
41
48
|
const body = await res.text().catch(() => "");
|
|
42
49
|
let detail = body;
|
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-DPhSmbr1.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -106,10 +106,10 @@ async function main() {
|
|
|
106
106
|
} else if (subcommand === "skills") {
|
|
107
107
|
await handleSkillsCommand();
|
|
108
108
|
} else if (subcommand === "service" || subcommand === "svc") {
|
|
109
|
-
const { handleServiceCommand } = await import('./commands-
|
|
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-Dc_6JdE_.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-CvCDrFPH.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-DTwMJ7dx.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-DPhSmbr1.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-DPhSmbr1.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-DPhSmbr1.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-DPhSmbr1.mjs').then(function (n) { return n.h; });
|
|
224
|
+
const { GeminiTransport } = await import('./run-DPhSmbr1.mjs').then(function (n) { return n.G; });
|
|
225
|
+
const { DefaultTransport } = await import('./run-DPhSmbr1.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-FAWhkKtz.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-FAWhkKtz.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-FAWhkKtz.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-FAWhkKtz.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")
|
|
@@ -524,6 +524,40 @@ async function handleSessionCommand() {
|
|
|
524
524
|
process.exit(1);
|
|
525
525
|
}
|
|
526
526
|
await sessionRalphStatus(sessionArgs[1], targetMachineId);
|
|
527
|
+
} else if (sessionSubcommand === "set-title") {
|
|
528
|
+
const title = sessionArgs[1];
|
|
529
|
+
if (!title) {
|
|
530
|
+
console.error("Usage: svamp session set-title <title>");
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
const { sessionSetTitle } = await import('./agentCommands-C6iGblcL.mjs');
|
|
534
|
+
await sessionSetTitle(title);
|
|
535
|
+
} else if (sessionSubcommand === "set-link") {
|
|
536
|
+
const url = sessionArgs[1];
|
|
537
|
+
if (!url) {
|
|
538
|
+
console.error("Usage: svamp session set-link <url> [label]");
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
542
|
+
const { sessionSetLink } = await import('./agentCommands-C6iGblcL.mjs');
|
|
543
|
+
await sessionSetLink(url, label);
|
|
544
|
+
} else if (sessionSubcommand === "notify") {
|
|
545
|
+
const message = sessionArgs[1];
|
|
546
|
+
if (!message) {
|
|
547
|
+
console.error("Usage: svamp session notify <message> [--level info|warning|error]");
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
const level = parseFlagStr("--level") || "info";
|
|
551
|
+
const { sessionNotify } = await import('./agentCommands-C6iGblcL.mjs');
|
|
552
|
+
await sessionNotify(message, level);
|
|
553
|
+
} else if (sessionSubcommand === "broadcast") {
|
|
554
|
+
const action = sessionArgs[1];
|
|
555
|
+
if (!action) {
|
|
556
|
+
console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
const { sessionBroadcast } = await import('./agentCommands-C6iGblcL.mjs');
|
|
560
|
+
await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
|
|
527
561
|
} else if (sessionSubcommand === "queue") {
|
|
528
562
|
const queueSubcmd = sessionArgs[1];
|
|
529
563
|
if (queueSubcmd === "add") {
|
|
@@ -563,7 +597,7 @@ async function handleMachineCommand() {
|
|
|
563
597
|
return;
|
|
564
598
|
}
|
|
565
599
|
if (machineSubcommand === "share") {
|
|
566
|
-
const { machineShare } = await import('./commands-
|
|
600
|
+
const { machineShare } = await import('./commands-FAWhkKtz.mjs');
|
|
567
601
|
let machineId;
|
|
568
602
|
const shareArgs = [];
|
|
569
603
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
@@ -593,7 +627,7 @@ async function handleMachineCommand() {
|
|
|
593
627
|
}
|
|
594
628
|
await machineShare(machineId, { add, remove, list, configPath, showConfig });
|
|
595
629
|
} else if (machineSubcommand === "exec") {
|
|
596
|
-
const { machineExec } = await import('./commands-
|
|
630
|
+
const { machineExec } = await import('./commands-FAWhkKtz.mjs');
|
|
597
631
|
let machineId;
|
|
598
632
|
let cwd;
|
|
599
633
|
const cmdParts = [];
|
|
@@ -613,7 +647,7 @@ async function handleMachineCommand() {
|
|
|
613
647
|
}
|
|
614
648
|
await machineExec(machineId, command, cwd);
|
|
615
649
|
} else if (machineSubcommand === "info") {
|
|
616
|
-
const { machineInfo } = await import('./commands-
|
|
650
|
+
const { machineInfo } = await import('./commands-FAWhkKtz.mjs');
|
|
617
651
|
let machineId;
|
|
618
652
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
619
653
|
if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
|
|
@@ -621,8 +655,22 @@ async function handleMachineCommand() {
|
|
|
621
655
|
}
|
|
622
656
|
}
|
|
623
657
|
await machineInfo(machineId);
|
|
658
|
+
} else if (machineSubcommand === "notify") {
|
|
659
|
+
const message = machineArgs[1];
|
|
660
|
+
if (!message) {
|
|
661
|
+
console.error("Usage: svamp machine notify <message> [--level info|warning|error]");
|
|
662
|
+
process.exit(1);
|
|
663
|
+
}
|
|
664
|
+
let level = "info";
|
|
665
|
+
for (let i = 2; i < machineArgs.length; i++) {
|
|
666
|
+
if (machineArgs[i] === "--level" && i + 1 < machineArgs.length) {
|
|
667
|
+
level = machineArgs[++i];
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
const { machineNotify } = await import('./agentCommands-C6iGblcL.mjs');
|
|
671
|
+
await machineNotify(message, level);
|
|
624
672
|
} else if (machineSubcommand === "ls") {
|
|
625
|
-
const { machineLs } = await import('./commands-
|
|
673
|
+
const { machineLs } = await import('./commands-FAWhkKtz.mjs');
|
|
626
674
|
let machineId;
|
|
627
675
|
let showHidden = false;
|
|
628
676
|
let path;
|
|
@@ -650,7 +698,7 @@ async function handleSkillsCommand() {
|
|
|
650
698
|
printSkillsHelp();
|
|
651
699
|
return;
|
|
652
700
|
}
|
|
653
|
-
const { skillsFind, skillsInstall, skillsList, skillsRemove, skillsPublish } = await import('./commands-
|
|
701
|
+
const { skillsFind, skillsInstall, skillsList, skillsRemove, skillsPublish } = await import('./commands-UFi0_ESV.mjs');
|
|
654
702
|
if (skillsSubcommand === "find" || skillsSubcommand === "search") {
|
|
655
703
|
const query = skillsArgs.slice(1).filter((a) => !a.startsWith("--")).join(" ");
|
|
656
704
|
if (!query) {
|
|
@@ -1021,6 +1069,11 @@ Usage:
|
|
|
1021
1069
|
svamp skills find <query> Search the skills marketplace
|
|
1022
1070
|
svamp skills install <n> Install a skill from the marketplace
|
|
1023
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
|
|
1024
1077
|
svamp service expose <name> Expose a service from this sandbox
|
|
1025
1078
|
svamp service list List service groups
|
|
1026
1079
|
svamp service --help Show all service commands
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createServiceGroup, listServiceGroups, getServiceGroup, deleteServiceGroup, addBackend, removeBackend, addPort, removePort, renameSubdomain, getSandboxEnv } from './api-
|
|
1
|
+
import { createServiceGroup, listServiceGroups, getServiceGroup, deleteServiceGroup, addBackend, removeBackend, addPort, removePort, renameSubdomain, getSandboxEnv } from './api-BRbsyqJ4.mjs';
|
|
2
2
|
|
|
3
3
|
function getFlag(args, flag) {
|
|
4
4
|
const idx = args.indexOf(flag);
|
|
@@ -296,7 +296,7 @@ Service is live:`);
|
|
|
296
296
|
}
|
|
297
297
|
} else {
|
|
298
298
|
console.log(`No SANDBOX_ID detected \u2014 starting reverse tunnel.`);
|
|
299
|
-
const { runTunnel } = await import('./tunnel-
|
|
299
|
+
const { runTunnel } = await import('./tunnel-C3UsqTxi.mjs');
|
|
300
300
|
await runTunnel(name, ports);
|
|
301
301
|
}
|
|
302
302
|
} catch (err) {
|
|
@@ -312,7 +312,7 @@ async function serviceTunnel(args) {
|
|
|
312
312
|
console.error("Usage: svamp service tunnel <name> --port <port> [--port <port2>]");
|
|
313
313
|
process.exit(1);
|
|
314
314
|
}
|
|
315
|
-
const { runTunnel } = await import('./tunnel-
|
|
315
|
+
const { runTunnel } = await import('./tunnel-C3UsqTxi.mjs');
|
|
316
316
|
await runTunnel(name, ports);
|
|
317
317
|
}
|
|
318
318
|
async function handleServiceCommand() {
|
|
@@ -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-FAWhkKtz.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-DPhSmbr1.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-DPhSmbr1.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
|
@@ -9,6 +9,18 @@ const SKILLS_DIR = join(os__default.homedir(), ".claude", "skills");
|
|
|
9
9
|
function getArtifactBaseUrl() {
|
|
10
10
|
return `${SKILLS_SERVER}/${SKILLS_WORKSPACE}/artifacts`;
|
|
11
11
|
}
|
|
12
|
+
async function fetchWithTimeout(url, options = {}, timeoutMs = 3e4) {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
15
|
+
try {
|
|
16
|
+
return await fetch(url, { ...options, signal: controller.signal });
|
|
17
|
+
} catch (err) {
|
|
18
|
+
if (err.name === "AbortError") throw new Error(`Request timed out after ${timeoutMs}ms: ${url}`);
|
|
19
|
+
throw err;
|
|
20
|
+
} finally {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
12
24
|
function parseFrontmatter(content) {
|
|
13
25
|
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
14
26
|
if (!match) return null;
|
|
@@ -54,7 +66,7 @@ async function searchSkills(query) {
|
|
|
54
66
|
const base = getArtifactBaseUrl();
|
|
55
67
|
const filters = encodeURIComponent(JSON.stringify({ type: "skill" }));
|
|
56
68
|
const url = `${base}/${SKILLS_COLLECTION}/children?keywords=${encodeURIComponent(query)}&filters=${filters}&limit=50`;
|
|
57
|
-
const resp = await
|
|
69
|
+
const resp = await fetchWithTimeout(url);
|
|
58
70
|
if (!resp.ok) {
|
|
59
71
|
if (resp.status === 404) return [];
|
|
60
72
|
throw new Error(`Search failed: ${resp.status} ${resp.statusText}`);
|
|
@@ -65,7 +77,7 @@ async function searchSkills(query) {
|
|
|
65
77
|
async function getSkillInfo(skillAlias) {
|
|
66
78
|
const base = getArtifactBaseUrl();
|
|
67
79
|
const url = `${base}/${skillAlias}`;
|
|
68
|
-
const resp = await
|
|
80
|
+
const resp = await fetchWithTimeout(url);
|
|
69
81
|
if (!resp.ok) {
|
|
70
82
|
if (resp.status === 404) return null;
|
|
71
83
|
throw new Error(`Get skill failed: ${resp.status} ${resp.statusText}`);
|
|
@@ -77,7 +89,7 @@ async function listSkillFiles(skillAlias, dir = "") {
|
|
|
77
89
|
const base = getArtifactBaseUrl();
|
|
78
90
|
const pathPart = dir ? `/${dir}/` : "/";
|
|
79
91
|
const url = `${base}/${skillAlias}/files${pathPart}`;
|
|
80
|
-
const resp = await
|
|
92
|
+
const resp = await fetchWithTimeout(url);
|
|
81
93
|
if (!resp.ok) {
|
|
82
94
|
throw new Error(`List files failed: ${resp.status} ${resp.statusText}`);
|
|
83
95
|
}
|
|
@@ -87,7 +99,7 @@ async function listSkillFiles(skillAlias, dir = "") {
|
|
|
87
99
|
async function downloadSkillFile(skillAlias, filePath) {
|
|
88
100
|
const base = getArtifactBaseUrl();
|
|
89
101
|
const url = `${base}/${skillAlias}/files/${filePath}`;
|
|
90
|
-
const resp = await
|
|
102
|
+
const resp = await fetchWithTimeout(url, { redirect: "follow" }, 6e4);
|
|
91
103
|
if (!resp.ok) {
|
|
92
104
|
throw new Error(`Download failed for ${filePath}: ${resp.status} ${resp.statusText}`);
|
|
93
105
|
}
|
|
@@ -221,6 +233,10 @@ async function skillsInstall(skillName, opts) {
|
|
|
221
233
|
try {
|
|
222
234
|
const content = await downloadSkillFile(skillName, filePath);
|
|
223
235
|
const localPath = join(targetDir, filePath);
|
|
236
|
+
if (!localPath.startsWith(targetDir + "/")) {
|
|
237
|
+
errors.push(` ${filePath}: path outside skill directory (blocked)`);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
224
240
|
fs.mkdirSync(join(localPath, ".."), { recursive: true });
|
|
225
241
|
fs.writeFileSync(localPath, content, "utf-8");
|
|
226
242
|
downloaded++;
|
|
@@ -229,12 +245,10 @@ async function skillsInstall(skillName, opts) {
|
|
|
229
245
|
}
|
|
230
246
|
}
|
|
231
247
|
if (errors.length > 0) {
|
|
232
|
-
console.error(`Warning: ${errors.length} file(s) failed to download:`);
|
|
233
|
-
errors.forEach((e) => console.error(e));
|
|
234
|
-
}
|
|
235
|
-
if (downloaded === 0) {
|
|
236
248
|
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
237
|
-
console.error(
|
|
249
|
+
console.error(`Installation failed \u2014 ${errors.length} file(s) could not be downloaded:`);
|
|
250
|
+
errors.forEach((e) => console.error(e));
|
|
251
|
+
console.error("Partial installation cleaned up. Run the command again to retry.");
|
|
238
252
|
process.exit(1);
|
|
239
253
|
}
|
|
240
254
|
const skillMdPath = join(targetDir, "SKILL.md");
|
|
@@ -383,25 +397,35 @@ async function skillsPublish(skillPath, opts) {
|
|
|
383
397
|
}
|
|
384
398
|
let artifactId;
|
|
385
399
|
let isUpdate = false;
|
|
400
|
+
let existingArtifact = null;
|
|
386
401
|
try {
|
|
387
|
-
|
|
402
|
+
existingArtifact = await am.read({
|
|
388
403
|
artifact_id: `${SKILLS_WORKSPACE}/${manifest.name}`,
|
|
389
404
|
_rkwargs: true
|
|
390
405
|
});
|
|
391
|
-
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
if (existingArtifact) {
|
|
409
|
+
artifactId = existingArtifact.id;
|
|
392
410
|
isUpdate = true;
|
|
393
411
|
console.log(`Updating existing skill "${manifest.name}"...`);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
412
|
+
try {
|
|
413
|
+
await am.edit({
|
|
414
|
+
artifact_id: artifactId,
|
|
415
|
+
manifest: {
|
|
416
|
+
name: manifest.name,
|
|
417
|
+
description: manifest.description,
|
|
418
|
+
...manifest.metadata && { metadata: manifest.metadata }
|
|
419
|
+
},
|
|
420
|
+
stage: true,
|
|
421
|
+
_rkwargs: true
|
|
422
|
+
});
|
|
423
|
+
} catch (err) {
|
|
424
|
+
console.error(`Failed to stage skill artifact for editing: ${err.message}`);
|
|
425
|
+
await server.disconnect();
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
405
429
|
try {
|
|
406
430
|
const created = await am.create({
|
|
407
431
|
alias: manifest.name,
|
|
@@ -441,11 +465,11 @@ async function skillsPublish(skillPath, opts) {
|
|
|
441
465
|
uploadErrors.push(`${relPath}: failed to get upload URL`);
|
|
442
466
|
continue;
|
|
443
467
|
}
|
|
444
|
-
const resp = await
|
|
468
|
+
const resp = await fetchWithTimeout(putUrl, {
|
|
445
469
|
method: "PUT",
|
|
446
470
|
body: content,
|
|
447
471
|
headers: { "Content-Type": "application/octet-stream" }
|
|
448
|
-
});
|
|
472
|
+
}, 12e4);
|
|
449
473
|
if (!resp.ok) {
|
|
450
474
|
uploadErrors.push(`${relPath}: upload returned ${resp.status}`);
|
|
451
475
|
continue;
|
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-DPhSmbr1.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.65";
|
|
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",
|
|
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",
|