svamp-cli 0.1.28 → 0.1.30

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.
Files changed (80) hide show
  1. package/dist/cli.mjs +114 -42
  2. package/dist/commands-B2xQb9u7.mjs +862 -0
  3. package/dist/commands-C5pW2VmI.mjs +862 -0
  4. package/dist/commands-CtO1WRt4.mjs +862 -0
  5. package/dist/commands-TZkNivgV.mjs +862 -0
  6. package/dist/index.mjs +8 -2
  7. package/dist/{package-CgBD49cA.mjs → package-CKOQ5lA7.mjs} +2 -2
  8. package/dist/{run-DjfPjgOb.mjs → run-BCSNMhiz.mjs} +1200 -132
  9. package/dist/{run-Cmostc0S.mjs → run-CtCTd6if.mjs} +1225 -169
  10. package/dist/{run-DYhBROuo.mjs → run-Cxdc5Zmw.mjs} +1253 -211
  11. package/dist/{run-CL-FS4Yc.mjs → run-dBWhjQRf.mjs} +1255 -211
  12. package/package.json +3 -6
  13. package/bin/svamp-agent.mjs +0 -34
  14. package/dist/agent-cli.mjs +0 -453
  15. package/dist/commands-1CYZC6Xh.mjs +0 -481
  16. package/dist/commands-B1DcpgLW.mjs +0 -481
  17. package/dist/commands-BGmdgMAC.mjs +0 -485
  18. package/dist/commands-BOeSil-P.mjs +0 -459
  19. package/dist/commands-BU4GZQuH.mjs +0 -481
  20. package/dist/commands-Ba66PxtQ.mjs +0 -481
  21. package/dist/commands-C0-xqIIc.mjs +0 -481
  22. package/dist/commands-C7Qy5n6d.mjs +0 -481
  23. package/dist/commands-CKpC8R9T.mjs +0 -481
  24. package/dist/commands-CNqOjR1y.mjs +0 -481
  25. package/dist/commands-CVKh1tWr.mjs +0 -485
  26. package/dist/commands-CYBblX73.mjs +0 -485
  27. package/dist/commands-CZBYmj16.mjs +0 -485
  28. package/dist/commands-CcWIvCA4.mjs +0 -481
  29. package/dist/commands-Cfwf-cQG.mjs +0 -481
  30. package/dist/commands-DCNO2m66.mjs +0 -471
  31. package/dist/commands-DRIFvhmC.mjs +0 -481
  32. package/dist/commands-DXmw2dzy.mjs +0 -481
  33. package/dist/commands-DkSvlKFF.mjs +0 -485
  34. package/dist/commands-DnDd4Sew.mjs +0 -481
  35. package/dist/commands-DnpnAFQW.mjs +0 -485
  36. package/dist/commands-Do-TVYFm.mjs +0 -481
  37. package/dist/commands-Kzm0_XNH.mjs +0 -481
  38. package/dist/commands-MQvNbIid.mjs +0 -481
  39. package/dist/commands-_uCC3U1U.mjs +0 -481
  40. package/dist/commands-y2WG29W9.mjs +0 -485
  41. package/dist/hyphaClient-DLkclazm.mjs +0 -39
  42. package/dist/package-B2FOzHaM.mjs +0 -57
  43. package/dist/package-Bk_PFVA0.mjs +0 -57
  44. package/dist/package-Bnij-ZtR.mjs +0 -57
  45. package/dist/package-BtRbHfjz.mjs +0 -57
  46. package/dist/package-C5B0twb8.mjs +0 -57
  47. package/dist/package-CC5d8_0L.mjs +0 -57
  48. package/dist/package-CCJ045H0.mjs +0 -60
  49. package/dist/package-CS219SXn.mjs +0 -57
  50. package/dist/package-Cd-9ktpd.mjs +0 -60
  51. package/dist/package-CvnNnsm7.mjs +0 -60
  52. package/dist/package-DpqWz9Cr.mjs +0 -60
  53. package/dist/package-JqEt5Ib4.mjs +0 -57
  54. package/dist/package-nzkXV1aM.mjs +0 -57
  55. package/dist/package-pNo6GC3a.mjs +0 -60
  56. package/dist/package-pZp14zKI.mjs +0 -57
  57. package/dist/run-4fyJcaRE.mjs +0 -3856
  58. package/dist/run-BI32lPRK.mjs +0 -3870
  59. package/dist/run-BQHneHfW.mjs +0 -3834
  60. package/dist/run-Bb4fyIWZ.mjs +0 -3812
  61. package/dist/run-BglwnB-A.mjs +0 -3889
  62. package/dist/run-BjVWuitO.mjs +0 -3919
  63. package/dist/run-BzUE-JUT.mjs +0 -3708
  64. package/dist/run-BzqS97Sx.mjs +0 -3666
  65. package/dist/run-C6snRxyh.mjs +0 -3826
  66. package/dist/run-C8CI8Ujj.mjs +0 -3693
  67. package/dist/run-CS1Z4GcM.mjs +0 -3786
  68. package/dist/run-CW26vPqj.mjs +0 -3919
  69. package/dist/run-CkTufc0D.mjs +0 -3875
  70. package/dist/run-Cp3kKdzm.mjs +0 -3865
  71. package/dist/run-D0bCTY72.mjs +0 -3816
  72. package/dist/run-DMW8ibIw.mjs +0 -3958
  73. package/dist/run-DO52unxE.mjs +0 -3950
  74. package/dist/run-Dptna3Je.mjs +0 -3867
  75. package/dist/run-DwK3dfHd.mjs +0 -3875
  76. package/dist/run-M_SMt96j.mjs +0 -3913
  77. package/dist/run-MlpxQUPN.mjs +0 -3869
  78. package/dist/run-PuTIelbv.mjs +0 -3706
  79. package/dist/run-h37iSCUB.mjs +0 -3934
  80. package/dist/run-lpV0oguG.mjs +0 -3897
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "type": "module",
8
8
  "bin": {
9
- "svamp": "./bin/svamp.mjs",
10
- "svamp-agent": "./bin/svamp-agent.mjs"
9
+ "svamp": "./bin/svamp.mjs"
11
10
  },
12
11
  "files": [
13
12
  "dist",
@@ -16,8 +15,7 @@
16
15
  "main": "./dist/index.mjs",
17
16
  "exports": {
18
17
  ".": "./dist/index.mjs",
19
- "./cli": "./dist/cli.mjs",
20
- "./agent-cli": "./dist/agent-cli.mjs"
18
+ "./cli": "./dist/cli.mjs"
21
19
  },
22
20
  "scripts": {
23
21
  "build": "tsc --noEmit && pkgroll",
@@ -30,7 +28,6 @@
30
28
  "dependencies": {
31
29
  "@agentclientprotocol/sdk": "^0.14.1",
32
30
  "@modelcontextprotocol/sdk": "^1.25.3",
33
- "commander": "^13.1.0",
34
31
  "hypha-rpc": "0.21.20",
35
32
  "zod": "^3.24.4"
36
33
  },
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // Svamp Agent CLI entry point
4
- // Loads .env from ~/.svamp/.env if present, then runs the agent CLI
5
-
6
- import { existsSync, readFileSync } from 'fs';
7
- import { join } from 'path';
8
- import { homedir } from 'os';
9
-
10
- // Simple .env loader — load from SVAMP_HOME or ~/.svamp/
11
- const envDir = process.env.SVAMP_HOME || join(homedir(), '.svamp');
12
- const envFile = join(envDir, '.env');
13
-
14
- if (existsSync(envFile)) {
15
- const lines = readFileSync(envFile, 'utf-8').split('\n');
16
- for (const line of lines) {
17
- const trimmed = line.trim();
18
- if (!trimmed || trimmed.startsWith('#')) continue;
19
- const eqIdx = trimmed.indexOf('=');
20
- if (eqIdx === -1) continue;
21
- const key = trimmed.slice(0, eqIdx).trim();
22
- const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, '');
23
- if (!process.env[key]) {
24
- process.env[key] = value;
25
- }
26
- }
27
- }
28
-
29
- // Import and run the agent CLI
30
- import('../dist/agent-cli.mjs').catch((err) => {
31
- console.error('Failed to load compiled agent CLI. Try running: yarn workspace svamp-cli build');
32
- console.error(err.message);
33
- process.exit(1);
34
- });
@@ -1,453 +0,0 @@
1
- import { Command } from 'commander';
2
- import { connectAndGetMachine, resolveSessionId } from './commands-BOeSil-P.mjs';
3
- import 'node:fs';
4
- import 'node:path';
5
- import 'node:os';
6
- import './hyphaClient-DLkclazm.mjs';
7
-
8
- function formatIsoTime(ts) {
9
- if (!ts) return "-";
10
- const date = new Date(ts);
11
- if (Number.isNaN(date.getTime())) return "-";
12
- return date.toISOString();
13
- }
14
- function toMarkdownInline(value) {
15
- const escaped = value.replace(/`/g, "\\`");
16
- return `\`${escaped}\``;
17
- }
18
- function normalizeCodeBlockText(value) {
19
- const text = value.trim().length > 0 ? value : "(empty)";
20
- return text.replace(/```/g, "``\\`");
21
- }
22
- function formatSessionTable(sessions) {
23
- if (sessions.length === 0) {
24
- return "## Sessions\n\n- Total: 0\n- Items: none";
25
- }
26
- const sections = sessions.map((s, index) => {
27
- const name = s.name || "-";
28
- const path = s.path || s.directory || "-";
29
- const status = s.active ? "active" : "inactive";
30
- return [
31
- `### Session ${index + 1}`,
32
- `- ID: ${toMarkdownInline(s.sessionId)}`,
33
- `- Agent: ${s.flavor || "claude"}`,
34
- `- Name: ${name}`,
35
- `- Path: ${path}`,
36
- `- Status: ${status}`
37
- ].join("\n");
38
- });
39
- return `## Sessions
40
-
41
- - Total: ${sessions.length}
42
-
43
- ${sections.join("\n\n")}`;
44
- }
45
- function formatSessionStatus(data) {
46
- const lines = [
47
- "## Session Status",
48
- "",
49
- `- Session ID: ${toMarkdownInline(data.sessionId)}`,
50
- `- Agent: ${data.flavor}`
51
- ];
52
- if (data.name) lines.push(`- Name: ${data.name}`);
53
- if (data.summary) lines.push(`- Summary: ${data.summary}`);
54
- if (data.path) lines.push(`- Path: ${data.path}`);
55
- if (data.host) lines.push(`- Host: ${data.host}`);
56
- if (data.lifecycleState) lines.push(`- Lifecycle: ${data.lifecycleState}`);
57
- lines.push(`- Active: ${data.active ? "yes" : "no"}`);
58
- lines.push(`- Thinking: ${data.thinking ? "yes" : "no"}`);
59
- lines.push(`- Agent Status: ${data.active ? "busy" : "idle"}`);
60
- if (data.startedBy) lines.push(`- Started By: ${data.startedBy}`);
61
- if (data.claudeSessionId) lines.push(`- Claude Session: ${data.claudeSessionId}`);
62
- if (data.sessionLink) lines.push(`- Link: ${data.sessionLink}`);
63
- return lines.join("\n");
64
- }
65
- function formatMessageHistory(messages) {
66
- if (messages.length === 0) {
67
- return "## Message History\n\n- Count: 0\n- Items: none";
68
- }
69
- const sections = messages.map((msg, index) => {
70
- return [
71
- `### Message ${index + 1}`,
72
- `- ID: ${toMarkdownInline(msg.id)}`,
73
- `- Time: ${formatIsoTime(msg.createdAt)}`,
74
- `- Role: ${msg.role}`,
75
- "- Text:",
76
- "```text",
77
- normalizeCodeBlockText(msg.text),
78
- "```"
79
- ].join("\n");
80
- });
81
- return `## Message History
82
-
83
- - Count: ${messages.length}
84
-
85
- ${sections.join("\n\n")}`;
86
- }
87
- function formatJson(data) {
88
- return JSON.stringify(data, null, 2);
89
- }
90
-
91
- async function resolveSessionByPath(machine, server, path) {
92
- const sessions = await machine.listSessions();
93
- const active = sessions.filter((s) => s.active);
94
- let matches = active.filter((s) => s.directory === path);
95
- if (matches.length === 0) {
96
- for (const s of active) {
97
- try {
98
- const svc = await server.getService(`svamp-session-${s.sessionId}`);
99
- const { metadata } = await svc.getMetadata();
100
- if (metadata?.path === path) {
101
- matches.push(s);
102
- }
103
- } catch {
104
- }
105
- }
106
- }
107
- if (matches.length === 0) {
108
- throw new Error(`No active session found for path "${path}"`);
109
- }
110
- return matches[0];
111
- }
112
- function extractMessageText(msg) {
113
- const content = msg.content;
114
- if (!content) return null;
115
- const role = content.role || "unknown";
116
- let text = "";
117
- if (role === "user") {
118
- const data = content.content;
119
- if (typeof data === "string") {
120
- try {
121
- const parsed = JSON.parse(data);
122
- text = parsed?.text || parsed?.content?.text || data;
123
- } catch {
124
- text = data;
125
- }
126
- } else if (data?.text) {
127
- text = data.text;
128
- } else if (data?.type === "text") {
129
- text = data.text || "";
130
- } else {
131
- text = typeof data === "object" ? JSON.stringify(data) : String(data || "");
132
- }
133
- } else if (role === "agent" || role === "assistant") {
134
- const data = content.content?.data || content.content;
135
- if (!data) return null;
136
- if (data.type === "assistant" && Array.isArray(data.content)) {
137
- const parts = [];
138
- for (const block of data.content) {
139
- if (block.type === "text" && block.text) {
140
- parts.push(block.text);
141
- } else if (block.type === "tool_use") {
142
- parts.push(`[tool: ${block.name}]`);
143
- }
144
- }
145
- text = parts.join("\n");
146
- } else if (data.type === "result") {
147
- text = data.result || "";
148
- } else if (data.type === "output") {
149
- const inner = data.data;
150
- if (inner?.type === "assistant" && Array.isArray(inner.content)) {
151
- const parts = [];
152
- for (const block of inner.content) {
153
- if (block.type === "text" && block.text) {
154
- parts.push(block.text);
155
- } else if (block.type === "tool_use") {
156
- parts.push(`[tool: ${block.name}]`);
157
- }
158
- }
159
- text = parts.join("\n");
160
- } else if (inner?.type === "result") {
161
- text = inner.result || "";
162
- }
163
- }
164
- } else if (role === "session") {
165
- text = "[session event]";
166
- }
167
- return {
168
- id: msg.id || "",
169
- seq: msg.seq || 0,
170
- role,
171
- text,
172
- createdAt: msg.createdAt || 0
173
- };
174
- }
175
- async function waitForIdle(server, sessionId, timeoutMs) {
176
- const svc = await server.getService(`svamp-session-${sessionId}`);
177
- const pollInterval = 2e3;
178
- const deadline = Date.now() + timeoutMs;
179
- while (Date.now() < deadline) {
180
- const activity = await svc.getActivityState();
181
- if (activity && !activity.active) {
182
- return;
183
- }
184
- await new Promise((r) => setTimeout(r, pollInterval));
185
- }
186
- throw new Error("Timeout waiting for agent to become idle");
187
- }
188
- async function waitForBusyThenIdle(server, sessionId, timeoutMs = 3e5, busyTimeoutMs = 1e4) {
189
- const svc = await server.getService(`svamp-session-${sessionId}`);
190
- const pollInterval = 2e3;
191
- const deadline = Date.now() + timeoutMs;
192
- const busyDeadline = Date.now() + busyTimeoutMs;
193
- let sawBusy = false;
194
- while (Date.now() < deadline) {
195
- const activity = await svc.getActivityState();
196
- const isActive = activity?.active === true;
197
- if (isActive) {
198
- sawBusy = true;
199
- }
200
- if (sawBusy && !isActive) {
201
- return;
202
- }
203
- if (!sawBusy && Date.now() > busyDeadline) {
204
- return;
205
- }
206
- await new Promise((r) => setTimeout(r, pollInterval));
207
- }
208
- throw new Error("Timeout waiting for agent to become idle");
209
- }
210
- const program = new Command();
211
- program.name("svamp-agent").description("CLI client for controlling Svamp sessions remotely").version("0.1.0");
212
- program.command("list").description("List all sessions").option("--active", "Show only active sessions").option("--json", "Output as JSON").action(async (opts) => {
213
- const { server, machine } = await connectAndGetMachine();
214
- try {
215
- const sessions = await machine.listSessions();
216
- const filtered = opts.active ? sessions.filter((s) => s.active) : sessions;
217
- const enriched = [];
218
- for (const s of filtered) {
219
- let flavor = "claude";
220
- let name = "";
221
- let path = s.directory || "";
222
- let host = "";
223
- if (s.metadata) {
224
- flavor = s.metadata.flavor || "claude";
225
- name = s.metadata.name || "";
226
- }
227
- if (s.active) {
228
- try {
229
- const svc = await server.getService(`svamp-session-${s.sessionId}`);
230
- const { metadata } = await svc.getMetadata();
231
- flavor = metadata?.flavor || flavor;
232
- name = metadata?.name || name;
233
- path = metadata?.path || path;
234
- host = metadata?.host || "";
235
- } catch {
236
- }
237
- }
238
- enriched.push({ ...s, flavor, name, path, host });
239
- }
240
- if (opts.json) {
241
- console.log(formatJson(enriched.map((s) => ({
242
- sessionId: s.sessionId,
243
- agent: s.flavor,
244
- name: s.name,
245
- path: s.path,
246
- host: s.host,
247
- active: s.active,
248
- directory: s.directory
249
- }))));
250
- } else {
251
- console.log(formatSessionTable(enriched));
252
- }
253
- } finally {
254
- await server.disconnect();
255
- }
256
- });
257
- program.command("status").description("Get live session state").argument("<session-id>", "Session ID or prefix").option("--json", "Output as JSON").action(async (sessionId, opts) => {
258
- const { server, machine } = await connectAndGetMachine();
259
- try {
260
- const sessions = await machine.listSessions();
261
- const match = resolveSessionId(sessions, sessionId);
262
- const fullId = match.sessionId;
263
- let metadata = {};
264
- let activity = {};
265
- try {
266
- const svc = await server.getService(`svamp-session-${fullId}`);
267
- const metaResult = await svc.getMetadata();
268
- metadata = metaResult.metadata || {};
269
- activity = await svc.getActivityState();
270
- } catch {
271
- }
272
- const statusData = {
273
- sessionId: fullId,
274
- flavor: metadata.flavor || "claude",
275
- name: metadata.name || "",
276
- path: metadata.path || match.directory || "",
277
- host: metadata.host || "",
278
- lifecycleState: metadata.lifecycleState || "unknown",
279
- active: activity.active ?? false,
280
- thinking: activity.thinking ?? false,
281
- startedBy: metadata.startedBy || match.startedBy || "",
282
- summary: metadata.summary?.text || void 0,
283
- claudeSessionId: metadata.claudeSessionId || void 0,
284
- sessionLink: metadata.sessionLink?.url || void 0
285
- };
286
- if (opts.json) {
287
- console.log(formatJson(statusData));
288
- } else {
289
- console.log(formatSessionStatus(statusData));
290
- }
291
- } finally {
292
- await server.disconnect();
293
- }
294
- });
295
- program.command("create").description("Create a new session").option("--agent <agent>", "Agent type (claude, gemini, opencode)", "claude").option("--directory <path>", "Working directory path").option("--json", "Output as JSON").action(async (opts) => {
296
- const { server, machine } = await connectAndGetMachine();
297
- try {
298
- const directory = opts.directory ?? process.cwd();
299
- const result = await machine.spawnSession({
300
- directory,
301
- agent: opts.agent
302
- });
303
- if (result.type === "success") {
304
- if (opts.json) {
305
- console.log(formatJson({
306
- sessionId: result.sessionId,
307
- agent: opts.agent,
308
- directory
309
- }));
310
- } else {
311
- console.log([
312
- "## Session Created",
313
- "",
314
- `- Session ID: \`${result.sessionId}\``,
315
- `- Agent: ${opts.agent}`,
316
- `- Directory: ${directory}`,
317
- ...result.message ? [`- Note: ${result.message}`] : []
318
- ].join("\n"));
319
- }
320
- } else if (result.type === "requestToApproveDirectoryCreation") {
321
- throw new Error(`Directory ${result.directory} does not exist. Create it first or use an existing directory.`);
322
- } else {
323
- throw new Error(result.errorMessage || "Unknown error creating session");
324
- }
325
- } finally {
326
- await server.disconnect();
327
- }
328
- });
329
- program.command("send").description("Send a message to a session").argument("<session-id>", "Session ID or prefix (ignored when --by-path is used)").argument("<message>", "Message text").option("--wait", "Wait for agent to become idle").option("--by-path <path>", "Resolve session by working directory path (active sessions only)").option("--json", "Output as JSON").action(async (sessionId, message, opts) => {
330
- const { server, machine } = await connectAndGetMachine();
331
- try {
332
- let match;
333
- if (opts.byPath) {
334
- match = await resolveSessionByPath(machine, server, opts.byPath);
335
- } else {
336
- const sessions = await machine.listSessions();
337
- match = resolveSessionId(sessions, sessionId);
338
- }
339
- const fullId = match.sessionId;
340
- const svc = await server.getService(`svamp-session-${fullId}`);
341
- const result = await svc.sendMessage(
342
- JSON.stringify({
343
- role: "user",
344
- content: { type: "text", text: message },
345
- meta: { sentFrom: "svamp-agent" }
346
- })
347
- );
348
- if (opts.wait) {
349
- await waitForBusyThenIdle(server, fullId);
350
- }
351
- if (opts.json) {
352
- console.log(formatJson({
353
- sessionId: fullId,
354
- message,
355
- sent: true,
356
- seq: result.seq,
357
- waited: !!opts.wait
358
- }));
359
- } else {
360
- console.log([
361
- "## Message Sent",
362
- "",
363
- `- Session ID: \`${fullId}\``,
364
- `- Sequence: ${result.seq}`,
365
- ...opts.wait ? [`- Waited For Idle: yes`] : []
366
- ].join("\n"));
367
- }
368
- } finally {
369
- await server.disconnect();
370
- }
371
- });
372
- program.command("history").description("Read message history").argument("<session-id>", "Session ID or prefix").option("--limit <n>", "Limit number of messages", (v) => {
373
- const n = parseInt(v, 10);
374
- if (isNaN(n) || n <= 0) throw new Error("--limit must be a positive integer");
375
- return n;
376
- }).option("--after <seq>", "Only show messages after this sequence number", (v) => {
377
- const n = parseInt(v, 10);
378
- if (isNaN(n) || n < 0) throw new Error("--after must be a non-negative integer");
379
- return n;
380
- }).option("--json", "Output as JSON").action(async (sessionId, opts) => {
381
- const { server, machine } = await connectAndGetMachine();
382
- try {
383
- const sessions = await machine.listSessions();
384
- const match = resolveSessionId(sessions, sessionId);
385
- const fullId = match.sessionId;
386
- const svc = await server.getService(`svamp-session-${fullId}`);
387
- const afterSeq = opts.after ?? 0;
388
- const apiLimit = opts.limit ?? 500;
389
- const { messages } = await svc.getMessages(afterSeq, apiLimit);
390
- let formatted = [];
391
- for (const msg of messages) {
392
- const extracted = extractMessageText(msg);
393
- if (extracted) {
394
- formatted.push(extracted);
395
- }
396
- }
397
- formatted.sort((a, b) => a.createdAt - b.createdAt);
398
- if (opts.json) {
399
- console.log(formatJson(formatted));
400
- } else {
401
- console.log(formatMessageHistory(formatted));
402
- }
403
- } finally {
404
- await server.disconnect();
405
- }
406
- });
407
- program.command("stop").description("Stop a session").argument("<session-id>", "Session ID or prefix").action(async (sessionId) => {
408
- const { server, machine } = await connectAndGetMachine();
409
- try {
410
- const sessions = await machine.listSessions();
411
- const match = resolveSessionId(sessions, sessionId);
412
- const success = await machine.stopSession(match.sessionId);
413
- if (success) {
414
- console.log([
415
- "## Session Stopped",
416
- "",
417
- `- Session ID: \`${match.sessionId}\``
418
- ].join("\n"));
419
- } else {
420
- throw new Error("Failed to stop session (not found on daemon)");
421
- }
422
- } finally {
423
- await server.disconnect();
424
- }
425
- });
426
- program.command("wait").description("Wait for agent to become idle").argument("<session-id>", "Session ID or prefix").option("--timeout <seconds>", "Timeout in seconds", (v) => {
427
- const n = parseInt(v, 10);
428
- if (isNaN(n) || n <= 0) throw new Error("--timeout must be a positive integer");
429
- return n;
430
- }, 300).action(async (sessionId, opts) => {
431
- const { server, machine } = await connectAndGetMachine();
432
- try {
433
- const sessions = await machine.listSessions();
434
- const match = resolveSessionId(sessions, sessionId);
435
- const fullId = match.sessionId;
436
- await waitForIdle(server, fullId, opts.timeout * 1e3);
437
- console.log([
438
- "## Session Idle",
439
- "",
440
- `- Session ID: \`${fullId}\``
441
- ].join("\n"));
442
- } catch (err) {
443
- const msg = err instanceof Error ? err.message : String(err);
444
- console.error(msg);
445
- process.exitCode = 1;
446
- } finally {
447
- await server.disconnect();
448
- }
449
- });
450
- program.parseAsync(process.argv).catch((err) => {
451
- console.error(err instanceof Error ? err.message : String(err));
452
- process.exitCode = 1;
453
- });