svamp-cli 0.1.74 → 0.1.76
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/agentCommands-NVZzP_Vo.mjs +298 -0
- package/dist/cli.mjs +188 -164
- package/dist/{commands-dKkUOvUb.mjs → commands-7Iw1nFwf.mjs} +3 -3
- package/dist/{commands-BLjcT1Vl.mjs → commands-CADr1mQg.mjs} +66 -2
- package/dist/{commands-UFi0_ESV.mjs → commands-DJoYOM_1.mjs} +25 -25
- package/dist/{commands-ClMc3nSg.mjs → commands-lJ8V7MJE.mjs} +77 -141
- package/dist/index.mjs +1 -1
- package/dist/{package-CCjeil_X.mjs → package-Dpz1MLO4.mjs} +4 -3
- package/dist/{run-BgwhZgkT.mjs → run-B29grSMh.mjs} +1 -1
- package/dist/{run-6QgabuQN.mjs → run-BnFGIK0c.mjs} +321 -329
- package/dist/staticServer-B-S9sl6E.mjs +198 -0
- package/package.json +4 -3
- package/dist/agentCommands-7GGmL2zY.mjs +0 -157
|
@@ -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);
|
|
@@ -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-B-S9sl6E.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-C2kqST5d.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];
|
|
@@ -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,5 +1,5 @@
|
|
|
1
1
|
import os__default from 'os';
|
|
2
|
-
import
|
|
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 (!
|
|
132
|
-
const lines =
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
227
|
-
|
|
226
|
+
if (fs__default.existsSync(targetDir) && opts?.force) {
|
|
227
|
+
fs__default.rmSync(targetDir, { recursive: true, force: true });
|
|
228
228
|
}
|
|
229
|
-
|
|
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
|
-
|
|
241
|
-
|
|
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
|
-
|
|
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 (
|
|
256
|
-
const content =
|
|
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 =
|
|
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 (!
|
|
296
|
+
if (!fs__default.existsSync(skillMd)) {
|
|
297
297
|
status = "missing SKILL.md";
|
|
298
298
|
} else {
|
|
299
299
|
try {
|
|
300
|
-
const content =
|
|
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 (!
|
|
321
|
+
if (!fs__default.existsSync(targetDir)) {
|
|
322
322
|
console.error(`Skill "${name}" is not installed.`);
|
|
323
323
|
process.exit(1);
|
|
324
324
|
}
|
|
325
|
-
if (!
|
|
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
|
-
|
|
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 (!
|
|
334
|
+
if (!fs__default.existsSync(absPath)) {
|
|
335
335
|
console.error(`Path does not exist: ${absPath}`);
|
|
336
336
|
process.exit(1);
|
|
337
337
|
}
|
|
338
|
-
if (!
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
3
|
import { resolve, join } from 'node:path';
|
|
5
4
|
import os from 'node:os';
|
|
6
|
-
import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-
|
|
5
|
+
import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-BnFGIK0c.mjs';
|
|
7
6
|
import 'os';
|
|
8
7
|
import 'fs/promises';
|
|
9
8
|
import 'fs';
|
|
@@ -11,6 +10,7 @@ import 'path';
|
|
|
11
10
|
import 'url';
|
|
12
11
|
import 'child_process';
|
|
13
12
|
import 'crypto';
|
|
13
|
+
import 'node:crypto';
|
|
14
14
|
import '@agentclientprotocol/sdk';
|
|
15
15
|
import '@modelcontextprotocol/sdk/client/index.js';
|
|
16
16
|
import '@modelcontextprotocol/sdk/client/stdio.js';
|
|
@@ -1706,75 +1706,31 @@ async function sessionRalphStatus(sessionIdPartial, machineId) {
|
|
|
1706
1706
|
await server.disconnect();
|
|
1707
1707
|
}
|
|
1708
1708
|
}
|
|
1709
|
-
async function
|
|
1709
|
+
async function sessionInboxSend(sessionIdPartial, body, machineId, opts) {
|
|
1710
1710
|
const { server, machine } = await connectAndGetMachine(machineId);
|
|
1711
1711
|
try {
|
|
1712
1712
|
const sessions = await machine.listSessions();
|
|
1713
1713
|
const match = resolveSessionId(sessions, sessionIdPartial);
|
|
1714
1714
|
const fullId = match.sessionId;
|
|
1715
1715
|
const svc = await server.getService(`svamp-session-${fullId}`);
|
|
1716
|
-
const
|
|
1717
|
-
messageId: `msg-${randomUUID()}`,
|
|
1718
|
-
body: message,
|
|
1719
|
-
timestamp: Date.now(),
|
|
1720
|
-
read: false
|
|
1721
|
-
};
|
|
1722
|
-
await svc.sendInboxMessage(newMsg);
|
|
1723
|
-
console.log(`Message queued on session ${fullId.slice(0, 8)}`);
|
|
1724
|
-
} finally {
|
|
1725
|
-
await server.disconnect();
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
async function sessionQueueList(sessionIdPartial, machineId) {
|
|
1729
|
-
return sessionInboxList(sessionIdPartial, machineId);
|
|
1730
|
-
}
|
|
1731
|
-
async function sessionQueueClear(sessionIdPartial, machineId) {
|
|
1732
|
-
return sessionInboxClear(sessionIdPartial, machineId);
|
|
1733
|
-
}
|
|
1734
|
-
async function sessionInboxSend(targetSessionId, machineId, opts) {
|
|
1735
|
-
if (!opts?.subject || !opts?.body) {
|
|
1736
|
-
console.error('Usage: svamp session inbox send <target-session-id> --subject "..." --body "..."');
|
|
1737
|
-
process.exit(1);
|
|
1738
|
-
}
|
|
1739
|
-
const { server, machine } = await connectAndGetMachine(machineId);
|
|
1740
|
-
try {
|
|
1741
|
-
const sessions = await machine.listSessions();
|
|
1742
|
-
const match = resolveSessionId(sessions, targetSessionId);
|
|
1743
|
-
const fullTargetId = match.sessionId;
|
|
1744
|
-
const senderSessionId = process.env.SVAMP_SESSION_ID || "cli";
|
|
1745
|
-
const senderEmail = process.env.USER_EMAIL || process.env.USER || os.userInfo().username;
|
|
1746
|
-
const from = senderSessionId === "cli" ? `user:${senderEmail}` : `agent:${senderSessionId}`;
|
|
1716
|
+
const { randomUUID } = await import('node:crypto');
|
|
1747
1717
|
const message = {
|
|
1748
|
-
messageId:
|
|
1749
|
-
|
|
1750
|
-
fromSession: senderSessionId,
|
|
1751
|
-
to: fullTargetId,
|
|
1752
|
-
subject: opts.subject,
|
|
1753
|
-
body: opts.body,
|
|
1754
|
-
urgency: opts.urgency || "normal",
|
|
1755
|
-
replyTo: opts.replyTo,
|
|
1756
|
-
cc: opts.cc,
|
|
1718
|
+
messageId: randomUUID(),
|
|
1719
|
+
body,
|
|
1757
1720
|
timestamp: Date.now(),
|
|
1758
1721
|
read: false,
|
|
1759
|
-
|
|
1760
|
-
|
|
1722
|
+
from: `cli:${os.userInfo().username}`,
|
|
1723
|
+
to: fullId,
|
|
1724
|
+
subject: opts?.subject,
|
|
1725
|
+
urgency: opts?.urgency || "normal",
|
|
1726
|
+
replyTo: opts?.replyTo,
|
|
1727
|
+
threadId: opts?.threadId
|
|
1761
1728
|
};
|
|
1762
|
-
const svc = await server.getService(`svamp-session-${fullTargetId}`);
|
|
1763
1729
|
const result = await svc.sendInboxMessage(message);
|
|
1764
|
-
if (opts
|
|
1765
|
-
|
|
1766
|
-
await waitForBusyThenIdle(server, fullTargetId, timeoutMs);
|
|
1767
|
-
}
|
|
1768
|
-
if (opts.json) {
|
|
1769
|
-
console.log(formatJson({
|
|
1770
|
-
messageId: message.messageId,
|
|
1771
|
-
targetSessionId: fullTargetId,
|
|
1772
|
-
subject: opts.subject,
|
|
1773
|
-
urgency: message.urgency,
|
|
1774
|
-
delivered: result.delivered
|
|
1775
|
-
}));
|
|
1730
|
+
if (opts?.json) {
|
|
1731
|
+
console.log(formatJson({ sessionId: fullId, messageId: result.messageId, sent: true }));
|
|
1776
1732
|
} else {
|
|
1777
|
-
console.log(`Inbox message sent to session ${
|
|
1733
|
+
console.log(`Inbox message sent to session ${fullId.slice(0, 8)} (id: ${result.messageId.slice(0, 8)})`);
|
|
1778
1734
|
}
|
|
1779
1735
|
} finally {
|
|
1780
1736
|
await server.disconnect();
|
|
@@ -1787,114 +1743,97 @@ async function sessionInboxList(sessionIdPartial, machineId, opts) {
|
|
|
1787
1743
|
const match = resolveSessionId(sessions, sessionIdPartial);
|
|
1788
1744
|
const fullId = match.sessionId;
|
|
1789
1745
|
const svc = await server.getService(`svamp-session-${fullId}`);
|
|
1790
|
-
const result = await svc.getInbox({
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1746
|
+
const result = await svc.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.`);
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
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}
|
|
1801
1769
|
`);
|
|
1802
|
-
for (const msg of msgs) {
|
|
1803
|
-
const readMark = msg.read ? " " : "*";
|
|
1804
|
-
const date = new Date(msg.timestamp).toLocaleString();
|
|
1805
|
-
console.log(`${readMark} [${msg.messageId.slice(0, 12)}] ${date}`);
|
|
1806
|
-
console.log(` From: ${msg.from} | Subject: ${msg.subject} | Urgency: ${msg.urgency}`);
|
|
1807
|
-
if (msg.replyTo) console.log(` Reply-To: ${msg.replyTo.slice(0, 12)}`);
|
|
1808
|
-
console.log(` ${msg.body.slice(0, 120)}${msg.body.length > 120 ? "..." : ""}`);
|
|
1809
|
-
console.log();
|
|
1810
|
-
}
|
|
1811
1770
|
}
|
|
1812
1771
|
} finally {
|
|
1813
1772
|
await server.disconnect();
|
|
1814
1773
|
}
|
|
1815
1774
|
}
|
|
1816
|
-
async function sessionInboxRead(sessionIdPartial, messageId, machineId
|
|
1775
|
+
async function sessionInboxRead(sessionIdPartial, messageId, machineId) {
|
|
1817
1776
|
const { server, machine } = await connectAndGetMachine(machineId);
|
|
1818
1777
|
try {
|
|
1819
1778
|
const sessions = await machine.listSessions();
|
|
1820
1779
|
const match = resolveSessionId(sessions, sessionIdPartial);
|
|
1821
1780
|
const fullId = match.sessionId;
|
|
1822
1781
|
const svc = await server.getService(`svamp-session-${fullId}`);
|
|
1823
|
-
const result = await svc.getInbox(
|
|
1824
|
-
const msg =
|
|
1825
|
-
(m) => m.messageId === messageId || m.messageId.startsWith(messageId)
|
|
1826
|
-
);
|
|
1782
|
+
const result = await svc.getInbox();
|
|
1783
|
+
const msg = result.messages.find((m) => m.messageId === messageId || m.messageId.startsWith(messageId));
|
|
1827
1784
|
if (!msg) {
|
|
1828
1785
|
console.error(`Message ${messageId} not found in inbox.`);
|
|
1829
1786
|
process.exit(1);
|
|
1830
1787
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
console.log(formatJson(msg));
|
|
1834
|
-
} else {
|
|
1835
|
-
console.log(`Message-ID: ${msg.messageId}`);
|
|
1836
|
-
console.log(`From: ${msg.from}`);
|
|
1837
|
-
console.log(`From-Session: ${msg.fromSession}`);
|
|
1838
|
-
console.log(`Subject: ${msg.subject}`);
|
|
1839
|
-
console.log(`Urgency: ${msg.urgency}`);
|
|
1840
|
-
console.log(`Date: ${new Date(msg.timestamp).toLocaleString()}`);
|
|
1841
|
-
if (msg.replyTo) console.log(`Reply-To: ${msg.replyTo}`);
|
|
1842
|
-
if (msg.cc?.length) console.log(`CC: ${msg.cc.join(", ")}`);
|
|
1843
|
-
console.log(`---`);
|
|
1844
|
-
console.log(msg.body);
|
|
1788
|
+
if (!msg.read) {
|
|
1789
|
+
await svc.markInboxRead(msg.messageId);
|
|
1845
1790
|
}
|
|
1791
|
+
console.log(`From: ${msg.from || "(unknown)"}`);
|
|
1792
|
+
if (msg.subject) console.log(`Subject: ${msg.subject}`);
|
|
1793
|
+
console.log(`Date: ${new Date(msg.timestamp).toLocaleString()}`);
|
|
1794
|
+
if (msg.urgency === "urgent") console.log("Urgency: URGENT");
|
|
1795
|
+
if (msg.replyTo) console.log(`Reply-To: ${msg.replyTo.slice(0, 8)}`);
|
|
1796
|
+
if (msg.threadId) console.log(`Thread: ${msg.threadId.slice(0, 8)}`);
|
|
1797
|
+
console.log(`
|
|
1798
|
+
${msg.body}`);
|
|
1846
1799
|
} finally {
|
|
1847
1800
|
await server.disconnect();
|
|
1848
1801
|
}
|
|
1849
1802
|
}
|
|
1850
|
-
async function sessionInboxReply(sessionIdPartial, messageId,
|
|
1851
|
-
if (!opts?.body) {
|
|
1852
|
-
console.error('Usage: svamp session inbox reply <session-id> <message-id> --body "..."');
|
|
1853
|
-
process.exit(1);
|
|
1854
|
-
}
|
|
1803
|
+
async function sessionInboxReply(sessionIdPartial, messageId, body, machineId) {
|
|
1855
1804
|
const { server, machine } = await connectAndGetMachine(machineId);
|
|
1856
1805
|
try {
|
|
1857
1806
|
const sessions = await machine.listSessions();
|
|
1858
1807
|
const match = resolveSessionId(sessions, sessionIdPartial);
|
|
1859
1808
|
const fullId = match.sessionId;
|
|
1860
1809
|
const svc = await server.getService(`svamp-session-${fullId}`);
|
|
1861
|
-
const
|
|
1862
|
-
const
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
if (!originalMsg) {
|
|
1866
|
-
console.error(`Message ${messageId} not found in inbox. Cannot reply.`);
|
|
1810
|
+
const result = await svc.getInbox();
|
|
1811
|
+
const original = result.messages.find((m) => m.messageId === messageId || m.messageId.startsWith(messageId));
|
|
1812
|
+
if (!original) {
|
|
1813
|
+
console.error(`Message ${messageId} not found in inbox.`);
|
|
1867
1814
|
process.exit(1);
|
|
1868
1815
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1816
|
+
if (!original.fromSession) {
|
|
1817
|
+
console.error("Cannot reply: original message has no fromSession.");
|
|
1818
|
+
process.exit(1);
|
|
1819
|
+
}
|
|
1820
|
+
const targetSvc = await server.getService(`svamp-session-${original.fromSession}`);
|
|
1821
|
+
const { randomUUID } = await import('node:crypto');
|
|
1872
1822
|
const reply = {
|
|
1873
|
-
messageId:
|
|
1874
|
-
|
|
1875
|
-
fromSession: senderSessionId,
|
|
1876
|
-
to: originalMsg.fromSession,
|
|
1877
|
-
subject: `Re: ${originalMsg.subject}`,
|
|
1878
|
-
body: opts.body,
|
|
1879
|
-
urgency: opts.urgency || "normal",
|
|
1880
|
-
replyTo: originalMsg.messageId,
|
|
1881
|
-
threadId: originalMsg.threadId || originalMsg.messageId,
|
|
1823
|
+
messageId: randomUUID(),
|
|
1824
|
+
body,
|
|
1882
1825
|
timestamp: Date.now(),
|
|
1883
|
-
read: false
|
|
1826
|
+
read: false,
|
|
1827
|
+
from: `session:${fullId}`,
|
|
1828
|
+
fromSession: fullId,
|
|
1829
|
+
to: original.fromSession,
|
|
1830
|
+
subject: original.subject ? `Re: ${original.subject}` : void 0,
|
|
1831
|
+
urgency: "normal",
|
|
1832
|
+
replyTo: original.messageId,
|
|
1833
|
+
threadId: original.threadId || original.messageId
|
|
1884
1834
|
};
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1887
|
-
if (opts.json) {
|
|
1888
|
-
console.log(formatJson({
|
|
1889
|
-
messageId: reply.messageId,
|
|
1890
|
-
targetSessionId: originalMsg.fromSession,
|
|
1891
|
-
subject: reply.subject,
|
|
1892
|
-
urgency: reply.urgency,
|
|
1893
|
-
delivered: result.delivered
|
|
1894
|
-
}));
|
|
1895
|
-
} else {
|
|
1896
|
-
console.log(`Reply sent to session ${originalMsg.fromSession.slice(0, 8)}: "${reply.subject}"`);
|
|
1897
|
-
}
|
|
1835
|
+
const sendResult = await targetSvc.sendInboxMessage(reply);
|
|
1836
|
+
console.log(`Reply sent to session ${original.fromSession.slice(0, 8)} (id: ${sendResult.messageId.slice(0, 8)})`);
|
|
1898
1837
|
} finally {
|
|
1899
1838
|
await server.disconnect();
|
|
1900
1839
|
}
|
|
@@ -1906,14 +1845,11 @@ async function sessionInboxClear(sessionIdPartial, machineId, opts) {
|
|
|
1906
1845
|
const match = resolveSessionId(sessions, sessionIdPartial);
|
|
1907
1846
|
const fullId = match.sessionId;
|
|
1908
1847
|
const svc = await server.getService(`svamp-session-${fullId}`);
|
|
1909
|
-
const
|
|
1910
|
-
|
|
1911
|
-
if (opts?.json) ; else {
|
|
1912
|
-
console.log(`Cleared ${result.removed} message(s) from inbox on session ${fullId.slice(0, 8)}`);
|
|
1913
|
-
}
|
|
1848
|
+
const result = await svc.clearInbox({ all: opts?.all });
|
|
1849
|
+
console.log(`Cleared ${result.removed} message(s) from inbox on session ${fullId.slice(0, 8)}`);
|
|
1914
1850
|
} finally {
|
|
1915
1851
|
await server.disconnect();
|
|
1916
1852
|
}
|
|
1917
1853
|
}
|
|
1918
1854
|
|
|
1919
|
-
export { connectAndGetMachine, createWorktree, generateWorktreeName, machineExec, machineInfo, machineLs, machineShare, parseShareArg, renderMessage, resolveSessionId, sessionApprove, sessionAttach, sessionDeny, sessionInboxClear, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxSend, sessionInfo, sessionList, sessionMachines, sessionMessages,
|
|
1855
|
+
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 };
|
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-BnFGIK0c.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.
|
|
2
|
+
var version = "0.1.76";
|
|
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,7 +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.
|
|
31
|
+
"hypha-rpc": "0.21.34",
|
|
32
|
+
"node-pty": "^1.1.0",
|
|
32
33
|
ws: "^8.18.0",
|
|
33
34
|
yaml: "^2.8.2",
|
|
34
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-
|
|
5
|
+
import { c as connectToHypha, a as registerSessionService } from './run-BnFGIK0c.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|