codeam-cli 1.3.7 → 1.3.9
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/index.js +253 -114
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/commands/start.ts
|
|
27
|
-
var
|
|
27
|
+
var fs4 = __toESM(require("fs"));
|
|
28
28
|
var os4 = __toESM(require("os"));
|
|
29
|
-
var
|
|
29
|
+
var path4 = __toESM(require("path"));
|
|
30
30
|
var import_crypto = require("crypto");
|
|
31
31
|
var import_picocolors2 = __toESM(require("picocolors"));
|
|
32
32
|
|
|
@@ -114,7 +114,7 @@ var import_picocolors = __toESM(require("picocolors"));
|
|
|
114
114
|
// package.json
|
|
115
115
|
var package_default = {
|
|
116
116
|
name: "codeam-cli",
|
|
117
|
-
version: "1.3.
|
|
117
|
+
version: "1.3.9",
|
|
118
118
|
description: "Remote control Claude Code from your mobile device",
|
|
119
119
|
main: "dist/index.js",
|
|
120
120
|
bin: {
|
|
@@ -511,11 +511,27 @@ var CommandRelayService = class {
|
|
|
511
511
|
}
|
|
512
512
|
};
|
|
513
513
|
|
|
514
|
-
// src/services/
|
|
515
|
-
var import_child_process = require("child_process");
|
|
514
|
+
// src/services/pty/types.ts
|
|
516
515
|
var fs2 = __toESM(require("fs"));
|
|
517
|
-
var os3 = __toESM(require("os"));
|
|
518
516
|
var path2 = __toESM(require("path"));
|
|
517
|
+
function findInPath(name) {
|
|
518
|
+
const dirs = (process.env.PATH ?? "").split(path2.delimiter);
|
|
519
|
+
for (const dir of dirs) {
|
|
520
|
+
const full = `${dir}/${name}`;
|
|
521
|
+
try {
|
|
522
|
+
fs2.accessSync(full, fs2.constants.X_OK);
|
|
523
|
+
return full;
|
|
524
|
+
} catch {
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// src/services/pty/unix.strategy.ts
|
|
531
|
+
var import_child_process = require("child_process");
|
|
532
|
+
var fs3 = __toESM(require("fs"));
|
|
533
|
+
var os3 = __toESM(require("os"));
|
|
534
|
+
var path3 = __toESM(require("path"));
|
|
519
535
|
var PYTHON_PTY_HELPER = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
|
|
520
536
|
m,s=pty.openpty()
|
|
521
537
|
try:
|
|
@@ -571,53 +587,30 @@ try:
|
|
|
571
587
|
sys.exit((st>>8)&0xFF)
|
|
572
588
|
except Exception:sys.exit(0)
|
|
573
589
|
`;
|
|
574
|
-
var
|
|
590
|
+
var UnixPtyStrategy = class {
|
|
575
591
|
constructor(opts) {
|
|
576
592
|
this.opts = opts;
|
|
577
593
|
}
|
|
578
594
|
opts;
|
|
579
595
|
proc = null;
|
|
580
596
|
helperPath = null;
|
|
581
|
-
spawn() {
|
|
582
|
-
if (this.proc) {
|
|
583
|
-
this.cleanup();
|
|
584
|
-
this.proc.kill();
|
|
585
|
-
this.proc = null;
|
|
586
|
-
}
|
|
587
|
-
if (!findInPath("claude") && !findInPath("claude-code")) {
|
|
588
|
-
console.error(
|
|
589
|
-
"\n \u2717 claude not found in PATH.\n Install it with: npm install -g @anthropic-ai/claude-code\n"
|
|
590
|
-
);
|
|
591
|
-
process.exit(1);
|
|
592
|
-
}
|
|
593
|
-
const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
|
|
594
|
-
if (process.platform === "win32") {
|
|
595
|
-
this.spawnDirect(claudeCmd);
|
|
596
|
-
} else {
|
|
597
|
-
this.spawnWithPty(claudeCmd);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
/**
|
|
601
|
-
* macOS / Linux: use a Python PTY helper to give Claude a real TTY.
|
|
602
|
-
* Falls back to direct spawn if python3 is not available.
|
|
603
|
-
*/
|
|
604
|
-
spawnWithPty(claudeCmd) {
|
|
597
|
+
spawn(cmd, cwd) {
|
|
605
598
|
const python = findInPath("python3") ?? findInPath("python");
|
|
606
599
|
if (!python) {
|
|
607
600
|
console.error(
|
|
608
601
|
" \xB7 python3 not found; mobile command injection may be limited.\n"
|
|
609
602
|
);
|
|
610
|
-
this.spawnDirect(
|
|
603
|
+
this.spawnDirect(cmd, cwd);
|
|
611
604
|
return;
|
|
612
605
|
}
|
|
613
606
|
const shell = process.env.SHELL || "/bin/sh";
|
|
614
607
|
const cols = process.stdout.columns || 220;
|
|
615
608
|
const rows = process.stdout.rows || 50;
|
|
616
|
-
this.helperPath =
|
|
617
|
-
|
|
618
|
-
this.proc = (0, import_child_process.spawn)(python, [this.helperPath, shell, "-c", `exec ${
|
|
609
|
+
this.helperPath = path3.join(os3.tmpdir(), "codeam-pty-helper.py");
|
|
610
|
+
fs3.writeFileSync(this.helperPath, PYTHON_PTY_HELPER, { mode: 420 });
|
|
611
|
+
this.proc = (0, import_child_process.spawn)(python, [this.helperPath, shell, "-c", `exec ${cmd}`], {
|
|
619
612
|
stdio: ["pipe", "pipe", "inherit"],
|
|
620
|
-
cwd
|
|
613
|
+
cwd,
|
|
621
614
|
env: {
|
|
622
615
|
...process.env,
|
|
623
616
|
TERM: "xterm-256color",
|
|
@@ -636,7 +629,7 @@ var ClaudeService = class {
|
|
|
636
629
|
});
|
|
637
630
|
this.proc.stdout?.on("data", (chunk) => {
|
|
638
631
|
process.stdout.write(chunk);
|
|
639
|
-
this.opts.onData
|
|
632
|
+
this.opts.onData(chunk.toString("utf8"));
|
|
640
633
|
});
|
|
641
634
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
642
635
|
process.stdin.resume();
|
|
@@ -644,18 +637,18 @@ var ClaudeService = class {
|
|
|
644
637
|
process.on("SIGWINCH", this.handleResize);
|
|
645
638
|
this.proc.on("exit", (code) => {
|
|
646
639
|
this.removeTempFile();
|
|
647
|
-
this.
|
|
640
|
+
this.dispose();
|
|
648
641
|
this.opts.onExit(code ?? 0);
|
|
649
642
|
});
|
|
650
643
|
}
|
|
651
644
|
/**
|
|
652
|
-
*
|
|
653
|
-
* Mobile command injection is limited
|
|
645
|
+
* Python-unavailable fallback: direct spawn without PTY.
|
|
646
|
+
* Mobile command injection is limited (no real TTY for Claude).
|
|
654
647
|
*/
|
|
655
|
-
spawnDirect(
|
|
656
|
-
this.proc = (0, import_child_process.spawn)(
|
|
648
|
+
spawnDirect(cmd, cwd) {
|
|
649
|
+
this.proc = (0, import_child_process.spawn)(cmd, [], {
|
|
657
650
|
stdio: ["pipe", "inherit", "inherit"],
|
|
658
|
-
cwd
|
|
651
|
+
cwd,
|
|
659
652
|
env: process.env,
|
|
660
653
|
shell: true
|
|
661
654
|
});
|
|
@@ -674,54 +667,27 @@ var ClaudeService = class {
|
|
|
674
667
|
process.stdin.on("data", this.stdinHandler);
|
|
675
668
|
process.on("SIGWINCH", this.handleResize);
|
|
676
669
|
this.proc.on("exit", (code) => {
|
|
677
|
-
this.
|
|
670
|
+
this.dispose();
|
|
678
671
|
this.opts.onExit(code ?? 0);
|
|
679
672
|
});
|
|
680
673
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
this.proc?.stdin?.write(text + "\r");
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* Navigate a React Ink selector to the given 0-based index and confirm.
|
|
687
|
-
*
|
|
688
|
-
* Why not sendCommand(arrows + Enter) in one write()?
|
|
689
|
-
* All bytes arrive as one chunk → readline fires all keypress events in the
|
|
690
|
-
* same synchronous run → React Ink batches the state updates → each arrow
|
|
691
|
-
* sees selectedIndex=0 → final state is still 0 or 1 → wrong option selected.
|
|
692
|
-
*
|
|
693
|
-
* Fix: send each down-arrow in a separate write(), ARROW_MS apart, so React
|
|
694
|
-
* has time to process and re-render between each keystroke. Enter is sent
|
|
695
|
-
* ENTER_MS after the last arrow.
|
|
696
|
-
*/
|
|
697
|
-
selectOption(index) {
|
|
698
|
-
if (index <= 0) {
|
|
699
|
-
this.proc?.stdin?.write("\r");
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
const ARROW_MS = 80;
|
|
703
|
-
const ENTER_MS = 200;
|
|
704
|
-
for (let i = 0; i < index; i++) {
|
|
705
|
-
setTimeout(() => {
|
|
706
|
-
this.proc?.stdin?.write("\x1B[B");
|
|
707
|
-
}, i * ARROW_MS);
|
|
708
|
-
}
|
|
709
|
-
setTimeout(() => {
|
|
710
|
-
this.proc?.stdin?.write("\r");
|
|
711
|
-
}, index * ARROW_MS + ENTER_MS);
|
|
712
|
-
}
|
|
713
|
-
/** Send Escape key to Claude (cancels interactive prompts). */
|
|
714
|
-
sendEscape() {
|
|
715
|
-
this.proc?.stdin?.write("\x1B");
|
|
716
|
-
}
|
|
717
|
-
/** Send Ctrl+C to Claude. */
|
|
718
|
-
interrupt() {
|
|
719
|
-
this.proc?.stdin?.write("");
|
|
674
|
+
write(data) {
|
|
675
|
+
this.proc?.stdin?.write(data);
|
|
720
676
|
}
|
|
721
677
|
kill() {
|
|
722
678
|
this.proc?.kill();
|
|
723
679
|
this.removeTempFile();
|
|
724
|
-
this.
|
|
680
|
+
this.dispose();
|
|
681
|
+
}
|
|
682
|
+
dispose() {
|
|
683
|
+
process.removeListener("SIGWINCH", this.handleResize);
|
|
684
|
+
process.stdin.removeListener("data", this.stdinHandler);
|
|
685
|
+
if (process.stdin.isTTY) {
|
|
686
|
+
try {
|
|
687
|
+
process.stdin.setRawMode(false);
|
|
688
|
+
} catch {
|
|
689
|
+
}
|
|
690
|
+
}
|
|
725
691
|
}
|
|
726
692
|
stdinHandler = (chunk) => {
|
|
727
693
|
this.proc?.stdin?.write(chunk);
|
|
@@ -734,38 +700,156 @@ var ClaudeService = class {
|
|
|
734
700
|
}
|
|
735
701
|
}
|
|
736
702
|
};
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
process.stdin.removeListener("data", this.stdinHandler);
|
|
740
|
-
if (process.stdin.isTTY) {
|
|
703
|
+
removeTempFile() {
|
|
704
|
+
if (this.helperPath) {
|
|
741
705
|
try {
|
|
742
|
-
|
|
706
|
+
fs3.unlinkSync(this.helperPath);
|
|
743
707
|
} catch {
|
|
744
708
|
}
|
|
709
|
+
this.helperPath = null;
|
|
745
710
|
}
|
|
746
711
|
}
|
|
747
|
-
|
|
748
|
-
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
// src/services/pty/windows.strategy.ts
|
|
715
|
+
var import_child_process2 = require("child_process");
|
|
716
|
+
var WindowsPtyStrategy = class {
|
|
717
|
+
constructor(opts) {
|
|
718
|
+
this.opts = opts;
|
|
719
|
+
}
|
|
720
|
+
opts;
|
|
721
|
+
proc = null;
|
|
722
|
+
spawn(cmd, cwd) {
|
|
723
|
+
this.proc = (0, import_child_process2.spawn)(cmd, [], {
|
|
724
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
725
|
+
cwd,
|
|
726
|
+
env: {
|
|
727
|
+
...process.env,
|
|
728
|
+
TERM: "xterm-256color",
|
|
729
|
+
COLUMNS: "220",
|
|
730
|
+
LINES: "50"
|
|
731
|
+
},
|
|
732
|
+
shell: true
|
|
733
|
+
});
|
|
734
|
+
this.proc.on("error", (err) => {
|
|
735
|
+
console.error(
|
|
736
|
+
`
|
|
737
|
+
\u2717 Failed to launch Claude Code: ${err.message}
|
|
738
|
+
Make sure claude is correctly installed: npm install -g @anthropic-ai/claude-code
|
|
739
|
+
`
|
|
740
|
+
);
|
|
741
|
+
process.exit(1);
|
|
742
|
+
});
|
|
743
|
+
this.proc.stdout?.on("data", (chunk) => {
|
|
744
|
+
process.stdout.write(chunk);
|
|
745
|
+
this.opts.onData(chunk.toString("utf8"));
|
|
746
|
+
});
|
|
747
|
+
this.proc.stdin?.write("");
|
|
748
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
749
|
+
process.stdin.resume();
|
|
750
|
+
process.stdin.on("data", this.stdinHandler);
|
|
751
|
+
this.proc.on("exit", (code) => {
|
|
752
|
+
this.dispose();
|
|
753
|
+
this.opts.onExit(code ?? 0);
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
write(data) {
|
|
757
|
+
this.proc?.stdin?.write(data);
|
|
758
|
+
}
|
|
759
|
+
kill() {
|
|
760
|
+
this.proc?.kill();
|
|
761
|
+
this.dispose();
|
|
762
|
+
}
|
|
763
|
+
dispose() {
|
|
764
|
+
process.stdin.removeListener("data", this.stdinHandler);
|
|
765
|
+
if (process.stdin.isTTY) {
|
|
749
766
|
try {
|
|
750
|
-
|
|
767
|
+
process.stdin.setRawMode(false);
|
|
751
768
|
} catch {
|
|
752
769
|
}
|
|
753
|
-
this.helperPath = null;
|
|
754
770
|
}
|
|
755
771
|
}
|
|
772
|
+
stdinHandler = (chunk) => {
|
|
773
|
+
this.proc?.stdin?.write(chunk);
|
|
774
|
+
};
|
|
756
775
|
};
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
776
|
+
|
|
777
|
+
// src/services/claude.service.ts
|
|
778
|
+
var ClaudeService = class {
|
|
779
|
+
constructor(opts) {
|
|
780
|
+
this.opts = opts;
|
|
781
|
+
const strategyOpts = {
|
|
782
|
+
onData: opts.onData ?? (() => {
|
|
783
|
+
}),
|
|
784
|
+
onExit: opts.onExit
|
|
785
|
+
};
|
|
786
|
+
this.strategy = process.platform === "win32" ? new WindowsPtyStrategy(strategyOpts) : new UnixPtyStrategy(strategyOpts);
|
|
787
|
+
}
|
|
788
|
+
opts;
|
|
789
|
+
strategy;
|
|
790
|
+
spawn() {
|
|
791
|
+
if (!findInPath("claude") && !findInPath("claude-code")) {
|
|
792
|
+
console.error(
|
|
793
|
+
"\n \u2717 claude not found in PATH.\n Install it with: npm install -g @anthropic-ai/claude-code\n"
|
|
794
|
+
);
|
|
795
|
+
process.exit(1);
|
|
765
796
|
}
|
|
797
|
+
const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
|
|
798
|
+
this.strategy.spawn(claudeCmd, this.opts.cwd);
|
|
766
799
|
}
|
|
767
|
-
|
|
768
|
-
|
|
800
|
+
/** Send a command to Claude's stdin (remote control from mobile). */
|
|
801
|
+
sendCommand(text) {
|
|
802
|
+
this.strategy.write(text + "\r");
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Navigate a React Ink selector to the given 0-based target index and confirm.
|
|
806
|
+
*
|
|
807
|
+
* `fromIndex` is the current highlighted position (defaults to 0 for
|
|
808
|
+
* numbered selectors which always start at the first option). For list-style
|
|
809
|
+
* selectors (e.g. /mcp), the CLI sends `currentIndex` in the select_prompt
|
|
810
|
+
* chunk so the client can pass it back here as `fromIndex`, enabling both
|
|
811
|
+
* up-arrow and down-arrow navigation without always rewinding to position 0.
|
|
812
|
+
*
|
|
813
|
+
* Why not sendCommand(arrows + Enter) in one write()?
|
|
814
|
+
* All bytes arrive as one chunk → readline fires all keypress events in the
|
|
815
|
+
* same synchronous run → React Ink batches the state updates → each arrow
|
|
816
|
+
* sees selectedIndex=0 → final state is still 0 or 1 → wrong option selected.
|
|
817
|
+
*
|
|
818
|
+
* Fix: send each arrow in a separate write(), ARROW_MS apart, so React has
|
|
819
|
+
* time to process and re-render between each keystroke. Enter is sent
|
|
820
|
+
* ENTER_MS after the last arrow.
|
|
821
|
+
*/
|
|
822
|
+
selectOption(targetIndex, fromIndex = 0) {
|
|
823
|
+
const delta = targetIndex - fromIndex;
|
|
824
|
+
const steps = Math.abs(delta);
|
|
825
|
+
const arrow = delta >= 0 ? "\x1B[B" : "\x1B[A";
|
|
826
|
+
const ARROW_MS = 80;
|
|
827
|
+
const ENTER_MS = 200;
|
|
828
|
+
if (steps === 0) {
|
|
829
|
+
this.strategy.write("\r");
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
for (let i = 0; i < steps; i++) {
|
|
833
|
+
setTimeout(() => {
|
|
834
|
+
this.strategy.write(arrow);
|
|
835
|
+
}, i * ARROW_MS);
|
|
836
|
+
}
|
|
837
|
+
setTimeout(() => {
|
|
838
|
+
this.strategy.write("\r");
|
|
839
|
+
}, steps * ARROW_MS + ENTER_MS);
|
|
840
|
+
}
|
|
841
|
+
/** Send Escape key to Claude (cancels interactive prompts). */
|
|
842
|
+
sendEscape() {
|
|
843
|
+
this.strategy.write("\x1B");
|
|
844
|
+
}
|
|
845
|
+
/** Send Ctrl+C to Claude. */
|
|
846
|
+
interrupt() {
|
|
847
|
+
this.strategy.write("");
|
|
848
|
+
}
|
|
849
|
+
kill() {
|
|
850
|
+
this.strategy.kill();
|
|
851
|
+
}
|
|
852
|
+
};
|
|
769
853
|
|
|
770
854
|
// src/services/output.service.ts
|
|
771
855
|
var https2 = __toESM(require("https"));
|
|
@@ -920,7 +1004,60 @@ function detectSelector(lines) {
|
|
|
920
1004
|
return {
|
|
921
1005
|
question,
|
|
922
1006
|
options: keys.map((k) => optionLabels.get(k)),
|
|
923
|
-
optionDescriptions: keys.map((k) => (optionDescs.get(k) ?? []).join(" ").trim())
|
|
1007
|
+
optionDescriptions: keys.map((k) => (optionDescs.get(k) ?? []).join(" ").trim()),
|
|
1008
|
+
currentIndex: 0
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
function detectListSelector(lines) {
|
|
1012
|
+
if (!lines.some((l) => /[↑↓].*navigate/i.test(l.trim()))) return null;
|
|
1013
|
+
if (lines.some((l) => /^❯\s*\d+\./.test(l.trim()))) return null;
|
|
1014
|
+
if (!lines.some((l) => /^\s+❯\s+\S/.test(l))) return null;
|
|
1015
|
+
const isSelected = (line) => /^\s+❯\s+\S/.test(line);
|
|
1016
|
+
const isUnselected = (line) => /^ \S/.test(line);
|
|
1017
|
+
const isItem = (line) => isSelected(line) || isUnselected(line);
|
|
1018
|
+
let optionStartIdx = -1;
|
|
1019
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1020
|
+
if (isItem(lines[i])) {
|
|
1021
|
+
optionStartIdx = i;
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (optionStartIdx === -1) return null;
|
|
1026
|
+
const questionParts = [];
|
|
1027
|
+
for (let i = 0; i < optionStartIdx; i++) {
|
|
1028
|
+
const t = lines[i].trim();
|
|
1029
|
+
if (!t) continue;
|
|
1030
|
+
if (/^[─━—═\-]{3,}$/.test(t)) continue;
|
|
1031
|
+
if (/[┌└│┐┘├┤┬┴┼]/.test(t)) {
|
|
1032
|
+
const inner = t.replace(/[│┌└┐┘├┤┬┴┼─]/g, "").trim();
|
|
1033
|
+
if (inner) questionParts.push(inner);
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
if (/^[>❯]\s/.test(t)) continue;
|
|
1037
|
+
if (/[↑↓].*navigate/i.test(t)) continue;
|
|
1038
|
+
questionParts.push(t);
|
|
1039
|
+
}
|
|
1040
|
+
const question = questionParts.join(" ").trim();
|
|
1041
|
+
const options = [];
|
|
1042
|
+
let currentIndex = 0;
|
|
1043
|
+
for (const line of lines.slice(optionStartIdx)) {
|
|
1044
|
+
const t = line.trim();
|
|
1045
|
+
if (!t) continue;
|
|
1046
|
+
if (/[↑↓].*navigate/i.test(t)) break;
|
|
1047
|
+
if (/^[─━—═\-]{3,}$/.test(t)) continue;
|
|
1048
|
+
if (isSelected(line)) {
|
|
1049
|
+
currentIndex = options.length;
|
|
1050
|
+
options.push(t.replace(/^❯\s+/, "").trim());
|
|
1051
|
+
} else if (isUnselected(line)) {
|
|
1052
|
+
options.push(t);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (options.length < 2) return null;
|
|
1056
|
+
return {
|
|
1057
|
+
question,
|
|
1058
|
+
options,
|
|
1059
|
+
optionDescriptions: options.map(() => ""),
|
|
1060
|
+
currentIndex
|
|
924
1061
|
};
|
|
925
1062
|
}
|
|
926
1063
|
function filterChrome(lines) {
|
|
@@ -1002,13 +1139,13 @@ var OutputService = class _OutputService {
|
|
|
1002
1139
|
return;
|
|
1003
1140
|
}
|
|
1004
1141
|
const lines = renderToLines(this.rawBuffer);
|
|
1005
|
-
const selector = detectSelector(lines);
|
|
1142
|
+
const selector = detectSelector(lines) ?? detectListSelector(lines);
|
|
1006
1143
|
if (selector) {
|
|
1007
1144
|
const idleMs2 = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
|
|
1008
1145
|
if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
|
|
1009
1146
|
this.stopPoll();
|
|
1010
1147
|
this.active = false;
|
|
1011
|
-
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, optionDescriptions: selector.optionDescriptions, done: true }).catch(() => {
|
|
1148
|
+
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, optionDescriptions: selector.optionDescriptions, currentIndex: selector.currentIndex, done: true }).catch(() => {
|
|
1012
1149
|
});
|
|
1013
1150
|
}
|
|
1014
1151
|
return;
|
|
@@ -1031,11 +1168,11 @@ var OutputService = class _OutputService {
|
|
|
1031
1168
|
}
|
|
1032
1169
|
finalize() {
|
|
1033
1170
|
const lines = renderToLines(this.rawBuffer);
|
|
1034
|
-
const selector = detectSelector(lines);
|
|
1171
|
+
const selector = detectSelector(lines) ?? detectListSelector(lines);
|
|
1035
1172
|
this.stopPoll();
|
|
1036
1173
|
this.active = false;
|
|
1037
1174
|
if (selector) {
|
|
1038
|
-
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, optionDescriptions: selector.optionDescriptions, done: true }).catch(() => {
|
|
1175
|
+
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, optionDescriptions: selector.optionDescriptions, currentIndex: selector.currentIndex, done: true }).catch(() => {
|
|
1039
1176
|
});
|
|
1040
1177
|
} else {
|
|
1041
1178
|
const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
@@ -1103,8 +1240,8 @@ var OutputService = class _OutputService {
|
|
|
1103
1240
|
function saveFilesTemp(files) {
|
|
1104
1241
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
1105
1242
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
1106
|
-
const tmpPath =
|
|
1107
|
-
|
|
1243
|
+
const tmpPath = path4.join(os4.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
|
|
1244
|
+
fs4.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
1108
1245
|
return tmpPath;
|
|
1109
1246
|
});
|
|
1110
1247
|
}
|
|
@@ -1140,7 +1277,7 @@ async function start() {
|
|
|
1140
1277
|
setTimeout(() => {
|
|
1141
1278
|
for (const p2 of paths) {
|
|
1142
1279
|
try {
|
|
1143
|
-
|
|
1280
|
+
fs4.unlinkSync(p2);
|
|
1144
1281
|
} catch {
|
|
1145
1282
|
}
|
|
1146
1283
|
}
|
|
@@ -1157,8 +1294,9 @@ async function start() {
|
|
|
1157
1294
|
}
|
|
1158
1295
|
case "select_option": {
|
|
1159
1296
|
const index = cmd.payload.index ?? 0;
|
|
1297
|
+
const from = cmd.payload.from ?? 0;
|
|
1160
1298
|
outputSvc.newTurn();
|
|
1161
|
-
claude.selectOption(index);
|
|
1299
|
+
claude.selectOption(index, from);
|
|
1162
1300
|
break;
|
|
1163
1301
|
}
|
|
1164
1302
|
case "escape_key":
|
|
@@ -1191,7 +1329,7 @@ async function start() {
|
|
|
1191
1329
|
setTimeout(() => {
|
|
1192
1330
|
for (const p2 of paths) {
|
|
1193
1331
|
try {
|
|
1194
|
-
|
|
1332
|
+
fs4.unlinkSync(p2);
|
|
1195
1333
|
} catch {
|
|
1196
1334
|
}
|
|
1197
1335
|
}
|
|
@@ -1204,8 +1342,9 @@ async function start() {
|
|
|
1204
1342
|
if (input) sendPrompt(input);
|
|
1205
1343
|
} else if (cmdType === "select_option") {
|
|
1206
1344
|
const index = inner.index ?? 0;
|
|
1345
|
+
const from = inner.from ?? 0;
|
|
1207
1346
|
outputSvc.newTurn();
|
|
1208
|
-
claude.selectOption(index);
|
|
1347
|
+
claude.selectOption(index, from);
|
|
1209
1348
|
} else if (cmdType === "escape_key") {
|
|
1210
1349
|
outputSvc.newTurn();
|
|
1211
1350
|
claude.sendEscape();
|