codeam-cli 2.12.10 → 2.12.12
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/CHANGELOG.md +24 -0
- package/dist/index.js +249 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ All notable changes to `codeam-cli` are documented here.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.12.11] — 2026-05-14
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Codex-specific renderer with DECSTBM scroll-region support — captures full multi-paragraph replies
|
|
12
|
+
|
|
13
|
+
## [2.12.10] — 2026-05-14
|
|
14
|
+
|
|
15
|
+
## [2.12.9] — 2026-05-14
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **shared:** Keep scrollback across alt-screen toggles + CSI 2J so multi-paragraph agent replies survive
|
|
20
|
+
|
|
21
|
+
### Debug
|
|
22
|
+
|
|
23
|
+
- **cli:** Always-on filter-input dump so we can diagnose multi-line drops
|
|
24
|
+
|
|
25
|
+
## [2.12.8] — 2026-05-14
|
|
26
|
+
|
|
27
|
+
### Debug
|
|
28
|
+
|
|
29
|
+
- **cli:** Always-on filter-input dump so we can diagnose multi-line drops
|
|
30
|
+
|
|
7
31
|
## [2.12.7] — 2026-05-14
|
|
8
32
|
|
|
9
33
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -1682,7 +1682,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
1682
1682
|
// package.json
|
|
1683
1683
|
var package_default = {
|
|
1684
1684
|
name: "codeam-cli",
|
|
1685
|
-
version: "2.12.
|
|
1685
|
+
version: "2.12.12",
|
|
1686
1686
|
description: "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands \u2014 from anywhere.",
|
|
1687
1687
|
type: "commonjs",
|
|
1688
1688
|
main: "dist/index.js",
|
|
@@ -5834,6 +5834,7 @@ function filterCodexChrome(lines) {
|
|
|
5834
5834
|
}
|
|
5835
5835
|
out.push(t2);
|
|
5836
5836
|
}
|
|
5837
|
+
const wrapped = wrapCodexCodeBlocks(out);
|
|
5837
5838
|
const hasRealInput = lines.some((l) => /\w/.test(l));
|
|
5838
5839
|
if (out.length > 0 || hasRealInput) {
|
|
5839
5840
|
const sampleIn = lines.slice(-50).map((l, i) => ` in[${i}] ${JSON.stringify(l)}`).join("\n");
|
|
@@ -5845,7 +5846,77 @@ ${sampleOut}`);
|
|
|
5845
5846
|
} else {
|
|
5846
5847
|
log.trace("codex-parse", `filterCodexChrome in=${lines.length} out=${out.length}`);
|
|
5847
5848
|
}
|
|
5848
|
-
return
|
|
5849
|
+
return wrapped;
|
|
5850
|
+
}
|
|
5851
|
+
var CODE_CHAR_RE = /[;{}]|=>|^\s*(?:import|public|private|static|class|function|interface|type|const|let|var|def|return|if|else|for|while)\b/;
|
|
5852
|
+
function inferLanguage(block) {
|
|
5853
|
+
const head = block.slice(0, 10).join("\n");
|
|
5854
|
+
if (/\bpublic\s+(?:static\s+)?(?:class|void|int|String)\b|System\.out\.println|\bjava\.util/.test(head)) return "java";
|
|
5855
|
+
if (/\b(?:interface|type)\s+\w+\s*=?\s*[{<]|\bas\s+(?:string|number|boolean)\b|\b(?:string|number|boolean)\s*[;,)\]]/.test(head)) return "typescript";
|
|
5856
|
+
if (/\bimport\s+\w+\s+from\s+['"]|=>\s*[{(]|\bconst\s+\w+\s*=\s*(?:async\s+)?\(/.test(head)) return "javascript";
|
|
5857
|
+
if (/^\s*def\s+\w+\(|^\s*from\s+\w+\s+import|print\(/m.test(head)) return "python";
|
|
5858
|
+
if (/^\s*package\s+\w+|^\s*func\s+\w+\(|\binterface\s*{/m.test(head)) return "go";
|
|
5859
|
+
if (/^\s*fn\s+\w+\(|^\s*use\s+\w+::|^\s*impl\s+/m.test(head)) return "rust";
|
|
5860
|
+
if (/#include\s*<|int\s+main\s*\(/.test(head)) return "cpp";
|
|
5861
|
+
return "";
|
|
5862
|
+
}
|
|
5863
|
+
function wrapCodexCodeBlocks(lines) {
|
|
5864
|
+
const result = [];
|
|
5865
|
+
let i = 0;
|
|
5866
|
+
while (i < lines.length) {
|
|
5867
|
+
const line = lines[i];
|
|
5868
|
+
if (!CODE_CHAR_RE.test(line)) {
|
|
5869
|
+
result.push(line);
|
|
5870
|
+
i++;
|
|
5871
|
+
continue;
|
|
5872
|
+
}
|
|
5873
|
+
const start2 = i;
|
|
5874
|
+
let end = i;
|
|
5875
|
+
let j2 = i + 1;
|
|
5876
|
+
while (j2 < lines.length) {
|
|
5877
|
+
const l = lines[j2];
|
|
5878
|
+
if (CODE_CHAR_RE.test(l)) {
|
|
5879
|
+
end = j2;
|
|
5880
|
+
j2++;
|
|
5881
|
+
continue;
|
|
5882
|
+
}
|
|
5883
|
+
if (l.trim() === "") {
|
|
5884
|
+
let k2 = j2 + 1;
|
|
5885
|
+
while (k2 < lines.length && lines[k2].trim() === "") k2++;
|
|
5886
|
+
if (k2 < lines.length && CODE_CHAR_RE.test(lines[k2])) {
|
|
5887
|
+
end = k2;
|
|
5888
|
+
j2 = k2 + 1;
|
|
5889
|
+
continue;
|
|
5890
|
+
}
|
|
5891
|
+
break;
|
|
5892
|
+
}
|
|
5893
|
+
if (/^\s{2,}\S/.test(l)) {
|
|
5894
|
+
end = j2;
|
|
5895
|
+
j2++;
|
|
5896
|
+
continue;
|
|
5897
|
+
}
|
|
5898
|
+
break;
|
|
5899
|
+
}
|
|
5900
|
+
const runLen = end - start2 + 1;
|
|
5901
|
+
const codeShapedCount = lines.slice(start2, end + 1).filter((l) => CODE_CHAR_RE.test(l)).length;
|
|
5902
|
+
if (codeShapedCount >= 3) {
|
|
5903
|
+
const body = lines.slice(start2, end + 1);
|
|
5904
|
+
const lang = inferLanguage(body);
|
|
5905
|
+
result.push("```" + lang);
|
|
5906
|
+
for (const l of body) result.push(l);
|
|
5907
|
+
while (result.length > 0 && result[result.length - 1].trim() === "") {
|
|
5908
|
+
result.pop();
|
|
5909
|
+
}
|
|
5910
|
+
result.push("```");
|
|
5911
|
+
i = end + 1;
|
|
5912
|
+
} else {
|
|
5913
|
+
for (let k2 = start2; k2 <= end && k2 < lines.length; k2++) result.push(lines[k2]);
|
|
5914
|
+
i = end + 1;
|
|
5915
|
+
if (i === start2) i++;
|
|
5916
|
+
}
|
|
5917
|
+
void runLen;
|
|
5918
|
+
}
|
|
5919
|
+
return result;
|
|
5849
5920
|
}
|
|
5850
5921
|
function parseCodexChrome(_line) {
|
|
5851
5922
|
return null;
|
|
@@ -5854,6 +5925,168 @@ function detectCodexSelector(_lines) {
|
|
|
5854
5925
|
return null;
|
|
5855
5926
|
}
|
|
5856
5927
|
|
|
5928
|
+
// src/agents/codex/renderer.ts
|
|
5929
|
+
function renderCodexBuffer(raw) {
|
|
5930
|
+
const scrollback = [];
|
|
5931
|
+
const screen = [""];
|
|
5932
|
+
let row = 0;
|
|
5933
|
+
let col = 0;
|
|
5934
|
+
let scrollTop = null;
|
|
5935
|
+
let scrollBottom = null;
|
|
5936
|
+
function ensureRow() {
|
|
5937
|
+
while (screen.length <= row) screen.push("");
|
|
5938
|
+
}
|
|
5939
|
+
function writeChar(ch) {
|
|
5940
|
+
ensureRow();
|
|
5941
|
+
if (col < screen[row].length) {
|
|
5942
|
+
screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);
|
|
5943
|
+
} else {
|
|
5944
|
+
while (screen[row].length < col) screen[row] += " ";
|
|
5945
|
+
screen[row] += ch;
|
|
5946
|
+
}
|
|
5947
|
+
col++;
|
|
5948
|
+
}
|
|
5949
|
+
function scrollRegionUp() {
|
|
5950
|
+
if (scrollTop === null || scrollBottom === null) {
|
|
5951
|
+
ensureRow();
|
|
5952
|
+
return;
|
|
5953
|
+
}
|
|
5954
|
+
while (screen.length <= scrollBottom) screen.push("");
|
|
5955
|
+
if (screen[scrollTop].trim() !== "") {
|
|
5956
|
+
scrollback.push(screen[scrollTop]);
|
|
5957
|
+
}
|
|
5958
|
+
for (let r = scrollTop; r < scrollBottom; r++) {
|
|
5959
|
+
screen[r] = screen[r + 1];
|
|
5960
|
+
}
|
|
5961
|
+
screen[scrollBottom] = "";
|
|
5962
|
+
}
|
|
5963
|
+
function scrollRegionDown() {
|
|
5964
|
+
if (scrollTop === null || scrollBottom === null) {
|
|
5965
|
+
return;
|
|
5966
|
+
}
|
|
5967
|
+
while (screen.length <= scrollBottom) screen.push("");
|
|
5968
|
+
if (screen[scrollBottom].trim() !== "") {
|
|
5969
|
+
scrollback.push(screen[scrollBottom]);
|
|
5970
|
+
}
|
|
5971
|
+
for (let r = scrollBottom; r > scrollTop; r--) {
|
|
5972
|
+
screen[r] = screen[r - 1];
|
|
5973
|
+
}
|
|
5974
|
+
screen[scrollTop] = "";
|
|
5975
|
+
}
|
|
5976
|
+
let i = 0;
|
|
5977
|
+
while (i < raw.length) {
|
|
5978
|
+
const ch = raw[i];
|
|
5979
|
+
if (ch === "\x1B") {
|
|
5980
|
+
i++;
|
|
5981
|
+
if (i >= raw.length) break;
|
|
5982
|
+
if (raw[i] === "[") {
|
|
5983
|
+
i++;
|
|
5984
|
+
let param = "";
|
|
5985
|
+
while (i < raw.length && !/[@-~]/.test(raw[i])) param += raw[i++];
|
|
5986
|
+
const cmd = raw[i] ?? "";
|
|
5987
|
+
const n = parseInt(param) || 1;
|
|
5988
|
+
if (cmd === "A") {
|
|
5989
|
+
row = Math.max(0, row - n);
|
|
5990
|
+
} else if (cmd === "B") {
|
|
5991
|
+
row += n;
|
|
5992
|
+
ensureRow();
|
|
5993
|
+
} else if (cmd === "C") {
|
|
5994
|
+
col += n;
|
|
5995
|
+
} else if (cmd === "D") {
|
|
5996
|
+
col = Math.max(0, col - n);
|
|
5997
|
+
} else if (cmd === "G") {
|
|
5998
|
+
col = Math.max(0, n - 1);
|
|
5999
|
+
} else if (cmd === "H" || cmd === "f") {
|
|
6000
|
+
const p2 = param.split(";");
|
|
6001
|
+
row = Math.max(0, (parseInt(p2[0] ?? "1") || 1) - 1);
|
|
6002
|
+
col = Math.max(0, (parseInt(p2[1] ?? "1") || 1) - 1);
|
|
6003
|
+
ensureRow();
|
|
6004
|
+
} else if (cmd === "J") {
|
|
6005
|
+
if (param === "2" || param === "3") {
|
|
6006
|
+
for (let r = 0; r < screen.length; r++) {
|
|
6007
|
+
if (screen[r].trim() !== "") scrollback.push(screen[r]);
|
|
6008
|
+
}
|
|
6009
|
+
screen.length = 1;
|
|
6010
|
+
screen[0] = "";
|
|
6011
|
+
row = 0;
|
|
6012
|
+
col = 0;
|
|
6013
|
+
} else if (param === "1") {
|
|
6014
|
+
for (let r = 0; r < row; r++) screen[r] = "";
|
|
6015
|
+
screen[row] = " ".repeat(col) + screen[row].slice(col);
|
|
6016
|
+
} else {
|
|
6017
|
+
screen[row] = screen[row].slice(0, col);
|
|
6018
|
+
screen.splice(row + 1);
|
|
6019
|
+
}
|
|
6020
|
+
} else if (cmd === "K") {
|
|
6021
|
+
ensureRow();
|
|
6022
|
+
if (param === "" || param === "0") screen[row] = screen[row].slice(0, col);
|
|
6023
|
+
else if (param === "1") screen[row] = " ".repeat(col) + screen[row].slice(col);
|
|
6024
|
+
else if (param === "2") screen[row] = "";
|
|
6025
|
+
} else if (cmd === "r") {
|
|
6026
|
+
if (param === "" || param === ";") {
|
|
6027
|
+
scrollTop = null;
|
|
6028
|
+
scrollBottom = null;
|
|
6029
|
+
} else {
|
|
6030
|
+
const p2 = param.split(";");
|
|
6031
|
+
const top = parseInt(p2[0] ?? "1") || 1;
|
|
6032
|
+
const bot = parseInt(p2[1] ?? "1") || 1;
|
|
6033
|
+
scrollTop = Math.max(0, top - 1);
|
|
6034
|
+
scrollBottom = Math.max(scrollTop, bot - 1);
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
6037
|
+
} else if (raw[i] === "]") {
|
|
6038
|
+
i++;
|
|
6039
|
+
while (i < raw.length) {
|
|
6040
|
+
if (raw[i] === "\x07") break;
|
|
6041
|
+
if (raw[i] === "\x1B" && i + 1 < raw.length && raw[i + 1] === "\\") {
|
|
6042
|
+
i++;
|
|
6043
|
+
break;
|
|
6044
|
+
}
|
|
6045
|
+
i++;
|
|
6046
|
+
}
|
|
6047
|
+
} else if (raw[i] === "M") {
|
|
6048
|
+
if (scrollTop !== null && row === scrollTop) {
|
|
6049
|
+
scrollRegionDown();
|
|
6050
|
+
} else {
|
|
6051
|
+
row = Math.max(0, row - 1);
|
|
6052
|
+
}
|
|
6053
|
+
} else if (raw[i] === "D") {
|
|
6054
|
+
if (scrollBottom !== null && row === scrollBottom) {
|
|
6055
|
+
scrollRegionUp();
|
|
6056
|
+
} else {
|
|
6057
|
+
row++;
|
|
6058
|
+
ensureRow();
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
} else if (ch === "\r") {
|
|
6062
|
+
if (i + 1 < raw.length && raw[i + 1] === "\n") {
|
|
6063
|
+
if (scrollBottom !== null && row === scrollBottom) {
|
|
6064
|
+
scrollRegionUp();
|
|
6065
|
+
} else {
|
|
6066
|
+
row++;
|
|
6067
|
+
ensureRow();
|
|
6068
|
+
}
|
|
6069
|
+
col = 0;
|
|
6070
|
+
i++;
|
|
6071
|
+
} else {
|
|
6072
|
+
col = 0;
|
|
6073
|
+
}
|
|
6074
|
+
} else if (ch === "\n") {
|
|
6075
|
+
if (scrollBottom !== null && row === scrollBottom) {
|
|
6076
|
+
scrollRegionUp();
|
|
6077
|
+
} else {
|
|
6078
|
+
row++;
|
|
6079
|
+
ensureRow();
|
|
6080
|
+
}
|
|
6081
|
+
col = 0;
|
|
6082
|
+
} else if (ch >= " " || ch === " ") {
|
|
6083
|
+
writeChar(ch);
|
|
6084
|
+
}
|
|
6085
|
+
i++;
|
|
6086
|
+
}
|
|
6087
|
+
return [...scrollback, ...screen];
|
|
6088
|
+
}
|
|
6089
|
+
|
|
5857
6090
|
// src/agents/codex/runtime.ts
|
|
5858
6091
|
var CODEX_CONTEXT_WINDOW = 272e3;
|
|
5859
6092
|
var CODEX_MODELS = [
|
|
@@ -5917,6 +6150,16 @@ var CodexRuntimeStrategy = class {
|
|
|
5917
6150
|
return { ptyInput: "/compact\r" };
|
|
5918
6151
|
}
|
|
5919
6152
|
// ─── TUI parser strategy methods ─────────────────────────────────
|
|
6153
|
+
/**
|
|
6154
|
+
* Codex needs its own virtual terminal because the Codex CLI uses
|
|
6155
|
+
* DECSTBM scroll regions (`\x1B[1;31r`) and Reverse Index (`\x1BM`)
|
|
6156
|
+
* to scroll chat history within a fixed top zone — bytes the shared
|
|
6157
|
+
* renderer drops, leaving the mobile feed with only the most recent
|
|
6158
|
+
* frame instead of the full reply. See ./renderer.ts.
|
|
6159
|
+
*/
|
|
6160
|
+
renderToLines(buffer) {
|
|
6161
|
+
return renderCodexBuffer(buffer);
|
|
6162
|
+
}
|
|
5920
6163
|
parseTuiChrome(line) {
|
|
5921
6164
|
return parseCodexChrome(line);
|
|
5922
6165
|
}
|
|
@@ -6431,7 +6674,7 @@ var OutputService = class _OutputService {
|
|
|
6431
6674
|
return;
|
|
6432
6675
|
}
|
|
6433
6676
|
if (elapsed < _OutputService.WARMUP_MS) return;
|
|
6434
|
-
const lines = renderLines(this.pty.content);
|
|
6677
|
+
const lines = this.runtime.renderToLines?.(this.pty.content) ?? renderLines(this.pty.content);
|
|
6435
6678
|
const parseLine2 = this.runtime.parseTuiChrome?.bind(this.runtime) ?? (() => null);
|
|
6436
6679
|
this.steps.ingest(lines, parseLine2);
|
|
6437
6680
|
const stepsDelta = this.steps.consumeDelta();
|
|
@@ -6489,7 +6732,7 @@ var OutputService = class _OutputService {
|
|
|
6489
6732
|
}
|
|
6490
6733
|
}
|
|
6491
6734
|
finalize() {
|
|
6492
|
-
const lines = renderLines(this.pty.content);
|
|
6735
|
+
const lines = this.runtime.renderToLines?.(this.pty.content) ?? renderLines(this.pty.content);
|
|
6493
6736
|
const parseLine2 = this.runtime.parseTuiChrome?.bind(this.runtime) ?? (() => null);
|
|
6494
6737
|
this.steps.ingest(lines, parseLine2);
|
|
6495
6738
|
const stepsDelta = this.steps.consumeDelta();
|
|
@@ -10142,7 +10385,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
10142
10385
|
// src/commands/version.ts
|
|
10143
10386
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
10144
10387
|
function version() {
|
|
10145
|
-
const v = true ? "2.12.
|
|
10388
|
+
const v = true ? "2.12.12" : "unknown";
|
|
10146
10389
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
10147
10390
|
}
|
|
10148
10391
|
|
|
@@ -10277,7 +10520,7 @@ function checkForUpdates() {
|
|
|
10277
10520
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
10278
10521
|
if (process.env.CI) return;
|
|
10279
10522
|
if (!process.stdout.isTTY) return;
|
|
10280
|
-
const current = true ? "2.12.
|
|
10523
|
+
const current = true ? "2.12.12" : null;
|
|
10281
10524
|
if (!current) return;
|
|
10282
10525
|
const cache = readCache();
|
|
10283
10526
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.12",
|
|
4
4
|
"description": "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands — from anywhere.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|