svamp-cli 0.1.21 → 0.1.22
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 +8 -8
- package/dist/commands-CKpC8R9T.mjs +481 -0
- package/dist/index.mjs +1 -1
- package/dist/package-JqEt5Ib4.mjs +57 -0
- package/dist/run-DwK3dfHd.mjs +3875 -0
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-
|
|
1
|
+
import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-DwK3dfHd.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -75,7 +75,7 @@ async function main() {
|
|
|
75
75
|
} else if (subcommand === "--help" || subcommand === "-h" || !subcommand) {
|
|
76
76
|
printHelp();
|
|
77
77
|
} else if (subcommand === "--version" || subcommand === "-v") {
|
|
78
|
-
const pkg = await import('./package-
|
|
78
|
+
const pkg = await import('./package-JqEt5Ib4.mjs').catch(() => ({ default: { version: "unknown" } }));
|
|
79
79
|
console.log(`svamp version: ${pkg.default.version}`);
|
|
80
80
|
} else {
|
|
81
81
|
console.error(`Unknown command: ${subcommand}`);
|
|
@@ -90,7 +90,7 @@ async function handleAgentCommand() {
|
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
if (agentArgs[0] === "list") {
|
|
93
|
-
const { KNOWN_ACP_AGENTS } = await import('./run-
|
|
93
|
+
const { KNOWN_ACP_AGENTS } = await import('./run-DwK3dfHd.mjs').then(function (n) { return n.f; });
|
|
94
94
|
console.log("Known ACP agents:");
|
|
95
95
|
for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
|
|
96
96
|
console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")}`);
|
|
@@ -99,10 +99,10 @@ async function handleAgentCommand() {
|
|
|
99
99
|
console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
|
-
const { resolveAcpAgentConfig } = await import('./run-
|
|
103
|
-
const { AcpBackend } = await import('./run-
|
|
104
|
-
const { GeminiTransport } = await import('./run-
|
|
105
|
-
const { DefaultTransport } = await import('./run-
|
|
102
|
+
const { resolveAcpAgentConfig } = await import('./run-DwK3dfHd.mjs').then(function (n) { return n.f; });
|
|
103
|
+
const { AcpBackend } = await import('./run-DwK3dfHd.mjs').then(function (n) { return n.e; });
|
|
104
|
+
const { GeminiTransport } = await import('./run-DwK3dfHd.mjs').then(function (n) { return n.G; });
|
|
105
|
+
const { DefaultTransport } = await import('./run-DwK3dfHd.mjs').then(function (n) { return n.D; });
|
|
106
106
|
let cwd = process.cwd();
|
|
107
107
|
const filteredArgs = [];
|
|
108
108
|
for (let i = 0; i < agentArgs.length; i++) {
|
|
@@ -231,7 +231,7 @@ async function handleSessionCommand() {
|
|
|
231
231
|
printSessionHelp();
|
|
232
232
|
return;
|
|
233
233
|
}
|
|
234
|
-
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach } = await import('./commands-
|
|
234
|
+
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach } = await import('./commands-CKpC8R9T.mjs');
|
|
235
235
|
if (sessionSubcommand === "list" || sessionSubcommand === "ls") {
|
|
236
236
|
await sessionList();
|
|
237
237
|
} else if (sessionSubcommand === "spawn") {
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { c as connectToHypha } from './run-DwK3dfHd.mjs';
|
|
5
|
+
import 'os';
|
|
6
|
+
import 'fs/promises';
|
|
7
|
+
import 'fs';
|
|
8
|
+
import 'path';
|
|
9
|
+
import 'url';
|
|
10
|
+
import 'child_process';
|
|
11
|
+
import 'crypto';
|
|
12
|
+
import 'node:crypto';
|
|
13
|
+
import 'node:child_process';
|
|
14
|
+
import '@agentclientprotocol/sdk';
|
|
15
|
+
|
|
16
|
+
const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
|
|
17
|
+
const DAEMON_STATE_FILE = join(SVAMP_HOME, "daemon.state.json");
|
|
18
|
+
const ENV_FILE = join(SVAMP_HOME, ".env");
|
|
19
|
+
function loadDotEnv() {
|
|
20
|
+
if (!existsSync(ENV_FILE)) return;
|
|
21
|
+
const lines = readFileSync(ENV_FILE, "utf-8").split("\n");
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
const trimmed = line.trim();
|
|
24
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
25
|
+
const eqIdx = trimmed.indexOf("=");
|
|
26
|
+
if (eqIdx === -1) continue;
|
|
27
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
28
|
+
const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
29
|
+
if (!process.env[key]) {
|
|
30
|
+
process.env[key] = value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function readDaemonState() {
|
|
35
|
+
if (!existsSync(DAEMON_STATE_FILE)) return null;
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(readFileSync(DAEMON_STATE_FILE, "utf-8"));
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function isDaemonAlive(state) {
|
|
43
|
+
try {
|
|
44
|
+
process.kill(state.pid, 0);
|
|
45
|
+
return true;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function connectAndGetMachine() {
|
|
51
|
+
loadDotEnv();
|
|
52
|
+
const state = readDaemonState();
|
|
53
|
+
if (!state || !isDaemonAlive(state)) {
|
|
54
|
+
console.error('Daemon is not running. Start it with "svamp daemon start".');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const serverUrl = process.env.HYPHA_SERVER_URL || state.hyphaServerUrl;
|
|
58
|
+
const token = process.env.HYPHA_TOKEN;
|
|
59
|
+
if (!serverUrl) {
|
|
60
|
+
console.error('No Hypha server URL. Run "svamp login <url>" first.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const origLog = console.log;
|
|
64
|
+
const origWarn = console.warn;
|
|
65
|
+
const origInfo = console.info;
|
|
66
|
+
const origError = console.error;
|
|
67
|
+
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
|
68
|
+
const stderrWrite = process.stderr.write.bind(process.stderr);
|
|
69
|
+
const isHyphaLog = (chunk) => typeof chunk === "string" && (chunk.includes("WebSocket connection") || chunk.includes("Connection established") || chunk.includes("registering service built-in") || chunk.includes("registered service") || chunk.includes("registered all") || chunk.includes("Subscribing to client_") || chunk.includes("subscribed to client_") || chunk.includes("subscribe to client_") || chunk.includes("Cleaning up all sessions") || chunk.includes("WebSocket connection disconnected") || chunk.includes("local RPC disconnection") || chunk.includes("Timeout registering service") || chunk.includes("Failed to subscribe to client_disconnected") || chunk.includes("Timeout subscribing to client_disconnected"));
|
|
70
|
+
console.log = () => {
|
|
71
|
+
};
|
|
72
|
+
console.warn = () => {
|
|
73
|
+
};
|
|
74
|
+
console.info = () => {
|
|
75
|
+
};
|
|
76
|
+
console.error = (...args) => {
|
|
77
|
+
if (args.some((a) => isHyphaLog(a))) return;
|
|
78
|
+
origError(...args);
|
|
79
|
+
};
|
|
80
|
+
process.stdout.write = (chunk, ...args) => {
|
|
81
|
+
if (isHyphaLog(chunk)) return true;
|
|
82
|
+
return stdoutWrite(chunk, ...args);
|
|
83
|
+
};
|
|
84
|
+
process.stderr.write = (chunk, ...args) => {
|
|
85
|
+
if (isHyphaLog(chunk)) return true;
|
|
86
|
+
return stderrWrite(chunk, ...args);
|
|
87
|
+
};
|
|
88
|
+
const restoreConsole = () => {
|
|
89
|
+
console.log = origLog;
|
|
90
|
+
console.warn = origWarn;
|
|
91
|
+
console.info = origInfo;
|
|
92
|
+
console.error = origError;
|
|
93
|
+
};
|
|
94
|
+
let server;
|
|
95
|
+
try {
|
|
96
|
+
server = await connectToHypha({
|
|
97
|
+
serverUrl,
|
|
98
|
+
token,
|
|
99
|
+
name: "svamp-session-cli"
|
|
100
|
+
});
|
|
101
|
+
} catch (err) {
|
|
102
|
+
restoreConsole();
|
|
103
|
+
console.error(`Failed to connect to Hypha: ${err.message}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
let machine;
|
|
107
|
+
if (state.machineId) {
|
|
108
|
+
try {
|
|
109
|
+
machine = await server.getService(`svamp-machine-${state.machineId}`);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!machine) {
|
|
114
|
+
try {
|
|
115
|
+
const services = await server.listServices();
|
|
116
|
+
const machineServices = services.filter((svc) => {
|
|
117
|
+
const sid = svc.id || svc.name || "";
|
|
118
|
+
return sid.includes("svamp-machine-");
|
|
119
|
+
});
|
|
120
|
+
if (machineServices.length === 0) {
|
|
121
|
+
restoreConsole();
|
|
122
|
+
console.error("No machine service found. Is the daemon registered on Hypha?");
|
|
123
|
+
await server.disconnect();
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const svcId = machineServices[0].id || machineServices[0].name;
|
|
127
|
+
machine = await server.getService(svcId);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
restoreConsole();
|
|
130
|
+
console.error(`Failed to discover machine service: ${err.message}`);
|
|
131
|
+
await server.disconnect();
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
restoreConsole();
|
|
136
|
+
return { server, machine };
|
|
137
|
+
}
|
|
138
|
+
function resolveSessionId(sessions, partial) {
|
|
139
|
+
const exact = sessions.find((s) => s.sessionId === partial);
|
|
140
|
+
if (exact) return exact;
|
|
141
|
+
const matches = sessions.filter((s) => s.sessionId.startsWith(partial));
|
|
142
|
+
if (matches.length === 1) return matches[0];
|
|
143
|
+
if (matches.length === 0) {
|
|
144
|
+
console.error(`No session found matching: ${partial}`);
|
|
145
|
+
console.error('Run "svamp session list" to see active sessions.');
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
console.error(`Ambiguous session ID "${partial}". Matches:`);
|
|
149
|
+
for (const s of matches) {
|
|
150
|
+
console.error(` ${s.sessionId}`);
|
|
151
|
+
}
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
function truncate(str, max) {
|
|
155
|
+
if (str.length <= max) return str;
|
|
156
|
+
return "..." + str.slice(str.length - max + 3);
|
|
157
|
+
}
|
|
158
|
+
function renderMessage(msg) {
|
|
159
|
+
const content = msg.content;
|
|
160
|
+
if (!content) return;
|
|
161
|
+
const role = content.role;
|
|
162
|
+
if (role === "user") {
|
|
163
|
+
const data = content.content;
|
|
164
|
+
let text;
|
|
165
|
+
if (typeof data === "string") {
|
|
166
|
+
try {
|
|
167
|
+
const parsed = JSON.parse(data);
|
|
168
|
+
text = parsed?.text || parsed?.content?.text || data;
|
|
169
|
+
} catch {
|
|
170
|
+
text = data;
|
|
171
|
+
}
|
|
172
|
+
} else if (data?.text) {
|
|
173
|
+
text = data.text;
|
|
174
|
+
} else if (data?.type === "text") {
|
|
175
|
+
text = data.text || "";
|
|
176
|
+
} else {
|
|
177
|
+
text = typeof data === "object" ? JSON.stringify(data) : String(data || "");
|
|
178
|
+
}
|
|
179
|
+
console.log(`\x1B[36m[user]\x1B[0m ${text}`);
|
|
180
|
+
} else if (role === "agent" || role === "assistant") {
|
|
181
|
+
const data = content.content?.data || content.content;
|
|
182
|
+
if (!data) return;
|
|
183
|
+
if (data.type === "assistant" && Array.isArray(data.content)) {
|
|
184
|
+
for (const block of data.content) {
|
|
185
|
+
if (block.type === "text" && block.text) {
|
|
186
|
+
process.stdout.write(block.text);
|
|
187
|
+
if (!block.text.endsWith("\n")) process.stdout.write("\n");
|
|
188
|
+
} else if (block.type === "tool_use") {
|
|
189
|
+
const argsStr = JSON.stringify(block.input || {}).slice(0, 120);
|
|
190
|
+
console.log(`\x1B[33m[tool]\x1B[0m ${block.name}(${argsStr})`);
|
|
191
|
+
} else if (block.type === "tool_result") {
|
|
192
|
+
const resultStr = typeof block.content === "string" ? block.content : JSON.stringify(block.content || "");
|
|
193
|
+
console.log(`\x1B[90m[result]\x1B[0m ${resultStr.slice(0, 200)}${resultStr.length > 200 ? "..." : ""}`);
|
|
194
|
+
} else if (block.type === "thinking") {
|
|
195
|
+
const text = block.thinking || block.text || "";
|
|
196
|
+
if (text) console.log(`\x1B[90m[thinking] ${text.slice(0, 200)}\x1B[0m`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} else if (data.type === "result") {
|
|
200
|
+
if (data.result) console.log(`\x1B[32m[done]\x1B[0m ${data.result}`);
|
|
201
|
+
} else if (data.type === "output") {
|
|
202
|
+
const inner = data.data;
|
|
203
|
+
if (inner?.type === "assistant" && Array.isArray(inner.content)) {
|
|
204
|
+
for (const block of inner.content) {
|
|
205
|
+
if (block.type === "text" && block.text) {
|
|
206
|
+
process.stdout.write(block.text);
|
|
207
|
+
if (!block.text.endsWith("\n")) process.stdout.write("\n");
|
|
208
|
+
} else if (block.type === "tool_use") {
|
|
209
|
+
const argsStr = JSON.stringify(block.input || {}).slice(0, 120);
|
|
210
|
+
console.log(`\x1B[33m[tool]\x1B[0m ${block.name}(${argsStr})`);
|
|
211
|
+
} else if (block.type === "tool_result") {
|
|
212
|
+
const resultStr = typeof block.content === "string" ? block.content : JSON.stringify(block.content || "");
|
|
213
|
+
console.log(`\x1B[90m[result]\x1B[0m ${resultStr.slice(0, 200)}${resultStr.length > 200 ? "..." : ""}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} else if (inner?.type === "result") {
|
|
217
|
+
if (inner.result) console.log(`\x1B[32m[done]\x1B[0m ${inner.result}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} else if (role === "session") {
|
|
221
|
+
const data = content.content?.data;
|
|
222
|
+
if (data?.type === "system" && data?.subtype === "init") {
|
|
223
|
+
console.log(`\x1B[90m[session init]\x1B[0m`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function sessionList() {
|
|
228
|
+
const { server, machine } = await connectAndGetMachine();
|
|
229
|
+
try {
|
|
230
|
+
const sessions = await machine.listSessions();
|
|
231
|
+
if (sessions.length === 0) {
|
|
232
|
+
console.log("No active sessions.");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const enriched = [];
|
|
236
|
+
for (const s of sessions) {
|
|
237
|
+
let flavor = "claude";
|
|
238
|
+
let name = "";
|
|
239
|
+
if (s.metadata) {
|
|
240
|
+
flavor = s.metadata.flavor || "claude";
|
|
241
|
+
name = s.metadata.name || "";
|
|
242
|
+
}
|
|
243
|
+
if (!name && s.active) {
|
|
244
|
+
try {
|
|
245
|
+
const svc = await server.getService(`svamp-session-${s.sessionId}`);
|
|
246
|
+
const { metadata } = await svc.getMetadata();
|
|
247
|
+
flavor = metadata?.flavor || flavor;
|
|
248
|
+
name = metadata?.name || "";
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
enriched.push({ ...s, flavor, name });
|
|
253
|
+
}
|
|
254
|
+
const header = `${"ID".padEnd(10)} ${"AGENT".padEnd(10)} ${"STATUS".padEnd(9)} ${"NAME".padEnd(25)} ${"DIRECTORY".padEnd(35)}`;
|
|
255
|
+
console.log(header);
|
|
256
|
+
console.log("-".repeat(header.length));
|
|
257
|
+
for (const s of enriched) {
|
|
258
|
+
const id = s.sessionId.slice(0, 8);
|
|
259
|
+
const agent = (s.flavor || "claude").padEnd(10);
|
|
260
|
+
const status = s.active ? "\x1B[32mactive\x1B[0m " : "\x1B[90minactive\x1B[0m";
|
|
261
|
+
const name = truncate(s.name || "-", 25).padEnd(25);
|
|
262
|
+
const dir = truncate(s.directory || "-", 33).padEnd(35);
|
|
263
|
+
console.log(`${id.padEnd(10)} ${agent} ${status} ${name} ${dir}`);
|
|
264
|
+
}
|
|
265
|
+
} finally {
|
|
266
|
+
await server.disconnect();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function sessionSpawn(agent, directory) {
|
|
270
|
+
const { server, machine } = await connectAndGetMachine();
|
|
271
|
+
try {
|
|
272
|
+
console.log(`Spawning ${agent} session in ${directory}...`);
|
|
273
|
+
const result = await machine.spawnSession({
|
|
274
|
+
directory,
|
|
275
|
+
agent
|
|
276
|
+
});
|
|
277
|
+
if (result.type === "success") {
|
|
278
|
+
console.log(`Session started: ${result.sessionId}`);
|
|
279
|
+
if (result.message) console.log(` ${result.message}`);
|
|
280
|
+
} else if (result.type === "requestToApproveDirectoryCreation") {
|
|
281
|
+
console.error(`Directory ${result.directory} does not exist. Create it first or use an existing directory.`);
|
|
282
|
+
process.exit(1);
|
|
283
|
+
} else {
|
|
284
|
+
console.error(`Failed: ${result.errorMessage || "Unknown error"}`);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
} finally {
|
|
288
|
+
await server.disconnect();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function sessionStop(sessionId) {
|
|
292
|
+
const { server, machine } = await connectAndGetMachine();
|
|
293
|
+
try {
|
|
294
|
+
const sessions = await machine.listSessions();
|
|
295
|
+
const match = resolveSessionId(sessions, sessionId);
|
|
296
|
+
const success = await machine.stopSession(match.sessionId);
|
|
297
|
+
if (success) {
|
|
298
|
+
console.log(`Session ${match.sessionId.slice(0, 8)} stopped.`);
|
|
299
|
+
} else {
|
|
300
|
+
console.error("Failed to stop session (not found on daemon).");
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
} finally {
|
|
304
|
+
await server.disconnect();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function sessionInfo(sessionId) {
|
|
308
|
+
const { server, machine } = await connectAndGetMachine();
|
|
309
|
+
try {
|
|
310
|
+
const sessions = await machine.listSessions();
|
|
311
|
+
const match = resolveSessionId(sessions, sessionId);
|
|
312
|
+
const fullId = match.sessionId;
|
|
313
|
+
let metadata = {};
|
|
314
|
+
let activity = {};
|
|
315
|
+
try {
|
|
316
|
+
const svc = await server.getService(`svamp-session-${fullId}`);
|
|
317
|
+
const metaResult = await svc.getMetadata();
|
|
318
|
+
metadata = metaResult.metadata || {};
|
|
319
|
+
activity = await svc.getActivityState();
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
console.log(`Session: ${fullId}`);
|
|
323
|
+
console.log(`Agent: ${metadata.flavor || "claude"}`);
|
|
324
|
+
console.log(`Name: ${metadata.name || "(unnamed)"}`);
|
|
325
|
+
console.log(`Directory: ${metadata.path || match.directory || "-"}`);
|
|
326
|
+
console.log(`Host: ${metadata.host || "-"}`);
|
|
327
|
+
console.log(`State: ${metadata.lifecycleState || "unknown"}`);
|
|
328
|
+
console.log(`Active: ${activity.active ?? "-"}`);
|
|
329
|
+
console.log(`Thinking: ${activity.thinking ?? "-"}`);
|
|
330
|
+
console.log(`Started by: ${metadata.startedBy || match.startedBy || "-"}`);
|
|
331
|
+
if (metadata.summary?.text) {
|
|
332
|
+
console.log(`Summary: ${metadata.summary.text}`);
|
|
333
|
+
}
|
|
334
|
+
if (metadata.claudeSessionId) {
|
|
335
|
+
console.log(`Claude ID: ${metadata.claudeSessionId}`);
|
|
336
|
+
}
|
|
337
|
+
if (metadata.sessionLink?.url) {
|
|
338
|
+
console.log(`Link: ${metadata.sessionLink.url}`);
|
|
339
|
+
}
|
|
340
|
+
} finally {
|
|
341
|
+
await server.disconnect();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async function sessionMessages(sessionId, last) {
|
|
345
|
+
const { server, machine } = await connectAndGetMachine();
|
|
346
|
+
try {
|
|
347
|
+
const sessions = await machine.listSessions();
|
|
348
|
+
const match = resolveSessionId(sessions, sessionId);
|
|
349
|
+
const fullId = match.sessionId;
|
|
350
|
+
const svc = await server.getService(`svamp-session-${fullId}`);
|
|
351
|
+
const { messages } = await svc.getMessages(0, 1e3);
|
|
352
|
+
const toShow = last ? messages.slice(-last) : messages;
|
|
353
|
+
if (toShow.length === 0) {
|
|
354
|
+
console.log("No messages yet.");
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
for (const msg of toShow) {
|
|
358
|
+
renderMessage(msg);
|
|
359
|
+
}
|
|
360
|
+
} finally {
|
|
361
|
+
await server.disconnect();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async function sessionAttach(sessionId) {
|
|
365
|
+
const { server, machine } = await connectAndGetMachine();
|
|
366
|
+
const sessions = await machine.listSessions();
|
|
367
|
+
const match = resolveSessionId(sessions, sessionId);
|
|
368
|
+
const fullId = match.sessionId;
|
|
369
|
+
let svc;
|
|
370
|
+
try {
|
|
371
|
+
svc = await server.getService(`svamp-session-${fullId}`);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
console.error(`Could not find session service: ${err.message}`);
|
|
374
|
+
await server.disconnect();
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
const { metadata } = await svc.getMetadata();
|
|
378
|
+
const flavor = metadata?.flavor || "claude";
|
|
379
|
+
const name = metadata?.name || fullId.slice(0, 8);
|
|
380
|
+
console.log(`Attached to ${flavor} session "${name}". Commands: /quit /abort /kill
|
|
381
|
+
`);
|
|
382
|
+
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
383
|
+
let replayDone = false;
|
|
384
|
+
await svc.registerListener({
|
|
385
|
+
onUpdate: (update) => {
|
|
386
|
+
if (update.type === "new-message") {
|
|
387
|
+
const msg = update.message;
|
|
388
|
+
if (!msg?.id) return;
|
|
389
|
+
if (seenMessageIds.has(msg.id)) return;
|
|
390
|
+
seenMessageIds.add(msg.id);
|
|
391
|
+
if (!replayDone) return;
|
|
392
|
+
renderMessage(msg);
|
|
393
|
+
} else if (update.type === "activity") {
|
|
394
|
+
if (!replayDone) return;
|
|
395
|
+
if (update.thinking) {
|
|
396
|
+
process.stdout.write("\x1B[90m[thinking...]\x1B[0m\r");
|
|
397
|
+
} else if (!update.active) {
|
|
398
|
+
process.stdout.write("\n> ");
|
|
399
|
+
}
|
|
400
|
+
} else if (update.type === "update-session") ;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
404
|
+
replayDone = true;
|
|
405
|
+
console.log(`\x1B[90m(${seenMessageIds.size} messages in history)\x1B[0m`);
|
|
406
|
+
process.stdout.write("> ");
|
|
407
|
+
const readline = await import('readline');
|
|
408
|
+
const rl = readline.createInterface({
|
|
409
|
+
input: process.stdin,
|
|
410
|
+
output: process.stdout,
|
|
411
|
+
terminal: true
|
|
412
|
+
});
|
|
413
|
+
rl.on("line", async (line) => {
|
|
414
|
+
const trimmed = line.trim();
|
|
415
|
+
if (!trimmed) {
|
|
416
|
+
process.stdout.write("> ");
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (trimmed === "/quit" || trimmed === "/detach") {
|
|
420
|
+
console.log("Detaching (session continues running)...");
|
|
421
|
+
rl.close();
|
|
422
|
+
await server.disconnect();
|
|
423
|
+
process.exit(0);
|
|
424
|
+
}
|
|
425
|
+
if (trimmed === "/abort" || trimmed === "/cancel") {
|
|
426
|
+
try {
|
|
427
|
+
await svc.abort();
|
|
428
|
+
console.log("Abort sent.");
|
|
429
|
+
} catch (err) {
|
|
430
|
+
console.error(`Abort failed: ${err.message}`);
|
|
431
|
+
}
|
|
432
|
+
process.stdout.write("> ");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (trimmed === "/kill") {
|
|
436
|
+
try {
|
|
437
|
+
await svc.killSession();
|
|
438
|
+
console.log("Session killed.");
|
|
439
|
+
} catch (err) {
|
|
440
|
+
console.error(`Kill failed: ${err.message}`);
|
|
441
|
+
}
|
|
442
|
+
rl.close();
|
|
443
|
+
await server.disconnect();
|
|
444
|
+
process.exit(0);
|
|
445
|
+
}
|
|
446
|
+
if (trimmed === "/info") {
|
|
447
|
+
try {
|
|
448
|
+
const { metadata: m } = await svc.getMetadata();
|
|
449
|
+
const act = await svc.getActivityState();
|
|
450
|
+
console.log(` Agent: ${m?.flavor || "claude"}, State: ${m?.lifecycleState || "?"}, Active: ${act?.active}, Thinking: ${act?.thinking}`);
|
|
451
|
+
} catch (err) {
|
|
452
|
+
console.error(`Info failed: ${err.message}`);
|
|
453
|
+
}
|
|
454
|
+
process.stdout.write("> ");
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
await svc.sendMessage(
|
|
459
|
+
JSON.stringify({
|
|
460
|
+
role: "user",
|
|
461
|
+
content: { type: "text", text: trimmed }
|
|
462
|
+
})
|
|
463
|
+
);
|
|
464
|
+
} catch (err) {
|
|
465
|
+
console.error(`Send failed: ${err.message}`);
|
|
466
|
+
process.stdout.write("> ");
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
rl.on("close", async () => {
|
|
470
|
+
await server.disconnect();
|
|
471
|
+
process.exit(0);
|
|
472
|
+
});
|
|
473
|
+
process.on("SIGINT", async () => {
|
|
474
|
+
console.log("\nDetaching (session continues running)...");
|
|
475
|
+
rl.close();
|
|
476
|
+
await server.disconnect();
|
|
477
|
+
process.exit(0);
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export { sessionAttach, sessionInfo, sessionList, sessionMessages, sessionSpawn, sessionStop };
|
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-DwK3dfHd.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
var name = "svamp-cli";
|
|
2
|
+
var version = "0.1.22";
|
|
3
|
+
var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
|
|
4
|
+
var author = "Amun AI AB";
|
|
5
|
+
var license = "SEE LICENSE IN LICENSE";
|
|
6
|
+
var type = "module";
|
|
7
|
+
var bin = {
|
|
8
|
+
svamp: "./bin/svamp.mjs"
|
|
9
|
+
};
|
|
10
|
+
var files = [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin"
|
|
13
|
+
];
|
|
14
|
+
var main = "./dist/index.mjs";
|
|
15
|
+
var exports$1 = {
|
|
16
|
+
".": "./dist/index.mjs",
|
|
17
|
+
"./cli": "./dist/cli.mjs"
|
|
18
|
+
};
|
|
19
|
+
var scripts = {
|
|
20
|
+
build: "tsc --noEmit && pkgroll",
|
|
21
|
+
typecheck: "tsc --noEmit",
|
|
22
|
+
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
23
|
+
dev: "tsx src/cli.ts",
|
|
24
|
+
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
25
|
+
"test:e2e": "node --no-warnings test/e2e-session-tests.mjs"
|
|
26
|
+
};
|
|
27
|
+
var dependencies = {
|
|
28
|
+
"@agentclientprotocol/sdk": "^0.14.1",
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
30
|
+
"hypha-rpc": "0.21.15",
|
|
31
|
+
zod: "^3.24.4"
|
|
32
|
+
};
|
|
33
|
+
var devDependencies = {
|
|
34
|
+
"@types/node": ">=20",
|
|
35
|
+
pkgroll: "^2.14.2",
|
|
36
|
+
tsx: "^4.20.6",
|
|
37
|
+
typescript: "5.9.3"
|
|
38
|
+
};
|
|
39
|
+
var packageManager = "yarn@1.22.22";
|
|
40
|
+
var _package = {
|
|
41
|
+
name: name,
|
|
42
|
+
version: version,
|
|
43
|
+
description: description,
|
|
44
|
+
author: author,
|
|
45
|
+
license: license,
|
|
46
|
+
type: type,
|
|
47
|
+
bin: bin,
|
|
48
|
+
files: files,
|
|
49
|
+
main: main,
|
|
50
|
+
exports: exports$1,
|
|
51
|
+
scripts: scripts,
|
|
52
|
+
dependencies: dependencies,
|
|
53
|
+
devDependencies: devDependencies,
|
|
54
|
+
packageManager: packageManager
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export { author, bin, _package as default, dependencies, description, devDependencies, exports$1 as exports, files, license, main, name, packageManager, scripts, type, version };
|