svamp-cli 0.1.29 → 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 (95) hide show
  1. package/dist/cli.mjs +7 -7
  2. package/dist/{commands-CgT3AgJ0.mjs → commands-B2xQb9u7.mjs} +1 -1
  3. package/dist/{commands-CKTIJoV0.mjs → commands-C5pW2VmI.mjs} +286 -60
  4. package/dist/{commands-DDB3y1L1.mjs → commands-TZkNivgV.mjs} +295 -44
  5. package/dist/index.mjs +1 -1
  6. package/dist/{run-DZmxHj-e.mjs → run-CtCTd6if.mjs} +234 -50
  7. package/dist/{run-CuN6K7pN.mjs → run-Cxdc5Zmw.mjs} +316 -164
  8. package/dist/{run-DMD0N00A.mjs → run-dBWhjQRf.mjs} +316 -76
  9. package/package.json +1 -1
  10. package/dist/agent-cli.mjs +0 -453
  11. package/dist/commands-1CYZC6Xh.mjs +0 -481
  12. package/dist/commands-B1DcpgLW.mjs +0 -481
  13. package/dist/commands-BGmdgMAC.mjs +0 -485
  14. package/dist/commands-BOeSil-P.mjs +0 -459
  15. package/dist/commands-BU4GZQuH.mjs +0 -481
  16. package/dist/commands-Ba66PxtQ.mjs +0 -481
  17. package/dist/commands-C0-xqIIc.mjs +0 -481
  18. package/dist/commands-C7Qy5n6d.mjs +0 -481
  19. package/dist/commands-CKpC8R9T.mjs +0 -481
  20. package/dist/commands-CNqOjR1y.mjs +0 -481
  21. package/dist/commands-CVKh1tWr.mjs +0 -485
  22. package/dist/commands-CYBblX73.mjs +0 -485
  23. package/dist/commands-CZBYmj16.mjs +0 -485
  24. package/dist/commands-CcWIvCA4.mjs +0 -481
  25. package/dist/commands-Cfwf-cQG.mjs +0 -481
  26. package/dist/commands-DCNO2m66.mjs +0 -471
  27. package/dist/commands-DRIFvhmC.mjs +0 -481
  28. package/dist/commands-DXmw2dzy.mjs +0 -481
  29. package/dist/commands-DkSvlKFF.mjs +0 -485
  30. package/dist/commands-DnDd4Sew.mjs +0 -481
  31. package/dist/commands-DnpnAFQW.mjs +0 -485
  32. package/dist/commands-Do-TVYFm.mjs +0 -481
  33. package/dist/commands-GEXri0yz.mjs +0 -484
  34. package/dist/commands-Kzm0_XNH.mjs +0 -481
  35. package/dist/commands-MQvNbIid.mjs +0 -481
  36. package/dist/commands-_uCC3U1U.mjs +0 -481
  37. package/dist/commands-y2WG29W9.mjs +0 -485
  38. package/dist/hyphaClient-DLkclazm.mjs +0 -39
  39. package/dist/package-ASJ9pMHk.mjs +0 -60
  40. package/dist/package-B2FOzHaM.mjs +0 -57
  41. package/dist/package-Bk_PFVA0.mjs +0 -57
  42. package/dist/package-Bnij-ZtR.mjs +0 -57
  43. package/dist/package-BtRbHfjz.mjs +0 -57
  44. package/dist/package-C5B0twb8.mjs +0 -57
  45. package/dist/package-CC5d8_0L.mjs +0 -57
  46. package/dist/package-CCJ045H0.mjs +0 -60
  47. package/dist/package-CS219SXn.mjs +0 -57
  48. package/dist/package-Cd-9ktpd.mjs +0 -60
  49. package/dist/package-CgBD49cA.mjs +0 -57
  50. package/dist/package-CvnNnsm7.mjs +0 -60
  51. package/dist/package-DPXkSwHu.mjs +0 -57
  52. package/dist/package-DpqWz9Cr.mjs +0 -60
  53. package/dist/package-JqEt5Ib4.mjs +0 -57
  54. package/dist/package-k18Su1iE.mjs +0 -58
  55. package/dist/package-nzkXV1aM.mjs +0 -57
  56. package/dist/package-pNo6GC3a.mjs +0 -60
  57. package/dist/package-pZp14zKI.mjs +0 -57
  58. package/dist/run-4fyJcaRE.mjs +0 -3856
  59. package/dist/run-B6oqR83K.mjs +0 -4631
  60. package/dist/run-BI32lPRK.mjs +0 -3870
  61. package/dist/run-BQHneHfW.mjs +0 -3834
  62. package/dist/run-Bb4fyIWZ.mjs +0 -3812
  63. package/dist/run-BglwnB-A.mjs +0 -3889
  64. package/dist/run-BjVWuitO.mjs +0 -3919
  65. package/dist/run-BzUE-JUT.mjs +0 -3708
  66. package/dist/run-BzqS97Sx.mjs +0 -3666
  67. package/dist/run-C6snRxyh.mjs +0 -3826
  68. package/dist/run-C8CI8Ujj.mjs +0 -3693
  69. package/dist/run-CL-FS4Yc.mjs +0 -3933
  70. package/dist/run-CS1Z4GcM.mjs +0 -3786
  71. package/dist/run-CT7uizQo.mjs +0 -4492
  72. package/dist/run-CUIj4xbE.mjs +0 -4880
  73. package/dist/run-CW26vPqj.mjs +0 -3919
  74. package/dist/run-CkTufc0D.mjs +0 -3875
  75. package/dist/run-Cmostc0S.mjs +0 -3902
  76. package/dist/run-Cp3kKdzm.mjs +0 -3865
  77. package/dist/run-D0bCTY72.mjs +0 -3816
  78. package/dist/run-D4N6FQON.mjs +0 -4673
  79. package/dist/run-D4dlA0jo.mjs +0 -4813
  80. package/dist/run-DMW8ibIw.mjs +0 -3958
  81. package/dist/run-DO52unxE.mjs +0 -3950
  82. package/dist/run-DQ5FOQ_c.mjs +0 -4788
  83. package/dist/run-DT7FgL8L.mjs +0 -4339
  84. package/dist/run-DYhBROuo.mjs +0 -3934
  85. package/dist/run-DjfPjgOb.mjs +0 -3904
  86. package/dist/run-DlL4JALM.mjs +0 -4719
  87. package/dist/run-Dp2JPkGI.mjs +0 -3913
  88. package/dist/run-Dptna3Je.mjs +0 -3867
  89. package/dist/run-DwK3dfHd.mjs +0 -3875
  90. package/dist/run-M_SMt96j.mjs +0 -3913
  91. package/dist/run-MlpxQUPN.mjs +0 -3869
  92. package/dist/run-PuTIelbv.mjs +0 -3706
  93. package/dist/run-h37iSCUB.mjs +0 -3934
  94. package/dist/run-lpV0oguG.mjs +0 -3897
  95. package/dist/run-oHmTMcv8.mjs +0 -4721
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-BCSNMhiz.mjs';
1
+ import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-dBWhjQRf.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -104,7 +104,7 @@ async function handleAgentCommand() {
104
104
  return;
105
105
  }
106
106
  if (agentArgs[0] === "list") {
107
- const { KNOWN_ACP_AGENTS } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.f; });
107
+ const { KNOWN_ACP_AGENTS } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.f; });
108
108
  console.log("Known ACP agents:");
109
109
  for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
110
110
  console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")}`);
@@ -113,10 +113,10 @@ async function handleAgentCommand() {
113
113
  console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
114
114
  return;
115
115
  }
116
- const { resolveAcpAgentConfig } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.f; });
117
- const { AcpBackend } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.e; });
118
- const { GeminiTransport } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.G; });
119
- const { DefaultTransport } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.D; });
116
+ const { resolveAcpAgentConfig } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.f; });
117
+ const { AcpBackend } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.e; });
118
+ const { GeminiTransport } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.G; });
119
+ const { DefaultTransport } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.D; });
120
120
  let cwd = process.cwd();
121
121
  const filteredArgs = [];
122
122
  for (let i = 0; i < agentArgs.length; i++) {
@@ -255,7 +255,7 @@ async function handleSessionCommand() {
255
255
  printSessionHelp();
256
256
  return;
257
257
  }
258
- const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait } = await import('./commands-CtO1WRt4.mjs');
258
+ const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait } = await import('./commands-C5pW2VmI.mjs');
259
259
  const parseFlagStr = (flag, shortFlag) => {
260
260
  for (let i = 1; i < sessionArgs.length; i++) {
261
261
  if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
@@ -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-DZmxHj-e.mjs';
4
+ import { c as connectToHypha } from './run-CtCTd6if.mjs';
5
5
  import 'os';
6
6
  import 'fs/promises';
7
7
  import 'fs';
@@ -1,7 +1,51 @@
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 './hyphaClient-DLkclazm.mjs';
4
+ import { c as connectToHypha } from './run-dBWhjQRf.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
+ import '@modelcontextprotocol/sdk/client/index.js';
16
+ import '@modelcontextprotocol/sdk/client/stdio.js';
17
+ import '@modelcontextprotocol/sdk/types.js';
18
+ import 'zod';
19
+ import 'node:fs/promises';
20
+ import 'node:util';
21
+
22
+ function toMarkdownInline(value) {
23
+ const escaped = value.replace(/`/g, "\\`");
24
+ return `\`${escaped}\``;
25
+ }
26
+ function formatSessionStatus(data) {
27
+ const lines = [
28
+ "## Session Status",
29
+ "",
30
+ `- Session ID: ${toMarkdownInline(data.sessionId)}`,
31
+ `- Agent: ${data.flavor}`
32
+ ];
33
+ if (data.name) lines.push(`- Name: ${data.name}`);
34
+ if (data.summary) lines.push(`- Summary: ${data.summary}`);
35
+ if (data.path) lines.push(`- Path: ${data.path}`);
36
+ if (data.host) lines.push(`- Host: ${data.host}`);
37
+ if (data.lifecycleState) lines.push(`- Lifecycle: ${data.lifecycleState}`);
38
+ lines.push(`- Active: ${data.active ? "yes" : "no"}`);
39
+ lines.push(`- Thinking: ${data.thinking ? "yes" : "no"}`);
40
+ lines.push(`- Agent Status: ${data.active ? "busy" : "idle"}`);
41
+ if (data.startedBy) lines.push(`- Started By: ${data.startedBy}`);
42
+ if (data.claudeSessionId) lines.push(`- Claude Session: ${data.claudeSessionId}`);
43
+ if (data.sessionLink) lines.push(`- Link: ${data.sessionLink}`);
44
+ return lines.join("\n");
45
+ }
46
+ function formatJson(data) {
47
+ return JSON.stringify(data, null, 2);
48
+ }
5
49
 
6
50
  const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
7
51
  const DAEMON_STATE_FILE = join(SVAMP_HOME, "daemon.state.json");
@@ -354,49 +398,168 @@ function renderMessage(msg) {
354
398
  }
355
399
  }
356
400
  }
357
- async function sessionList(machineId) {
401
+ function extractMessageText(msg) {
402
+ const content = msg.content;
403
+ if (!content) return null;
404
+ const role = content.role || "unknown";
405
+ let text = "";
406
+ if (role === "user") {
407
+ const data = content.content;
408
+ if (typeof data === "string") {
409
+ try {
410
+ const parsed = JSON.parse(data);
411
+ text = parsed?.text || parsed?.content?.text || data;
412
+ } catch {
413
+ text = data;
414
+ }
415
+ } else if (data?.text) {
416
+ text = data.text;
417
+ } else if (data?.type === "text") {
418
+ text = data.text || "";
419
+ } else {
420
+ text = typeof data === "object" ? JSON.stringify(data) : String(data || "");
421
+ }
422
+ } else if (role === "agent" || role === "assistant") {
423
+ const data = content.content?.data || content.content;
424
+ if (!data) return null;
425
+ if (data.type === "assistant" && Array.isArray(data.content)) {
426
+ const parts = [];
427
+ for (const block of data.content) {
428
+ if (block.type === "text" && block.text) {
429
+ parts.push(block.text);
430
+ } else if (block.type === "tool_use") {
431
+ parts.push(`[tool: ${block.name}]`);
432
+ }
433
+ }
434
+ text = parts.join("\n");
435
+ } else if (data.type === "result") {
436
+ text = data.result || "";
437
+ } else if (data.type === "output") {
438
+ const inner = data.data;
439
+ if (inner?.type === "assistant" && Array.isArray(inner.content)) {
440
+ const parts = [];
441
+ for (const block of inner.content) {
442
+ if (block.type === "text" && block.text) {
443
+ parts.push(block.text);
444
+ } else if (block.type === "tool_use") {
445
+ parts.push(`[tool: ${block.name}]`);
446
+ }
447
+ }
448
+ text = parts.join("\n");
449
+ } else if (inner?.type === "result") {
450
+ text = inner.result || "";
451
+ }
452
+ }
453
+ } else if (role === "session") {
454
+ text = "[session event]";
455
+ }
456
+ return {
457
+ id: msg.id || "",
458
+ seq: msg.seq || 0,
459
+ role,
460
+ text,
461
+ createdAt: msg.createdAt || 0
462
+ };
463
+ }
464
+ async function waitForIdle(server, sessionId, timeoutMs) {
465
+ const svc = await server.getService(`svamp-session-${sessionId}`);
466
+ const pollInterval = 2e3;
467
+ const deadline = Date.now() + timeoutMs;
468
+ while (Date.now() < deadline) {
469
+ const activity = await svc.getActivityState();
470
+ if (activity && !activity.active) {
471
+ return;
472
+ }
473
+ await new Promise((r) => setTimeout(r, pollInterval));
474
+ }
475
+ throw new Error("Timeout waiting for agent to become idle");
476
+ }
477
+ async function waitForBusyThenIdle(server, sessionId, timeoutMs = 3e5, busyTimeoutMs = 1e4) {
478
+ const svc = await server.getService(`svamp-session-${sessionId}`);
479
+ const pollInterval = 2e3;
480
+ const deadline = Date.now() + timeoutMs;
481
+ const busyDeadline = Date.now() + busyTimeoutMs;
482
+ let sawBusy = false;
483
+ while (Date.now() < deadline) {
484
+ const activity = await svc.getActivityState();
485
+ const isActive = activity?.active === true;
486
+ if (isActive) {
487
+ sawBusy = true;
488
+ }
489
+ if (sawBusy && !isActive) {
490
+ return;
491
+ }
492
+ if (!sawBusy && Date.now() > busyDeadline) {
493
+ return;
494
+ }
495
+ await new Promise((r) => setTimeout(r, pollInterval));
496
+ }
497
+ throw new Error("Timeout waiting for agent to become idle");
498
+ }
499
+ async function sessionList(machineId, opts) {
358
500
  const { server, machine } = await connectAndGetMachine(machineId);
359
501
  try {
360
502
  const sessions = await machine.listSessions();
361
- if (sessions.length === 0) {
362
- console.log("No active sessions.");
503
+ const filtered = opts?.active ? sessions.filter((s) => s.active) : sessions;
504
+ if (filtered.length === 0) {
505
+ if (opts?.json) {
506
+ console.log(formatJson([]));
507
+ } else {
508
+ console.log("No active sessions.");
509
+ }
363
510
  return;
364
511
  }
365
512
  const enriched = [];
366
- for (const s of sessions) {
513
+ for (const s of filtered) {
367
514
  let flavor = "claude";
368
515
  let name = "";
516
+ let path = s.directory || "";
517
+ let host = "";
369
518
  if (s.metadata) {
370
519
  flavor = s.metadata.flavor || "claude";
371
520
  name = s.metadata.name || "";
372
521
  }
373
- if (!name && s.active) {
522
+ if (s.active) {
374
523
  try {
375
524
  const svc = await server.getService(`svamp-session-${s.sessionId}`);
376
525
  const { metadata } = await svc.getMetadata();
377
526
  flavor = metadata?.flavor || flavor;
378
- name = metadata?.name || "";
527
+ name = metadata?.name || name;
528
+ path = metadata?.path || path;
529
+ host = metadata?.host || "";
379
530
  } catch {
380
531
  }
381
532
  }
382
- enriched.push({ ...s, flavor, name });
533
+ enriched.push({ ...s, flavor, name, path, host });
383
534
  }
384
- const header = `${"ID".padEnd(10)} ${"AGENT".padEnd(10)} ${"STATUS".padEnd(9)} ${"NAME".padEnd(25)} ${"DIRECTORY".padEnd(35)}`;
385
- console.log(header);
386
- console.log("-".repeat(header.length));
387
- for (const s of enriched) {
388
- const id = s.sessionId.slice(0, 8);
389
- const agent = (s.flavor || "claude").padEnd(10);
390
- const status = s.active ? "\x1B[32mactive\x1B[0m " : "\x1B[90minactive\x1B[0m";
391
- const name = truncate(s.name || "-", 25).padEnd(25);
392
- const dir = truncate(s.directory || "-", 33).padEnd(35);
393
- console.log(`${id.padEnd(10)} ${agent} ${status} ${name} ${dir}`);
535
+ if (opts?.json) {
536
+ console.log(formatJson(enriched.map((s) => ({
537
+ sessionId: s.sessionId,
538
+ agent: s.flavor,
539
+ name: s.name,
540
+ path: s.path,
541
+ host: s.host,
542
+ active: s.active,
543
+ directory: s.directory
544
+ }))));
545
+ } else {
546
+ const header = `${"ID".padEnd(10)} ${"AGENT".padEnd(10)} ${"STATUS".padEnd(9)} ${"NAME".padEnd(25)} ${"DIRECTORY".padEnd(35)}`;
547
+ console.log(header);
548
+ console.log("-".repeat(header.length));
549
+ for (const s of enriched) {
550
+ const id = s.sessionId.slice(0, 8);
551
+ const agent = (s.flavor || "claude").padEnd(10);
552
+ const status = s.active ? "\x1B[32mactive\x1B[0m " : "\x1B[90minactive\x1B[0m";
553
+ const name = truncate(s.name || "-", 25).padEnd(25);
554
+ const dir = truncate(s.directory || "-", 33).padEnd(35);
555
+ console.log(`${id.padEnd(10)} ${agent} ${status} ${name} ${dir}`);
556
+ }
394
557
  }
395
558
  } finally {
396
559
  await server.disconnect();
397
560
  }
398
561
  }
399
- async function sessionSpawn(agent, directory, machineId) {
562
+ async function sessionSpawn(agent, directory, machineId, opts) {
400
563
  const { server, machine } = await connectAndGetMachine(machineId);
401
564
  try {
402
565
  console.log(`Spawning ${agent} session in ${directory}...`);
@@ -407,6 +570,22 @@ async function sessionSpawn(agent, directory, machineId) {
407
570
  if (result.type === "success") {
408
571
  console.log(`Session started: ${result.sessionId}`);
409
572
  if (result.message) console.log(` ${result.message}`);
573
+ if (opts?.message && result.sessionId) {
574
+ const svc = await server.getService(`svamp-session-${result.sessionId}`);
575
+ const sendResult = await svc.sendMessage(
576
+ JSON.stringify({
577
+ role: "user",
578
+ content: { type: "text", text: opts.message },
579
+ meta: { sentFrom: "svamp-cli" }
580
+ })
581
+ );
582
+ console.log(`Message sent (seq: ${sendResult.seq})`);
583
+ if (opts.wait) {
584
+ console.log("Waiting for agent to become idle...");
585
+ await waitForBusyThenIdle(server, result.sessionId);
586
+ console.log("Agent is idle.");
587
+ }
588
+ }
410
589
  } else if (result.type === "requestToApproveDirectoryCreation") {
411
590
  console.error(`Directory ${result.directory} does not exist. Create it first or use an existing directory.`);
412
591
  process.exit(1);
@@ -434,7 +613,7 @@ async function sessionStop(sessionId, machineId) {
434
613
  await server.disconnect();
435
614
  }
436
615
  }
437
- async function sessionInfo(sessionId, machineId) {
616
+ async function sessionInfo(sessionId, machineId, opts) {
438
617
  const { server, machine } = await connectAndGetMachine(machineId);
439
618
  try {
440
619
  const sessions = await machine.listSessions();
@@ -449,43 +628,62 @@ async function sessionInfo(sessionId, machineId) {
449
628
  activity = await svc.getActivityState();
450
629
  } catch {
451
630
  }
452
- console.log(`Session: ${fullId}`);
453
- console.log(`Agent: ${metadata.flavor || "claude"}`);
454
- console.log(`Name: ${metadata.name || "(unnamed)"}`);
455
- console.log(`Directory: ${metadata.path || match.directory || "-"}`);
456
- console.log(`Host: ${metadata.host || "-"}`);
457
- console.log(`State: ${metadata.lifecycleState || "unknown"}`);
458
- console.log(`Active: ${activity.active ?? "-"}`);
459
- console.log(`Thinking: ${activity.thinking ?? "-"}`);
460
- console.log(`Started by: ${metadata.startedBy || match.startedBy || "-"}`);
461
- if (metadata.summary?.text) {
462
- console.log(`Summary: ${metadata.summary.text}`);
463
- }
464
- if (metadata.claudeSessionId) {
465
- console.log(`Claude ID: ${metadata.claudeSessionId}`);
466
- }
467
- if (metadata.sessionLink?.url) {
468
- console.log(`Link: ${metadata.sessionLink.url}`);
631
+ const statusData = {
632
+ sessionId: fullId,
633
+ flavor: metadata.flavor || "claude",
634
+ name: metadata.name || "",
635
+ path: metadata.path || match.directory || "",
636
+ host: metadata.host || "",
637
+ lifecycleState: metadata.lifecycleState || "unknown",
638
+ active: activity.active ?? false,
639
+ thinking: activity.thinking ?? false,
640
+ startedBy: metadata.startedBy || match.startedBy || "",
641
+ summary: metadata.summary?.text || void 0,
642
+ claudeSessionId: metadata.claudeSessionId || void 0,
643
+ sessionLink: metadata.sessionLink?.url || void 0
644
+ };
645
+ if (opts?.json) {
646
+ console.log(formatJson(statusData));
647
+ } else {
648
+ console.log(formatSessionStatus(statusData));
469
649
  }
470
650
  } finally {
471
651
  await server.disconnect();
472
652
  }
473
653
  }
474
- async function sessionMessages(sessionId, last, machineId) {
654
+ async function sessionMessages(sessionId, machineId, opts) {
475
655
  const { server, machine } = await connectAndGetMachine(machineId);
476
656
  try {
477
657
  const sessions = await machine.listSessions();
478
658
  const match = resolveSessionId(sessions, sessionId);
479
659
  const fullId = match.sessionId;
480
660
  const svc = await server.getService(`svamp-session-${fullId}`);
481
- const { messages } = await svc.getMessages(0, 1e3);
482
- const toShow = last ? messages.slice(-last) : messages;
661
+ const afterSeq = opts?.after ?? 0;
662
+ const apiLimit = opts?.limit ?? 1e3;
663
+ const { messages } = await svc.getMessages(afterSeq, apiLimit);
664
+ const toShow = opts?.last ? messages.slice(-opts.last) : messages;
483
665
  if (toShow.length === 0) {
484
- console.log("No messages yet.");
666
+ if (opts?.json) {
667
+ console.log(formatJson([]));
668
+ } else {
669
+ console.log("No messages yet.");
670
+ }
485
671
  return;
486
672
  }
487
- for (const msg of toShow) {
488
- renderMessage(msg);
673
+ if (opts?.json) {
674
+ const formatted = [];
675
+ for (const msg of toShow) {
676
+ const extracted = extractMessageText(msg);
677
+ if (extracted) {
678
+ formatted.push(extracted);
679
+ }
680
+ }
681
+ formatted.sort((a, b) => a.createdAt - b.createdAt);
682
+ console.log(formatJson(formatted));
683
+ } else {
684
+ for (const msg of toShow) {
685
+ renderMessage(msg);
686
+ }
489
687
  }
490
688
  } finally {
491
689
  await server.disconnect();
@@ -607,30 +805,58 @@ async function sessionAttach(sessionId, machineId) {
607
805
  process.exit(0);
608
806
  });
609
807
  }
610
- async function sessionRecover(machineId) {
808
+ async function sessionSend(sessionId, message, machineId, opts) {
611
809
  const { server, machine } = await connectAndGetMachine(machineId);
612
810
  try {
613
- console.log("Scanning for crashed sessions to recover...");
614
- const result = await machine.recoverSessions();
615
- if (result.recovered === 0 && result.failed === 0) {
616
- console.log("No crashed sessions found to recover.");
617
- if (result.alreadyActive > 0) {
618
- console.log(`${result.alreadyActive} session(s) already active.`);
619
- }
620
- return;
811
+ const sessions = await machine.listSessions();
812
+ const match = resolveSessionId(sessions, sessionId);
813
+ const fullId = match.sessionId;
814
+ const svc = await server.getService(`svamp-session-${fullId}`);
815
+ const result = await svc.sendMessage(
816
+ JSON.stringify({
817
+ role: "user",
818
+ content: { type: "text", text: message },
819
+ meta: { sentFrom: "svamp-cli" }
820
+ })
821
+ );
822
+ if (opts?.wait) {
823
+ const timeoutMs = (opts.timeout || 300) * 1e3;
824
+ await waitForBusyThenIdle(server, fullId, timeoutMs);
621
825
  }
622
- for (const detail of result.details) {
623
- if (detail.status === "recovered") {
624
- console.log(` \x1B[32m\u2713\x1B[0m ${detail.sessionId.slice(0, 8)} (${detail.directory})`);
625
- } else if (detail.status === "failed") {
626
- console.log(` \x1B[31m\u2717\x1B[0m ${detail.sessionId.slice(0, 8)} (${detail.directory}): ${detail.error}`);
826
+ if (opts?.json) {
827
+ console.log(formatJson({
828
+ sessionId: fullId,
829
+ message,
830
+ sent: true,
831
+ seq: result.seq,
832
+ waited: !!opts.wait
833
+ }));
834
+ } else {
835
+ console.log(`Message sent to session ${fullId.slice(0, 8)} (seq: ${result.seq})`);
836
+ if (opts?.wait) {
837
+ console.log("Agent is idle.");
627
838
  }
628
839
  }
629
- console.log(`
630
- Recovered: ${result.recovered}, Failed: ${result.failed}, Already active: ${result.alreadyActive}`);
840
+ } finally {
841
+ await server.disconnect();
842
+ }
843
+ }
844
+ async function sessionWait(sessionId, machineId, opts) {
845
+ const { server, machine } = await connectAndGetMachine(machineId);
846
+ try {
847
+ const sessions = await machine.listSessions();
848
+ const match = resolveSessionId(sessions, sessionId);
849
+ const fullId = match.sessionId;
850
+ const timeoutMs = (opts?.timeout || 300) * 1e3;
851
+ await waitForIdle(server, fullId, timeoutMs);
852
+ console.log(`Session ${fullId.slice(0, 8)} is idle.`);
853
+ } catch (err) {
854
+ const msg = err instanceof Error ? err.message : String(err);
855
+ console.error(msg);
856
+ process.exitCode = 1;
631
857
  } finally {
632
858
  await server.disconnect();
633
859
  }
634
860
  }
635
861
 
636
- export { connectAndGetMachine, renderMessage, resolveSessionId, sessionAttach, sessionInfo, sessionList, sessionMachines, sessionMessages, sessionRecover, sessionSpawn, sessionStop };
862
+ export { connectAndGetMachine, renderMessage, resolveSessionId, sessionAttach, sessionInfo, sessionList, sessionMachines, sessionMessages, sessionSend, sessionSpawn, sessionStop, sessionWait };