codeam-cli 2.12.1 → 2.12.2
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 +335 -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.2",
|
|
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,46 @@ 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
|
+
function filterCodexChrome(lines) {
|
|
5805
|
+
const out = [];
|
|
5806
|
+
let skipEchoContinuation = false;
|
|
5807
|
+
for (const line of lines) {
|
|
5808
|
+
const t2 = line.trimEnd();
|
|
5809
|
+
const trimmed = t2.trimStart();
|
|
5810
|
+
if (!trimmed) {
|
|
5811
|
+
skipEchoContinuation = false;
|
|
5812
|
+
continue;
|
|
5813
|
+
}
|
|
5814
|
+
if (BOX_DRAW_RE.test(trimmed)) continue;
|
|
5815
|
+
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;
|
|
5816
|
+
if (TIP_RE.test(t2) || LEARN_MORE_RE.test(t2)) continue;
|
|
5817
|
+
if (CODEX_AGENT_REPLY_RE.test(trimmed)) {
|
|
5818
|
+
skipEchoContinuation = false;
|
|
5819
|
+
out.push(t2.replace(/^(\s*)•\s/, "$1"));
|
|
5820
|
+
continue;
|
|
5821
|
+
}
|
|
5822
|
+
if (CODEX_USER_ECHO_RE.test(trimmed)) {
|
|
5823
|
+
skipEchoContinuation = true;
|
|
5824
|
+
continue;
|
|
5825
|
+
}
|
|
5826
|
+
if (skipEchoContinuation) continue;
|
|
5827
|
+
out.push(t2);
|
|
5828
|
+
}
|
|
5829
|
+
return out;
|
|
5830
|
+
}
|
|
5831
|
+
function parseCodexChrome(_line) {
|
|
5832
|
+
return null;
|
|
5833
|
+
}
|
|
5834
|
+
function detectCodexSelector(_lines) {
|
|
5835
|
+
return null;
|
|
5836
|
+
}
|
|
5837
|
+
|
|
5791
5838
|
// src/agents/codex/runtime.ts
|
|
5792
5839
|
var CODEX_CONTEXT_WINDOW = 272e3;
|
|
5793
5840
|
var CODEX_MODELS = [
|
|
@@ -5850,6 +5897,16 @@ var CodexRuntimeStrategy = class {
|
|
|
5850
5897
|
summarizeInstruction(_mode) {
|
|
5851
5898
|
return { ptyInput: "/compact\r" };
|
|
5852
5899
|
}
|
|
5900
|
+
// ─── TUI parser strategy methods ─────────────────────────────────
|
|
5901
|
+
parseTuiChrome(line) {
|
|
5902
|
+
return parseCodexChrome(line);
|
|
5903
|
+
}
|
|
5904
|
+
filterTuiOutput(lines) {
|
|
5905
|
+
return filterCodexChrome(lines);
|
|
5906
|
+
}
|
|
5907
|
+
detectInteractivePrompt(lines) {
|
|
5908
|
+
return detectCodexSelector(lines);
|
|
5909
|
+
}
|
|
5853
5910
|
};
|
|
5854
5911
|
async function installCodexViaNpm() {
|
|
5855
5912
|
return new Promise((resolve2, reject) => {
|
|
@@ -5978,9 +6035,17 @@ var ChromeStepTracker = class {
|
|
|
5978
6035
|
this.history = [];
|
|
5979
6036
|
this.sentCount = 0;
|
|
5980
6037
|
}
|
|
5981
|
-
/**
|
|
5982
|
-
|
|
5983
|
-
|
|
6038
|
+
/**
|
|
6039
|
+
* Parse the rendered lines using the supplied per-agent parser,
|
|
6040
|
+
* appending unseen steps to the cumulative history.
|
|
6041
|
+
*
|
|
6042
|
+
* @param lines Screen lines from `renderToLines`.
|
|
6043
|
+
* @param parseLine Per-agent chrome parser — `runtime.parseTuiChrome`
|
|
6044
|
+
* bound to the active strategy, or a no-op `() => null`
|
|
6045
|
+
* when the agent doesn't surface tool-call chrome.
|
|
6046
|
+
*/
|
|
6047
|
+
ingest(lines, parseLine2) {
|
|
6048
|
+
const visible = lines.map((l) => parseLine2(l)).filter((s) => s !== null);
|
|
5984
6049
|
if (visible.length === 0) return;
|
|
5985
6050
|
for (const step of visible) {
|
|
5986
6051
|
const exists = this.history.some(
|
|
@@ -6191,18 +6256,13 @@ function hasPrintable(raw) {
|
|
|
6191
6256
|
function renderLines(buffer) {
|
|
6192
6257
|
return renderToLines(buffer);
|
|
6193
6258
|
}
|
|
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
6259
|
|
|
6201
6260
|
// src/services/output.service.ts
|
|
6202
6261
|
var OutputService = class _OutputService {
|
|
6203
6262
|
pty = new PtyBuffer();
|
|
6204
6263
|
steps = new ChromeStepTracker();
|
|
6205
6264
|
emitter;
|
|
6265
|
+
runtime;
|
|
6206
6266
|
lastSentContent = "";
|
|
6207
6267
|
pollTimer = null;
|
|
6208
6268
|
startTime = 0;
|
|
@@ -6227,7 +6287,7 @@ var OutputService = class _OutputService {
|
|
|
6227
6287
|
static EMPTY_TIMEOUT_MS = 6e4;
|
|
6228
6288
|
/** Hard turn cap — pathological no-op turns get cut after 2 minutes. */
|
|
6229
6289
|
static MAX_MS = 12e4;
|
|
6230
|
-
constructor(sessionId, pluginId, onSessionIdDetected, onRateLimitDetected, onTurnComplete, onTerminalTurnDetected, pluginAuthToken) {
|
|
6290
|
+
constructor(sessionId, pluginId, onSessionIdDetected, onRateLimitDetected, onTurnComplete, onTerminalTurnDetected, pluginAuthToken, runtime) {
|
|
6231
6291
|
this.onSessionIdDetected = onSessionIdDetected;
|
|
6232
6292
|
this.onRateLimitDetected = onRateLimitDetected;
|
|
6233
6293
|
this.onTurnComplete = onTurnComplete;
|
|
@@ -6237,6 +6297,21 @@ var OutputService = class _OutputService {
|
|
|
6237
6297
|
pluginId,
|
|
6238
6298
|
pluginAuthToken
|
|
6239
6299
|
});
|
|
6300
|
+
this.runtime = runtime ?? {
|
|
6301
|
+
id: "claude",
|
|
6302
|
+
meta: {},
|
|
6303
|
+
prepareLaunch: async () => ({ cmd: "", args: [] }),
|
|
6304
|
+
resumeLaunchArgs: () => [],
|
|
6305
|
+
resolveHistoryDir: () => null,
|
|
6306
|
+
parseHistoryFile: () => [],
|
|
6307
|
+
getCurrentUsage: () => null,
|
|
6308
|
+
fetchWeeklyUsage: async () => null,
|
|
6309
|
+
listModels: async () => [],
|
|
6310
|
+
changeModelInstruction: () => ({ type: "pty" }),
|
|
6311
|
+
summarizeInstruction: () => ({ ptyInput: "" }),
|
|
6312
|
+
filterTuiOutput: (lines) => lines,
|
|
6313
|
+
detectInteractivePrompt: () => null
|
|
6314
|
+
};
|
|
6240
6315
|
}
|
|
6241
6316
|
// ─── Turn lifecycle ──────────────────────────────────────────────
|
|
6242
6317
|
/**
|
|
@@ -6333,13 +6408,14 @@ var OutputService = class _OutputService {
|
|
|
6333
6408
|
}
|
|
6334
6409
|
if (elapsed < _OutputService.WARMUP_MS) return;
|
|
6335
6410
|
const lines = renderLines(this.pty.content);
|
|
6336
|
-
this.
|
|
6411
|
+
const parseLine2 = this.runtime.parseTuiChrome?.bind(this.runtime) ?? (() => null);
|
|
6412
|
+
this.steps.ingest(lines, parseLine2);
|
|
6337
6413
|
const stepsDelta = this.steps.consumeDelta();
|
|
6338
6414
|
if (stepsDelta.length > 0) {
|
|
6339
6415
|
this.send({ type: "chrome_steps", appendSteps: stepsDelta }).catch(() => {
|
|
6340
6416
|
});
|
|
6341
6417
|
}
|
|
6342
|
-
const selector =
|
|
6418
|
+
const selector = this.runtime.detectInteractivePrompt(lines);
|
|
6343
6419
|
if (selector) {
|
|
6344
6420
|
const idleMs2 = this.pty.lastPushTime > 0 ? now - this.pty.lastPushTime : elapsed;
|
|
6345
6421
|
log.trace(
|
|
@@ -6364,7 +6440,7 @@ var OutputService = class _OutputService {
|
|
|
6364
6440
|
}
|
|
6365
6441
|
return;
|
|
6366
6442
|
}
|
|
6367
|
-
const content =
|
|
6443
|
+
const content = this.runtime.filterTuiOutput(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
6368
6444
|
if (!content) {
|
|
6369
6445
|
log.trace(
|
|
6370
6446
|
"outputSvc",
|
|
@@ -6390,13 +6466,14 @@ var OutputService = class _OutputService {
|
|
|
6390
6466
|
}
|
|
6391
6467
|
finalize() {
|
|
6392
6468
|
const lines = renderLines(this.pty.content);
|
|
6393
|
-
this.
|
|
6469
|
+
const parseLine2 = this.runtime.parseTuiChrome?.bind(this.runtime) ?? (() => null);
|
|
6470
|
+
this.steps.ingest(lines, parseLine2);
|
|
6394
6471
|
const stepsDelta = this.steps.consumeDelta();
|
|
6395
6472
|
if (stepsDelta.length > 0) {
|
|
6396
6473
|
this.send({ type: "chrome_steps", appendSteps: stepsDelta }).catch(() => {
|
|
6397
6474
|
});
|
|
6398
6475
|
}
|
|
6399
|
-
const selector =
|
|
6476
|
+
const selector = this.runtime.detectInteractivePrompt(lines);
|
|
6400
6477
|
this.stopPoll();
|
|
6401
6478
|
this.pty.deactivate();
|
|
6402
6479
|
if (selector) {
|
|
@@ -6413,7 +6490,7 @@ var OutputService = class _OutputService {
|
|
|
6413
6490
|
).catch(() => {
|
|
6414
6491
|
});
|
|
6415
6492
|
} else {
|
|
6416
|
-
const content =
|
|
6493
|
+
const content = this.runtime.filterTuiOutput(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
6417
6494
|
this.send(
|
|
6418
6495
|
{ type: "text", content, done: true },
|
|
6419
6496
|
{ critical: true }
|
|
@@ -7741,7 +7818,8 @@ async function start() {
|
|
|
7741
7818
|
const prevCount = historySvc.getCurrentMessageCount();
|
|
7742
7819
|
historySvc.waitForNewUserMessage(prevCount).then((userText) => outputSvc.startTerminalTurn(userText ?? void 0)).catch(() => outputSvc.startTerminalTurn(void 0));
|
|
7743
7820
|
},
|
|
7744
|
-
session.pluginAuthToken
|
|
7821
|
+
session.pluginAuthToken,
|
|
7822
|
+
runtime
|
|
7745
7823
|
);
|
|
7746
7824
|
const claude = new AgentService(
|
|
7747
7825
|
runtime,
|
|
@@ -10040,7 +10118,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
10040
10118
|
// src/commands/version.ts
|
|
10041
10119
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
10042
10120
|
function version() {
|
|
10043
|
-
const v = true ? "2.12.
|
|
10121
|
+
const v = true ? "2.12.2" : "unknown";
|
|
10044
10122
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
10045
10123
|
}
|
|
10046
10124
|
|
|
@@ -10175,7 +10253,7 @@ function checkForUpdates() {
|
|
|
10175
10253
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
10176
10254
|
if (process.env.CI) return;
|
|
10177
10255
|
if (!process.stdout.isTTY) return;
|
|
10178
|
-
const current = true ? "2.12.
|
|
10256
|
+
const current = true ? "2.12.2" : null;
|
|
10179
10257
|
if (!current) return;
|
|
10180
10258
|
const cache = readCache();
|
|
10181
10259
|
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.2",
|
|
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",
|