svamp-cli 0.1.75 → 0.1.78

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.
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { execSync } from 'node:child_process';
3
3
  import { resolve, join } from 'node:path';
4
4
  import os from 'node:os';
5
- import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-lhAjX4NB.mjs';
5
+ import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-DsXDjwLW.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -613,12 +613,11 @@ function extractMessageText(msg) {
613
613
  createdAt: msg.createdAt || 0
614
614
  };
615
615
  }
616
- async function waitForIdle(server, sessionId, timeoutMs) {
617
- const svc = await server.getService(`svamp-session-${sessionId}`);
616
+ async function waitForIdle(machine, sessionId, timeoutMs) {
618
617
  const pollInterval = 2e3;
619
618
  const deadline = Date.now() + timeoutMs;
620
619
  while (Date.now() < deadline) {
621
- const activity = await svc.getActivityState();
620
+ const activity = await machine.sessionRPC(sessionId, "getActivityState", []);
622
621
  if (activity?.pendingPermissions?.length > 0) {
623
622
  return { idle: false, pendingPermissions: activity.pendingPermissions };
624
623
  }
@@ -629,14 +628,13 @@ async function waitForIdle(server, sessionId, timeoutMs) {
629
628
  }
630
629
  throw new Error("Timeout waiting for agent to become idle");
631
630
  }
632
- async function waitForBusyThenIdle(server, sessionId, timeoutMs = 3e5, busyTimeoutMs = 1e4) {
633
- const svc = await server.getService(`svamp-session-${sessionId}`);
631
+ async function waitForBusyThenIdle(machine, sessionId, timeoutMs = 3e5, busyTimeoutMs = 1e4) {
634
632
  const pollInterval = 2e3;
635
633
  const deadline = Date.now() + timeoutMs;
636
634
  const busyDeadline = Date.now() + busyTimeoutMs;
637
635
  let sawBusy = false;
638
636
  while (Date.now() < deadline) {
639
- const activity = await svc.getActivityState();
637
+ const activity = await machine.sessionRPC(sessionId, "getActivityState", []);
640
638
  if (activity?.pendingPermissions?.length > 0) {
641
639
  return { idle: false, pendingPermissions: activity.pendingPermissions };
642
640
  }
@@ -934,7 +932,7 @@ async function sessionSpawn(agent, directory, machineId, opts) {
934
932
  console.log(`Message sent (seq: ${sendResult.seq})`);
935
933
  if (opts.wait) {
936
934
  console.log("Waiting for agent to become idle...");
937
- await waitForBusyThenIdle(server, result.sessionId);
935
+ await waitForBusyThenIdle(machine, result.sessionId);
938
936
  console.log("Agent is idle.");
939
937
  }
940
938
  }
@@ -1268,18 +1266,22 @@ async function sessionSend(sessionId, message, machineId, opts) {
1268
1266
  const sessions = await machine.listSessions();
1269
1267
  const match = resolveSessionId(sessions, sessionId);
1270
1268
  const fullId = match.sessionId;
1271
- const svc = await server.getService(`svamp-session-${fullId}`);
1272
- const result = await svc.sendMessage(
1273
- JSON.stringify({
1274
- role: "user",
1275
- content: { type: "text", text: message },
1276
- meta: { sentFrom: "svamp-cli" }
1277
- })
1278
- );
1269
+ const { randomUUID } = await import('node:crypto');
1270
+ const inboxMessage = {
1271
+ messageId: randomUUID(),
1272
+ body: message,
1273
+ timestamp: Date.now(),
1274
+ read: false,
1275
+ from: `cli:${os.userInfo().username}`,
1276
+ to: fullId,
1277
+ subject: opts?.subject,
1278
+ urgency: opts?.urgency || "urgent"
1279
+ };
1280
+ const result = await machine.sessionRPC(fullId, "sendInboxMessage", [inboxMessage]);
1279
1281
  let waitResult;
1280
1282
  if (opts?.wait) {
1281
1283
  const timeoutMs = (opts.timeout || 300) * 1e3;
1282
- waitResult = await waitForBusyThenIdle(server, fullId, timeoutMs);
1284
+ waitResult = await waitForBusyThenIdle(machine, fullId, timeoutMs);
1283
1285
  }
1284
1286
  if (waitResult?.pendingPermissions?.length) {
1285
1287
  if (opts?.json) {
@@ -1287,12 +1289,12 @@ async function sessionSend(sessionId, message, machineId, opts) {
1287
1289
  sessionId: fullId,
1288
1290
  message,
1289
1291
  sent: true,
1290
- seq: result.seq,
1292
+ messageId: result.messageId,
1291
1293
  status: "permission-pending",
1292
1294
  pendingPermissions: waitResult.pendingPermissions
1293
1295
  }));
1294
1296
  } else {
1295
- console.log(`Message sent to session ${fullId.slice(0, 8)} (seq: ${result.seq})`);
1297
+ console.log(`Message sent to session ${fullId.slice(0, 8)} (id: ${result.messageId.slice(0, 8)})`);
1296
1298
  console.log("Agent is waiting for permission approval:");
1297
1299
  for (const p of waitResult.pendingPermissions) {
1298
1300
  const argsStr = JSON.stringify(p.arguments || {}).slice(0, 120);
@@ -1307,12 +1309,12 @@ Use: svamp session approve ${fullId.slice(0, 8)}`);
1307
1309
  sessionId: fullId,
1308
1310
  message,
1309
1311
  sent: true,
1310
- seq: result.seq,
1312
+ messageId: result.messageId,
1311
1313
  waited: !!opts.wait,
1312
1314
  status: opts.wait ? "idle" : "sent"
1313
1315
  }));
1314
1316
  } else {
1315
- console.log(`Message sent to session ${fullId.slice(0, 8)} (seq: ${result.seq})`);
1317
+ console.log(`Message sent to session ${fullId.slice(0, 8)} (id: ${result.messageId.slice(0, 8)})`);
1316
1318
  if (opts?.wait) {
1317
1319
  console.log("Agent is idle.");
1318
1320
  }
@@ -1328,7 +1330,7 @@ async function sessionWait(sessionId, machineId, opts) {
1328
1330
  const match = resolveSessionId(sessions, sessionId);
1329
1331
  const fullId = match.sessionId;
1330
1332
  const timeoutMs = (opts?.timeout || 300) * 1e3;
1331
- const result = await waitForIdle(server, fullId, timeoutMs);
1333
+ const result = await waitForIdle(machine, fullId, timeoutMs);
1332
1334
  if (result.pendingPermissions?.length) {
1333
1335
  if (opts?.json) {
1334
1336
  console.log(formatJson({
@@ -1706,78 +1708,144 @@ async function sessionRalphStatus(sessionIdPartial, machineId) {
1706
1708
  await server.disconnect();
1707
1709
  }
1708
1710
  }
1709
- async function sessionQueueAdd(sessionIdPartial, message, machineId) {
1711
+ async function sessionInboxSend(sessionIdPartial, body, machineId, opts) {
1710
1712
  const { server, machine } = await connectAndGetMachine(machineId);
1711
1713
  try {
1712
1714
  const sessions = await machine.listSessions();
1713
1715
  const match = resolveSessionId(sessions, sessionIdPartial);
1714
1716
  const fullId = match.sessionId;
1715
- const svc = await server.getService(`svamp-session-${fullId}`);
1716
- const { metadata, version } = await svc.getMetadata();
1717
- const existingQueue = metadata?.messageQueue || [];
1718
- const newMsg = {
1719
- id: `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1720
- text: message,
1721
- createdAt: Date.now()
1717
+ const { randomUUID } = await import('node:crypto');
1718
+ const message = {
1719
+ messageId: randomUUID(),
1720
+ body,
1721
+ timestamp: Date.now(),
1722
+ read: false,
1723
+ from: `cli:${os.userInfo().username}`,
1724
+ to: fullId,
1725
+ subject: opts?.subject,
1726
+ urgency: opts?.urgency || "normal",
1727
+ replyTo: opts?.replyTo,
1728
+ threadId: opts?.threadId
1722
1729
  };
1723
- const updatedQueue = [...existingQueue, newMsg];
1724
- const updatedMetadata = { ...metadata, messageQueue: updatedQueue };
1725
- const result = await svc.updateMetadata(updatedMetadata, version);
1726
- if (result.result !== "success") {
1727
- console.error(`Failed to add to queue: ${result.result}`);
1728
- process.exit(1);
1730
+ const result = await machine.sessionRPC(fullId, "sendInboxMessage", [message]);
1731
+ if (opts?.json) {
1732
+ console.log(formatJson({ sessionId: fullId, messageId: result.messageId, sent: true }));
1733
+ } else {
1734
+ console.log(`Inbox message sent to session ${fullId.slice(0, 8)} (id: ${result.messageId.slice(0, 8)})`);
1729
1735
  }
1730
- console.log(`Message queued on session ${fullId.slice(0, 8)} (${updatedQueue.length} total)`);
1731
1736
  } finally {
1732
1737
  await server.disconnect();
1733
1738
  }
1734
1739
  }
1735
- async function sessionQueueList(sessionIdPartial, machineId) {
1740
+ async function sessionInboxList(sessionIdPartial, machineId, opts) {
1736
1741
  const { server, machine } = await connectAndGetMachine(machineId);
1737
1742
  try {
1738
1743
  const sessions = await machine.listSessions();
1739
1744
  const match = resolveSessionId(sessions, sessionIdPartial);
1740
1745
  const fullId = match.sessionId;
1741
- const svc = await server.getService(`svamp-session-${fullId}`);
1742
- const { metadata } = await svc.getMetadata();
1743
- const queue = metadata?.messageQueue || [];
1744
- if (queue.length === 0) {
1745
- console.log("Queue is empty.");
1746
+ const result = await machine.sessionRPC(fullId, "getInbox", [{ unread: opts?.unread, limit: opts?.limit }]);
1747
+ const messages = result.messages;
1748
+ if (opts?.json) {
1749
+ console.log(formatJson({ sessionId: fullId, messages }));
1750
+ return;
1751
+ }
1752
+ if (messages.length === 0) {
1753
+ console.log(`Inbox for session ${fullId.slice(0, 8)} is empty.`);
1746
1754
  return;
1747
1755
  }
1748
- console.log(`Queue for session ${fullId.slice(0, 8)} (${queue.length} messages):`);
1749
- for (let i = 0; i < queue.length; i++) {
1750
- const msg = queue[i];
1751
- const time = new Date(msg.createdAt).toLocaleTimeString();
1752
- console.log(` ${i + 1}. [${time}] ${msg.text.slice(0, 80)}${msg.text.length > 80 ? "..." : ""}`);
1756
+ const unreadCount = messages.filter((m) => !m.read).length;
1757
+ console.log(`Inbox for session ${fullId.slice(0, 8)} (${messages.length} message(s), ${unreadCount} unread):
1758
+ `);
1759
+ for (const msg of messages) {
1760
+ const status = msg.read ? " " : "\u25CF";
1761
+ const from = msg.from ? ` from ${msg.from}` : "";
1762
+ const subject = msg.subject ? ` \u2014 ${msg.subject}` : "";
1763
+ const urgencyTag = msg.urgency === "urgent" ? " [URGENT]" : "";
1764
+ const date = new Date(msg.timestamp).toLocaleString();
1765
+ const preview = msg.body.length > 100 ? msg.body.slice(0, 97) + "..." : msg.body;
1766
+ console.log(` ${status} ${msg.messageId.slice(0, 8)}${urgencyTag}${from}${subject}`);
1767
+ console.log(` ${date}`);
1768
+ console.log(` ${preview}
1769
+ `);
1753
1770
  }
1754
1771
  } finally {
1755
1772
  await server.disconnect();
1756
1773
  }
1757
1774
  }
1758
- async function sessionQueueClear(sessionIdPartial, machineId) {
1775
+ async function sessionInboxRead(sessionIdPartial, messageId, machineId) {
1759
1776
  const { server, machine } = await connectAndGetMachine(machineId);
1760
1777
  try {
1761
1778
  const sessions = await machine.listSessions();
1762
1779
  const match = resolveSessionId(sessions, sessionIdPartial);
1763
1780
  const fullId = match.sessionId;
1764
- const svc = await server.getService(`svamp-session-${fullId}`);
1765
- const { metadata, version } = await svc.getMetadata();
1766
- const count = (metadata?.messageQueue || []).length;
1767
- if (count === 0) {
1768
- console.log("Queue is already empty.");
1769
- return;
1781
+ const result = await machine.sessionRPC(fullId, "getInbox", []);
1782
+ const msg = result.messages.find((m) => m.messageId === messageId || m.messageId.startsWith(messageId));
1783
+ if (!msg) {
1784
+ console.error(`Message ${messageId} not found in inbox.`);
1785
+ process.exit(1);
1786
+ }
1787
+ if (!msg.read) {
1788
+ await machine.sessionRPC(fullId, "markInboxRead", [msg.messageId]);
1770
1789
  }
1771
- const updatedMetadata = { ...metadata, messageQueue: void 0 };
1772
- const result = await svc.updateMetadata(updatedMetadata, version);
1773
- if (result.result !== "success") {
1774
- console.error(`Failed to clear queue: ${result.result}`);
1790
+ console.log(`From: ${msg.from || "(unknown)"}`);
1791
+ if (msg.subject) console.log(`Subject: ${msg.subject}`);
1792
+ console.log(`Date: ${new Date(msg.timestamp).toLocaleString()}`);
1793
+ if (msg.urgency === "urgent") console.log("Urgency: URGENT");
1794
+ if (msg.replyTo) console.log(`Reply-To: ${msg.replyTo.slice(0, 8)}`);
1795
+ if (msg.threadId) console.log(`Thread: ${msg.threadId.slice(0, 8)}`);
1796
+ console.log(`
1797
+ ${msg.body}`);
1798
+ } finally {
1799
+ await server.disconnect();
1800
+ }
1801
+ }
1802
+ async function sessionInboxReply(sessionIdPartial, messageId, body, machineId) {
1803
+ const { server, machine } = await connectAndGetMachine(machineId);
1804
+ try {
1805
+ const sessions = await machine.listSessions();
1806
+ const match = resolveSessionId(sessions, sessionIdPartial);
1807
+ const fullId = match.sessionId;
1808
+ const result = await machine.sessionRPC(fullId, "getInbox", []);
1809
+ const original = result.messages.find((m) => m.messageId === messageId || m.messageId.startsWith(messageId));
1810
+ if (!original) {
1811
+ console.error(`Message ${messageId} not found in inbox.`);
1775
1812
  process.exit(1);
1776
1813
  }
1777
- console.log(`Cleared ${count} message(s) from queue on session ${fullId.slice(0, 8)}`);
1814
+ if (!original.fromSession) {
1815
+ console.error("Cannot reply: original message has no fromSession.");
1816
+ process.exit(1);
1817
+ }
1818
+ const { randomUUID } = await import('node:crypto');
1819
+ const reply = {
1820
+ messageId: randomUUID(),
1821
+ body,
1822
+ timestamp: Date.now(),
1823
+ read: false,
1824
+ from: `session:${fullId}`,
1825
+ fromSession: fullId,
1826
+ to: original.fromSession,
1827
+ subject: original.subject ? `Re: ${original.subject}` : void 0,
1828
+ urgency: "normal",
1829
+ replyTo: original.messageId,
1830
+ threadId: original.threadId || original.messageId
1831
+ };
1832
+ const sendResult = await machine.sessionRPC(original.fromSession, "sendInboxMessage", [reply]);
1833
+ console.log(`Reply sent to session ${original.fromSession.slice(0, 8)} (id: ${sendResult.messageId.slice(0, 8)})`);
1834
+ } finally {
1835
+ await server.disconnect();
1836
+ }
1837
+ }
1838
+ async function sessionInboxClear(sessionIdPartial, machineId, opts) {
1839
+ const { server, machine } = await connectAndGetMachine(machineId);
1840
+ try {
1841
+ const sessions = await machine.listSessions();
1842
+ const match = resolveSessionId(sessions, sessionIdPartial);
1843
+ const fullId = match.sessionId;
1844
+ const result = await machine.sessionRPC(fullId, "clearInbox", [{ all: opts?.all }]);
1845
+ console.log(`Cleared inbox on session ${fullId.slice(0, 8)} (${result.remaining} remaining)`);
1778
1846
  } finally {
1779
1847
  await server.disconnect();
1780
1848
  }
1781
1849
  }
1782
1850
 
1783
- export { connectAndGetMachine, createWorktree, generateWorktreeName, machineExec, machineInfo, machineLs, machineShare, parseShareArg, renderMessage, resolveSessionId, sessionApprove, sessionAttach, sessionDeny, sessionInfo, sessionList, sessionMachines, sessionMessages, sessionQueueAdd, sessionQueueClear, sessionQueueList, sessionRalphCancel, sessionRalphStart, sessionRalphStatus, sessionSend, sessionShare, sessionSpawn, sessionStop, sessionWait };
1851
+ export { connectAndGetMachine, createWorktree, generateWorktreeName, machineExec, machineInfo, machineLs, machineShare, parseShareArg, renderMessage, resolveSessionId, sessionApprove, sessionAttach, sessionDeny, sessionInboxClear, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxSend, sessionInfo, sessionList, sessionMachines, sessionMessages, sessionRalphCancel, sessionRalphStart, sessionRalphStatus, sessionSend, sessionShare, sessionSpawn, sessionStop, sessionWait };
@@ -1,4 +1,4 @@
1
- import { createServiceGroup, listServiceGroups, getServiceGroup, deleteServiceGroup, addBackend, removeBackend, addPort, removePort, renameSubdomain, getSandboxEnv } from './api-BRbsyqJ4.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { createServiceGroup, listServiceGroups, getServiceGroup, deleteServiceGroup, addBackend, removeBackend, addPort, removePort, renameSubdomain, getSandboxEnv } from './api-BRbsyqJ4.mjs';
2
2
 
3
3
  function getFlag(args, flag) {
4
4
  const idx = args.indexOf(flag);
@@ -296,7 +296,7 @@ Service is live:`);
296
296
  }
297
297
  } else {
298
298
  console.log(`No SANDBOX_ID detected \u2014 starting reverse tunnel.`);
299
- const { runTunnel } = await import('./tunnel-C2kqST5d.mjs');
299
+ const { runTunnel } = await import('./tunnel-BDKdemh0.mjs');
300
300
  await runTunnel(name, ports);
301
301
  }
302
302
  } catch (err) {
@@ -304,6 +304,61 @@ Service is live:`);
304
304
  process.exit(1);
305
305
  }
306
306
  }
307
+ async function serviceServe(args) {
308
+ const positional = positionalArgs(args);
309
+ const name = positional[0];
310
+ const directory = positional[1] || ".";
311
+ const subdomain = getFlag(args, "--subdomain");
312
+ const healthPath = getFlag(args, "--health-path");
313
+ const healthIntervalStr = getFlag(args, "--health-interval");
314
+ const noListing = hasFlag(args, "--no-listing");
315
+ if (!name) {
316
+ console.error("Usage: svamp service serve <name> [directory] [--subdomain <sub>] [--no-listing]");
317
+ console.error(" directory defaults to current directory");
318
+ process.exit(1);
319
+ }
320
+ if (subdomain) validateSubdomain(subdomain);
321
+ const healthInterval = healthIntervalStr ? parseInt(healthIntervalStr, 10) : void 0;
322
+ try {
323
+ const { startStaticServer } = await import('./staticServer-CWcmMF5V.mjs');
324
+ const resolvedDir = require("path").resolve(directory);
325
+ console.log(`Serving ${resolvedDir}`);
326
+ const staticServer = await startStaticServer({
327
+ directory: resolvedDir,
328
+ listing: !noListing
329
+ });
330
+ console.log(`Static server listening on 127.0.0.1:${staticServer.port}`);
331
+ const ports = [staticServer.port];
332
+ const env = getSandboxEnv();
333
+ const group = await createServiceGroup(name, ports, {
334
+ subdomain,
335
+ healthPath,
336
+ healthInterval
337
+ });
338
+ if (env.sandboxId) {
339
+ const result = await addBackend(name);
340
+ console.log(`Backend added: ${result.sandbox_id} (${result.pod_ip})`);
341
+ console.log(`
342
+ Serving ${resolvedDir}:`);
343
+ for (const p of group.ports) {
344
+ console.log(` ${p.url}`);
345
+ }
346
+ } else {
347
+ console.log(`No SANDBOX_ID detected \u2014 starting reverse tunnel.`);
348
+ const { runTunnel } = await import('./tunnel-BDKdemh0.mjs');
349
+ await runTunnel(name, ports);
350
+ }
351
+ const cleanup = () => {
352
+ staticServer.close();
353
+ process.exit(0);
354
+ };
355
+ process.on("SIGINT", cleanup);
356
+ process.on("SIGTERM", cleanup);
357
+ } catch (err) {
358
+ console.error(`Error serving directory: ${err.message}`);
359
+ process.exit(1);
360
+ }
361
+ }
307
362
  async function serviceTunnel(args) {
308
363
  const positional = positionalArgs(args);
309
364
  const name = positional[0];
@@ -312,7 +367,7 @@ async function serviceTunnel(args) {
312
367
  console.error("Usage: svamp service tunnel <name> --port <port> [--port <port2>]");
313
368
  process.exit(1);
314
369
  }
315
- const { runTunnel } = await import('./tunnel-C2kqST5d.mjs');
370
+ const { runTunnel } = await import('./tunnel-BDKdemh0.mjs');
316
371
  await runTunnel(name, ports);
317
372
  }
318
373
  async function handleServiceCommand() {
@@ -344,6 +399,8 @@ async function handleServiceCommand() {
344
399
  await serviceRename(commandArgs);
345
400
  } else if (sub === "expose") {
346
401
  await serviceExpose(commandArgs);
402
+ } else if (sub === "serve") {
403
+ await serviceServe(commandArgs);
347
404
  } else if (sub === "tunnel") {
348
405
  await serviceTunnel(commandArgs);
349
406
  } else {
@@ -367,6 +424,7 @@ Usage:
367
424
  svamp service remove-port <name> --port <port> Remove port from group
368
425
  svamp service rename <name> --port <port> --subdomain <sub> Rename subdomain of a port
369
426
  svamp service expose <name> --port <port> [--port <port2>] [options] Create + join (auto-detects tunnel)
427
+ svamp service serve <name> [directory] [options] Serve a directory as static files
370
428
  svamp service tunnel <name> --port <port> [--port <port2>] Tunnel local ports to service group
371
429
 
372
430
  Create/Expose options:
@@ -391,9 +449,15 @@ Environment variables (set by provisioner):
391
449
  SANDBOX_NAMESPACE User's sandbox namespace
392
450
  SANDBOX_ID This pod's sandbox ID
393
451
 
452
+ Serve options:
453
+ --no-listing Disable directory listing
454
+ --subdomain <sub> Custom subdomain (must contain at least 2 hyphens)
455
+
394
456
  Examples:
395
457
  svamp service expose my-api --port 8000
396
458
  svamp service expose my-api --port 8000 --port 3000 --health-path /health
459
+ svamp service serve my-site ./dist
460
+ svamp service serve my-site (serves current directory)
397
461
  svamp service add-port my-api --port 5000
398
462
  svamp service remove-port my-api --port 5000
399
463
  svamp service list
@@ -402,4 +466,4 @@ Examples:
402
466
  `.trim());
403
467
  }
404
468
 
405
- export { handleServiceCommand, serviceAddBackend, serviceAddPort, serviceCreate, serviceDelete, serviceExpose, serviceInfo, serviceList, serviceRemoveBackend, serviceRemovePort, serviceRename, serviceTunnel };
469
+ export { handleServiceCommand, serviceAddBackend, serviceAddPort, serviceCreate, serviceDelete, serviceExpose, serviceInfo, serviceList, serviceRemoveBackend, serviceRemovePort, serviceRename, serviceServe, serviceTunnel };
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-a7p1jW-t.mjs';
3
+ import { connectAndGetMachine } from './commands-B6FEeZeP.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:child_process';
6
6
  import 'node:path';
7
7
  import 'node:os';
8
- import './run-lhAjX4NB.mjs';
8
+ import './run-DsXDjwLW.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
@@ -1,5 +1,5 @@
1
1
  import os__default from 'os';
2
- import fs from 'fs';
2
+ import fs__default from 'fs';
3
3
  import { join, resolve, relative } from 'path';
4
4
 
5
5
  const SKILLS_SERVER = process.env.HYPHA_SKILLS_SERVER || "https://hypha.aicell.io";
@@ -128,8 +128,8 @@ function normalizeSkill(item) {
128
128
  const SVAMP_HOME = process.env.SVAMP_HOME || join(os__default.homedir(), ".svamp");
129
129
  const ENV_FILE = join(SVAMP_HOME, ".env");
130
130
  function loadDotEnv() {
131
- if (!fs.existsSync(ENV_FILE)) return;
132
- const lines = fs.readFileSync(ENV_FILE, "utf-8").split("\n");
131
+ if (!fs__default.existsSync(ENV_FILE)) return;
132
+ const lines = fs__default.readFileSync(ENV_FILE, "utf-8").split("\n");
133
133
  for (const line of lines) {
134
134
  const trimmed = line.trim();
135
135
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -143,7 +143,7 @@ function loadDotEnv() {
143
143
  }
144
144
  }
145
145
  function ensureSkillsDir() {
146
- fs.mkdirSync(SKILLS_DIR, { recursive: true });
146
+ fs__default.mkdirSync(SKILLS_DIR, { recursive: true });
147
147
  }
148
148
  function formatTable(rows, widths) {
149
149
  if (rows.length === 0) return "";
@@ -190,7 +190,7 @@ async function skillsInstall(skillName, opts) {
190
190
  process.exit(1);
191
191
  }
192
192
  const targetDir = join(SKILLS_DIR, skillName);
193
- if (fs.existsSync(targetDir) && !opts?.force) {
193
+ if (fs__default.existsSync(targetDir) && !opts?.force) {
194
194
  console.error(`Skill "${skillName}" is already installed at ${targetDir}`);
195
195
  console.error("Use --force to overwrite.");
196
196
  process.exit(1);
@@ -223,10 +223,10 @@ async function skillsInstall(skillName, opts) {
223
223
  process.exit(1);
224
224
  }
225
225
  ensureSkillsDir();
226
- if (fs.existsSync(targetDir) && opts?.force) {
227
- fs.rmSync(targetDir, { recursive: true, force: true });
226
+ if (fs__default.existsSync(targetDir) && opts?.force) {
227
+ fs__default.rmSync(targetDir, { recursive: true, force: true });
228
228
  }
229
- fs.mkdirSync(targetDir, { recursive: true });
229
+ fs__default.mkdirSync(targetDir, { recursive: true });
230
230
  let downloaded = 0;
231
231
  const errors = [];
232
232
  for (const filePath of files) {
@@ -237,23 +237,23 @@ async function skillsInstall(skillName, opts) {
237
237
  errors.push(` ${filePath}: path outside skill directory (blocked)`);
238
238
  continue;
239
239
  }
240
- fs.mkdirSync(join(localPath, ".."), { recursive: true });
241
- fs.writeFileSync(localPath, content, "utf-8");
240
+ fs__default.mkdirSync(join(localPath, ".."), { recursive: true });
241
+ fs__default.writeFileSync(localPath, content, "utf-8");
242
242
  downloaded++;
243
243
  } catch (err) {
244
244
  errors.push(` ${filePath}: ${err.message}`);
245
245
  }
246
246
  }
247
247
  if (errors.length > 0) {
248
- fs.rmSync(targetDir, { recursive: true, force: true });
248
+ fs__default.rmSync(targetDir, { recursive: true, force: true });
249
249
  console.error(`Installation failed \u2014 ${errors.length} file(s) could not be downloaded:`);
250
250
  errors.forEach((e) => console.error(e));
251
251
  console.error("Partial installation cleaned up. Run the command again to retry.");
252
252
  process.exit(1);
253
253
  }
254
254
  const skillMdPath = join(targetDir, "SKILL.md");
255
- if (fs.existsSync(skillMdPath)) {
256
- const content = fs.readFileSync(skillMdPath, "utf-8");
255
+ if (fs__default.existsSync(skillMdPath)) {
256
+ const content = fs__default.readFileSync(skillMdPath, "utf-8");
257
257
  const fm = parseFrontmatter(content);
258
258
  if (!fm) {
259
259
  console.error("Warning: Installed SKILL.md has invalid or missing frontmatter (name/description).");
@@ -280,7 +280,7 @@ async function collectAllFiles(skillAlias, dir = "") {
280
280
  }
281
281
  async function skillsList() {
282
282
  ensureSkillsDir();
283
- const entries = fs.readdirSync(SKILLS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory());
283
+ const entries = fs__default.readdirSync(SKILLS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory());
284
284
  if (entries.length === 0) {
285
285
  console.log("No skills installed.");
286
286
  console.log(`Install one with: svamp skills install <name>`);
@@ -293,11 +293,11 @@ async function skillsList() {
293
293
  const skillMd = join(skillDir, "SKILL.md");
294
294
  let description = "-";
295
295
  let status = "ok";
296
- if (!fs.existsSync(skillMd)) {
296
+ if (!fs__default.existsSync(skillMd)) {
297
297
  status = "missing SKILL.md";
298
298
  } else {
299
299
  try {
300
- const content = fs.readFileSync(skillMd, "utf-8");
300
+ const content = fs__default.readFileSync(skillMd, "utf-8");
301
301
  const fm = parseFrontmatter(content);
302
302
  if (fm) {
303
303
  description = fm.description.length > 45 ? fm.description.slice(0, 42) + "..." : fm.description;
@@ -318,34 +318,34 @@ async function skillsRemove(name) {
318
318
  process.exit(1);
319
319
  }
320
320
  const targetDir = join(SKILLS_DIR, name);
321
- if (!fs.existsSync(targetDir)) {
321
+ if (!fs__default.existsSync(targetDir)) {
322
322
  console.error(`Skill "${name}" is not installed.`);
323
323
  process.exit(1);
324
324
  }
325
- if (!fs.statSync(targetDir).isDirectory()) {
325
+ if (!fs__default.statSync(targetDir).isDirectory()) {
326
326
  console.error(`"${name}" is not a skill directory.`);
327
327
  process.exit(1);
328
328
  }
329
- fs.rmSync(targetDir, { recursive: true, force: true });
329
+ fs__default.rmSync(targetDir, { recursive: true, force: true });
330
330
  console.log(`Removed skill "${name}" from ${targetDir}`);
331
331
  }
332
332
  async function skillsPublish(skillPath, opts) {
333
333
  const absPath = resolve(skillPath);
334
- if (!fs.existsSync(absPath)) {
334
+ if (!fs__default.existsSync(absPath)) {
335
335
  console.error(`Path does not exist: ${absPath}`);
336
336
  process.exit(1);
337
337
  }
338
- if (!fs.statSync(absPath).isDirectory()) {
338
+ if (!fs__default.statSync(absPath).isDirectory()) {
339
339
  console.error(`Not a directory: ${absPath}`);
340
340
  process.exit(1);
341
341
  }
342
342
  const skillMdPath = join(absPath, "SKILL.md");
343
- if (!fs.existsSync(skillMdPath)) {
343
+ if (!fs__default.existsSync(skillMdPath)) {
344
344
  console.error(`No SKILL.md found in ${absPath}`);
345
345
  console.error("A valid skill must contain a SKILL.md file with name and description frontmatter.");
346
346
  process.exit(1);
347
347
  }
348
- const skillMdContent = fs.readFileSync(skillMdPath, "utf-8");
348
+ const skillMdContent = fs__default.readFileSync(skillMdPath, "utf-8");
349
349
  const manifest = parseFrontmatter(skillMdContent);
350
350
  if (!manifest) {
351
351
  console.error('SKILL.md must have YAML frontmatter with "name" and "description" fields.');
@@ -455,7 +455,7 @@ async function skillsPublish(skillPath, opts) {
455
455
  for (const relPath of filesToUpload) {
456
456
  try {
457
457
  const fullPath = join(absPath, relPath);
458
- const content = fs.readFileSync(fullPath);
458
+ const content = fs__default.readFileSync(fullPath);
459
459
  const putUrl = await am.put_file({
460
460
  artifact_id: artifactId,
461
461
  file_path: relPath,
@@ -515,7 +515,7 @@ async function skillsPublish(skillPath, opts) {
515
515
  function collectLocalFiles(dir, base) {
516
516
  const root = base || dir;
517
517
  const result = [];
518
- const entries = fs.readdirSync(dir, { withFileTypes: true });
518
+ const entries = fs__default.readdirSync(dir, { withFileTypes: true });
519
519
  for (const entry of entries) {
520
520
  const fullPath = join(dir, entry.name);
521
521
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
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-lhAjX4NB.mjs';
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-DsXDjwLW.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.1.75";
2
+ var version = "0.1.78";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -19,7 +19,7 @@ var exports$1 = {
19
19
  var scripts = {
20
20
  build: "rm -rf dist && tsc --noEmit && pkgroll",
21
21
  typecheck: "tsc --noEmit",
22
- test: "npx tsx test/test-authorize.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-ralph-loop.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs",
22
+ test: "npx tsx test/test-authorize.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-ralph-loop.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs",
23
23
  "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
24
24
  dev: "tsx src/cli.ts",
25
25
  "dev:daemon": "tsx src/cli.ts daemon start-sync",
@@ -28,8 +28,8 @@ var scripts = {
28
28
  var dependencies = {
29
29
  "@agentclientprotocol/sdk": "^0.14.1",
30
30
  "@modelcontextprotocol/sdk": "^1.25.3",
31
- "hypha-rpc": "0.21.29",
32
- "node-pty": "^1.1.0",
31
+ "hypha-rpc": "0.21.34",
32
+ "node-pty": "1.2.0-beta.11",
33
33
  ws: "^8.18.0",
34
34
  yaml: "^2.8.2",
35
35
  zod: "^3.24.4"
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
2
2
  import os from 'node:os';
3
3
  import { join, resolve } from 'node:path';
4
4
  import { mkdirSync, writeFileSync, existsSync, unlinkSync, readFileSync, watch } from 'node:fs';
5
- import { c as connectToHypha, a as registerSessionService } from './run-lhAjX4NB.mjs';
5
+ import { c as connectToHypha, a as registerSessionService } from './run-DsXDjwLW.mjs';
6
6
  import { createServer } from 'node:http';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { createInterface } from 'node:readline';