codeam-cli 1.3.6 → 1.3.8
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 +201 -113
- 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.8",
|
|
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,13 +667,139 @@ 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();
|
|
671
|
+
this.opts.onExit(code ?? 0);
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
write(data) {
|
|
675
|
+
this.proc?.stdin?.write(data);
|
|
676
|
+
}
|
|
677
|
+
kill() {
|
|
678
|
+
this.proc?.kill();
|
|
679
|
+
this.removeTempFile();
|
|
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
|
+
}
|
|
691
|
+
}
|
|
692
|
+
stdinHandler = (chunk) => {
|
|
693
|
+
this.proc?.stdin?.write(chunk);
|
|
694
|
+
};
|
|
695
|
+
handleResize = () => {
|
|
696
|
+
if (this.proc?.pid) {
|
|
697
|
+
try {
|
|
698
|
+
process.kill(this.proc.pid, "SIGWINCH");
|
|
699
|
+
} catch {
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
removeTempFile() {
|
|
704
|
+
if (this.helperPath) {
|
|
705
|
+
try {
|
|
706
|
+
fs3.unlinkSync(this.helperPath);
|
|
707
|
+
} catch {
|
|
708
|
+
}
|
|
709
|
+
this.helperPath = null;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
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();
|
|
678
753
|
this.opts.onExit(code ?? 0);
|
|
679
754
|
});
|
|
680
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) {
|
|
766
|
+
try {
|
|
767
|
+
process.stdin.setRawMode(false);
|
|
768
|
+
} catch {
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
stdinHandler = (chunk) => {
|
|
773
|
+
this.proc?.stdin?.write(chunk);
|
|
774
|
+
};
|
|
775
|
+
};
|
|
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);
|
|
796
|
+
}
|
|
797
|
+
const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
|
|
798
|
+
this.strategy.spawn(claudeCmd, this.opts.cwd);
|
|
799
|
+
}
|
|
681
800
|
/** Send a command to Claude's stdin (remote control from mobile). */
|
|
682
801
|
sendCommand(text) {
|
|
683
|
-
this.
|
|
802
|
+
this.strategy.write(text + "\r");
|
|
684
803
|
}
|
|
685
804
|
/**
|
|
686
805
|
* Navigate a React Ink selector to the given 0-based index and confirm.
|
|
@@ -696,76 +815,32 @@ var ClaudeService = class {
|
|
|
696
815
|
*/
|
|
697
816
|
selectOption(index) {
|
|
698
817
|
if (index <= 0) {
|
|
699
|
-
this.
|
|
818
|
+
this.strategy.write("\r");
|
|
700
819
|
return;
|
|
701
820
|
}
|
|
702
821
|
const ARROW_MS = 80;
|
|
703
822
|
const ENTER_MS = 200;
|
|
704
823
|
for (let i = 0; i < index; i++) {
|
|
705
824
|
setTimeout(() => {
|
|
706
|
-
this.
|
|
825
|
+
this.strategy.write("\x1B[B");
|
|
707
826
|
}, i * ARROW_MS);
|
|
708
827
|
}
|
|
709
828
|
setTimeout(() => {
|
|
710
|
-
this.
|
|
829
|
+
this.strategy.write("\r");
|
|
711
830
|
}, index * ARROW_MS + ENTER_MS);
|
|
712
831
|
}
|
|
713
832
|
/** Send Escape key to Claude (cancels interactive prompts). */
|
|
714
833
|
sendEscape() {
|
|
715
|
-
this.
|
|
834
|
+
this.strategy.write("\x1B");
|
|
716
835
|
}
|
|
717
836
|
/** Send Ctrl+C to Claude. */
|
|
718
837
|
interrupt() {
|
|
719
|
-
this.
|
|
838
|
+
this.strategy.write("");
|
|
720
839
|
}
|
|
721
840
|
kill() {
|
|
722
|
-
this.
|
|
723
|
-
this.removeTempFile();
|
|
724
|
-
this.cleanup();
|
|
725
|
-
}
|
|
726
|
-
stdinHandler = (chunk) => {
|
|
727
|
-
this.proc?.stdin?.write(chunk);
|
|
728
|
-
};
|
|
729
|
-
handleResize = () => {
|
|
730
|
-
if (this.proc?.pid) {
|
|
731
|
-
try {
|
|
732
|
-
process.kill(this.proc.pid, "SIGWINCH");
|
|
733
|
-
} catch {
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
};
|
|
737
|
-
cleanup() {
|
|
738
|
-
process.removeListener("SIGWINCH", this.handleResize);
|
|
739
|
-
process.stdin.removeListener("data", this.stdinHandler);
|
|
740
|
-
if (process.stdin.isTTY) {
|
|
741
|
-
try {
|
|
742
|
-
process.stdin.setRawMode(false);
|
|
743
|
-
} catch {
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
removeTempFile() {
|
|
748
|
-
if (this.helperPath) {
|
|
749
|
-
try {
|
|
750
|
-
fs2.unlinkSync(this.helperPath);
|
|
751
|
-
} catch {
|
|
752
|
-
}
|
|
753
|
-
this.helperPath = null;
|
|
754
|
-
}
|
|
841
|
+
this.strategy.kill();
|
|
755
842
|
}
|
|
756
843
|
};
|
|
757
|
-
function findInPath(name) {
|
|
758
|
-
const dirs = (process.env.PATH ?? "").split(path2.delimiter);
|
|
759
|
-
for (const dir of dirs) {
|
|
760
|
-
const full = `${dir}/${name}`;
|
|
761
|
-
try {
|
|
762
|
-
fs2.accessSync(full, fs2.constants.X_OK);
|
|
763
|
-
return full;
|
|
764
|
-
} catch {
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
return null;
|
|
768
|
-
}
|
|
769
844
|
|
|
770
845
|
// src/services/output.service.ts
|
|
771
846
|
var https2 = __toESM(require("https"));
|
|
@@ -924,21 +999,34 @@ function detectSelector(lines) {
|
|
|
924
999
|
};
|
|
925
1000
|
}
|
|
926
1001
|
function filterChrome(lines) {
|
|
927
|
-
|
|
1002
|
+
const result = [];
|
|
1003
|
+
let skipEchoContinuation = false;
|
|
1004
|
+
for (const line of lines) {
|
|
928
1005
|
const t = line.trim();
|
|
929
|
-
if (!t)
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
if (
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
if (
|
|
938
|
-
if (/
|
|
939
|
-
if (
|
|
940
|
-
|
|
941
|
-
|
|
1006
|
+
if (!t) {
|
|
1007
|
+
skipEchoContinuation = false;
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
if (/^[─━—═─\-]{3,}$/.test(t)) {
|
|
1011
|
+
skipEchoContinuation = false;
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
if (/^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/.test(t)) continue;
|
|
1015
|
+
if (/esc.{0,5}to.{0,5}interrupt/i.test(t)) continue;
|
|
1016
|
+
if (/high\s*[·•]\s*\/effort/i.test(t)) continue;
|
|
1017
|
+
if (/^[❯>]\s*$/.test(t)) continue;
|
|
1018
|
+
if (/^\(thinking\)\s*$/.test(t)) continue;
|
|
1019
|
+
if (/^\?\s.*shortcut/i.test(t)) continue;
|
|
1020
|
+
if (/spending limit|usage limit/i.test(t) && t.length < 80) continue;
|
|
1021
|
+
if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t)) continue;
|
|
1022
|
+
if (/^[❯>]\s+\S/.test(t) && !/^[❯>]\s*\d+\./.test(t)) {
|
|
1023
|
+
skipEchoContinuation = true;
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
if (skipEchoContinuation) continue;
|
|
1027
|
+
result.push(line);
|
|
1028
|
+
}
|
|
1029
|
+
return result;
|
|
942
1030
|
}
|
|
943
1031
|
var OutputService = class _OutputService {
|
|
944
1032
|
constructor(sessionId, pluginId) {
|
|
@@ -1090,8 +1178,8 @@ var OutputService = class _OutputService {
|
|
|
1090
1178
|
function saveFilesTemp(files) {
|
|
1091
1179
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
1092
1180
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
1093
|
-
const tmpPath =
|
|
1094
|
-
|
|
1181
|
+
const tmpPath = path4.join(os4.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
|
|
1182
|
+
fs4.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
1095
1183
|
return tmpPath;
|
|
1096
1184
|
});
|
|
1097
1185
|
}
|
|
@@ -1127,7 +1215,7 @@ async function start() {
|
|
|
1127
1215
|
setTimeout(() => {
|
|
1128
1216
|
for (const p2 of paths) {
|
|
1129
1217
|
try {
|
|
1130
|
-
|
|
1218
|
+
fs4.unlinkSync(p2);
|
|
1131
1219
|
} catch {
|
|
1132
1220
|
}
|
|
1133
1221
|
}
|
|
@@ -1178,7 +1266,7 @@ async function start() {
|
|
|
1178
1266
|
setTimeout(() => {
|
|
1179
1267
|
for (const p2 of paths) {
|
|
1180
1268
|
try {
|
|
1181
|
-
|
|
1269
|
+
fs4.unlinkSync(p2);
|
|
1182
1270
|
} catch {
|
|
1183
1271
|
}
|
|
1184
1272
|
}
|