codeam-cli 1.1.2 → 1.1.4
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 +66 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -110,7 +110,7 @@ var import_picocolors = __toESM(require("picocolors"));
|
|
|
110
110
|
// package.json
|
|
111
111
|
var package_default = {
|
|
112
112
|
name: "codeam-cli",
|
|
113
|
-
version: "1.1.
|
|
113
|
+
version: "1.1.4",
|
|
114
114
|
description: "Remote control Claude Code from your mobile device",
|
|
115
115
|
main: "dist/index.js",
|
|
116
116
|
bin: {
|
|
@@ -840,27 +840,50 @@ function renderToLines(raw) {
|
|
|
840
840
|
}
|
|
841
841
|
return screen;
|
|
842
842
|
}
|
|
843
|
+
function detectSelector(raw) {
|
|
844
|
+
const stripped = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[@-Z\\-_]/g, "");
|
|
845
|
+
if (!/Enter to select/i.test(stripped)) return null;
|
|
846
|
+
const segments = stripped.split(/[\r\n]+/);
|
|
847
|
+
const optionMap = /* @__PURE__ */ new Map();
|
|
848
|
+
for (const seg of segments) {
|
|
849
|
+
const t = seg.trim();
|
|
850
|
+
if (!t) continue;
|
|
851
|
+
const m = t.match(/^[❯>]?\s*(\d+)\.\s+(.+)/);
|
|
852
|
+
if (m) {
|
|
853
|
+
const num = parseInt(m[1], 10);
|
|
854
|
+
if (!optionMap.has(num)) optionMap.set(num, m[2].trim());
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
const options = [...optionMap.entries()].sort(([a], [b]) => a - b).map(([, label]) => label);
|
|
858
|
+
if (options.length === 0) return null;
|
|
859
|
+
let question = "";
|
|
860
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
861
|
+
const t = segments[i].trim();
|
|
862
|
+
if (t.endsWith("?") && !/Enter to select/i.test(t) && !/^[❯>]?\s*\d+\./.test(t)) {
|
|
863
|
+
question = t;
|
|
864
|
+
break;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return { question, options };
|
|
868
|
+
}
|
|
843
869
|
function filterChrome(lines) {
|
|
844
870
|
return lines.filter((line) => {
|
|
845
871
|
const t = line.trim();
|
|
846
872
|
if (!t) return false;
|
|
847
|
-
if (/^[
|
|
873
|
+
if (/^[─━—═─\-]{3,}$/.test(t)) return false;
|
|
848
874
|
if (/^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/.test(t)) return false;
|
|
849
875
|
if (/esc.{0,5}to.{0,5}interrupt/i.test(t)) return false;
|
|
850
876
|
if (/high\s*[·•]\s*\/effort/i.test(t)) return false;
|
|
851
877
|
if (/^[❯>]\s*$/.test(t)) return false;
|
|
852
|
-
if (/^[❯>]\s+\S/.test(t)) return false;
|
|
878
|
+
if (/^[❯>]\s+\S/.test(t) && !/^[❯>]\s*\d+\./.test(t)) return false;
|
|
853
879
|
if (/^\(thinking\)\s*$/.test(t)) return false;
|
|
854
880
|
if (/^\?\s.*shortcut/i.test(t)) return false;
|
|
855
881
|
if (/spending limit|usage limit/i.test(t) && t.length < 80) return false;
|
|
882
|
+
if (/Enter to select/i.test(t)) return false;
|
|
883
|
+
if (/↑\s*\/\s*↓\s*to\s*navigate/i.test(t)) return false;
|
|
856
884
|
return true;
|
|
857
885
|
});
|
|
858
886
|
}
|
|
859
|
-
function extractContent(raw) {
|
|
860
|
-
const lines = renderToLines(raw);
|
|
861
|
-
const filtered = filterChrome(lines);
|
|
862
|
-
return filtered.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
863
|
-
}
|
|
864
887
|
var OutputService = class _OutputService {
|
|
865
888
|
constructor(sessionId, pluginId) {
|
|
866
889
|
this.sessionId = sessionId;
|
|
@@ -868,20 +891,17 @@ var OutputService = class _OutputService {
|
|
|
868
891
|
}
|
|
869
892
|
sessionId;
|
|
870
893
|
pluginId;
|
|
871
|
-
/** Raw PTY bytes — processed only when we need to send content. */
|
|
872
894
|
rawBuffer = "";
|
|
873
895
|
lastSentContent = "";
|
|
874
896
|
pollTimer = null;
|
|
875
897
|
startTime = 0;
|
|
876
898
|
active = false;
|
|
877
|
-
/** When the last chunk of printable raw content arrived. */
|
|
878
899
|
lastPushTime = 0;
|
|
879
900
|
static POLL_MS = 1e3;
|
|
880
|
-
/** No new printable content for 3 s → Claude finished responding. */
|
|
881
901
|
static IDLE_MS = 3e3;
|
|
882
|
-
/**
|
|
902
|
+
/** Shorter idle threshold for selector detection (UI is ready immediately). */
|
|
903
|
+
static SELECTOR_IDLE_MS = 1500;
|
|
883
904
|
static EMPTY_TIMEOUT_MS = 3e4;
|
|
884
|
-
/** Hard cap — clear typing state after 2 min no matter what. */
|
|
885
905
|
static MAX_MS = 12e4;
|
|
886
906
|
newTurn() {
|
|
887
907
|
this.stopPoll();
|
|
@@ -912,7 +932,19 @@ var OutputService = class _OutputService {
|
|
|
912
932
|
this.finalize();
|
|
913
933
|
return;
|
|
914
934
|
}
|
|
915
|
-
const
|
|
935
|
+
const lines = renderToLines(this.rawBuffer);
|
|
936
|
+
const selector = detectSelector(this.rawBuffer);
|
|
937
|
+
if (selector) {
|
|
938
|
+
const idleMs2 = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
|
|
939
|
+
if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
|
|
940
|
+
this.stopPoll();
|
|
941
|
+
this.active = false;
|
|
942
|
+
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, done: true }).catch(() => {
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
916
948
|
if (!content) {
|
|
917
949
|
if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) this.finalize();
|
|
918
950
|
return;
|
|
@@ -929,11 +961,18 @@ var OutputService = class _OutputService {
|
|
|
929
961
|
}
|
|
930
962
|
}
|
|
931
963
|
finalize() {
|
|
932
|
-
const
|
|
964
|
+
const lines = renderToLines(this.rawBuffer);
|
|
965
|
+
const selector = detectSelector(this.rawBuffer);
|
|
933
966
|
this.stopPoll();
|
|
934
967
|
this.active = false;
|
|
935
|
-
|
|
936
|
-
|
|
968
|
+
if (selector) {
|
|
969
|
+
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, done: true }).catch(() => {
|
|
970
|
+
});
|
|
971
|
+
} else {
|
|
972
|
+
const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
973
|
+
this.postChunk({ type: "text", content, done: true }).catch(() => {
|
|
974
|
+
});
|
|
975
|
+
}
|
|
937
976
|
}
|
|
938
977
|
stopPoll() {
|
|
939
978
|
if (this.pollTimer) {
|
|
@@ -1022,6 +1061,12 @@ async function start() {
|
|
|
1022
1061
|
if (input) sendPrompt(input);
|
|
1023
1062
|
break;
|
|
1024
1063
|
}
|
|
1064
|
+
case "select_option": {
|
|
1065
|
+
const index = cmd.payload.index ?? 0;
|
|
1066
|
+
const arrows = "\x1B[B".repeat(Math.max(0, index));
|
|
1067
|
+
sendPrompt(arrows);
|
|
1068
|
+
break;
|
|
1069
|
+
}
|
|
1025
1070
|
case "stop_task":
|
|
1026
1071
|
claude.interrupt();
|
|
1027
1072
|
break;
|
|
@@ -1042,6 +1087,10 @@ async function start() {
|
|
|
1042
1087
|
} else if (cmdType === "provide_input") {
|
|
1043
1088
|
const input = inner.input;
|
|
1044
1089
|
if (input) sendPrompt(input);
|
|
1090
|
+
} else if (cmdType === "select_option") {
|
|
1091
|
+
const index = inner.index ?? 0;
|
|
1092
|
+
const arrows = "\x1B[B".repeat(Math.max(0, index));
|
|
1093
|
+
sendPrompt(arrows);
|
|
1045
1094
|
} else if (cmdType === "stop_task") {
|
|
1046
1095
|
claude.interrupt();
|
|
1047
1096
|
}
|