svamp-cli 0.1.64 → 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/cli.mjs +66 -18
- package/dist/{commands-BfMlD9o4.mjs → commands-Dc_6JdE_.mjs} +2 -2
- package/dist/{commands-Brx7D-77.mjs → commands-FAWhkKtz.mjs} +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{package-Dg0hQJRC.mjs → package-CvCDrFPH.mjs} +1 -1
- package/dist/{run-BImPgXHd.mjs → run-DPhSmbr1.mjs} +114 -27
- package/dist/{run-C7VxH4X8.mjs → run-DTwMJ7dx.mjs} +1 -1
- package/package.json +1 -1
|
@@ -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 };
|
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';
|
|
@@ -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-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;
|
|
@@ -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';
|
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';
|
|
@@ -385,21 +385,22 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
385
385
|
const machineOwner = currentMetadata.sharing?.owner;
|
|
386
386
|
const isSharedUser = callerEmail && machineOwner && callerEmail.toLowerCase() !== machineOwner.toLowerCase();
|
|
387
387
|
if (isSharedUser) {
|
|
388
|
-
const machineUser = currentMetadata.sharing?.allowedUsers?.find(
|
|
389
|
-
(u) => u.email.toLowerCase() === callerEmail.toLowerCase()
|
|
390
|
-
);
|
|
391
|
-
const callerRole = machineUser?.role || "interact";
|
|
392
388
|
const sharing = {
|
|
393
389
|
enabled: true,
|
|
394
|
-
owner:
|
|
390
|
+
owner: callerEmail,
|
|
391
|
+
// spawning user owns their session
|
|
395
392
|
allowedUsers: [
|
|
396
|
-
|
|
393
|
+
// Machine owner gets admin access (can monitor/control sessions on their machine)
|
|
397
394
|
{
|
|
398
|
-
email:
|
|
399
|
-
role:
|
|
395
|
+
email: machineOwner,
|
|
396
|
+
role: "admin",
|
|
400
397
|
addedAt: Date.now(),
|
|
401
398
|
addedBy: "machine-auto"
|
|
402
|
-
}
|
|
399
|
+
},
|
|
400
|
+
// Preserve any explicitly requested allowedUsers (e.g. additional collaborators)
|
|
401
|
+
...(options.sharing?.allowedUsers || []).filter(
|
|
402
|
+
(u) => u.email.toLowerCase() !== machineOwner.toLowerCase()
|
|
403
|
+
)
|
|
403
404
|
]
|
|
404
405
|
};
|
|
405
406
|
options = { ...options, sharing };
|
|
@@ -419,16 +420,11 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
419
420
|
...options,
|
|
420
421
|
securityContext: mergeSecurityContexts(machineCtx, options.securityContext)
|
|
421
422
|
};
|
|
422
|
-
if (machineCtx.role && options.sharing?.enabled) {
|
|
423
|
-
const user = options.sharing.allowedUsers?.find(
|
|
424
|
-
(u) => u.email.toLowerCase() === callerEmail.toLowerCase()
|
|
425
|
-
);
|
|
426
|
-
if (user && !user.role) {
|
|
427
|
-
user.role = machineCtx.role;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
423
|
}
|
|
431
424
|
}
|
|
425
|
+
if (options.injectPlatformGuidance === void 0 && currentMetadata.injectPlatformGuidance !== void 0) {
|
|
426
|
+
options = { ...options, injectPlatformGuidance: currentMetadata.injectPlatformGuidance };
|
|
427
|
+
}
|
|
432
428
|
const result = await handlers.spawnSession({
|
|
433
429
|
...options,
|
|
434
430
|
machineId
|
|
@@ -486,7 +482,8 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
486
482
|
metadataVersion++;
|
|
487
483
|
savePersistedMachineMetadata(metadata.svampHomeDir, {
|
|
488
484
|
sharing: currentMetadata.sharing,
|
|
489
|
-
securityContextConfig: currentMetadata.securityContextConfig
|
|
485
|
+
securityContextConfig: currentMetadata.securityContextConfig,
|
|
486
|
+
injectPlatformGuidance: currentMetadata.injectPlatformGuidance
|
|
490
487
|
});
|
|
491
488
|
notifyListeners({
|
|
492
489
|
type: "update-machine",
|
|
@@ -546,7 +543,8 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
546
543
|
metadataVersion++;
|
|
547
544
|
savePersistedMachineMetadata(metadata.svampHomeDir, {
|
|
548
545
|
sharing: currentMetadata.sharing,
|
|
549
|
-
securityContextConfig: currentMetadata.securityContextConfig
|
|
546
|
+
securityContextConfig: currentMetadata.securityContextConfig,
|
|
547
|
+
injectPlatformGuidance: currentMetadata.injectPlatformGuidance
|
|
550
548
|
});
|
|
551
549
|
notifyListeners({
|
|
552
550
|
type: "update-machine",
|
|
@@ -567,7 +565,8 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
567
565
|
metadataVersion++;
|
|
568
566
|
savePersistedMachineMetadata(metadata.svampHomeDir, {
|
|
569
567
|
sharing: currentMetadata.sharing,
|
|
570
|
-
securityContextConfig: currentMetadata.securityContextConfig
|
|
568
|
+
securityContextConfig: currentMetadata.securityContextConfig,
|
|
569
|
+
injectPlatformGuidance: currentMetadata.injectPlatformGuidance
|
|
571
570
|
});
|
|
572
571
|
notifyListeners({
|
|
573
572
|
type: "update-machine",
|
|
@@ -3754,13 +3753,17 @@ async function verifyNonoIsolation(binaryPath) {
|
|
|
3754
3753
|
"-s",
|
|
3755
3754
|
"--allow",
|
|
3756
3755
|
workDir,
|
|
3757
|
-
|
|
3756
|
+
// NOTE: Do NOT add --allow-cwd here. If the daemon's CWD happens to be
|
|
3757
|
+
// $HOME (common when started interactively), --allow-cwd would grant
|
|
3758
|
+
// access to $HOME, allowing the probe file write to succeed and making
|
|
3759
|
+
// verification incorrectly fail ("file leaked to host filesystem").
|
|
3760
|
+
// We already grant --allow workDir explicitly, so --allow-cwd is redundant.
|
|
3758
3761
|
"--trust-override",
|
|
3759
3762
|
"--",
|
|
3760
3763
|
"sh",
|
|
3761
3764
|
"-c",
|
|
3762
3765
|
testScript
|
|
3763
|
-
], { timeout: 15e3 });
|
|
3766
|
+
], { timeout: 15e3, cwd: workDir });
|
|
3764
3767
|
return parseIsolationTestOutput(stdout, probeFile);
|
|
3765
3768
|
} catch (e) {
|
|
3766
3769
|
return { passed: false, error: e.message };
|
|
@@ -4408,6 +4411,83 @@ class ProcessSupervisor {
|
|
|
4408
4411
|
|
|
4409
4412
|
const __filename$1 = fileURLToPath(import.meta.url);
|
|
4410
4413
|
const __dirname$1 = dirname(__filename$1);
|
|
4414
|
+
const CLAUDE_SKILLS_DIR = join(os__default.homedir(), ".claude", "skills");
|
|
4415
|
+
async function installSkillFromEndpoint(name, baseUrl) {
|
|
4416
|
+
const resp = await fetch(baseUrl, { signal: AbortSignal.timeout(15e3) });
|
|
4417
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status} from ${baseUrl}`);
|
|
4418
|
+
const index = await resp.json();
|
|
4419
|
+
const files = index.files || [];
|
|
4420
|
+
if (files.length === 0) throw new Error(`Skill index at ${baseUrl} has no files`);
|
|
4421
|
+
const targetDir = join(CLAUDE_SKILLS_DIR, name);
|
|
4422
|
+
mkdirSync(targetDir, { recursive: true });
|
|
4423
|
+
for (const filePath of files) {
|
|
4424
|
+
if (!filePath) continue;
|
|
4425
|
+
const url = `${baseUrl}${filePath}`;
|
|
4426
|
+
const fileResp = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
4427
|
+
if (!fileResp.ok) throw new Error(`Failed to download ${filePath}: HTTP ${fileResp.status}`);
|
|
4428
|
+
const content = await fileResp.text();
|
|
4429
|
+
const localPath = join(targetDir, filePath);
|
|
4430
|
+
if (!localPath.startsWith(targetDir + "/")) continue;
|
|
4431
|
+
mkdirSync(dirname(localPath), { recursive: true });
|
|
4432
|
+
writeFileSync(localPath, content, "utf-8");
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
async function installSkillFromMarketplace(name) {
|
|
4436
|
+
const BASE = `https://hypha.aicell.io/hypha-cloud/artifacts/${name}`;
|
|
4437
|
+
async function collectFiles(dir = "") {
|
|
4438
|
+
const url = dir ? `${BASE}/files/${dir}` : `${BASE}/files/`;
|
|
4439
|
+
const resp = await fetch(url, { signal: AbortSignal.timeout(15e3) });
|
|
4440
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status} listing files`);
|
|
4441
|
+
const data = await resp.json();
|
|
4442
|
+
const items = Array.isArray(data) ? data : data.items || [];
|
|
4443
|
+
const result = [];
|
|
4444
|
+
for (const item of items) {
|
|
4445
|
+
const itemPath = dir ? `${dir}/${item.name}` : item.name;
|
|
4446
|
+
if (item.type === "directory") {
|
|
4447
|
+
result.push(...await collectFiles(itemPath));
|
|
4448
|
+
} else {
|
|
4449
|
+
result.push(itemPath);
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
return result;
|
|
4453
|
+
}
|
|
4454
|
+
const files = await collectFiles();
|
|
4455
|
+
if (files.length === 0) throw new Error(`Skill ${name} has no files in marketplace`);
|
|
4456
|
+
const targetDir = join(CLAUDE_SKILLS_DIR, name);
|
|
4457
|
+
mkdirSync(targetDir, { recursive: true });
|
|
4458
|
+
for (const filePath of files) {
|
|
4459
|
+
const url = `${BASE}/files/${filePath}`;
|
|
4460
|
+
const resp = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
4461
|
+
if (!resp.ok) throw new Error(`Failed to download ${filePath}: HTTP ${resp.status}`);
|
|
4462
|
+
const content = await resp.text();
|
|
4463
|
+
const localPath = join(targetDir, filePath);
|
|
4464
|
+
if (!localPath.startsWith(targetDir + "/")) continue;
|
|
4465
|
+
mkdirSync(dirname(localPath), { recursive: true });
|
|
4466
|
+
writeFileSync(localPath, content, "utf-8");
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
async function ensureAutoInstalledSkills(logger) {
|
|
4470
|
+
const tasks = [
|
|
4471
|
+
{
|
|
4472
|
+
name: "svamp",
|
|
4473
|
+
install: () => installSkillFromMarketplace("svamp")
|
|
4474
|
+
},
|
|
4475
|
+
{
|
|
4476
|
+
name: "hypha",
|
|
4477
|
+
install: () => installSkillFromEndpoint("hypha", "https://hypha.aicell.io/ws/agent-skills/")
|
|
4478
|
+
}
|
|
4479
|
+
];
|
|
4480
|
+
for (const task of tasks) {
|
|
4481
|
+
const targetDir = join(CLAUDE_SKILLS_DIR, task.name);
|
|
4482
|
+
if (existsSync$1(targetDir)) continue;
|
|
4483
|
+
try {
|
|
4484
|
+
await task.install();
|
|
4485
|
+
logger.log(`[skills] Auto-installed: ${task.name}`);
|
|
4486
|
+
} catch (err) {
|
|
4487
|
+
logger.log(`[skills] Auto-install of "${task.name}" failed (non-fatal): ${err.message}`);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4411
4491
|
function loadEnvFile(path) {
|
|
4412
4492
|
if (!existsSync$1(path)) return false;
|
|
4413
4493
|
const lines = readFileSync$1(path, "utf-8").split("\n");
|
|
@@ -5172,6 +5252,8 @@ async function startDaemon(options) {
|
|
|
5172
5252
|
let server = null;
|
|
5173
5253
|
const supervisor = new ProcessSupervisor(join(SVAMP_HOME, "processes"));
|
|
5174
5254
|
await supervisor.init();
|
|
5255
|
+
ensureAutoInstalledSkills(logger).catch(() => {
|
|
5256
|
+
});
|
|
5175
5257
|
try {
|
|
5176
5258
|
logger.log("Connecting to Hypha server...");
|
|
5177
5259
|
server = await connectToHypha({
|
|
@@ -5328,7 +5410,8 @@ async function startDaemon(options) {
|
|
|
5328
5410
|
sharing: options2.sharing,
|
|
5329
5411
|
securityContext: options2.securityContext,
|
|
5330
5412
|
tags: options2.tags,
|
|
5331
|
-
parentSessionId: options2.parentSessionId
|
|
5413
|
+
parentSessionId: options2.parentSessionId,
|
|
5414
|
+
...options2.injectPlatformGuidance !== void 0 && { injectPlatformGuidance: options2.injectPlatformGuidance }
|
|
5332
5415
|
};
|
|
5333
5416
|
let claudeProcess = null;
|
|
5334
5417
|
const allPersisted = loadPersistedSessions();
|
|
@@ -5343,6 +5426,9 @@ async function startDaemon(options) {
|
|
|
5343
5426
|
const newState = processing ? "running" : "idle";
|
|
5344
5427
|
if (sessionMetadata.lifecycleState !== newState) {
|
|
5345
5428
|
sessionMetadata = { ...sessionMetadata, lifecycleState: newState };
|
|
5429
|
+
if (!processing) {
|
|
5430
|
+
sessionMetadata = { ...sessionMetadata, unread: true };
|
|
5431
|
+
}
|
|
5346
5432
|
sessionService.updateMetadata(sessionMetadata);
|
|
5347
5433
|
}
|
|
5348
5434
|
};
|
|
@@ -5957,7 +6043,7 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
5957
6043
|
isRestartingClaude = false;
|
|
5958
6044
|
}
|
|
5959
6045
|
};
|
|
5960
|
-
if (sessionMetadata.sharing?.enabled
|
|
6046
|
+
if (sessionMetadata.sharing?.enabled) {
|
|
5961
6047
|
try {
|
|
5962
6048
|
stagedCredentials = await stageCredentialsForSharing(sessionId);
|
|
5963
6049
|
logger.log(`[Session ${sessionId}] Credentials staged at ${stagedCredentials.homePath}`);
|
|
@@ -6954,7 +7040,7 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
6954
7040
|
const defaultHomeDir = existsSync$1("/data") ? "/data" : os__default.homedir();
|
|
6955
7041
|
const persistedMachineMeta = loadPersistedMachineMetadata(SVAMP_HOME);
|
|
6956
7042
|
if (persistedMachineMeta) {
|
|
6957
|
-
logger.log(`Restored machine metadata (sharing=${!!persistedMachineMeta.sharing}, securityContextConfig=${!!persistedMachineMeta.securityContextConfig})`);
|
|
7043
|
+
logger.log(`Restored machine metadata (sharing=${!!persistedMachineMeta.sharing}, securityContextConfig=${!!persistedMachineMeta.securityContextConfig}, injectPlatformGuidance=${persistedMachineMeta.injectPlatformGuidance})`);
|
|
6958
7044
|
}
|
|
6959
7045
|
const machineMetadata = {
|
|
6960
7046
|
host: os__default.hostname(),
|
|
@@ -6965,9 +7051,10 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
6965
7051
|
svampLibDir: join(__dirname$1, ".."),
|
|
6966
7052
|
displayName: process.env.SVAMP_DISPLAY_NAME || void 0,
|
|
6967
7053
|
isolationCapabilities,
|
|
6968
|
-
// Restore persisted sharing
|
|
7054
|
+
// Restore persisted sharing, security context config, and platform guidance flag
|
|
6969
7055
|
...persistedMachineMeta?.sharing && { sharing: persistedMachineMeta.sharing },
|
|
6970
|
-
...persistedMachineMeta?.securityContextConfig && { securityContextConfig: persistedMachineMeta.securityContextConfig }
|
|
7056
|
+
...persistedMachineMeta?.securityContextConfig && { securityContextConfig: persistedMachineMeta.securityContextConfig },
|
|
7057
|
+
...persistedMachineMeta?.injectPlatformGuidance !== void 0 && { injectPlatformGuidance: persistedMachineMeta.injectPlatformGuidance }
|
|
6971
7058
|
};
|
|
6972
7059
|
const initialDaemonState = {
|
|
6973
7060
|
status: "running",
|
|
@@ -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-DPhSmbr1.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|