codeam-cli 2.12.1 → 2.12.3
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 +6 -0
- package/dist/index.js +340 -257
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ 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.1] — 2026-05-14
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Parse Codex rollouts for full transcripts + token usage
|
|
12
|
+
|
|
7
13
|
## [2.11.0] — 2026-05-13
|
|
8
14
|
|
|
9
15
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -1392,87 +1392,6 @@ var require_src = __commonJS({
|
|
|
1392
1392
|
// src/commands/start.ts
|
|
1393
1393
|
var import_picocolors2 = __toESM(require("picocolors"));
|
|
1394
1394
|
|
|
1395
|
-
// ../../packages/shared/src/protocol/parseChrome.ts
|
|
1396
|
-
var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
|
|
1397
|
-
var BULLET_TOOL_RE = /^•\s+(?:Read(?:ing)?|Edit(?:ing)?|Writ(?:e|ing)|Bash|Runn(?:ing)?|Search(?:ing)?|Glob(?:bing)?|Grep(?:ping)?|Creat(?:e|ing)|Execut(?:e|ing)|Task|Agent|NotebookEdit)\b/i;
|
|
1398
|
-
var TREE_LINE_RE = /^└\s/;
|
|
1399
|
-
var STATUS_LINE_RE = /^(?:\+|[🔴🟠🟡🟢🔵🟣🟤⚫⚪🌀💭✨])\s/u;
|
|
1400
|
-
function isChromeLine(line) {
|
|
1401
|
-
const t2 = line.replace(/️/g, "").trim();
|
|
1402
|
-
if (!t2) return false;
|
|
1403
|
-
if (/^[─━—═─\-]{3,}$/.test(t2)) return true;
|
|
1404
|
-
if (SPINNER_RE.test(t2)) return true;
|
|
1405
|
-
if (BULLET_TOOL_RE.test(t2)) return true;
|
|
1406
|
-
if (TREE_LINE_RE.test(t2)) return true;
|
|
1407
|
-
if (STATUS_LINE_RE.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) return true;
|
|
1408
|
-
if (/^↓\s*\d+\s*tokens/i.test(t2)) return true;
|
|
1409
|
-
if (/^\bthought\s+for\s+\d+/i.test(t2)) return true;
|
|
1410
|
-
if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) return true;
|
|
1411
|
-
if (/high\s*[·•]\s*\/effort/i.test(t2)) return true;
|
|
1412
|
-
if (/^[❯>]\s*$/.test(t2)) return true;
|
|
1413
|
-
if (/^\(thinking\)\s*$/.test(t2)) return true;
|
|
1414
|
-
if (/^\?\s.*shortcut/i.test(t2)) return true;
|
|
1415
|
-
if (/spending limit|usage limit/i.test(t2) && t2.length < 80) return true;
|
|
1416
|
-
if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) return true;
|
|
1417
|
-
if (t2.replace(/\s/g, "").length === 1) return true;
|
|
1418
|
-
if ((t2.match(/─/g)?.length ?? 0) >= 6) return true;
|
|
1419
|
-
if (/ctrl\+?o\s+to\s+expand/i.test(t2)) return true;
|
|
1420
|
-
const hasBoxPrefix = /^[│╭╰╮╯┌└┐┘├┤┬┴┼]/.test(t2);
|
|
1421
|
-
const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
|
|
1422
|
-
if (hasBoxPrefix && /^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) return true;
|
|
1423
|
-
return false;
|
|
1424
|
-
}
|
|
1425
|
-
function parseChromeLine(line) {
|
|
1426
|
-
const t2 = line.replace(/️/g, "").trim();
|
|
1427
|
-
if (!t2) return null;
|
|
1428
|
-
if (/^[─━—═─\-]{3,}$/.test(t2)) return null;
|
|
1429
|
-
if (/^[❯>]\s*$/.test(t2)) return null;
|
|
1430
|
-
if (t2.replace(/\s/g, "").length === 1) return null;
|
|
1431
|
-
if ((t2.match(/─/g)?.length ?? 0) >= 6) return null;
|
|
1432
|
-
if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) return null;
|
|
1433
|
-
if (/high\s*[·•]\s*\/effort/i.test(t2)) return null;
|
|
1434
|
-
if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) return null;
|
|
1435
|
-
if (/ctrl\+?o\s+to\s+expand/i.test(t2)) return null;
|
|
1436
|
-
if (/spending limit|usage limit/i.test(t2)) return null;
|
|
1437
|
-
if (/^\(thinking\)\s*$/.test(t2)) {
|
|
1438
|
-
return { tool: "thinking", label: "Thinking\u2026", status: "running" };
|
|
1439
|
-
}
|
|
1440
|
-
if (TREE_LINE_RE.test(t2)) return null;
|
|
1441
|
-
if (STATUS_LINE_RE.test(t2)) {
|
|
1442
|
-
const label = t2.slice(2).replace(/….*/s, "").trim() || "Thinking\u2026";
|
|
1443
|
-
return { tool: "thinking", label, status: "running" };
|
|
1444
|
-
}
|
|
1445
|
-
let text = t2;
|
|
1446
|
-
if (SPINNER_RE.test(t2)) {
|
|
1447
|
-
text = t2.slice(2).trim().replace(/….*/s, "").trim();
|
|
1448
|
-
} else if (BULLET_TOOL_RE.test(t2)) {
|
|
1449
|
-
text = t2.slice(2).trim();
|
|
1450
|
-
text = text.replace(/\s*\(ctrl\+?o[^)]*\)/gi, "").replace(/,\s*reading\s+\d+\s+files?\s*…?/gi, "").replace(/,\s*\d+\s+files?\s*…?/gi, "").replace(/…$/, "").trim();
|
|
1451
|
-
}
|
|
1452
|
-
if (!text) return null;
|
|
1453
|
-
return classifyStep(text);
|
|
1454
|
-
}
|
|
1455
|
-
function classifyStep(text) {
|
|
1456
|
-
if (/^Read(?:ing)?\s+/i.test(text)) {
|
|
1457
|
-
const label2 = text.replace(/^Read(?:ing)?\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
1458
|
-
return { tool: "read", label: label2, status: "running" };
|
|
1459
|
-
}
|
|
1460
|
-
if (/^Edit(?:ing)?\s+|^Writ(?:e|ing|ing to)\s+|^Creat(?:e|ing)\s+/i.test(text)) {
|
|
1461
|
-
const label2 = text.replace(/^(?:Edit(?:ing)?|Writ(?:e|ing(?: to)?)|Creat(?:e|ing))\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
1462
|
-
return { tool: "edit", label: label2, status: "running" };
|
|
1463
|
-
}
|
|
1464
|
-
if (/^Runn(?:ing)?\s+|^Execut(?:e|ing)\s+|^Bash(?:ing)?\s*:|^\$\s+/i.test(text)) {
|
|
1465
|
-
const label2 = text.replace(/^(?:Runn(?:ing)?|Execut(?:e|ing)|Bash(?:ing)?:|\$)\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
1466
|
-
return { tool: "bash", label: label2, status: "running" };
|
|
1467
|
-
}
|
|
1468
|
-
if (/^Search(?:ing)?\s+for\s+|^Grep(?:ping)?\s*:/i.test(text)) {
|
|
1469
|
-
const label2 = text.replace(/^(?:Search(?:ing)?\s+for|Grep(?:ping)?:)\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
1470
|
-
return { tool: "search", label: label2, status: "running" };
|
|
1471
|
-
}
|
|
1472
|
-
const label = text.replace(/\.\.\.$/, "").trim();
|
|
1473
|
-
return { tool: "other", label, status: "running" };
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
1395
|
// ../../packages/shared/src/protocol/renderToLines.ts
|
|
1477
1396
|
function renderToLines(raw) {
|
|
1478
1397
|
const screen = [""];
|
|
@@ -1580,162 +1499,6 @@ function renderToLines(raw) {
|
|
|
1580
1499
|
return screen;
|
|
1581
1500
|
}
|
|
1582
1501
|
|
|
1583
|
-
// ../../packages/shared/src/protocol/selector.ts
|
|
1584
|
-
function detectSelector(lines) {
|
|
1585
|
-
if (lines.some((l) => /\?\s+for\s+shortcuts/i.test(l.trim()))) return null;
|
|
1586
|
-
const clean = lines.map(
|
|
1587
|
-
(l) => l.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "").replace(/\s*[│╭╰╮╯┌└┐┘├┤┬┴┼─━═]+\s*$/, "")
|
|
1588
|
-
);
|
|
1589
|
-
const hasCursor = clean.some((l) => /^[❯>]\s*\d+\./.test(l.trim()));
|
|
1590
|
-
const looksLikeTrust = clean.some(
|
|
1591
|
-
(l) => /\b(?:trust\s+the\s+files|trust\s+this\s+folder|safety\s+check)\b/i.test(l)
|
|
1592
|
-
);
|
|
1593
|
-
if (!hasCursor && !looksLikeTrust) return null;
|
|
1594
|
-
let optionStartIdx = -1;
|
|
1595
|
-
for (let i = 0; i < clean.length; i++) {
|
|
1596
|
-
if (/^(?:[❯>]\s*)?\d+\.\s/.test(clean[i].trim())) {
|
|
1597
|
-
optionStartIdx = i;
|
|
1598
|
-
break;
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
if (optionStartIdx === -1) return null;
|
|
1602
|
-
const questionParts = [];
|
|
1603
|
-
for (let i = 0; i < optionStartIdx; i++) {
|
|
1604
|
-
const t2 = clean[i].trim();
|
|
1605
|
-
if (!t2) continue;
|
|
1606
|
-
if (/^[─━—═\-]{3,}$/.test(t2)) continue;
|
|
1607
|
-
if (/^\[.*\]$/.test(t2)) continue;
|
|
1608
|
-
if (/^[>❯]\s/.test(t2)) continue;
|
|
1609
|
-
if (!t2.includes(" ") && t2.length > 15) continue;
|
|
1610
|
-
questionParts.push(t2);
|
|
1611
|
-
}
|
|
1612
|
-
const question = questionParts.filter((line, i, arr) => !arr.some((other, j2) => j2 !== i && other.includes(line))).join("\n").trim();
|
|
1613
|
-
const optionLabels = /* @__PURE__ */ new Map();
|
|
1614
|
-
const optionDescs = /* @__PURE__ */ new Map();
|
|
1615
|
-
let currentNum = -1;
|
|
1616
|
-
for (let i = optionStartIdx; i < clean.length; i++) {
|
|
1617
|
-
const t2 = clean[i].trim();
|
|
1618
|
-
if (!t2) continue;
|
|
1619
|
-
const m = t2.match(/^(?:[❯>]\s*)?(\d+)\.\s+(.+)/);
|
|
1620
|
-
if (m) {
|
|
1621
|
-
const num = parseInt(m[1], 10);
|
|
1622
|
-
if (!optionLabels.has(num)) {
|
|
1623
|
-
optionLabels.set(num, m[2].trim());
|
|
1624
|
-
optionDescs.set(num, []);
|
|
1625
|
-
}
|
|
1626
|
-
currentNum = num;
|
|
1627
|
-
} else if (currentNum !== -1 && !/^Enter to/i.test(t2) && !/^[─━—═\-]{3,}$/.test(t2) && !/↑.*↓.*navigate/i.test(t2) && !/Esc to/i.test(t2)) {
|
|
1628
|
-
optionDescs.get(currentNum)?.push(t2);
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
const keys = [...optionLabels.keys()].sort((a, b) => a - b);
|
|
1632
|
-
if (keys.length < 2 || keys[0] !== 1) return null;
|
|
1633
|
-
return {
|
|
1634
|
-
question,
|
|
1635
|
-
options: keys.map((k2) => optionLabels.get(k2)),
|
|
1636
|
-
optionDescriptions: keys.map((k2) => (optionDescs.get(k2) ?? []).join(" ").trim()),
|
|
1637
|
-
currentIndex: 0
|
|
1638
|
-
};
|
|
1639
|
-
}
|
|
1640
|
-
function detectListSelector(lines) {
|
|
1641
|
-
if (!lines.some((l) => /[↑↓].*navigate/i.test(l.trim()))) return null;
|
|
1642
|
-
if (lines.some((l) => /^❯\s*\d+\./.test(l.trim()))) return null;
|
|
1643
|
-
if (!lines.some((l) => /^\s+❯\s+\S/.test(l))) return null;
|
|
1644
|
-
const isSelected = (line) => /^\s+❯\s+\S/.test(line);
|
|
1645
|
-
const isUnselected = (line) => /^ \S/.test(line);
|
|
1646
|
-
const isItem = (line) => isSelected(line) || isUnselected(line);
|
|
1647
|
-
let optionStartIdx = -1;
|
|
1648
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1649
|
-
if (isItem(lines[i])) {
|
|
1650
|
-
optionStartIdx = i;
|
|
1651
|
-
break;
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
if (optionStartIdx === -1) return null;
|
|
1655
|
-
const questionParts = [];
|
|
1656
|
-
for (let i = 0; i < optionStartIdx; i++) {
|
|
1657
|
-
const t2 = lines[i].trim();
|
|
1658
|
-
if (!t2) continue;
|
|
1659
|
-
if (/^[─━—═\-]{3,}$/.test(t2)) continue;
|
|
1660
|
-
if (/[┌└│┐┘├┤┬┴┼]/.test(t2)) {
|
|
1661
|
-
const inner = t2.replace(/[│┌└┐┘├┤┬┴┼─]/g, "").trim();
|
|
1662
|
-
if (inner) questionParts.push(inner);
|
|
1663
|
-
continue;
|
|
1664
|
-
}
|
|
1665
|
-
if (/^[>❯]\s/.test(t2)) continue;
|
|
1666
|
-
if (/[↑↓].*navigate/i.test(t2)) continue;
|
|
1667
|
-
if (!t2.includes(" ") && t2.length > 15) continue;
|
|
1668
|
-
questionParts.push(t2);
|
|
1669
|
-
}
|
|
1670
|
-
const question = questionParts.filter((line, i, arr) => !arr.some((other, j2) => j2 !== i && other.includes(line))).join("\n").trim();
|
|
1671
|
-
const options = [];
|
|
1672
|
-
let currentIndex = 0;
|
|
1673
|
-
for (const line of lines.slice(optionStartIdx)) {
|
|
1674
|
-
const t2 = line.trim();
|
|
1675
|
-
if (!t2) continue;
|
|
1676
|
-
if (/[↑↓].*navigate/i.test(t2)) break;
|
|
1677
|
-
if (/^[─━—═\-]{3,}$/.test(t2)) continue;
|
|
1678
|
-
if (isSelected(line)) {
|
|
1679
|
-
currentIndex = options.length;
|
|
1680
|
-
options.push(t2.replace(/^❯\s+/, "").trim());
|
|
1681
|
-
} else if (isUnselected(line)) {
|
|
1682
|
-
options.push(t2);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
if (options.length < 2) return null;
|
|
1686
|
-
return {
|
|
1687
|
-
question,
|
|
1688
|
-
options,
|
|
1689
|
-
optionDescriptions: options.map(() => ""),
|
|
1690
|
-
currentIndex
|
|
1691
|
-
};
|
|
1692
|
-
}
|
|
1693
|
-
|
|
1694
|
-
// ../../packages/shared/src/protocol/filterChrome.ts
|
|
1695
|
-
function filterChrome(lines) {
|
|
1696
|
-
const result = [];
|
|
1697
|
-
let skipEchoContinuation = false;
|
|
1698
|
-
for (const line of lines) {
|
|
1699
|
-
const t2 = line.trim();
|
|
1700
|
-
if (!t2) {
|
|
1701
|
-
skipEchoContinuation = false;
|
|
1702
|
-
continue;
|
|
1703
|
-
}
|
|
1704
|
-
if (/^[─━—═─\-]{3,}$/.test(t2)) {
|
|
1705
|
-
skipEchoContinuation = false;
|
|
1706
|
-
continue;
|
|
1707
|
-
}
|
|
1708
|
-
if (/^[●⏺]\s/.test(t2)) skipEchoContinuation = false;
|
|
1709
|
-
if (/^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/.test(t2)) continue;
|
|
1710
|
-
if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) continue;
|
|
1711
|
-
if (/high\s*[·•]\s*\/effort/i.test(t2)) continue;
|
|
1712
|
-
if (/^[❯>]\s*$/.test(t2)) continue;
|
|
1713
|
-
if (/^\(thinking\)\s*$/.test(t2)) continue;
|
|
1714
|
-
if (/^\?\s.*shortcut/i.test(t2)) continue;
|
|
1715
|
-
if (/spending limit|usage limit/i.test(t2) && t2.length < 80) continue;
|
|
1716
|
-
if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) continue;
|
|
1717
|
-
if (t2.replace(/\s/g, "").length === 1) continue;
|
|
1718
|
-
if ((t2.match(/─/g)?.length ?? 0) >= 6) continue;
|
|
1719
|
-
if (/ctrl\+?o\s+to\s+expand/i.test(t2)) continue;
|
|
1720
|
-
if (/^•\s+(?:Read(?:ing)?|Edit(?:ing)?|Writ(?:e|ing)|Bash|Runn(?:ing)?|Search(?:ing)?|Glob(?:bing)?|Grep(?:ping)?|Creat(?:e|ing)|Execut(?:e|ing)|Task|Agent|NotebookEdit)\b/i.test(
|
|
1721
|
-
t2
|
|
1722
|
-
))
|
|
1723
|
-
continue;
|
|
1724
|
-
if (/^└\s/.test(t2)) continue;
|
|
1725
|
-
if (/^\+\s/.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) continue;
|
|
1726
|
-
if (/^↓\s*\d+\s*tokens/i.test(t2)) continue;
|
|
1727
|
-
if (/^\bthought\s+for\s+\d+/i.test(t2)) continue;
|
|
1728
|
-
const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
|
|
1729
|
-
if (/^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) {
|
|
1730
|
-
skipEchoContinuation = true;
|
|
1731
|
-
continue;
|
|
1732
|
-
}
|
|
1733
|
-
if (skipEchoContinuation) continue;
|
|
1734
|
-
result.push(line);
|
|
1735
|
-
}
|
|
1736
|
-
return result;
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
1502
|
// ../../packages/shared/src/models/pricing.ts
|
|
1740
1503
|
var MODEL_PRICING = {
|
|
1741
1504
|
// ── Anthropic / Claude ────────────────────────────────────
|
|
@@ -1919,7 +1682,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
1919
1682
|
// package.json
|
|
1920
1683
|
var package_default = {
|
|
1921
1684
|
name: "codeam-cli",
|
|
1922
|
-
version: "2.12.
|
|
1685
|
+
version: "2.12.3",
|
|
1923
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.",
|
|
1924
1687
|
type: "commonjs",
|
|
1925
1688
|
main: "dist/index.js",
|
|
@@ -5410,6 +5173,239 @@ function parseHistoryFile(filePath) {
|
|
|
5410
5173
|
return out;
|
|
5411
5174
|
}
|
|
5412
5175
|
|
|
5176
|
+
// src/agents/claude/parsing.ts
|
|
5177
|
+
function filterChrome(lines) {
|
|
5178
|
+
const result = [];
|
|
5179
|
+
let skipEchoContinuation = false;
|
|
5180
|
+
for (const line of lines) {
|
|
5181
|
+
const t2 = line.trim();
|
|
5182
|
+
if (!t2) {
|
|
5183
|
+
skipEchoContinuation = false;
|
|
5184
|
+
continue;
|
|
5185
|
+
}
|
|
5186
|
+
if (/^[─━—═─\-]{3,}$/.test(t2)) {
|
|
5187
|
+
skipEchoContinuation = false;
|
|
5188
|
+
continue;
|
|
5189
|
+
}
|
|
5190
|
+
if (/^[●⏺]\s/.test(t2)) skipEchoContinuation = false;
|
|
5191
|
+
if (/^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/.test(t2)) continue;
|
|
5192
|
+
if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) continue;
|
|
5193
|
+
if (/high\s*[·•]\s*\/effort/i.test(t2)) continue;
|
|
5194
|
+
if (/^[❯>]\s*$/.test(t2)) continue;
|
|
5195
|
+
if (/^\(thinking\)\s*$/.test(t2)) continue;
|
|
5196
|
+
if (/^\?\s.*shortcut/i.test(t2)) continue;
|
|
5197
|
+
if (/spending limit|usage limit/i.test(t2) && t2.length < 80) continue;
|
|
5198
|
+
if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) continue;
|
|
5199
|
+
if (t2.replace(/\s/g, "").length === 1) continue;
|
|
5200
|
+
if ((t2.match(/─/g)?.length ?? 0) >= 6) continue;
|
|
5201
|
+
if (/ctrl\+?o\s+to\s+expand/i.test(t2)) continue;
|
|
5202
|
+
if (/^•\s+(?:Read(?:ing)?|Edit(?:ing)?|Writ(?:e|ing)|Bash|Runn(?:ing)?|Search(?:ing)?|Glob(?:bing)?|Grep(?:ping)?|Creat(?:e|ing)|Execut(?:e|ing)|Task|Agent|NotebookEdit)\b/i.test(
|
|
5203
|
+
t2
|
|
5204
|
+
))
|
|
5205
|
+
continue;
|
|
5206
|
+
if (/^└\s/.test(t2)) continue;
|
|
5207
|
+
if (/^\+\s/.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) continue;
|
|
5208
|
+
if (/^↓\s*\d+\s*tokens/i.test(t2)) continue;
|
|
5209
|
+
if (/^\bthought\s+for\s+\d+/i.test(t2)) continue;
|
|
5210
|
+
const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
|
|
5211
|
+
if (/^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) {
|
|
5212
|
+
skipEchoContinuation = true;
|
|
5213
|
+
continue;
|
|
5214
|
+
}
|
|
5215
|
+
if (skipEchoContinuation) continue;
|
|
5216
|
+
result.push(line);
|
|
5217
|
+
}
|
|
5218
|
+
return result;
|
|
5219
|
+
}
|
|
5220
|
+
var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
|
|
5221
|
+
var BULLET_TOOL_RE = /^•\s+(?:Read(?:ing)?|Edit(?:ing)?|Writ(?:e|ing)|Bash|Runn(?:ing)?|Search(?:ing)?|Glob(?:bing)?|Grep(?:ping)?|Creat(?:e|ing)|Execut(?:e|ing)|Task|Agent|NotebookEdit)\b/i;
|
|
5222
|
+
var TREE_LINE_RE = /^└\s/;
|
|
5223
|
+
var STATUS_LINE_RE = /^(?:\+|[🔴🟠🟡🟢🔵🟣🟤⚫⚪🌀💭✨])\s/u;
|
|
5224
|
+
function isChromeLine(line) {
|
|
5225
|
+
const t2 = line.replace(/️/g, "").trim();
|
|
5226
|
+
if (!t2) return false;
|
|
5227
|
+
if (/^[─━—═─\-]{3,}$/.test(t2)) return true;
|
|
5228
|
+
if (SPINNER_RE.test(t2)) return true;
|
|
5229
|
+
if (BULLET_TOOL_RE.test(t2)) return true;
|
|
5230
|
+
if (TREE_LINE_RE.test(t2)) return true;
|
|
5231
|
+
if (STATUS_LINE_RE.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) return true;
|
|
5232
|
+
if (/^↓\s*\d+\s*tokens/i.test(t2)) return true;
|
|
5233
|
+
if (/^\bthought\s+for\s+\d+/i.test(t2)) return true;
|
|
5234
|
+
if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) return true;
|
|
5235
|
+
if (/high\s*[·•]\s*\/effort/i.test(t2)) return true;
|
|
5236
|
+
if (/^[❯>]\s*$/.test(t2)) return true;
|
|
5237
|
+
if (/^\(thinking\)\s*$/.test(t2)) return true;
|
|
5238
|
+
if (/^\?\s.*shortcut/i.test(t2)) return true;
|
|
5239
|
+
if (/spending limit|usage limit/i.test(t2) && t2.length < 80) return true;
|
|
5240
|
+
if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) return true;
|
|
5241
|
+
if (t2.replace(/\s/g, "").length === 1) return true;
|
|
5242
|
+
if ((t2.match(/─/g)?.length ?? 0) >= 6) return true;
|
|
5243
|
+
if (/ctrl\+?o\s+to\s+expand/i.test(t2)) return true;
|
|
5244
|
+
const hasBoxPrefix = /^[│╭╰╮╯┌└┐┘├┤┬┴┼]/.test(t2);
|
|
5245
|
+
const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
|
|
5246
|
+
if (hasBoxPrefix && /^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) return true;
|
|
5247
|
+
return false;
|
|
5248
|
+
}
|
|
5249
|
+
function parseChromeLine(line) {
|
|
5250
|
+
const t2 = line.replace(/️/g, "").trim();
|
|
5251
|
+
if (!t2) return null;
|
|
5252
|
+
if (/^[─━—═─\-]{3,}$/.test(t2)) return null;
|
|
5253
|
+
if (/^[❯>]\s*$/.test(t2)) return null;
|
|
5254
|
+
if (t2.replace(/\s/g, "").length === 1) return null;
|
|
5255
|
+
if ((t2.match(/─/g)?.length ?? 0) >= 6) return null;
|
|
5256
|
+
if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) return null;
|
|
5257
|
+
if (/high\s*[·•]\s*\/effort/i.test(t2)) return null;
|
|
5258
|
+
if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) return null;
|
|
5259
|
+
if (/ctrl\+?o\s+to\s+expand/i.test(t2)) return null;
|
|
5260
|
+
if (/spending limit|usage limit/i.test(t2)) return null;
|
|
5261
|
+
if (/^\(thinking\)\s*$/.test(t2)) {
|
|
5262
|
+
return { tool: "thinking", label: "Thinking\u2026", status: "running" };
|
|
5263
|
+
}
|
|
5264
|
+
if (TREE_LINE_RE.test(t2)) return null;
|
|
5265
|
+
if (STATUS_LINE_RE.test(t2)) {
|
|
5266
|
+
const label = t2.slice(2).replace(/….*/s, "").trim() || "Thinking\u2026";
|
|
5267
|
+
return { tool: "thinking", label, status: "running" };
|
|
5268
|
+
}
|
|
5269
|
+
let text = t2;
|
|
5270
|
+
if (SPINNER_RE.test(t2)) {
|
|
5271
|
+
text = t2.slice(2).trim().replace(/….*/s, "").trim();
|
|
5272
|
+
} else if (BULLET_TOOL_RE.test(t2)) {
|
|
5273
|
+
text = t2.slice(2).trim();
|
|
5274
|
+
text = text.replace(/\s*\(ctrl\+?o[^)]*\)/gi, "").replace(/,\s*reading\s+\d+\s+files?\s*…?/gi, "").replace(/,\s*\d+\s+files?\s*…?/gi, "").replace(/…$/, "").trim();
|
|
5275
|
+
}
|
|
5276
|
+
if (!text) return null;
|
|
5277
|
+
return classifyStep(text);
|
|
5278
|
+
}
|
|
5279
|
+
function classifyStep(text) {
|
|
5280
|
+
if (/^Read(?:ing)?\s+/i.test(text)) {
|
|
5281
|
+
const label2 = text.replace(/^Read(?:ing)?\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
5282
|
+
return { tool: "read", label: label2, status: "running" };
|
|
5283
|
+
}
|
|
5284
|
+
if (/^Edit(?:ing)?\s+|^Writ(?:e|ing|ing to)\s+|^Creat(?:e|ing)\s+/i.test(text)) {
|
|
5285
|
+
const label2 = text.replace(/^(?:Edit(?:ing)?|Writ(?:e|ing(?: to)?)|Creat(?:e|ing))\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
5286
|
+
return { tool: "edit", label: label2, status: "running" };
|
|
5287
|
+
}
|
|
5288
|
+
if (/^Runn(?:ing)?\s+|^Execut(?:e|ing)\s+|^Bash(?:ing)?\s*:|^\$\s+/i.test(text)) {
|
|
5289
|
+
const label2 = text.replace(/^(?:Runn(?:ing)?|Execut(?:e|ing)|Bash(?:ing)?:|\$)\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
5290
|
+
return { tool: "bash", label: label2, status: "running" };
|
|
5291
|
+
}
|
|
5292
|
+
if (/^Search(?:ing)?\s+for\s+|^Grep(?:ping)?\s*:/i.test(text)) {
|
|
5293
|
+
const label2 = text.replace(/^(?:Search(?:ing)?\s+for|Grep(?:ping)?:)\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
5294
|
+
return { tool: "search", label: label2, status: "running" };
|
|
5295
|
+
}
|
|
5296
|
+
const label = text.replace(/\.\.\.$/, "").trim();
|
|
5297
|
+
return { tool: "other", label, status: "running" };
|
|
5298
|
+
}
|
|
5299
|
+
function detectSelector(lines) {
|
|
5300
|
+
if (lines.some((l) => /\?\s+for\s+shortcuts/i.test(l.trim()))) return null;
|
|
5301
|
+
const clean = lines.map(
|
|
5302
|
+
(l) => l.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "").replace(/\s*[│╭╰╮╯┌└┐┘├┤┬┴┼─━═]+\s*$/, "")
|
|
5303
|
+
);
|
|
5304
|
+
const hasCursor = clean.some((l) => /^[❯>]\s*\d+\./.test(l.trim()));
|
|
5305
|
+
const looksLikeTrust = clean.some(
|
|
5306
|
+
(l) => /\b(?:trust\s+the\s+files|trust\s+this\s+folder|safety\s+check)\b/i.test(l)
|
|
5307
|
+
);
|
|
5308
|
+
if (!hasCursor && !looksLikeTrust) return null;
|
|
5309
|
+
let optionStartIdx = -1;
|
|
5310
|
+
for (let i = 0; i < clean.length; i++) {
|
|
5311
|
+
if (/^(?:[❯>]\s*)?\d+\.\s/.test(clean[i].trim())) {
|
|
5312
|
+
optionStartIdx = i;
|
|
5313
|
+
break;
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5316
|
+
if (optionStartIdx === -1) return null;
|
|
5317
|
+
const questionParts = [];
|
|
5318
|
+
for (let i = 0; i < optionStartIdx; i++) {
|
|
5319
|
+
const t2 = clean[i].trim();
|
|
5320
|
+
if (!t2) continue;
|
|
5321
|
+
if (/^[─━—═\-]{3,}$/.test(t2)) continue;
|
|
5322
|
+
if (/^\[.*\]$/.test(t2)) continue;
|
|
5323
|
+
if (/^[>❯]\s/.test(t2)) continue;
|
|
5324
|
+
if (!t2.includes(" ") && t2.length > 15) continue;
|
|
5325
|
+
questionParts.push(t2);
|
|
5326
|
+
}
|
|
5327
|
+
const question = questionParts.filter((line, i, arr) => !arr.some((other, j2) => j2 !== i && other.includes(line))).join("\n").trim();
|
|
5328
|
+
const optionLabels = /* @__PURE__ */ new Map();
|
|
5329
|
+
const optionDescs = /* @__PURE__ */ new Map();
|
|
5330
|
+
let currentNum = -1;
|
|
5331
|
+
for (let i = optionStartIdx; i < clean.length; i++) {
|
|
5332
|
+
const t2 = clean[i].trim();
|
|
5333
|
+
if (!t2) continue;
|
|
5334
|
+
const m = t2.match(/^(?:[❯>]\s*)?(\d+)\.\s+(.+)/);
|
|
5335
|
+
if (m) {
|
|
5336
|
+
const num = parseInt(m[1], 10);
|
|
5337
|
+
if (!optionLabels.has(num)) {
|
|
5338
|
+
optionLabels.set(num, m[2].trim());
|
|
5339
|
+
optionDescs.set(num, []);
|
|
5340
|
+
}
|
|
5341
|
+
currentNum = num;
|
|
5342
|
+
} else if (currentNum !== -1 && !/^Enter to/i.test(t2) && !/^[─━—═\-]{3,}$/.test(t2) && !/↑.*↓.*navigate/i.test(t2) && !/Esc to/i.test(t2)) {
|
|
5343
|
+
optionDescs.get(currentNum)?.push(t2);
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
5346
|
+
const keys = [...optionLabels.keys()].sort((a, b) => a - b);
|
|
5347
|
+
if (keys.length < 2 || keys[0] !== 1) return null;
|
|
5348
|
+
return {
|
|
5349
|
+
question,
|
|
5350
|
+
options: keys.map((k2) => optionLabels.get(k2)),
|
|
5351
|
+
optionDescriptions: keys.map((k2) => (optionDescs.get(k2) ?? []).join(" ").trim()),
|
|
5352
|
+
currentIndex: 0
|
|
5353
|
+
};
|
|
5354
|
+
}
|
|
5355
|
+
function detectListSelector(lines) {
|
|
5356
|
+
if (!lines.some((l) => /[↑↓].*navigate/i.test(l.trim()))) return null;
|
|
5357
|
+
if (lines.some((l) => /^❯\s*\d+\./.test(l.trim()))) return null;
|
|
5358
|
+
if (!lines.some((l) => /^\s+❯\s+\S/.test(l))) return null;
|
|
5359
|
+
const isSelected = (line) => /^\s+❯\s+\S/.test(line);
|
|
5360
|
+
const isUnselected = (line) => /^ \S/.test(line);
|
|
5361
|
+
const isItem = (line) => isSelected(line) || isUnselected(line);
|
|
5362
|
+
let optionStartIdx = -1;
|
|
5363
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5364
|
+
if (isItem(lines[i])) {
|
|
5365
|
+
optionStartIdx = i;
|
|
5366
|
+
break;
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
5369
|
+
if (optionStartIdx === -1) return null;
|
|
5370
|
+
const questionParts = [];
|
|
5371
|
+
for (let i = 0; i < optionStartIdx; i++) {
|
|
5372
|
+
const t2 = lines[i].trim();
|
|
5373
|
+
if (!t2) continue;
|
|
5374
|
+
if (/^[─━—═\-]{3,}$/.test(t2)) continue;
|
|
5375
|
+
if (/[┌└│┐┘├┤┬┴┼]/.test(t2)) {
|
|
5376
|
+
const inner = t2.replace(/[│┌└┐┘├┤┬┴┼─]/g, "").trim();
|
|
5377
|
+
if (inner) questionParts.push(inner);
|
|
5378
|
+
continue;
|
|
5379
|
+
}
|
|
5380
|
+
if (/^[>❯]\s/.test(t2)) continue;
|
|
5381
|
+
if (/[↑↓].*navigate/i.test(t2)) continue;
|
|
5382
|
+
if (!t2.includes(" ") && t2.length > 15) continue;
|
|
5383
|
+
questionParts.push(t2);
|
|
5384
|
+
}
|
|
5385
|
+
const question = questionParts.filter((line, i, arr) => !arr.some((other, j2) => j2 !== i && other.includes(line))).join("\n").trim();
|
|
5386
|
+
const options = [];
|
|
5387
|
+
let currentIndex = 0;
|
|
5388
|
+
for (const line of lines.slice(optionStartIdx)) {
|
|
5389
|
+
const t2 = line.trim();
|
|
5390
|
+
if (!t2) continue;
|
|
5391
|
+
if (/[↑↓].*navigate/i.test(t2)) break;
|
|
5392
|
+
if (/^[─━—═\-]{3,}$/.test(t2)) continue;
|
|
5393
|
+
if (isSelected(line)) {
|
|
5394
|
+
currentIndex = options.length;
|
|
5395
|
+
options.push(t2.replace(/^❯\s+/, "").trim());
|
|
5396
|
+
} else if (isUnselected(line)) {
|
|
5397
|
+
options.push(t2);
|
|
5398
|
+
}
|
|
5399
|
+
}
|
|
5400
|
+
if (options.length < 2) return null;
|
|
5401
|
+
return {
|
|
5402
|
+
question,
|
|
5403
|
+
options,
|
|
5404
|
+
optionDescriptions: options.map(() => ""),
|
|
5405
|
+
currentIndex
|
|
5406
|
+
};
|
|
5407
|
+
}
|
|
5408
|
+
|
|
5413
5409
|
// src/agents/claude/runtime.ts
|
|
5414
5410
|
var ClaudeRuntimeStrategy = class {
|
|
5415
5411
|
id = "claude";
|
|
@@ -5449,6 +5445,17 @@ var ClaudeRuntimeStrategy = class {
|
|
|
5449
5445
|
if (mode === "normal") return { ptyInput: "/compact\r" };
|
|
5450
5446
|
return { ptyInput: "/compact\r" };
|
|
5451
5447
|
}
|
|
5448
|
+
// ─── TUI parser strategy methods ─────────────────────────────────
|
|
5449
|
+
parseTuiChrome(line) {
|
|
5450
|
+
if (!isChromeLine(line)) return null;
|
|
5451
|
+
return parseChromeLine(line);
|
|
5452
|
+
}
|
|
5453
|
+
filterTuiOutput(lines) {
|
|
5454
|
+
return filterChrome(lines);
|
|
5455
|
+
}
|
|
5456
|
+
detectInteractivePrompt(lines) {
|
|
5457
|
+
return detectSelector(lines) ?? detectListSelector(lines);
|
|
5458
|
+
}
|
|
5452
5459
|
};
|
|
5453
5460
|
|
|
5454
5461
|
// src/agents/claude/deploy.ts
|
|
@@ -5788,6 +5795,51 @@ function getCurrentUsage2(historyDir) {
|
|
|
5788
5795
|
};
|
|
5789
5796
|
}
|
|
5790
5797
|
|
|
5798
|
+
// src/agents/codex/parsing.ts
|
|
5799
|
+
var BOX_DRAW_RE = /^[╭─╮│╰╯]/u;
|
|
5800
|
+
var CODEX_USER_ECHO_RE = /^[›>]\s+\S/u;
|
|
5801
|
+
var CODEX_AGENT_REPLY_RE = /^[•·]\s/u;
|
|
5802
|
+
var TIP_RE = /^\s*Tip:\s/i;
|
|
5803
|
+
var LEARN_MORE_RE = /^\s*Learn more:\s/i;
|
|
5804
|
+
var CODEX_STATUS_FOOTER_RE = /\bdefault\s+[·•]\s+\S+/i;
|
|
5805
|
+
function filterCodexChrome(lines) {
|
|
5806
|
+
const out = [];
|
|
5807
|
+
let skipEchoContinuation = false;
|
|
5808
|
+
for (const line of lines) {
|
|
5809
|
+
const t2 = line.trimEnd();
|
|
5810
|
+
const trimmed = t2.trimStart();
|
|
5811
|
+
if (!trimmed) {
|
|
5812
|
+
skipEchoContinuation = false;
|
|
5813
|
+
continue;
|
|
5814
|
+
}
|
|
5815
|
+
if (BOX_DRAW_RE.test(trimmed)) continue;
|
|
5816
|
+
if (/^OpenAI Codex\b/i.test(trimmed) || /^>_\s+OpenAI Codex\b/i.test(trimmed) || /^model:\s/i.test(trimmed) || /^directory:\s/i.test(trimmed)) continue;
|
|
5817
|
+
if (TIP_RE.test(t2) || LEARN_MORE_RE.test(t2)) continue;
|
|
5818
|
+
if (CODEX_STATUS_FOOTER_RE.test(trimmed)) {
|
|
5819
|
+
skipEchoContinuation = false;
|
|
5820
|
+
continue;
|
|
5821
|
+
}
|
|
5822
|
+
if (CODEX_AGENT_REPLY_RE.test(trimmed)) {
|
|
5823
|
+
skipEchoContinuation = false;
|
|
5824
|
+
out.push(t2.replace(/^(\s*)[•·]\s/, "$1"));
|
|
5825
|
+
continue;
|
|
5826
|
+
}
|
|
5827
|
+
if (CODEX_USER_ECHO_RE.test(trimmed)) {
|
|
5828
|
+
skipEchoContinuation = true;
|
|
5829
|
+
continue;
|
|
5830
|
+
}
|
|
5831
|
+
if (skipEchoContinuation) continue;
|
|
5832
|
+
out.push(t2);
|
|
5833
|
+
}
|
|
5834
|
+
return out;
|
|
5835
|
+
}
|
|
5836
|
+
function parseCodexChrome(_line) {
|
|
5837
|
+
return null;
|
|
5838
|
+
}
|
|
5839
|
+
function detectCodexSelector(_lines) {
|
|
5840
|
+
return null;
|
|
5841
|
+
}
|
|
5842
|
+
|
|
5791
5843
|
// src/agents/codex/runtime.ts
|
|
5792
5844
|
var CODEX_CONTEXT_WINDOW = 272e3;
|
|
5793
5845
|
var CODEX_MODELS = [
|
|
@@ -5850,6 +5902,16 @@ var CodexRuntimeStrategy = class {
|
|
|
5850
5902
|
summarizeInstruction(_mode) {
|
|
5851
5903
|
return { ptyInput: "/compact\r" };
|
|
5852
5904
|
}
|
|
5905
|
+
// ─── TUI parser strategy methods ─────────────────────────────────
|
|
5906
|
+
parseTuiChrome(line) {
|
|
5907
|
+
return parseCodexChrome(line);
|
|
5908
|
+
}
|
|
5909
|
+
filterTuiOutput(lines) {
|
|
5910
|
+
return filterCodexChrome(lines);
|
|
5911
|
+
}
|
|
5912
|
+
detectInteractivePrompt(lines) {
|
|
5913
|
+
return detectCodexSelector(lines);
|
|
5914
|
+
}
|
|
5853
5915
|
};
|
|
5854
5916
|
async function installCodexViaNpm() {
|
|
5855
5917
|
return new Promise((resolve2, reject) => {
|
|
@@ -5978,9 +6040,17 @@ var ChromeStepTracker = class {
|
|
|
5978
6040
|
this.history = [];
|
|
5979
6041
|
this.sentCount = 0;
|
|
5980
6042
|
}
|
|
5981
|
-
/**
|
|
5982
|
-
|
|
5983
|
-
|
|
6043
|
+
/**
|
|
6044
|
+
* Parse the rendered lines using the supplied per-agent parser,
|
|
6045
|
+
* appending unseen steps to the cumulative history.
|
|
6046
|
+
*
|
|
6047
|
+
* @param lines Screen lines from `renderToLines`.
|
|
6048
|
+
* @param parseLine Per-agent chrome parser — `runtime.parseTuiChrome`
|
|
6049
|
+
* bound to the active strategy, or a no-op `() => null`
|
|
6050
|
+
* when the agent doesn't surface tool-call chrome.
|
|
6051
|
+
*/
|
|
6052
|
+
ingest(lines, parseLine2) {
|
|
6053
|
+
const visible = lines.map((l) => parseLine2(l)).filter((s) => s !== null);
|
|
5984
6054
|
if (visible.length === 0) return;
|
|
5985
6055
|
for (const step of visible) {
|
|
5986
6056
|
const exists = this.history.some(
|
|
@@ -6191,18 +6261,13 @@ function hasPrintable(raw) {
|
|
|
6191
6261
|
function renderLines(buffer) {
|
|
6192
6262
|
return renderToLines(buffer);
|
|
6193
6263
|
}
|
|
6194
|
-
function detectAnySelector(lines) {
|
|
6195
|
-
return detectSelector(lines) ?? detectListSelector(lines);
|
|
6196
|
-
}
|
|
6197
|
-
function extractContent(lines) {
|
|
6198
|
-
return filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
6199
|
-
}
|
|
6200
6264
|
|
|
6201
6265
|
// src/services/output.service.ts
|
|
6202
6266
|
var OutputService = class _OutputService {
|
|
6203
6267
|
pty = new PtyBuffer();
|
|
6204
6268
|
steps = new ChromeStepTracker();
|
|
6205
6269
|
emitter;
|
|
6270
|
+
runtime;
|
|
6206
6271
|
lastSentContent = "";
|
|
6207
6272
|
pollTimer = null;
|
|
6208
6273
|
startTime = 0;
|
|
@@ -6227,7 +6292,7 @@ var OutputService = class _OutputService {
|
|
|
6227
6292
|
static EMPTY_TIMEOUT_MS = 6e4;
|
|
6228
6293
|
/** Hard turn cap — pathological no-op turns get cut after 2 minutes. */
|
|
6229
6294
|
static MAX_MS = 12e4;
|
|
6230
|
-
constructor(sessionId, pluginId, onSessionIdDetected, onRateLimitDetected, onTurnComplete, onTerminalTurnDetected, pluginAuthToken) {
|
|
6295
|
+
constructor(sessionId, pluginId, onSessionIdDetected, onRateLimitDetected, onTurnComplete, onTerminalTurnDetected, pluginAuthToken, runtime) {
|
|
6231
6296
|
this.onSessionIdDetected = onSessionIdDetected;
|
|
6232
6297
|
this.onRateLimitDetected = onRateLimitDetected;
|
|
6233
6298
|
this.onTurnComplete = onTurnComplete;
|
|
@@ -6237,6 +6302,21 @@ var OutputService = class _OutputService {
|
|
|
6237
6302
|
pluginId,
|
|
6238
6303
|
pluginAuthToken
|
|
6239
6304
|
});
|
|
6305
|
+
this.runtime = runtime ?? {
|
|
6306
|
+
id: "claude",
|
|
6307
|
+
meta: {},
|
|
6308
|
+
prepareLaunch: async () => ({ cmd: "", args: [] }),
|
|
6309
|
+
resumeLaunchArgs: () => [],
|
|
6310
|
+
resolveHistoryDir: () => null,
|
|
6311
|
+
parseHistoryFile: () => [],
|
|
6312
|
+
getCurrentUsage: () => null,
|
|
6313
|
+
fetchWeeklyUsage: async () => null,
|
|
6314
|
+
listModels: async () => [],
|
|
6315
|
+
changeModelInstruction: () => ({ type: "pty" }),
|
|
6316
|
+
summarizeInstruction: () => ({ ptyInput: "" }),
|
|
6317
|
+
filterTuiOutput: (lines) => lines,
|
|
6318
|
+
detectInteractivePrompt: () => null
|
|
6319
|
+
};
|
|
6240
6320
|
}
|
|
6241
6321
|
// ─── Turn lifecycle ──────────────────────────────────────────────
|
|
6242
6322
|
/**
|
|
@@ -6333,13 +6413,14 @@ var OutputService = class _OutputService {
|
|
|
6333
6413
|
}
|
|
6334
6414
|
if (elapsed < _OutputService.WARMUP_MS) return;
|
|
6335
6415
|
const lines = renderLines(this.pty.content);
|
|
6336
|
-
this.
|
|
6416
|
+
const parseLine2 = this.runtime.parseTuiChrome?.bind(this.runtime) ?? (() => null);
|
|
6417
|
+
this.steps.ingest(lines, parseLine2);
|
|
6337
6418
|
const stepsDelta = this.steps.consumeDelta();
|
|
6338
6419
|
if (stepsDelta.length > 0) {
|
|
6339
6420
|
this.send({ type: "chrome_steps", appendSteps: stepsDelta }).catch(() => {
|
|
6340
6421
|
});
|
|
6341
6422
|
}
|
|
6342
|
-
const selector =
|
|
6423
|
+
const selector = this.runtime.detectInteractivePrompt(lines);
|
|
6343
6424
|
if (selector) {
|
|
6344
6425
|
const idleMs2 = this.pty.lastPushTime > 0 ? now - this.pty.lastPushTime : elapsed;
|
|
6345
6426
|
log.trace(
|
|
@@ -6364,7 +6445,7 @@ var OutputService = class _OutputService {
|
|
|
6364
6445
|
}
|
|
6365
6446
|
return;
|
|
6366
6447
|
}
|
|
6367
|
-
const content =
|
|
6448
|
+
const content = this.runtime.filterTuiOutput(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
6368
6449
|
if (!content) {
|
|
6369
6450
|
log.trace(
|
|
6370
6451
|
"outputSvc",
|
|
@@ -6390,13 +6471,14 @@ var OutputService = class _OutputService {
|
|
|
6390
6471
|
}
|
|
6391
6472
|
finalize() {
|
|
6392
6473
|
const lines = renderLines(this.pty.content);
|
|
6393
|
-
this.
|
|
6474
|
+
const parseLine2 = this.runtime.parseTuiChrome?.bind(this.runtime) ?? (() => null);
|
|
6475
|
+
this.steps.ingest(lines, parseLine2);
|
|
6394
6476
|
const stepsDelta = this.steps.consumeDelta();
|
|
6395
6477
|
if (stepsDelta.length > 0) {
|
|
6396
6478
|
this.send({ type: "chrome_steps", appendSteps: stepsDelta }).catch(() => {
|
|
6397
6479
|
});
|
|
6398
6480
|
}
|
|
6399
|
-
const selector =
|
|
6481
|
+
const selector = this.runtime.detectInteractivePrompt(lines);
|
|
6400
6482
|
this.stopPoll();
|
|
6401
6483
|
this.pty.deactivate();
|
|
6402
6484
|
if (selector) {
|
|
@@ -6413,7 +6495,7 @@ var OutputService = class _OutputService {
|
|
|
6413
6495
|
).catch(() => {
|
|
6414
6496
|
});
|
|
6415
6497
|
} else {
|
|
6416
|
-
const content =
|
|
6498
|
+
const content = this.runtime.filterTuiOutput(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
6417
6499
|
this.send(
|
|
6418
6500
|
{ type: "text", content, done: true },
|
|
6419
6501
|
{ critical: true }
|
|
@@ -7741,7 +7823,8 @@ async function start() {
|
|
|
7741
7823
|
const prevCount = historySvc.getCurrentMessageCount();
|
|
7742
7824
|
historySvc.waitForNewUserMessage(prevCount).then((userText) => outputSvc.startTerminalTurn(userText ?? void 0)).catch(() => outputSvc.startTerminalTurn(void 0));
|
|
7743
7825
|
},
|
|
7744
|
-
session.pluginAuthToken
|
|
7826
|
+
session.pluginAuthToken,
|
|
7827
|
+
runtime
|
|
7745
7828
|
);
|
|
7746
7829
|
const claude = new AgentService(
|
|
7747
7830
|
runtime,
|
|
@@ -10040,7 +10123,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
10040
10123
|
// src/commands/version.ts
|
|
10041
10124
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
10042
10125
|
function version() {
|
|
10043
|
-
const v = true ? "2.12.
|
|
10126
|
+
const v = true ? "2.12.3" : "unknown";
|
|
10044
10127
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
10045
10128
|
}
|
|
10046
10129
|
|
|
@@ -10175,7 +10258,7 @@ function checkForUpdates() {
|
|
|
10175
10258
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
10176
10259
|
if (process.env.CI) return;
|
|
10177
10260
|
if (!process.stdout.isTTY) return;
|
|
10178
|
-
const current = true ? "2.12.
|
|
10261
|
+
const current = true ? "2.12.3" : null;
|
|
10179
10262
|
if (!current) return;
|
|
10180
10263
|
const cache = readCache();
|
|
10181
10264
|
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.3",
|
|
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",
|