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