svamp-cli 0.1.94 → 0.1.96

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.
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
3
  import os from 'node:os';
4
+ import { requireNotSandboxed } from './sandboxDetect-BWA3zQlA.mjs';
4
5
 
5
6
  const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
6
7
  function getConfigPath(sessionId) {
@@ -94,6 +95,7 @@ async function sessionSetLink(url, label) {
94
95
  console.log(`Session link set: "${resolvedLabel}" \u2192 ${url.trim()}`);
95
96
  }
96
97
  async function sessionNotify(message, level = "info") {
98
+ requireNotSandboxed("session notify");
97
99
  const sessionId = process.env.SVAMP_SESSION_ID;
98
100
  if (!sessionId) {
99
101
  console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
@@ -106,6 +108,7 @@ async function sessionNotify(message, level = "info") {
106
108
  console.log(`Notification sent [${level}]: ${message}`);
107
109
  }
108
110
  async function sessionBroadcast(action, args) {
111
+ requireNotSandboxed("session broadcast");
109
112
  const sessionId = process.env.SVAMP_SESSION_ID;
110
113
  if (!sessionId) {
111
114
  console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
@@ -145,10 +148,11 @@ async function sessionBroadcast(action, args) {
145
148
  console.log(`Broadcast sent: ${action}`);
146
149
  }
147
150
  async function connectToMachineService() {
148
- const { connectAndGetMachine } = await import('./commands-Be7nMudu.mjs');
151
+ const { connectAndGetMachine } = await import('./commands-CFQRfeSs.mjs');
149
152
  return connectAndGetMachine();
150
153
  }
151
154
  async function inboxSend(targetSessionId, opts) {
155
+ requireNotSandboxed("inbox send");
152
156
  const sessionId = process.env.SVAMP_SESSION_ID;
153
157
  if (!sessionId) {
154
158
  console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
@@ -161,7 +165,7 @@ async function inboxSend(targetSessionId, opts) {
161
165
  }
162
166
  const { server, machine } = await connectToMachineService();
163
167
  try {
164
- const { resolveSessionId } = await import('./commands-Be7nMudu.mjs');
168
+ const { resolveSessionId } = await import('./commands-CFQRfeSs.mjs');
165
169
  const sessions = await machine.listSessions();
166
170
  const match = resolveSessionId(sessions, targetSessionId);
167
171
  const fullTargetId = match.sessionId;
@@ -184,6 +188,7 @@ async function inboxSend(targetSessionId, opts) {
184
188
  }
185
189
  }
186
190
  async function inboxList(opts) {
191
+ requireNotSandboxed("inbox list");
187
192
  const sessionId = process.env.SVAMP_SESSION_ID;
188
193
  if (!sessionId) {
189
194
  console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
@@ -214,6 +219,7 @@ async function inboxList(opts) {
214
219
  }
215
220
  }
216
221
  async function inboxReply(messageId, body) {
222
+ requireNotSandboxed("inbox reply");
217
223
  const sessionId = process.env.SVAMP_SESSION_ID;
218
224
  if (!sessionId) {
219
225
  console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
@@ -252,6 +258,7 @@ async function inboxReply(messageId, body) {
252
258
  }
253
259
  }
254
260
  async function machineNotify(message, level = "info") {
261
+ requireNotSandboxed("machine notify");
255
262
  const machineId = process.env.SVAMP_MACHINE_ID;
256
263
  await connectAndEmit({
257
264
  type: "svamp:machine-notify",
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { b as stopDaemon, s as startDaemon, d as daemonStatus } from './run-B1p0kGdr.mjs';
1
+ import { b as stopDaemon, s as startDaemon, d as daemonStatus } from './run-DxZEyfKG.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -204,14 +204,34 @@ async function main() {
204
204
  } else if (subcommand === "session") {
205
205
  await handleSessionCommand();
206
206
  } else if (subcommand === "machine") {
207
+ const { isSandboxed } = await import('./sandboxDetect-BWA3zQlA.mjs');
208
+ if (isSandboxed()) {
209
+ console.error("svamp machine: Machine commands are not available in sandboxed sessions.");
210
+ process.exit(1);
211
+ }
207
212
  await handleMachineCommand();
208
213
  } else if (subcommand === "skills") {
214
+ const { isSandboxed } = await import('./sandboxDetect-BWA3zQlA.mjs');
215
+ if (isSandboxed()) {
216
+ console.error("svamp skills: Skills commands are not available in sandboxed sessions.");
217
+ process.exit(1);
218
+ }
209
219
  await handleSkillsCommand();
210
220
  } else if (subcommand === "service" || subcommand === "svc") {
221
+ const { isSandboxed } = await import('./sandboxDetect-BWA3zQlA.mjs');
222
+ if (isSandboxed()) {
223
+ console.error("svamp service: Service commands are not available in sandboxed sessions.");
224
+ process.exit(1);
225
+ }
211
226
  const { handleServiceCommand } = await import('./commands-BYbuedOK.mjs');
212
227
  await handleServiceCommand();
213
228
  } else if (subcommand === "process" || subcommand === "proc") {
214
- const { processCommand } = await import('./commands-DUkN2-4K.mjs');
229
+ const { isSandboxed: isSandboxedProc } = await import('./sandboxDetect-BWA3zQlA.mjs');
230
+ if (isSandboxedProc()) {
231
+ console.error("svamp process: Process commands are not available in sandboxed sessions.");
232
+ process.exit(1);
233
+ }
234
+ const { processCommand } = await import('./commands-D1hYL4Vi.mjs');
215
235
  let machineId;
216
236
  const processArgs = args.slice(1);
217
237
  const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
@@ -229,7 +249,7 @@ async function main() {
229
249
  } else if (!subcommand || subcommand === "start") {
230
250
  await handleInteractiveCommand();
231
251
  } else if (subcommand === "--version" || subcommand === "-v") {
232
- const pkg = await import('./package-6Vk_An57.mjs').catch(() => ({ default: { version: "unknown" } }));
252
+ const pkg = await import('./package-YOLex1RV.mjs').catch(() => ({ default: { version: "unknown" } }));
233
253
  console.log(`svamp version: ${pkg.default.version}`);
234
254
  } else {
235
255
  console.error(`Unknown command: ${subcommand}`);
@@ -238,7 +258,7 @@ async function main() {
238
258
  }
239
259
  }
240
260
  async function handleInteractiveCommand() {
241
- const { runInteractive } = await import('./run-HWBNSz_5.mjs');
261
+ const { runInteractive } = await import('./run-jvUqdZAp.mjs');
242
262
  const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
243
263
  let directory = process.cwd();
244
264
  let resumeSessionId;
@@ -283,7 +303,7 @@ async function handleAgentCommand() {
283
303
  return;
284
304
  }
285
305
  if (agentArgs[0] === "list") {
286
- const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-B1p0kGdr.mjs').then(function (n) { return n.i; });
306
+ const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-DxZEyfKG.mjs').then(function (n) { return n.i; });
287
307
  console.log("Known agents:");
288
308
  for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
289
309
  console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
@@ -295,7 +315,7 @@ async function handleAgentCommand() {
295
315
  console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
296
316
  return;
297
317
  }
298
- const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-B1p0kGdr.mjs').then(function (n) { return n.i; });
318
+ const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-DxZEyfKG.mjs').then(function (n) { return n.i; });
299
319
  let cwd = process.cwd();
300
320
  const filteredArgs = [];
301
321
  for (let i = 0; i < agentArgs.length; i++) {
@@ -319,12 +339,12 @@ async function handleAgentCommand() {
319
339
  console.log(`Starting ${config.agentName} agent in ${cwd}...`);
320
340
  let backend;
321
341
  if (KNOWN_MCP_AGENTS[config.agentName]) {
322
- const { CodexMcpBackend } = await import('./run-B1p0kGdr.mjs').then(function (n) { return n.j; });
342
+ const { CodexMcpBackend } = await import('./run-DxZEyfKG.mjs').then(function (n) { return n.j; });
323
343
  backend = new CodexMcpBackend({ cwd, log: logFn });
324
344
  } else {
325
- const { AcpBackend } = await import('./run-B1p0kGdr.mjs').then(function (n) { return n.h; });
326
- const { GeminiTransport } = await import('./run-B1p0kGdr.mjs').then(function (n) { return n.G; });
327
- const { DefaultTransport } = await import('./run-B1p0kGdr.mjs').then(function (n) { return n.D; });
345
+ const { AcpBackend } = await import('./run-DxZEyfKG.mjs').then(function (n) { return n.h; });
346
+ const { GeminiTransport } = await import('./run-DxZEyfKG.mjs').then(function (n) { return n.G; });
347
+ const { DefaultTransport } = await import('./run-DxZEyfKG.mjs').then(function (n) { return n.D; });
328
348
  const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
329
349
  backend = new AcpBackend({
330
350
  agentName: config.agentName,
@@ -442,7 +462,16 @@ async function handleSessionCommand() {
442
462
  printSessionHelp();
443
463
  return;
444
464
  }
445
- const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-Be7nMudu.mjs');
465
+ const SANDBOX_SAFE_SESSION_CMDS = /* @__PURE__ */ new Set(["set-title", "set-link"]);
466
+ if (!SANDBOX_SAFE_SESSION_CMDS.has(sessionSubcommand)) {
467
+ const { isSandboxed } = await import('./sandboxDetect-BWA3zQlA.mjs');
468
+ if (isSandboxed()) {
469
+ console.error(`svamp session ${sessionSubcommand}: This command is not available in sandboxed sessions.`);
470
+ console.error("Available commands: set-title, set-link");
471
+ process.exit(1);
472
+ }
473
+ }
474
+ const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-CFQRfeSs.mjs');
446
475
  const parseFlagStr = (flag, shortFlag) => {
447
476
  for (let i = 1; i < sessionArgs.length; i++) {
448
477
  if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
@@ -502,7 +531,7 @@ async function handleSessionCommand() {
502
531
  allowDomain.push(sessionArgs[++i]);
503
532
  }
504
533
  }
505
- const { parseShareArg } = await import('./commands-Be7nMudu.mjs');
534
+ const { parseShareArg } = await import('./commands-CFQRfeSs.mjs');
506
535
  const shareEntries = share.map((s) => parseShareArg(s));
507
536
  await sessionSpawn(agent, dir, targetMachineId, {
508
537
  message,
@@ -588,7 +617,7 @@ async function handleSessionCommand() {
588
617
  console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
589
618
  process.exit(1);
590
619
  }
591
- const { sessionApprove } = await import('./commands-Be7nMudu.mjs');
620
+ const { sessionApprove } = await import('./commands-CFQRfeSs.mjs');
592
621
  const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
593
622
  await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
594
623
  json: hasFlag("--json")
@@ -598,7 +627,7 @@ async function handleSessionCommand() {
598
627
  console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
599
628
  process.exit(1);
600
629
  }
601
- const { sessionDeny } = await import('./commands-Be7nMudu.mjs');
630
+ const { sessionDeny } = await import('./commands-CFQRfeSs.mjs');
602
631
  const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
603
632
  await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
604
633
  json: hasFlag("--json")
@@ -634,7 +663,7 @@ async function handleSessionCommand() {
634
663
  console.error("Usage: svamp session set-title <title>");
635
664
  process.exit(1);
636
665
  }
637
- const { sessionSetTitle } = await import('./agentCommands-Cz70sSe9.mjs');
666
+ const { sessionSetTitle } = await import('./agentCommands-CJNxmnV5.mjs');
638
667
  await sessionSetTitle(title);
639
668
  } else if (sessionSubcommand === "set-link") {
640
669
  const url = sessionArgs[1];
@@ -643,7 +672,7 @@ async function handleSessionCommand() {
643
672
  process.exit(1);
644
673
  }
645
674
  const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
646
- const { sessionSetLink } = await import('./agentCommands-Cz70sSe9.mjs');
675
+ const { sessionSetLink } = await import('./agentCommands-CJNxmnV5.mjs');
647
676
  await sessionSetLink(url, label);
648
677
  } else if (sessionSubcommand === "notify") {
649
678
  const message = sessionArgs[1];
@@ -652,7 +681,7 @@ async function handleSessionCommand() {
652
681
  process.exit(1);
653
682
  }
654
683
  const level = parseFlagStr("--level") || "info";
655
- const { sessionNotify } = await import('./agentCommands-Cz70sSe9.mjs');
684
+ const { sessionNotify } = await import('./agentCommands-CJNxmnV5.mjs');
656
685
  await sessionNotify(message, level);
657
686
  } else if (sessionSubcommand === "broadcast") {
658
687
  const action = sessionArgs[1];
@@ -660,7 +689,7 @@ async function handleSessionCommand() {
660
689
  console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
661
690
  process.exit(1);
662
691
  }
663
- const { sessionBroadcast } = await import('./agentCommands-Cz70sSe9.mjs');
692
+ const { sessionBroadcast } = await import('./agentCommands-CJNxmnV5.mjs');
664
693
  await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
665
694
  } else if (sessionSubcommand === "inbox") {
666
695
  const inboxSubcmd = sessionArgs[1];
@@ -671,7 +700,7 @@ async function handleSessionCommand() {
671
700
  process.exit(1);
672
701
  }
673
702
  if (agentSessionId) {
674
- const { inboxSend } = await import('./agentCommands-Cz70sSe9.mjs');
703
+ const { inboxSend } = await import('./agentCommands-CJNxmnV5.mjs');
675
704
  await inboxSend(sessionArgs[2], {
676
705
  body: sessionArgs[3],
677
706
  subject: parseFlagStr("--subject"),
@@ -686,7 +715,7 @@ async function handleSessionCommand() {
686
715
  }
687
716
  } else if (inboxSubcmd === "list" || inboxSubcmd === "ls") {
688
717
  if (agentSessionId && !sessionArgs[2]) {
689
- const { inboxList } = await import('./agentCommands-Cz70sSe9.mjs');
718
+ const { inboxList } = await import('./agentCommands-CJNxmnV5.mjs');
690
719
  await inboxList({
691
720
  unread: hasFlag("--unread"),
692
721
  limit: parseFlagInt("--limit"),
@@ -708,7 +737,7 @@ async function handleSessionCommand() {
708
737
  process.exit(1);
709
738
  }
710
739
  if (agentSessionId && !sessionArgs[3]) {
711
- const { inboxList } = await import('./agentCommands-Cz70sSe9.mjs');
740
+ const { inboxList } = await import('./agentCommands-CJNxmnV5.mjs');
712
741
  await sessionInboxRead(agentSessionId, sessionArgs[2], targetMachineId);
713
742
  } else if (sessionArgs[3]) {
714
743
  await sessionInboxRead(sessionArgs[2], sessionArgs[3], targetMachineId);
@@ -718,7 +747,7 @@ async function handleSessionCommand() {
718
747
  }
719
748
  } else if (inboxSubcmd === "reply") {
720
749
  if (agentSessionId && sessionArgs[2] && sessionArgs[3] && !sessionArgs[4]) {
721
- const { inboxReply } = await import('./agentCommands-Cz70sSe9.mjs');
750
+ const { inboxReply } = await import('./agentCommands-CJNxmnV5.mjs');
722
751
  await inboxReply(sessionArgs[2], sessionArgs[3]);
723
752
  } else if (sessionArgs[2] && sessionArgs[3] && sessionArgs[4]) {
724
753
  await sessionInboxReply(sessionArgs[2], sessionArgs[3], sessionArgs[4], targetMachineId);
@@ -754,7 +783,7 @@ async function handleMachineCommand() {
754
783
  return;
755
784
  }
756
785
  if (machineSubcommand === "share") {
757
- const { machineShare } = await import('./commands-Be7nMudu.mjs');
786
+ const { machineShare } = await import('./commands-CFQRfeSs.mjs');
758
787
  let machineId;
759
788
  const shareArgs = [];
760
789
  for (let i = 1; i < machineArgs.length; i++) {
@@ -784,7 +813,7 @@ async function handleMachineCommand() {
784
813
  }
785
814
  await machineShare(machineId, { add, remove, list, configPath, showConfig });
786
815
  } else if (machineSubcommand === "exec") {
787
- const { machineExec } = await import('./commands-Be7nMudu.mjs');
816
+ const { machineExec } = await import('./commands-CFQRfeSs.mjs');
788
817
  let machineId;
789
818
  let cwd;
790
819
  const cmdParts = [];
@@ -804,7 +833,7 @@ async function handleMachineCommand() {
804
833
  }
805
834
  await machineExec(machineId, command, cwd);
806
835
  } else if (machineSubcommand === "info") {
807
- const { machineInfo } = await import('./commands-Be7nMudu.mjs');
836
+ const { machineInfo } = await import('./commands-CFQRfeSs.mjs');
808
837
  let machineId;
809
838
  for (let i = 1; i < machineArgs.length; i++) {
810
839
  if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
@@ -824,10 +853,10 @@ async function handleMachineCommand() {
824
853
  level = machineArgs[++i];
825
854
  }
826
855
  }
827
- const { machineNotify } = await import('./agentCommands-Cz70sSe9.mjs');
856
+ const { machineNotify } = await import('./agentCommands-CJNxmnV5.mjs');
828
857
  await machineNotify(message, level);
829
858
  } else if (machineSubcommand === "ls") {
830
- const { machineLs } = await import('./commands-Be7nMudu.mjs');
859
+ const { machineLs } = await import('./commands-CFQRfeSs.mjs');
831
860
  let machineId;
832
861
  let showHidden = false;
833
862
  let path;
@@ -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-B1p0kGdr.mjs';
5
+ import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-DxZEyfKG.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-Be7nMudu.mjs';
3
+ import { connectAndGetMachine } from './commands-CFQRfeSs.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-B1p0kGdr.mjs';
8
+ import './run-DxZEyfKG.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
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-B1p0kGdr.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-DxZEyfKG.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.94";
2
+ var version = "0.1.96";
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 && npx tsx test/test-inbox.mjs && npx tsx test/test-session-rpc-dispatch.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 && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.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",
@@ -1,5 +1,5 @@
1
1
  import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import os__default from 'os';
2
- import fs, { mkdir as mkdir$1, readdir, readFile, writeFile as writeFile$1, rename, unlink } from 'fs/promises';
2
+ import fs, { mkdir as mkdir$1, readdir as readdir$1, readFile, writeFile as writeFile$1, rename, unlink } from 'fs/promises';
3
3
  import { readFileSync as readFileSync$1, mkdirSync, writeFileSync, renameSync, existsSync as existsSync$1, copyFileSync, unlinkSync, watch, rmdirSync } from 'fs';
4
4
  import path__default, { join, dirname, resolve, basename } from 'path';
5
5
  import { fileURLToPath } from 'url';
@@ -15,7 +15,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
15
15
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
16
16
  import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
17
17
  import { z } from 'zod';
18
- import { mkdir, rm, chmod, access, mkdtemp, copyFile, writeFile } from 'node:fs/promises';
18
+ import { mkdir, rm, chmod, access, mkdtemp, copyFile, writeFile, stat, readdir } from 'node:fs/promises';
19
19
  import { promisify } from 'node:util';
20
20
 
21
21
  let connectToServerFn = null;
@@ -381,12 +381,42 @@ async function registerMachineService(server, machineId, metadata, daemonState,
381
381
  config: { visibility: "unlisted", require_context: true },
382
382
  // Machine info
383
383
  getMachineInfo: async (context) => {
384
- authorizeRequest(context, currentMetadata.sharing, "view");
384
+ let hasMachineAccess = false;
385
+ try {
386
+ authorizeRequest(context, currentMetadata.sharing, "view");
387
+ hasMachineAccess = true;
388
+ } catch {
389
+ }
390
+ if (hasMachineAccess) {
391
+ return {
392
+ machineId,
393
+ metadata: currentMetadata,
394
+ metadataVersion,
395
+ daemonState: currentDaemonState,
396
+ daemonStateVersion
397
+ };
398
+ }
399
+ const sessionIds = handlers.getSessionIds?.() || [];
400
+ let hasSessionAccess = false;
401
+ for (const sid of sessionIds) {
402
+ const rpc = handlers.getSessionRPCHandlers?.(sid);
403
+ if (!rpc) continue;
404
+ try {
405
+ await rpc.getMetadata(context);
406
+ hasSessionAccess = true;
407
+ break;
408
+ } catch {
409
+ }
410
+ }
411
+ if (!hasSessionAccess) {
412
+ throw new Error("Access denied: no machine or session-level access");
413
+ }
414
+ const { sharing: _s, securityContextConfig: _sc, ...safeMetadata } = currentMetadata;
385
415
  return {
386
416
  machineId,
387
- metadata: currentMetadata,
417
+ metadata: safeMetadata,
388
418
  metadataVersion,
389
- daemonState: currentDaemonState,
419
+ daemonState: { status: currentDaemonState.status },
390
420
  daemonStateVersion
391
421
  };
392
422
  },
@@ -400,16 +430,42 @@ async function registerMachineService(server, machineId, metadata, daemonState,
400
430
  };
401
431
  },
402
432
  // List active sessions on this machine
433
+ // If machine sharing grants access, return all. Otherwise return only
434
+ // sessions whose own sharing config grants access to this user.
403
435
  listSessions: async (context) => {
404
- authorizeRequest(context, currentMetadata.sharing, "view");
405
- return handlers.getTrackedSessions();
436
+ let hasMachineAccess = false;
437
+ try {
438
+ authorizeRequest(context, currentMetadata.sharing, "view");
439
+ hasMachineAccess = true;
440
+ } catch {
441
+ }
442
+ const tracked = handlers.getTrackedSessions();
443
+ if (hasMachineAccess) return tracked;
444
+ const result = [];
445
+ for (const t of tracked) {
446
+ const rpc = handlers.getSessionRPCHandlers?.(t.sessionId);
447
+ if (!rpc) continue;
448
+ try {
449
+ const meta = await rpc.getMetadata(context);
450
+ result.push(t);
451
+ } catch {
452
+ }
453
+ }
454
+ return result;
406
455
  },
407
456
  /**
408
457
  * Get summary info for all sessions (metadata, agent state, activity).
409
458
  * Replaces the need to discover and query N individual session services.
459
+ * For session-shared users (no machine-level access), only returns
460
+ * sessions whose sharing config grants them access.
410
461
  */
411
462
  getSessions: async (context) => {
412
- authorizeRequest(context, currentMetadata.sharing, "view");
463
+ let hasMachineAccess = false;
464
+ try {
465
+ authorizeRequest(context, currentMetadata.sharing, "view");
466
+ hasMachineAccess = true;
467
+ } catch {
468
+ }
413
469
  const sessionIds = handlers.getSessionIds?.() || [];
414
470
  const sessions = [];
415
471
  for (const sid of sessionIds) {
@@ -446,11 +502,21 @@ async function registerMachineService(server, machineId, metadata, daemonState,
446
502
  * eliminating the need for per-session Hypha service registration.
447
503
  */
448
504
  sessionRPC: async (sessionId, method, kwargs, context) => {
449
- authorizeRequest(context, currentMetadata.sharing, "view");
450
505
  const rpc = handlers.getSessionRPCHandlers?.(sessionId);
451
506
  if (!rpc) {
452
507
  throw new Error(`Session ${sessionId} not found on this machine`);
453
508
  }
509
+ try {
510
+ authorizeRequest(context, currentMetadata.sharing, "view");
511
+ } catch (machineErr) {
512
+ try {
513
+ const meta = await rpc.getMetadata(context);
514
+ const sessionSharing = meta?.metadata?.sharing;
515
+ authorizeRequest(context, sessionSharing, "view");
516
+ } catch {
517
+ throw machineErr;
518
+ }
519
+ }
454
520
  const handler = rpc[method];
455
521
  if (typeof handler !== "function") {
456
522
  throw new Error(`Unknown session method: ${method}`);
@@ -466,11 +532,21 @@ async function registerMachineService(server, machineId, metadata, daemonState,
466
532
  * Delegates to the session store's registerListener.
467
533
  */
468
534
  registerSessionListener: async (sessionId, callback, context) => {
469
- authorizeRequest(context, currentMetadata.sharing, "view");
470
535
  const rpc = handlers.getSessionRPCHandlers?.(sessionId);
471
536
  if (!rpc) {
472
537
  throw new Error(`Session ${sessionId} not found on this machine`);
473
538
  }
539
+ try {
540
+ authorizeRequest(context, currentMetadata.sharing, "view");
541
+ } catch (machineErr) {
542
+ try {
543
+ const meta = await rpc.getMetadata(context);
544
+ const sessionSharing = meta?.metadata?.sharing;
545
+ authorizeRequest(context, sessionSharing, "view");
546
+ } catch {
547
+ throw machineErr;
548
+ }
549
+ }
474
550
  return await rpc.registerListener(callback, context);
475
551
  },
476
552
  // Spawn a new session
@@ -4343,6 +4419,12 @@ async function stageCredentialsForSharing(sessionId) {
4343
4419
  } catch {
4344
4420
  }
4345
4421
  }
4422
+ const realSkillsDir = join$1(realClaudeDir, "skills");
4423
+ const stagedSkillsDir = join$1(stagedClaudeDir, "skills");
4424
+ try {
4425
+ await copyDirRecursive(realSkillsDir, stagedSkillsDir);
4426
+ } catch {
4427
+ }
4346
4428
  return {
4347
4429
  homePath: tmpHome,
4348
4430
  env: { HOME: tmpHome },
@@ -4359,6 +4441,21 @@ function sanitizeEnvForSharing(env) {
4359
4441
  }
4360
4442
  return sanitized;
4361
4443
  }
4444
+ async function copyDirRecursive(src, dest) {
4445
+ const srcStat = await stat(src);
4446
+ if (!srcStat.isDirectory()) return;
4447
+ await mkdir(dest, { recursive: true });
4448
+ const entries = await readdir(src, { withFileTypes: true });
4449
+ for (const entry of entries) {
4450
+ const srcPath = join$1(src, entry.name);
4451
+ const destPath = join$1(dest, entry.name);
4452
+ if (entry.isDirectory()) {
4453
+ await copyDirRecursive(srcPath, destPath);
4454
+ } else if (entry.isFile()) {
4455
+ await copyFile(srcPath, destPath);
4456
+ }
4457
+ }
4458
+ }
4362
4459
 
4363
4460
  const DEFAULT_PROBE_INTERVAL_S = 10;
4364
4461
  const DEFAULT_PROBE_TIMEOUT_S = 5;
@@ -4554,7 +4651,7 @@ class ProcessSupervisor {
4554
4651
  async loadAll() {
4555
4652
  let files;
4556
4653
  try {
4557
- files = await readdir(this.persistDir);
4654
+ files = await readdir$1(this.persistDir);
4558
4655
  } catch {
4559
4656
  return;
4560
4657
  }
@@ -6837,6 +6934,7 @@ The automated loop has finished. Review the progress above and let me know if yo
6837
6934
  logger.log(`[Session ${sessionId}] Inbox message received (urgency: ${message.urgency || "normal"}, from: ${message.from || "unknown"})`);
6838
6935
  const formatted = formatInboxMessageXml(message);
6839
6936
  sessionService.pushMessage(formatted, "user");
6937
+ sessionService.markInboxRead(message.messageId);
6840
6938
  if (message.urgency === "urgent") {
6841
6939
  logger.log(`[Session ${sessionId}] Delivering urgent inbox message to agent`);
6842
6940
  if (!claudeProcess || claudeProcess.exitCode !== null) {
@@ -7262,6 +7360,7 @@ The automated loop has finished. Review the progress above and let me know if yo
7262
7360
  logger.log(`[${agentName} Session ${sessionId}] Inbox message received (urgency: ${message.urgency || "normal"}, from: ${message.from || "unknown"})`);
7263
7361
  const formatted = formatInboxMessageXml(message);
7264
7362
  sessionService.pushMessage(formatted, "user");
7363
+ sessionService.markInboxRead(message.messageId);
7265
7364
  if (message.urgency === "urgent" && acpBackendReady) {
7266
7365
  logger.log(`[${agentName} Session ${sessionId}] Delivering urgent inbox message to agent`);
7267
7366
  sessionMetadata = { ...sessionMetadata, lifecycleState: "running" };
@@ -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-B1p0kGdr.mjs';
5
+ import { c as connectToHypha, a as registerSessionService } from './run-DxZEyfKG.mjs';
6
6
  import { createServer } from 'node:http';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { createInterface } from 'node:readline';
@@ -0,0 +1,27 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import os from 'node:os';
4
+
5
+ function isSandboxed() {
6
+ if (!process.env.SVAMP_SESSION_ID) return false;
7
+ if (process.env.HYPHA_TOKEN) return false;
8
+ const svampHome = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
9
+ const envFile = join(svampHome, ".env");
10
+ try {
11
+ if (existsSync(envFile)) {
12
+ const content = readFileSync(envFile, "utf-8");
13
+ if (content.includes("HYPHA_TOKEN=")) return false;
14
+ }
15
+ } catch {
16
+ }
17
+ return true;
18
+ }
19
+ const SANDBOX_BLOCKED_MSG = "This command is not available in sandboxed sessions. Available commands: set-title, set-link";
20
+ function requireNotSandboxed(commandName) {
21
+ if (isSandboxed()) {
22
+ console.error(`${commandName}: ${SANDBOX_BLOCKED_MSG}`);
23
+ process.exit(1);
24
+ }
25
+ }
26
+
27
+ export { SANDBOX_BLOCKED_MSG, isSandboxed, requireNotSandboxed };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.1.94",
3
+ "version": "0.1.96",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -20,7 +20,7 @@
20
20
  "scripts": {
21
21
  "build": "rm -rf dist && tsc --noEmit && pkgroll",
22
22
  "typecheck": "tsc --noEmit",
23
- "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 && npx tsx test/test-session-rpc-dispatch.mjs",
23
+ "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 && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs",
24
24
  "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
25
25
  "dev": "tsx src/cli.ts",
26
26
  "dev:daemon": "tsx src/cli.ts daemon start-sync",