svamp-cli 0.1.34 → 0.1.36

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/cli.mjs CHANGED
@@ -91,7 +91,7 @@ async function main() {
91
91
  } else if (!subcommand || subcommand === "start") {
92
92
  await handleInteractiveCommand();
93
93
  } else if (subcommand === "--version" || subcommand === "-v") {
94
- const pkg = await import('./package-D2n0SOTg.mjs').catch(() => ({ default: { version: "unknown" } }));
94
+ const pkg = await import('./package-DqJE7fYO.mjs').catch(() => ({ default: { version: "unknown" } }));
95
95
  console.log(`svamp version: ${pkg.default.version}`);
96
96
  } else {
97
97
  console.error(`Unknown command: ${subcommand}`);
@@ -100,7 +100,7 @@ async function main() {
100
100
  }
101
101
  }
102
102
  async function handleInteractiveCommand() {
103
- const { runInteractive } = await import('./run-COUdcjR7.mjs');
103
+ const { runInteractive } = await import('./run-CZxKcatI.mjs');
104
104
  const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
105
105
  let directory = process.cwd();
106
106
  let resumeSessionId;
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.1.34";
2
+ var version = "0.1.36";
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";
@@ -233,7 +233,7 @@ async function runLocalMode(opts) {
233
233
  log(`[local] Spawning: claude ${args.join(" ")}`);
234
234
  const claudeBin = findClaudeBinary();
235
235
  if (!claudeBin) {
236
- console.error("Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code");
236
+ process.stderr.write("Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code\n");
237
237
  return { type: "exit", code: 1 };
238
238
  }
239
239
  process.stdin.pause();
@@ -293,14 +293,15 @@ async function runRemoteMode(opts) {
293
293
  } catch {
294
294
  }
295
295
  if (!claudeBin || !existsSync(claudeBin)) {
296
- console.error("Claude Code CLI not found.");
296
+ process.stderr.write("Claude Code CLI not found.\n");
297
297
  return "exit";
298
298
  }
299
- console.log("\n\x1B[90m" + "\u2550".repeat(50) + "\x1B[0m");
300
- console.log("\x1B[36m Remote mode\x1B[0m \u2014 processing message from web app");
301
- console.log("\x1B[90m Press Space Space to switch to local mode\x1B[0m");
302
- console.log("\x1B[90m Press Ctrl-C Ctrl-C to exit\x1B[0m");
303
- console.log("\x1B[90m" + "\u2550".repeat(50) + "\x1B[0m\n");
299
+ const print = (s) => process.stderr.write(s + "\n");
300
+ print("\n\x1B[90m" + "\u2550".repeat(50) + "\x1B[0m");
301
+ print("\x1B[36m Remote mode\x1B[0m \u2014 processing message from web app");
302
+ print("\x1B[90m Press Space Space to switch to local mode\x1B[0m");
303
+ print("\x1B[90m Press Ctrl-C Ctrl-C to exit\x1B[0m");
304
+ print("\x1B[90m" + "\u2550".repeat(50) + "\x1B[0m\n");
304
305
  let exitReason = null;
305
306
  let lastSpace = 0;
306
307
  let lastCtrlC = 0;
@@ -385,7 +386,7 @@ async function runRemoteMode(opts) {
385
386
  continue;
386
387
  }
387
388
  if (!exitReason && !abortController.signal.aborted) {
388
- console.log("\n\x1B[90m Agent idle. Waiting for next message...\x1B[0m");
389
+ process.stderr.write("\n\x1B[90m Agent idle. Waiting for next message...\x1B[0m\n");
389
390
  }
390
391
  }
391
392
  } finally {
@@ -401,10 +402,8 @@ async function runClaudeTurn(opts) {
401
402
  "--output-format",
402
403
  "stream-json",
403
404
  "--verbose",
404
- "--input-format",
405
- "stream-json",
406
- "--permission-prompt-tool",
407
- "stdio"
405
+ "--print",
406
+ opts.message
408
407
  ];
409
408
  if (opts.hookSettingsPath) {
410
409
  args.push("--settings", opts.hookSettingsPath);
@@ -412,24 +411,19 @@ async function runClaudeTurn(opts) {
412
411
  if (opts.sessionId) {
413
412
  args.push("--resume", opts.sessionId);
414
413
  }
415
- const claudeMode = mapPermissionMode(opts.permissionMode);
416
- if (claudeMode) {
417
- args.push("--permission-mode", claudeMode);
418
- }
414
+ const claudeMode = mapPermissionMode(opts.permissionMode) || "bypassPermissions";
415
+ args.push("--permission-mode", claudeMode);
419
416
  if (opts.claudeArgs) {
420
417
  args.push(...opts.claudeArgs);
421
418
  }
422
419
  opts.log(`[remote] Spawning: claude ${args.join(" ")}`);
420
+ const spawnEnv = { ...process.env, CLAUDE_CODE_ENTRYPOINT: "sdk-ts" };
421
+ delete spawnEnv.CLAUDECODE;
423
422
  const child = spawn(opts.claudeBin, args, {
424
423
  stdio: ["pipe", "pipe", "pipe"],
425
424
  cwd: opts.cwd,
426
- env: process.env
427
- });
428
- const userMsg = JSON.stringify({
429
- type: "user",
430
- message: { role: "user", content: opts.message }
425
+ env: spawnEnv
431
426
  });
432
- child.stdin.write(userMsg + "\n");
433
427
  child.stdin.end();
434
428
  opts.onThinkingChange(true);
435
429
  let currentText = "";
@@ -455,10 +449,6 @@ async function runClaudeTurn(opts) {
455
449
  } catch {
456
450
  return;
457
451
  }
458
- if (msg.type === "control_request") {
459
- handleControlRequest(msg, child, opts.log);
460
- return;
461
- }
462
452
  handleSDKMessage(msg, opts, (text) => {
463
453
  process.stdout.write(text);
464
454
  currentText += text;
@@ -538,28 +528,6 @@ function handleSDKMessage(msg, opts, write) {
538
528
  opts.log(`[remote] Unknown msg type: ${msg.type}`);
539
529
  }
540
530
  }
541
- function handleControlRequest(msg, child, log) {
542
- const request = msg.request;
543
- const requestId = msg.request_id;
544
- if (request?.subtype === "can_use_tool") {
545
- log(`[remote] Auto-approving tool: ${request.tool_name}`);
546
- const response = JSON.stringify({
547
- type: "control_response",
548
- response: {
549
- subtype: "success",
550
- request_id: requestId,
551
- response: {
552
- behavior: "allow",
553
- updatedInput: request.input
554
- }
555
- }
556
- });
557
- try {
558
- child.stdin.write(response + "\n");
559
- } catch {
560
- }
561
- }
562
- }
563
531
  function mapPermissionMode(mode) {
564
532
  const map = {
565
533
  "default": "default",
@@ -667,17 +635,11 @@ async function runInteractive(options) {
667
635
  const token = process.env.HYPHA_TOKEN;
668
636
  if (serverUrl && token) {
669
637
  try {
670
- const origLog = console.log;
671
- const origWarn = console.warn;
672
- console.log = () => {
673
- };
674
- console.warn = () => {
675
- };
638
+ suppressHyphaLogs();
676
639
  server = await connectToHypha({ serverUrl, token, name: "svamp-interactive" });
677
- console.log = origLog;
678
- console.warn = origWarn;
679
640
  log("Connected to Hypha");
680
641
  } catch (err) {
642
+ restoreConsoleLogs();
681
643
  console.error(`\x1B[33mNote:\x1B[0m Could not connect to Hypha (${err.message}). Running in offline mode.`);
682
644
  }
683
645
  } else {
@@ -720,6 +682,7 @@ async function runInteractive(options) {
720
682
  lifecycleState: "running",
721
683
  flavor: "claude"
722
684
  };
685
+ let currentMode = "local";
723
686
  if (server) {
724
687
  const callbacks = {
725
688
  onUserMessage: (content, _meta) => {
@@ -744,6 +707,46 @@ async function runInteractive(options) {
744
707
  log("[hypha] Kill requested");
745
708
  cleanup();
746
709
  process.exit(0);
710
+ },
711
+ // File system operations — run locally since we have direct access
712
+ onBash: async (command, execCwd) => {
713
+ const { execSync } = await import('child_process');
714
+ const result = execSync(command, {
715
+ cwd: execCwd || cwd,
716
+ encoding: "utf-8",
717
+ timeout: 3e4,
718
+ maxBuffer: 1024 * 1024
719
+ });
720
+ return { output: result };
721
+ },
722
+ onReadFile: async (path) => {
723
+ const { readFileSync: readFileSync2 } = await import('fs');
724
+ return readFileSync2(path, "utf-8");
725
+ },
726
+ onWriteFile: async (path, content) => {
727
+ const { writeFileSync } = await import('fs');
728
+ writeFileSync(path, content, "utf-8");
729
+ },
730
+ onListDirectory: async (path) => {
731
+ const { readdirSync, statSync } = await import('fs');
732
+ const { join: joinPath } = await import('path');
733
+ return readdirSync(path).map((name) => {
734
+ try {
735
+ const st = statSync(joinPath(path, name));
736
+ return { name, type: st.isDirectory() ? "directory" : "file", size: st.size };
737
+ } catch {
738
+ return { name, type: "unknown", size: 0 };
739
+ }
740
+ });
741
+ },
742
+ onRipgrep: async (rgArgs, execCwd) => {
743
+ const { execSync } = await import('child_process');
744
+ return execSync(`rg ${rgArgs}`, {
745
+ cwd: execCwd || cwd,
746
+ encoding: "utf-8",
747
+ timeout: 15e3,
748
+ maxBuffer: 1024 * 1024
749
+ });
747
750
  }
748
751
  };
749
752
  try {
@@ -756,6 +759,7 @@ async function runInteractive(options) {
756
759
  );
757
760
  log(`Session service registered: svamp-session-${sessionId}`);
758
761
  } catch (err) {
762
+ restoreConsoleLogs();
759
763
  console.error(`\x1B[33mNote:\x1B[0m Could not register session on Hypha (${err.message}).`);
760
764
  }
761
765
  }
@@ -799,6 +803,7 @@ async function runInteractive(options) {
799
803
  messageWaiter.resolve(null);
800
804
  messageWaiter = null;
801
805
  }
806
+ restoreConsoleLogs();
802
807
  };
803
808
  let exiting = false;
804
809
  const handleExit = async () => {
@@ -813,12 +818,14 @@ async function runInteractive(options) {
813
818
  if (options.resumeSessionId) ; else if (options.continueSession) {
814
819
  claudeArgs.push("--continue");
815
820
  }
821
+ restoreConsoleLogs();
816
822
  console.log(`\x1B[36mSvamp interactive mode\x1B[0m`);
817
823
  if (server && sessionService) {
818
824
  console.log(`\x1B[90mSession synced to Hypha \u2014 visible in the web app\x1B[0m`);
819
825
  console.log(`\x1B[90mSession ID: ${sessionId.slice(0, 8)}\x1B[0m`);
820
826
  }
821
827
  console.log("");
828
+ if (server) suppressHyphaLogs();
822
829
  try {
823
830
  const exitCode = await loop({
824
831
  cwd,
@@ -829,7 +836,11 @@ async function runInteractive(options) {
829
836
  log,
830
837
  onModeChange: (mode) => {
831
838
  log(`Mode changed: ${mode}`);
839
+ currentMode = mode;
832
840
  if (sessionService) {
841
+ sessionService.updateAgentState({
842
+ controlledByUser: mode === "local"
843
+ });
833
844
  sessionService.updateMetadata({ ...metadata, lifecycleState: "running" });
834
845
  sessionService.sendKeepAlive(false, mode);
835
846
  }
@@ -893,5 +904,68 @@ function readMachineId() {
893
904
  return null;
894
905
  }
895
906
  }
907
+ let _origLog = null;
908
+ let _origWarn = null;
909
+ let _origInfo = null;
910
+ let _origStdoutWrite = null;
911
+ const HYPHA_LOG_PATTERNS = [
912
+ "WebSocket connection",
913
+ "Connection established",
914
+ "reporting services",
915
+ "Successfully registered",
916
+ "Subscribing to",
917
+ "Successfully subscribed",
918
+ "Cleaning up all sessions",
919
+ "Cleaned up session",
920
+ "ClTaned up",
921
+ "Cleaned up ",
922
+ "Handling disconnection",
923
+ "Client ws-user-",
924
+ "WebSocket connection disconnected",
925
+ "disconnected, cleaning up",
926
+ "HYPHA SESSION",
927
+ "Listener registered",
928
+ "local RPC disconnection"
929
+ ];
930
+ function isHyphaLogLine(text) {
931
+ return HYPHA_LOG_PATTERNS.some((p) => text.includes(p));
932
+ }
933
+ function suppressHyphaLogs() {
934
+ if (_origLog) return;
935
+ _origLog = console.log;
936
+ _origWarn = console.warn;
937
+ _origInfo = console.info;
938
+ _origStdoutWrite = process.stdout.write.bind(process.stdout);
939
+ console.log = (...args) => {
940
+ if (DEBUG) _origLog.call(console, "[hypha-log]", ...args);
941
+ };
942
+ console.warn = (...args) => {
943
+ if (DEBUG) _origWarn.call(console, "[hypha-warn]", ...args);
944
+ };
945
+ console.info = (...args) => {
946
+ if (DEBUG) _origInfo.call(console, "[hypha-info]", ...args);
947
+ };
948
+ process.stdout.write = function(chunk, ...rest) {
949
+ const text = typeof chunk === "string" ? chunk : chunk?.toString?.() || "";
950
+ if (isHyphaLogLine(text)) {
951
+ if (DEBUG) _origStdoutWrite(`[hypha-stdout] ${text}`);
952
+ return true;
953
+ }
954
+ return _origStdoutWrite.call(process.stdout, chunk, ...rest);
955
+ };
956
+ }
957
+ function restoreConsoleLogs() {
958
+ if (!_origLog) return;
959
+ console.log = _origLog;
960
+ console.warn = _origWarn;
961
+ console.info = _origInfo;
962
+ if (_origStdoutWrite) {
963
+ process.stdout.write = _origStdoutWrite;
964
+ }
965
+ _origLog = null;
966
+ _origWarn = null;
967
+ _origInfo = null;
968
+ _origStdoutWrite = null;
969
+ }
896
970
 
897
971
  export { runInteractive };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",