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-
|
|
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-
|
|
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;
|
|
@@ -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
|
-
|
|
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
|
-
|
|
296
|
+
process.stderr.write("Claude Code CLI not found.\n");
|
|
297
297
|
return "exit";
|
|
298
298
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
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
|
-
"--
|
|
405
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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 };
|