codeam-cli 2.10.8 → 2.11.0

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/index.js +1287 -890
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -519,9 +519,9 @@ var require_windowsPtyAgent = __commonJS({
519
519
  "use strict";
520
520
  Object.defineProperty(exports2, "__esModule", { value: true });
521
521
  exports2.argsToCommandLine = exports2.WindowsPtyAgent = void 0;
522
- var fs13 = require("fs");
523
- var os12 = require("os");
524
- var path19 = require("path");
522
+ var fs15 = require("fs");
523
+ var os14 = require("os");
524
+ var path21 = require("path");
525
525
  var child_process_1 = require("child_process");
526
526
  var net_1 = require("net");
527
527
  var windowsConoutConnection_1 = require_windowsConoutConnection();
@@ -557,7 +557,7 @@ var require_windowsPtyAgent = __commonJS({
557
557
  }
558
558
  }
559
559
  this._ptyNative = this._useConpty ? conptyNative : winptyNative;
560
- cwd = path19.resolve(cwd);
560
+ cwd = path21.resolve(cwd);
561
561
  var commandLine = argsToCommandLine(file, args2);
562
562
  var term;
563
563
  if (this._useConpty) {
@@ -578,7 +578,7 @@ var require_windowsPtyAgent = __commonJS({
578
578
  this._outSocket.on("connect", function() {
579
579
  _this._outSocket.emit("ready_datapipe");
580
580
  });
581
- var inSocketFD = fs13.openSync(term.conin, "w");
581
+ var inSocketFD = fs15.openSync(term.conin, "w");
582
582
  this._inSocket = new net_1.Socket({
583
583
  fd: inSocketFD,
584
584
  readable: false,
@@ -679,7 +679,7 @@ var require_windowsPtyAgent = __commonJS({
679
679
  WindowsPtyAgent2.prototype._getConsoleProcessList = function() {
680
680
  var _this = this;
681
681
  return new Promise(function(resolve2) {
682
- var agent = child_process_1.fork(path19.join(__dirname, "conpty_console_list_agent"), [_this._innerPid.toString()]);
682
+ var agent = child_process_1.fork(path21.join(__dirname, "conpty_console_list_agent"), [_this._innerPid.toString()]);
683
683
  agent.on("message", function(message) {
684
684
  clearTimeout(timeout);
685
685
  resolve2(message.consoleProcessList);
@@ -702,7 +702,7 @@ var require_windowsPtyAgent = __commonJS({
702
702
  configurable: true
703
703
  });
704
704
  WindowsPtyAgent2.prototype._getWindowsBuildNumber = function() {
705
- var osVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(os12.release());
705
+ var osVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(os14.release());
706
706
  var buildNumber = 0;
707
707
  if (osVersion && osVersion.length === 4) {
708
708
  buildNumber = parseInt(osVersion[3]);
@@ -1012,15 +1012,15 @@ var require_unixTerminal = __commonJS({
1012
1012
  })();
1013
1013
  Object.defineProperty(exports2, "__esModule", { value: true });
1014
1014
  exports2.UnixTerminal = void 0;
1015
- var fs13 = require("fs");
1016
- var path19 = require("path");
1015
+ var fs15 = require("fs");
1016
+ var path21 = require("path");
1017
1017
  var tty = require("tty");
1018
1018
  var terminal_1 = require_terminal();
1019
1019
  var utils_1 = require_utils();
1020
1020
  var native = utils_1.loadNativeModule("pty");
1021
1021
  var pty = native.module;
1022
1022
  var helperPath = native.dir + "/spawn-helper";
1023
- helperPath = path19.resolve(__dirname, helperPath);
1023
+ helperPath = path21.resolve(__dirname, helperPath);
1024
1024
  helperPath = helperPath.replace("app.asar", "app.asar.unpacked");
1025
1025
  helperPath = helperPath.replace("node_modules.asar", "node_modules.asar.unpacked");
1026
1026
  var DEFAULT_FILE = "sh";
@@ -1275,7 +1275,7 @@ var require_unixTerminal = __commonJS({
1275
1275
  return;
1276
1276
  }
1277
1277
  var task = this._writeQueue[0];
1278
- fs13.write(this._fd, task.buffer, task.offset, function(err, written) {
1278
+ fs15.write(this._fd, task.buffer, task.offset, function(err, written) {
1279
1279
  if (err) {
1280
1280
  if ("code" in err && err.code === "EAGAIN") {
1281
1281
  _this._writeImmediate = setImmediate(function() {
@@ -1392,6 +1392,419 @@ 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
+ // ../../packages/shared/src/protocol/renderToLines.ts
1477
+ function renderToLines(raw) {
1478
+ const screen = [""];
1479
+ let row = 0;
1480
+ let col = 0;
1481
+ function ensureRow() {
1482
+ while (screen.length <= row) screen.push("");
1483
+ }
1484
+ function writeChar(ch) {
1485
+ ensureRow();
1486
+ if (col < screen[row].length) {
1487
+ screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);
1488
+ } else {
1489
+ while (screen[row].length < col) screen[row] += " ";
1490
+ screen[row] += ch;
1491
+ }
1492
+ col++;
1493
+ }
1494
+ let i = 0;
1495
+ while (i < raw.length) {
1496
+ const ch = raw[i];
1497
+ if (ch === "\x1B") {
1498
+ i++;
1499
+ if (i >= raw.length) break;
1500
+ if (raw[i] === "[") {
1501
+ i++;
1502
+ let param = "";
1503
+ while (i < raw.length && !/[@-~]/.test(raw[i])) param += raw[i++];
1504
+ const cmd = raw[i] ?? "";
1505
+ const n = parseInt(param) || 1;
1506
+ if (cmd === "A") {
1507
+ row = Math.max(0, row - n);
1508
+ } else if (cmd === "B") {
1509
+ row += n;
1510
+ ensureRow();
1511
+ } else if (cmd === "C") {
1512
+ col += n;
1513
+ } else if (cmd === "D") {
1514
+ col = Math.max(0, col - n);
1515
+ } else if (cmd === "G") {
1516
+ col = Math.max(0, n - 1);
1517
+ } else if (cmd === "H" || cmd === "f") {
1518
+ const p2 = param.split(";");
1519
+ row = Math.max(0, (parseInt(p2[0] ?? "1") || 1) - 1);
1520
+ col = Math.max(0, (parseInt(p2[1] ?? "1") || 1) - 1);
1521
+ ensureRow();
1522
+ } else if (cmd === "J") {
1523
+ if (param === "2" || param === "3") {
1524
+ screen.length = 1;
1525
+ screen[0] = "";
1526
+ row = 0;
1527
+ col = 0;
1528
+ } else if (param === "1") {
1529
+ for (let r = 0; r < row; r++) screen[r] = "";
1530
+ screen[row] = " ".repeat(col) + screen[row].slice(col);
1531
+ } else {
1532
+ screen[row] = screen[row].slice(0, col);
1533
+ screen.splice(row + 1);
1534
+ }
1535
+ } else if (cmd === "K") {
1536
+ ensureRow();
1537
+ if (param === "" || param === "0") screen[row] = screen[row].slice(0, col);
1538
+ else if (param === "1") screen[row] = " ".repeat(col) + screen[row].slice(col);
1539
+ else if (param === "2") screen[row] = "";
1540
+ } else if (cmd === "h" && (param === "?1049" || param === "?47")) {
1541
+ screen.length = 1;
1542
+ screen[0] = "";
1543
+ row = 0;
1544
+ col = 0;
1545
+ } else if (cmd === "l" && (param === "?1049" || param === "?47")) {
1546
+ screen.length = 1;
1547
+ screen[0] = "";
1548
+ row = 0;
1549
+ col = 0;
1550
+ }
1551
+ } else if (raw[i] === "]") {
1552
+ i++;
1553
+ while (i < raw.length) {
1554
+ if (raw[i] === "\x07") break;
1555
+ if (raw[i] === "\x1B" && i + 1 < raw.length && raw[i + 1] === "\\") {
1556
+ i++;
1557
+ break;
1558
+ }
1559
+ i++;
1560
+ }
1561
+ }
1562
+ } else if (ch === "\r") {
1563
+ if (i + 1 < raw.length && raw[i + 1] === "\n") {
1564
+ row++;
1565
+ col = 0;
1566
+ ensureRow();
1567
+ i++;
1568
+ } else {
1569
+ col = 0;
1570
+ }
1571
+ } else if (ch === "\n") {
1572
+ row++;
1573
+ col = 0;
1574
+ ensureRow();
1575
+ } else if (ch >= " " || ch === " ") {
1576
+ writeChar(ch);
1577
+ }
1578
+ i++;
1579
+ }
1580
+ return screen;
1581
+ }
1582
+
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
+ // ../../packages/shared/src/models/pricing.ts
1740
+ var MODEL_PRICING = {
1741
+ "claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
1742
+ "claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
1743
+ "claude-3-5-sonnet": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
1744
+ "claude-3-5-haiku": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
1745
+ "claude-3-haiku": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 }
1746
+ };
1747
+ var MODEL_CONTEXT_WINDOW = {
1748
+ "claude-opus-4": 1e6,
1749
+ "claude-sonnet-4": 1e6,
1750
+ "claude-3-5-sonnet": 2e5,
1751
+ "claude-3-5-haiku": 2e5,
1752
+ "claude-3-haiku": 2e5
1753
+ };
1754
+ var DEFAULT_CONTEXT_WINDOW = 2e5;
1755
+ function getPricing(model) {
1756
+ for (const [prefix, pricing] of Object.entries(MODEL_PRICING)) {
1757
+ if (model.startsWith(prefix)) return pricing;
1758
+ }
1759
+ return MODEL_PRICING["claude-sonnet-4"];
1760
+ }
1761
+ function getContextWindow(model) {
1762
+ if (!model) return DEFAULT_CONTEXT_WINDOW;
1763
+ for (const [prefix, size] of Object.entries(MODEL_CONTEXT_WINDOW)) {
1764
+ if (model.startsWith(prefix)) return size;
1765
+ }
1766
+ return DEFAULT_CONTEXT_WINDOW;
1767
+ }
1768
+
1769
+ // ../../packages/shared/src/agents/registry.ts
1770
+ var AGENT_REGISTRY = {
1771
+ claude: {
1772
+ id: "claude",
1773
+ displayName: "Claude Code",
1774
+ binaryName: "claude",
1775
+ enabled: true,
1776
+ supportedAuthKinds: ["oauth_token", "api_key"],
1777
+ preferredAuthKind: "oauth_token"
1778
+ },
1779
+ codex: {
1780
+ id: "codex",
1781
+ displayName: "Codex CLI",
1782
+ binaryName: "codex",
1783
+ enabled: false,
1784
+ supportedAuthKinds: ["oauth_token", "api_key"],
1785
+ preferredAuthKind: "oauth_token"
1786
+ },
1787
+ copilot: {
1788
+ id: "copilot",
1789
+ displayName: "GitHub Copilot CLI",
1790
+ binaryName: "gh",
1791
+ enabled: false,
1792
+ supportedAuthKinds: ["oauth_token"],
1793
+ preferredAuthKind: "oauth_token"
1794
+ }
1795
+ };
1796
+ function getEnabledAgents() {
1797
+ return Object.values(AGENT_REGISTRY).filter((m) => m.enabled);
1798
+ }
1799
+ function getAgent(id) {
1800
+ const meta = AGENT_REGISTRY[id];
1801
+ if (!meta) throw new Error(`Unknown agent id: ${id}`);
1802
+ return meta;
1803
+ }
1804
+ function isKnownAgentId(id) {
1805
+ return id === "claude" || id === "codex" || id === "copilot";
1806
+ }
1807
+
1395
1808
  // src/config.ts
1396
1809
  var fs = __toESM(require("fs"));
1397
1810
  var os = __toESM(require("os"));
@@ -1402,6 +1815,9 @@ var EMPTY_CONFIG = () => ({
1402
1815
  activeSessionId: null,
1403
1816
  sessions: []
1404
1817
  });
1818
+ function migrateSession(s) {
1819
+ return { ...s, agent: s.agent ?? "claude" };
1820
+ }
1405
1821
  function makeConfig(baseDir) {
1406
1822
  const dir = path.join(baseDir ?? os.homedir(), ".codeam");
1407
1823
  const file = path.join(dir, "config.json");
@@ -1411,7 +1827,8 @@ function makeConfig(baseDir) {
1411
1827
  return {
1412
1828
  pluginId: typeof raw.pluginId === "string" ? raw.pluginId : crypto.randomUUID(),
1413
1829
  activeSessionId: typeof raw.activeSessionId === "string" ? raw.activeSessionId : null,
1414
- sessions: Array.isArray(raw.sessions) ? raw.sessions : []
1830
+ sessions: Array.isArray(raw.sessions) ? raw.sessions.map(migrateSession) : [],
1831
+ preferredAgent: typeof raw.preferredAgent === "string" ? raw.preferredAgent : void 0
1415
1832
  };
1416
1833
  } catch {
1417
1834
  return EMPTY_CONFIG();
@@ -1465,10 +1882,16 @@ function makeConfig(baseDir) {
1465
1882
  } catch {
1466
1883
  }
1467
1884
  }
1468
- return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, clearAll: clearAll2 };
1885
+ function saveCliConfig2(c2) {
1886
+ save(c2);
1887
+ }
1888
+ function loadCliConfig2() {
1889
+ return load();
1890
+ }
1891
+ return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, clearAll: clearAll2, saveCliConfig: saveCliConfig2, loadCliConfig: loadCliConfig2 };
1469
1892
  }
1470
1893
  var _default = makeConfig();
1471
- var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, clearAll } = _default;
1894
+ var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, clearAll, saveCliConfig, loadCliConfig } = _default;
1472
1895
 
1473
1896
  // src/ui/banner.ts
1474
1897
  var import_picocolors = __toESM(require("picocolors"));
@@ -1477,7 +1900,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
1477
1900
  // package.json
1478
1901
  var package_default = {
1479
1902
  name: "codeam-cli",
1480
- version: "2.10.8",
1903
+ version: "2.11.0",
1481
1904
  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.",
1482
1905
  type: "commonjs",
1483
1906
  main: "dist/index.js",
@@ -2393,7 +2816,7 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2393
2816
  resizeHandler = null;
2394
2817
  /**
2395
2818
  * Factory that returns a working ConPTY strategy or `null` if
2396
- * node-pty can't load. The caller (claude.service.ts) decides
2819
+ * node-pty can't load. The caller (agent.service.ts) decides
2397
2820
  * whether to fall back to the legacy pipe strategy.
2398
2821
  */
2399
2822
  static tryCreate(opts) {
@@ -4498,9 +4921,10 @@ function buildClaudeLaunch(extraArgs = []) {
4498
4921
  return { cmd: found, args: extraArgs };
4499
4922
  }
4500
4923
 
4501
- // src/services/claude.service.ts
4502
- var ClaudeService = class {
4503
- constructor(opts) {
4924
+ // src/services/agent.service.ts
4925
+ var AgentService = class {
4926
+ constructor(runtime, opts) {
4927
+ this.runtime = runtime;
4504
4928
  this.opts = opts;
4505
4929
  this.strategyOpts = {
4506
4930
  onData: (d3) => {
@@ -4514,6 +4938,7 @@ var ClaudeService = class {
4514
4938
  onExit: opts.onExit
4515
4939
  };
4516
4940
  }
4941
+ runtime;
4517
4942
  opts;
4518
4943
  // Strategy is selected lazily inside spawn() so we can fall back from
4519
4944
  // ConPTY → legacy pipe at runtime if the native binding fails to load.
@@ -4545,10 +4970,20 @@ var ClaudeService = class {
4545
4970
  this.pendingInputs.length = 0;
4546
4971
  }
4547
4972
  async spawn() {
4548
- let launch = buildClaudeLaunch();
4549
- if (!launch) {
4973
+ let launch;
4974
+ try {
4975
+ launch = await this.runtime.prepareLaunch();
4976
+ } catch {
4550
4977
  const installed = await ensureClaudeInstalled();
4551
- if (installed) launch = buildClaudeLaunch();
4978
+ if (installed) {
4979
+ try {
4980
+ launch = await this.runtime.prepareLaunch();
4981
+ } catch {
4982
+ launch = null;
4983
+ }
4984
+ } else {
4985
+ launch = null;
4986
+ }
4552
4987
  if (!launch) {
4553
4988
  const cmd = process.platform === "win32" ? "irm https://claude.ai/install.ps1 | iex" : "curl -fsSL https://claude.ai/install.sh | bash";
4554
4989
  console.error(
@@ -4661,6 +5096,20 @@ var ClaudeService = class {
4661
5096
  s.write("\r");
4662
5097
  }, steps * ARROW_MS + ENTER_MS);
4663
5098
  }
5099
+ /**
5100
+ * Write raw bytes to the PTY without any auto-appended `\r` or delay.
5101
+ * Use this when the caller already owns the full input (e.g. the
5102
+ * `ptyInput` returned by `RuntimeStrategy.changeModelInstruction()`
5103
+ * already contains the trailing `\r`).
5104
+ */
5105
+ sendRawPtyInput(text) {
5106
+ if (!this.strategy) {
5107
+ log.trace("claude", "sendRawPtyInput dropped (no strategy)");
5108
+ return;
5109
+ }
5110
+ log.trace("claude", `sendRawPtyInput len=${text.length}`);
5111
+ this.strategy.write(text);
5112
+ }
4664
5113
  /** Send Escape key to Claude (cancels interactive prompts). */
4665
5114
  sendEscape() {
4666
5115
  this.strategy?.write("\x1B");
@@ -4675,390 +5124,559 @@ var ClaudeService = class {
4675
5124
  /**
4676
5125
  * Kill the current Claude process and relaunch it resuming the given session.
4677
5126
  * Pass auto=true to add --dangerously-skip-permissions (no confirmation prompts).
5127
+ *
5128
+ * For agents that use CLI flags (Claude: --resume <id>), `resumeLaunchArgs`
5129
+ * returns a non-empty array and we pass those directly to the binary.
5130
+ * For agents that use a post-spawn PTY instruction (e.g. Codex), `resumeLaunchArgs`
5131
+ * returns [] and `postSpawnInstruction` types the resume command into the PTY.
4678
5132
  */
4679
5133
  restart(sessionId, auto = false) {
4680
5134
  if (!this.strategy) return;
4681
- const extraArgs = ["--resume", sessionId];
4682
- if (auto) extraArgs.push("--dangerously-skip-permissions");
4683
- const launch = buildClaudeLaunch(extraArgs);
5135
+ const resumeArgs = auto ? this.runtime.resumeLaunchArgs(sessionId) : ["--resume", sessionId];
5136
+ const launch = buildClaudeLaunch(resumeArgs);
4684
5137
  if (!launch) return;
4685
5138
  this.strategy.kill();
4686
5139
  this.strategy.spawn(launch.cmd, this.opts.cwd, launch.args);
5140
+ if (resumeArgs.length === 0 && this.runtime.postSpawnInstruction) {
5141
+ const { ptyInput } = this.runtime.postSpawnInstruction(sessionId);
5142
+ setTimeout(() => {
5143
+ this.strategy?.write(ptyInput);
5144
+ }, 500);
5145
+ }
4687
5146
  }
4688
5147
  };
4689
5148
 
4690
- // ../../packages/shared/src/protocol/parseChrome.ts
4691
- var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
4692
- 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;
4693
- var TREE_LINE_RE = /^└\s/;
4694
- var STATUS_LINE_RE = /^(?:\+|[🔴🟠🟡🟢🔵🟣🟤⚫⚪🌀💭✨])\s/u;
4695
- function isChromeLine(line) {
4696
- const t2 = line.replace(/️/g, "").trim();
4697
- if (!t2) return false;
4698
- if (/^[─━—═─\-]{3,}$/.test(t2)) return true;
4699
- if (SPINNER_RE.test(t2)) return true;
4700
- if (BULLET_TOOL_RE.test(t2)) return true;
4701
- if (TREE_LINE_RE.test(t2)) return true;
4702
- if (STATUS_LINE_RE.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) return true;
4703
- if (/^↓\s*\d+\s*tokens/i.test(t2)) return true;
4704
- if (/^\bthought\s+for\s+\d+/i.test(t2)) return true;
4705
- if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) return true;
4706
- if (/high\s*[·•]\s*\/effort/i.test(t2)) return true;
4707
- if (/^[❯>]\s*$/.test(t2)) return true;
4708
- if (/^\(thinking\)\s*$/.test(t2)) return true;
4709
- if (/^\?\s.*shortcut/i.test(t2)) return true;
4710
- if (/spending limit|usage limit/i.test(t2) && t2.length < 80) return true;
4711
- if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) return true;
4712
- if (t2.replace(/\s/g, "").length === 1) return true;
4713
- if ((t2.match(/─/g)?.length ?? 0) >= 6) return true;
4714
- if (/ctrl\+?o\s+to\s+expand/i.test(t2)) return true;
4715
- const hasBoxPrefix = /^[│╭╰╮╯┌└┐┘├┤┬┴┼]/.test(t2);
4716
- const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
4717
- if (hasBoxPrefix && /^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) return true;
4718
- return false;
4719
- }
4720
- function parseChromeLine(line) {
4721
- const t2 = line.replace(/️/g, "").trim();
4722
- if (!t2) return null;
4723
- if (/^[─━—═─\-]{3,}$/.test(t2)) return null;
4724
- if (/^[❯>]\s*$/.test(t2)) return null;
4725
- if (t2.replace(/\s/g, "").length === 1) return null;
4726
- if ((t2.match(/─/g)?.length ?? 0) >= 6) return null;
4727
- if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) return null;
4728
- if (/high\s*[·•]\s*\/effort/i.test(t2)) return null;
4729
- if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) return null;
4730
- if (/ctrl\+?o\s+to\s+expand/i.test(t2)) return null;
4731
- if (/spending limit|usage limit/i.test(t2)) return null;
4732
- if (/^\(thinking\)\s*$/.test(t2)) {
4733
- return { tool: "thinking", label: "Thinking\u2026", status: "running" };
4734
- }
4735
- if (TREE_LINE_RE.test(t2)) return null;
4736
- if (STATUS_LINE_RE.test(t2)) {
4737
- const label = t2.slice(2).replace(/….*/s, "").trim() || "Thinking\u2026";
4738
- return { tool: "thinking", label, status: "running" };
4739
- }
4740
- let text = t2;
4741
- if (SPINNER_RE.test(t2)) {
4742
- text = t2.slice(2).trim().replace(/….*/s, "").trim();
4743
- } else if (BULLET_TOOL_RE.test(t2)) {
4744
- text = t2.slice(2).trim();
4745
- 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();
5149
+ // src/agents/claude/quota.ts
5150
+ var fs5 = __toESM(require("fs"));
5151
+ var os6 = __toESM(require("os"));
5152
+ var path8 = __toESM(require("path"));
5153
+ var import_child_process4 = require("child_process");
5154
+ var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
5155
+ m,s=pty.openpty()
5156
+ try:
5157
+ fcntl.ioctl(s,termios.TIOCSWINSZ,struct.pack('HHHH',30,120,0,0))
5158
+ except Exception:pass
5159
+ pid=os.fork()
5160
+ if pid==0:
5161
+ os.close(m);os.setsid()
5162
+ try:fcntl.ioctl(s,termios.TIOCSCTTY,0)
5163
+ except Exception:pass
5164
+ for fd in[0,1,2]:os.dup2(s,fd)
5165
+ if s>2:os.close(s)
5166
+ os.execvp(sys.argv[1],sys.argv[1:])
5167
+ sys.exit(127)
5168
+ os.close(s)
5169
+ done=[False]
5170
+ def onchld(n,f):
5171
+ try:os.waitpid(pid,os.WNOHANG)
5172
+ except Exception:pass
5173
+ done[0]=True
5174
+ signal.signal(signal.SIGCHLD,onchld)
5175
+ i=sys.stdin.fileno();o=sys.stdout.fileno()
5176
+ while not done[0]:
5177
+ try:r,_,_=select.select([i,m],[],[],0.1)
5178
+ except OSError as e:
5179
+ if e.errno==errno.EINTR:continue
5180
+ break
5181
+ if i in r:
5182
+ try:
5183
+ d=os.read(i,4096)
5184
+ if d:os.write(m,d)
5185
+ else:break
5186
+ except OSError:break
5187
+ if m in r:
5188
+ try:
5189
+ d=os.read(m,4096)
5190
+ if d:os.write(o,d)
5191
+ except OSError:done[0]=True
5192
+ try:os.kill(pid,signal.SIGTERM)
5193
+ except Exception:pass
5194
+ try:
5195
+ _,st=os.waitpid(pid,0)
5196
+ sys.exit((st>>8)&0xFF)
5197
+ except Exception:sys.exit(0)
5198
+ `;
5199
+ function parseUsageOutput(raw) {
5200
+ const clean = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, " ");
5201
+ const weekMatch = clean.match(/(\d+)%\s*used/i) || clean.match(/(\d+)\s*%/);
5202
+ if (!weekMatch) return null;
5203
+ const percent = parseInt(weekMatch[1], 10);
5204
+ const resetMatch = clean.match(/resets\s+(.+?)(?:\s*[\(\)]|$)/im);
5205
+ const resetAt = resetMatch?.[1]?.trim() || void 0;
5206
+ return { percent, resetAt };
5207
+ }
5208
+ async function fetchClaudeQuota() {
5209
+ return new Promise((resolve2) => {
5210
+ const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
5211
+ if (!claudeCmd) {
5212
+ resolve2(null);
5213
+ return;
5214
+ }
5215
+ const helperPath = path8.join(os6.tmpdir(), "codeam-quota-helper.py");
5216
+ fs5.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
5217
+ const python = findInPath("python3") ?? findInPath("python");
5218
+ if (!python) {
5219
+ resolve2(null);
5220
+ return;
5221
+ }
5222
+ const proc = (0, import_child_process4.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
5223
+ stdio: ["pipe", "pipe", "ignore"],
5224
+ cwd: process.cwd(),
5225
+ env: { ...process.env, TERM: "dumb", COLUMNS: "120", LINES: "30" }
5226
+ });
5227
+ let output = "";
5228
+ let resolved = false;
5229
+ proc.stdout?.on("data", (chunk) => {
5230
+ output += chunk.toString("utf8");
5231
+ });
5232
+ setTimeout(() => {
5233
+ proc.stdin?.write("/usage\r");
5234
+ setTimeout(() => {
5235
+ if (resolved) return;
5236
+ resolved = true;
5237
+ const result = parseUsageOutput(output);
5238
+ try {
5239
+ proc.kill();
5240
+ } catch {
5241
+ }
5242
+ try {
5243
+ fs5.unlinkSync(helperPath);
5244
+ } catch {
5245
+ }
5246
+ resolve2(result);
5247
+ }, 5e3);
5248
+ }, 8e3);
5249
+ setTimeout(() => {
5250
+ if (!resolved) {
5251
+ resolved = true;
5252
+ resolve2(null);
5253
+ }
5254
+ try {
5255
+ proc.kill();
5256
+ } catch {
5257
+ }
5258
+ }, 2e4);
5259
+ });
5260
+ }
5261
+
5262
+ // src/agents/claude/history.ts
5263
+ var fs6 = __toESM(require("fs"));
5264
+ var path9 = __toESM(require("path"));
5265
+ var os7 = __toESM(require("os"));
5266
+ function encodeCwd(cwd) {
5267
+ return cwd.replace(/[\\/:]/g, "-");
5268
+ }
5269
+ function resolveHistoryDir(cwd, projectsRoot) {
5270
+ const root = projectsRoot ?? path9.join(os7.homedir(), ".claude", "projects");
5271
+ const primary = path9.join(root, encodeCwd(cwd));
5272
+ if (fs6.existsSync(primary)) return primary;
5273
+ try {
5274
+ const entries = fs6.readdirSync(root, { withFileTypes: true });
5275
+ const wanted = encodeCwd(cwd);
5276
+ for (const e of entries) {
5277
+ if (!e.isDirectory()) continue;
5278
+ const candidate = e.name.replace(/-+/g, "-");
5279
+ if (candidate === wanted.replace(/-+/g, "-")) {
5280
+ return path9.join(root, e.name);
5281
+ }
5282
+ }
5283
+ } catch {
4746
5284
  }
4747
- if (!text) return null;
4748
- return classifyStep(text);
5285
+ return null;
4749
5286
  }
4750
- function classifyStep(text) {
4751
- if (/^Read(?:ing)?\s+/i.test(text)) {
4752
- const label2 = text.replace(/^Read(?:ing)?\s+/i, "").replace(/\.\.\.$/, "").trim();
4753
- return { tool: "read", label: label2, status: "running" };
5287
+ function getCurrentUsage(historyDir, bootTimeMs = 0) {
5288
+ const GRACE_MS = 5e3;
5289
+ const cutoff = bootTimeMs > 0 ? bootTimeMs - GRACE_MS : 0;
5290
+ let entries;
5291
+ try {
5292
+ entries = fs6.readdirSync(historyDir, { withFileTypes: true });
5293
+ } catch {
5294
+ return null;
4754
5295
  }
4755
- if (/^Edit(?:ing)?\s+|^Writ(?:e|ing|ing to)\s+|^Creat(?:e|ing)\s+/i.test(text)) {
4756
- const label2 = text.replace(/^(?:Edit(?:ing)?|Writ(?:e|ing(?: to)?)|Creat(?:e|ing))\s+/i, "").replace(/\.\.\.$/, "").trim();
4757
- return { tool: "edit", label: label2, status: "running" };
5296
+ const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5297
+ try {
5298
+ const stat3 = fs6.statSync(path9.join(historyDir, e.name));
5299
+ return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
5300
+ } catch {
5301
+ return { name: e.name, mtime: 0, birthtime: 0 };
5302
+ }
5303
+ }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
5304
+ if (files.length === 0) return null;
5305
+ const filePath = path9.join(historyDir, files[0].name);
5306
+ let raw;
5307
+ try {
5308
+ raw = fs6.readFileSync(filePath, "utf8");
5309
+ } catch {
5310
+ return null;
4758
5311
  }
4759
- if (/^Runn(?:ing)?\s+|^Execut(?:e|ing)\s+|^Bash(?:ing)?\s*:|^\$\s+/i.test(text)) {
4760
- const label2 = text.replace(/^(?:Runn(?:ing)?|Execut(?:e|ing)|Bash(?:ing)?:|\$)\s+/i, "").replace(/\.\.\.$/, "").trim();
4761
- return { tool: "bash", label: label2, status: "running" };
5312
+ let lastUsage = null;
5313
+ let lastModel = null;
5314
+ for (const line of raw.split("\n").filter(Boolean)) {
5315
+ try {
5316
+ const record = JSON.parse(line);
5317
+ if (record["type"] !== "assistant") continue;
5318
+ const msg = record["message"];
5319
+ if (msg?.["model"] === "<synthetic>") continue;
5320
+ const usage = msg?.["usage"];
5321
+ if (usage && (usage["input_tokens"] !== void 0 || usage["prompt_tokens"] !== void 0)) {
5322
+ lastUsage = usage;
5323
+ }
5324
+ if (msg?.["model"]) lastModel = msg["model"];
5325
+ } catch {
5326
+ }
4762
5327
  }
4763
- if (/^Search(?:ing)?\s+for\s+|^Grep(?:ping)?\s*:/i.test(text)) {
4764
- const label2 = text.replace(/^(?:Search(?:ing)?\s+for|Grep(?:ping)?:)\s+/i, "").replace(/\.\.\.$/, "").trim();
4765
- return { tool: "search", label: label2, status: "running" };
5328
+ const total = getContextWindow(lastModel);
5329
+ if (!lastUsage) {
5330
+ if (!lastModel) return null;
5331
+ return { used: 0, total, percent: 0, model: lastModel };
4766
5332
  }
4767
- const label = text.replace(/\.\.\.$/, "").trim();
4768
- return { tool: "other", label, status: "running" };
5333
+ const inputTokens = (lastUsage["input_tokens"] ?? lastUsage["prompt_tokens"] ?? 0) + (lastUsage["cache_read_input_tokens"] ?? 0) + (lastUsage["cache_creation_input_tokens"] ?? 0);
5334
+ const percent = Math.min(100, Math.round(inputTokens / total * 100));
5335
+ return {
5336
+ used: inputTokens,
5337
+ total,
5338
+ percent,
5339
+ model: lastModel ?? void 0
5340
+ };
4769
5341
  }
4770
-
4771
- // ../../packages/shared/src/protocol/renderToLines.ts
4772
- function renderToLines(raw) {
4773
- const screen = [""];
4774
- let row = 0;
4775
- let col = 0;
4776
- function ensureRow() {
4777
- while (screen.length <= row) screen.push("");
5342
+ function extractText(content) {
5343
+ if (typeof content === "string") return content;
5344
+ if (Array.isArray(content)) {
5345
+ return content.filter((b) => b["type"] === "text").map((b) => b["text"]).join("\n");
4778
5346
  }
4779
- function writeChar(ch) {
4780
- ensureRow();
4781
- if (col < screen[row].length) {
4782
- screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);
4783
- } else {
4784
- while (screen[row].length < col) screen[row] += " ";
4785
- screen[row] += ch;
4786
- }
4787
- col++;
5347
+ return "";
5348
+ }
5349
+ function parseHistoryFile(filePath) {
5350
+ const out = [];
5351
+ let raw;
5352
+ try {
5353
+ raw = fs6.readFileSync(filePath, "utf8");
5354
+ } catch {
5355
+ return out;
4788
5356
  }
4789
- let i = 0;
4790
- while (i < raw.length) {
4791
- const ch = raw[i];
4792
- if (ch === "\x1B") {
4793
- i++;
4794
- if (i >= raw.length) break;
4795
- if (raw[i] === "[") {
4796
- i++;
4797
- let param = "";
4798
- while (i < raw.length && !/[@-~]/.test(raw[i])) param += raw[i++];
4799
- const cmd = raw[i] ?? "";
4800
- const n = parseInt(param) || 1;
4801
- if (cmd === "A") {
4802
- row = Math.max(0, row - n);
4803
- } else if (cmd === "B") {
4804
- row += n;
4805
- ensureRow();
4806
- } else if (cmd === "C") {
4807
- col += n;
4808
- } else if (cmd === "D") {
4809
- col = Math.max(0, col - n);
4810
- } else if (cmd === "G") {
4811
- col = Math.max(0, n - 1);
4812
- } else if (cmd === "H" || cmd === "f") {
4813
- const p2 = param.split(";");
4814
- row = Math.max(0, (parseInt(p2[0] ?? "1") || 1) - 1);
4815
- col = Math.max(0, (parseInt(p2[1] ?? "1") || 1) - 1);
4816
- ensureRow();
4817
- } else if (cmd === "J") {
4818
- if (param === "2" || param === "3") {
4819
- screen.length = 1;
4820
- screen[0] = "";
4821
- row = 0;
4822
- col = 0;
4823
- } else if (param === "1") {
4824
- for (let r = 0; r < row; r++) screen[r] = "";
4825
- screen[row] = " ".repeat(col) + screen[row].slice(col);
4826
- } else {
4827
- screen[row] = screen[row].slice(0, col);
4828
- screen.splice(row + 1);
4829
- }
4830
- } else if (cmd === "K") {
4831
- ensureRow();
4832
- if (param === "" || param === "0") screen[row] = screen[row].slice(0, col);
4833
- else if (param === "1") screen[row] = " ".repeat(col) + screen[row].slice(col);
4834
- else if (param === "2") screen[row] = "";
4835
- } else if (cmd === "h" && (param === "?1049" || param === "?47")) {
4836
- screen.length = 1;
4837
- screen[0] = "";
4838
- row = 0;
4839
- col = 0;
4840
- } else if (cmd === "l" && (param === "?1049" || param === "?47")) {
4841
- screen.length = 1;
4842
- screen[0] = "";
4843
- row = 0;
4844
- col = 0;
4845
- }
4846
- } else if (raw[i] === "]") {
4847
- i++;
4848
- while (i < raw.length) {
4849
- if (raw[i] === "\x07") break;
4850
- if (raw[i] === "\x1B" && i + 1 < raw.length && raw[i + 1] === "\\") {
4851
- i++;
4852
- break;
4853
- }
4854
- i++;
4855
- }
4856
- }
4857
- } else if (ch === "\r") {
4858
- if (i + 1 < raw.length && raw[i + 1] === "\n") {
4859
- row++;
4860
- col = 0;
4861
- ensureRow();
4862
- i++;
4863
- } else {
4864
- col = 0;
4865
- }
4866
- } else if (ch === "\n") {
4867
- row++;
4868
- col = 0;
4869
- ensureRow();
4870
- } else if (ch >= " " || ch === " ") {
4871
- writeChar(ch);
5357
+ for (const line of raw.split("\n").filter(Boolean)) {
5358
+ let rec;
5359
+ try {
5360
+ rec = JSON.parse(line);
5361
+ } catch {
5362
+ continue;
4872
5363
  }
4873
- i++;
5364
+ if (typeof rec !== "object" || rec === null) continue;
5365
+ const r = rec;
5366
+ if (r["isMeta"]) continue;
5367
+ const type = r["type"];
5368
+ if (type !== "user" && type !== "assistant") continue;
5369
+ const msg = r["message"];
5370
+ if (!msg) continue;
5371
+ const text = extractText(msg["content"]).trim();
5372
+ if (!text) continue;
5373
+ const ts = r["timestamp"];
5374
+ const timestamp = typeof ts === "string" ? ts : typeof ts === "number" ? new Date(ts).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
5375
+ const uuid = typeof r["uuid"] === "string" ? r["uuid"] : `${Date.now()}-${Math.random()}`;
5376
+ const usage = msg["usage"];
5377
+ out.push({
5378
+ id: uuid,
5379
+ role: type === "user" ? "user" : "agent",
5380
+ text,
5381
+ timestamp,
5382
+ modelId: typeof msg["model"] === "string" ? msg["model"] : void 0,
5383
+ usage: usage ? {
5384
+ input: usage["input_tokens"] ?? 0,
5385
+ output: usage["output_tokens"] ?? 0,
5386
+ cacheRead: usage["cache_read_input_tokens"],
5387
+ cacheCreation: usage["cache_creation_input_tokens"]
5388
+ } : void 0
5389
+ });
4874
5390
  }
4875
- return screen;
5391
+ return out;
4876
5392
  }
4877
5393
 
4878
- // ../../packages/shared/src/protocol/selector.ts
4879
- function detectSelector(lines) {
4880
- if (lines.some((l) => /\?\s+for\s+shortcuts/i.test(l.trim()))) return null;
4881
- const clean = lines.map(
4882
- (l) => l.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "").replace(/\s*[│╭╰╮╯┌└┐┘├┤┬┴┼─━═]+\s*$/, "")
4883
- );
4884
- const hasCursor = clean.some((l) => /^[❯>]\s*\d+\./.test(l.trim()));
4885
- const looksLikeTrust = clean.some(
4886
- (l) => /\b(?:trust\s+the\s+files|trust\s+this\s+folder|safety\s+check)\b/i.test(l)
4887
- );
4888
- if (!hasCursor && !looksLikeTrust) return null;
4889
- let optionStartIdx = -1;
4890
- for (let i = 0; i < clean.length; i++) {
4891
- if (/^(?:[❯>]\s*)?\d+\.\s/.test(clean[i].trim())) {
4892
- optionStartIdx = i;
4893
- break;
4894
- }
5394
+ // src/agents/claude/runtime.ts
5395
+ var ClaudeRuntimeStrategy = class {
5396
+ id = "claude";
5397
+ meta = getAgent("claude");
5398
+ async prepareLaunch() {
5399
+ const launch = buildClaudeLaunch();
5400
+ if (!launch) throw new Error("claude binary not found in PATH");
5401
+ return { cmd: launch.cmd, args: launch.args };
4895
5402
  }
4896
- if (optionStartIdx === -1) return null;
4897
- const questionParts = [];
4898
- for (let i = 0; i < optionStartIdx; i++) {
4899
- const t2 = clean[i].trim();
4900
- if (!t2) continue;
4901
- if (/^[─━—═\-]{3,}$/.test(t2)) continue;
4902
- if (/^\[.*\]$/.test(t2)) continue;
4903
- if (/^[>❯]\s/.test(t2)) continue;
4904
- if (!t2.includes(" ") && t2.length > 15) continue;
4905
- questionParts.push(t2);
5403
+ resumeLaunchArgs(sessionId) {
5404
+ return ["--resume", sessionId, "--dangerously-skip-permissions"];
4906
5405
  }
4907
- const question = questionParts.filter((line, i, arr) => !arr.some((other, j2) => j2 !== i && other.includes(line))).join("\n").trim();
4908
- const optionLabels = /* @__PURE__ */ new Map();
4909
- const optionDescs = /* @__PURE__ */ new Map();
4910
- let currentNum = -1;
4911
- for (let i = optionStartIdx; i < clean.length; i++) {
4912
- const t2 = clean[i].trim();
4913
- if (!t2) continue;
4914
- const m = t2.match(/^(?:[❯>]\s*)?(\d+)\.\s+(.+)/);
4915
- if (m) {
4916
- const num = parseInt(m[1], 10);
4917
- if (!optionLabels.has(num)) {
4918
- optionLabels.set(num, m[2].trim());
4919
- optionDescs.set(num, []);
4920
- }
4921
- currentNum = num;
4922
- } else if (currentNum !== -1 && !/^Enter to/i.test(t2) && !/^[─━—═\-]{3,}$/.test(t2) && !/↑.*↓.*navigate/i.test(t2) && !/Esc to/i.test(t2)) {
4923
- optionDescs.get(currentNum)?.push(t2);
4924
- }
5406
+ resolveHistoryDir(cwd) {
5407
+ return resolveHistoryDir(cwd);
4925
5408
  }
4926
- const keys = [...optionLabels.keys()].sort((a, b) => a - b);
4927
- if (keys.length < 2 || keys[0] !== 1) return null;
4928
- return {
4929
- question,
4930
- options: keys.map((k2) => optionLabels.get(k2)),
4931
- optionDescriptions: keys.map((k2) => (optionDescs.get(k2) ?? []).join(" ").trim()),
4932
- currentIndex: 0
4933
- };
4934
- }
4935
- function detectListSelector(lines) {
4936
- if (!lines.some((l) => /[↑↓].*navigate/i.test(l.trim()))) return null;
4937
- if (lines.some((l) => /^❯\s*\d+\./.test(l.trim()))) return null;
4938
- if (!lines.some((l) => /^\s+❯\s+\S/.test(l))) return null;
4939
- const isSelected = (line) => /^\s+❯\s+\S/.test(line);
4940
- const isUnselected = (line) => /^ \S/.test(line);
4941
- const isItem = (line) => isSelected(line) || isUnselected(line);
4942
- let optionStartIdx = -1;
4943
- for (let i = 0; i < lines.length; i++) {
4944
- if (isItem(lines[i])) {
4945
- optionStartIdx = i;
4946
- break;
5409
+ parseHistoryFile(filePath) {
5410
+ return parseHistoryFile(filePath);
5411
+ }
5412
+ getCurrentUsage(historyDir) {
5413
+ return getCurrentUsage(historyDir);
5414
+ }
5415
+ async fetchWeeklyUsage() {
5416
+ return fetchClaudeQuota();
5417
+ }
5418
+ async listModels() {
5419
+ return [
5420
+ { id: "claude-opus-4-7", label: "Claude Opus 4.7", contextWindow: 2e5 },
5421
+ { id: "claude-opus-4-6", label: "Claude Opus 4.6", contextWindow: 2e5 },
5422
+ { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", contextWindow: 2e5 },
5423
+ { id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", contextWindow: 2e5 }
5424
+ ];
5425
+ }
5426
+ changeModelInstruction(modelId) {
5427
+ return { type: "pty", ptyInput: `/model ${modelId}\r` };
5428
+ }
5429
+ summarizeInstruction(mode) {
5430
+ if (mode === "normal") return { ptyInput: "/compact\r" };
5431
+ return { ptyInput: "/compact\r" };
5432
+ }
5433
+ };
5434
+
5435
+ // src/agents/claude/deploy.ts
5436
+ var fs8 = __toESM(require("fs"));
5437
+ var os9 = __toESM(require("os"));
5438
+ var path11 = __toESM(require("path"));
5439
+
5440
+ // src/agents/claude/credentials.ts
5441
+ var import_child_process5 = require("child_process");
5442
+ var fs7 = __toESM(require("fs"));
5443
+ var os8 = __toESM(require("os"));
5444
+ var path10 = __toESM(require("path"));
5445
+ var import_util = require("util");
5446
+ var execFileP = (0, import_util.promisify)(import_child_process5.execFile);
5447
+ async function detectLocalClaudeCredentials() {
5448
+ const localClaudeDir = path10.join(os8.homedir(), ".claude");
5449
+ const flat = path10.join(localClaudeDir, ".credentials.json");
5450
+ if (fs7.existsSync(flat)) {
5451
+ return { source: "flat-file", description: "~/.claude/.credentials.json" };
5452
+ }
5453
+ if (os8.platform() === "darwin") {
5454
+ try {
5455
+ await execFileP(
5456
+ "security",
5457
+ ["find-generic-password", "-s", "Claude Code-credentials"],
5458
+ { maxBuffer: 1024 * 1024 }
5459
+ );
5460
+ return { source: "macos-keychain", description: "macOS Keychain" };
5461
+ } catch {
5462
+ return { source: "none", description: "" };
4947
5463
  }
4948
5464
  }
4949
- if (optionStartIdx === -1) return null;
4950
- const questionParts = [];
4951
- for (let i = 0; i < optionStartIdx; i++) {
4952
- const t2 = lines[i].trim();
4953
- if (!t2) continue;
4954
- if (/^[─━—═\-]{3,}$/.test(t2)) continue;
4955
- if (/[┌└│┐┘├┤┬┴┼]/.test(t2)) {
4956
- const inner = t2.replace(/[│┌└┐┘├┤┬┴┼─]/g, "").trim();
4957
- if (inner) questionParts.push(inner);
4958
- continue;
4959
- }
4960
- if (/^[>❯]\s/.test(t2)) continue;
4961
- if (/[↑↓].*navigate/i.test(t2)) continue;
4962
- if (!t2.includes(" ") && t2.length > 15) continue;
4963
- questionParts.push(t2);
5465
+ return { source: "none", description: "" };
5466
+ }
5467
+ async function bridgeClaudeCredentials(provider, workspaceId) {
5468
+ const localClaudeDir = path10.join(os8.homedir(), ".claude");
5469
+ const fileBased = path10.join(localClaudeDir, ".credentials.json");
5470
+ if (fs7.existsSync(fileBased)) {
5471
+ return { source: "flat-file", description: "~/.claude/.credentials.json" };
4964
5472
  }
4965
- const question = questionParts.filter((line, i, arr) => !arr.some((other, j2) => j2 !== i && other.includes(line))).join("\n").trim();
4966
- const options = [];
4967
- let currentIndex = 0;
4968
- for (const line of lines.slice(optionStartIdx)) {
4969
- const t2 = line.trim();
4970
- if (!t2) continue;
4971
- if (/[↑↓].*navigate/i.test(t2)) break;
4972
- if (/^[─━—═\-]{3,}$/.test(t2)) continue;
4973
- if (isSelected(line)) {
4974
- currentIndex = options.length;
4975
- options.push(t2.replace(/^❯\s+/, "").trim());
4976
- } else if (isUnselected(line)) {
4977
- options.push(t2);
5473
+ if (process.platform === "darwin") {
5474
+ try {
5475
+ const { stdout } = await execFileP(
5476
+ "security",
5477
+ ["find-generic-password", "-s", "Claude Code-credentials", "-w"],
5478
+ { maxBuffer: 1024 * 1024 }
5479
+ );
5480
+ const json = stdout.trim();
5481
+ if (json.length === 0) return { source: "none", description: "" };
5482
+ await provider.uploadFile(
5483
+ workspaceId,
5484
+ "/home/codespace/.claude/.credentials.json",
5485
+ json,
5486
+ { mode: 384 }
5487
+ );
5488
+ return { source: "macos-keychain", description: "macOS Keychain" };
5489
+ } catch {
5490
+ return { source: "none", description: "" };
4978
5491
  }
4979
5492
  }
4980
- if (options.length < 2) return null;
4981
- return {
4982
- question,
4983
- options,
4984
- optionDescriptions: options.map(() => ""),
4985
- currentIndex
4986
- };
5493
+ return { source: "none", description: "" };
5494
+ }
5495
+ async function runRemoteClaudeLogin(provider, workspaceId) {
5496
+ wt(
5497
+ [
5498
+ "A login URL will print below. Open it in your local browser, sign in,",
5499
+ "and paste any code Claude asks for back into this terminal."
5500
+ ].join("\n"),
5501
+ "Authenticating Claude on workspace"
5502
+ );
5503
+ const result = await provider.streamCommand(
5504
+ workspaceId,
5505
+ 'bash -lc "claude login || claude /login || true"'
5506
+ );
5507
+ if (result.code !== 0) {
5508
+ wt(
5509
+ "claude login exited non-zero. You can re-run it manually inside the codespace later.",
5510
+ "Heads up"
5511
+ );
5512
+ }
5513
+ }
5514
+ async function verifyClaudeAuth(provider, workspaceId) {
5515
+ const result = await provider.exec(
5516
+ workspaceId,
5517
+ 'bash -lc "claude auth status 2>/dev/null || true"'
5518
+ );
5519
+ if (result.code !== 0) return false;
5520
+ const jsonStart = result.stdout.indexOf("{");
5521
+ if (jsonStart < 0) return false;
5522
+ try {
5523
+ const parsed = JSON.parse(result.stdout.slice(jsonStart));
5524
+ return parsed.loggedIn === true;
5525
+ } catch {
5526
+ return false;
5527
+ }
4987
5528
  }
4988
5529
 
4989
- // ../../packages/shared/src/protocol/filterChrome.ts
4990
- function filterChrome(lines) {
4991
- const result = [];
4992
- let skipEchoContinuation = false;
4993
- for (const line of lines) {
4994
- const t2 = line.trim();
4995
- if (!t2) {
4996
- skipEchoContinuation = false;
4997
- continue;
5530
+ // src/agents/claude/deploy.ts
5531
+ var ClaudeDeployStrategy = class {
5532
+ id = "claude";
5533
+ async detectLocalCredentials() {
5534
+ return detectLocalClaudeCredentials();
5535
+ }
5536
+ async bridgeLocalCredentials(provider, workspaceId) {
5537
+ return bridgeClaudeCredentials(provider, workspaceId);
5538
+ }
5539
+ async runRemoteLogin(provider, workspaceId) {
5540
+ return runRemoteClaudeLogin(provider, workspaceId);
5541
+ }
5542
+ /**
5543
+ * Steps 5-9 of the deploy flow:
5544
+ * - Install Claude CLI on the workspace
5545
+ * - Copy local ~/.claude config dir (excluding heavy state)
5546
+ * - Ship ~/.claude.json (only when creds were bridged — privacy)
5547
+ * - Verify auth, fallback to runRemoteLogin
5548
+ */
5549
+ async setupOnWorkspace(provider, workspaceId, opts) {
5550
+ const claudeStep = fe();
5551
+ claudeStep.start("Installing Claude CLI on workspace\u2026");
5552
+ const installResult = await provider.exec(
5553
+ workspaceId,
5554
+ "curl -fsSL https://claude.ai/install.sh | bash"
5555
+ );
5556
+ if (installResult.code !== 0) {
5557
+ claudeStep.stop("\u2717 Claude CLI install failed");
5558
+ pt(installResult.stderr.slice(0, 1e3));
5559
+ process.exit(1);
4998
5560
  }
4999
- if (/^[─━—═─\-]{3,}$/.test(t2)) {
5000
- skipEchoContinuation = false;
5001
- continue;
5561
+ claudeStep.stop("\u2713 Claude CLI installed");
5562
+ const localClaudeDir = path11.join(os9.homedir(), ".claude");
5563
+ const haveLocalClaude = fs8.existsSync(localClaudeDir) && fs8.statSync(localClaudeDir).isDirectory();
5564
+ if (haveLocalClaude) {
5565
+ const copyStep = fe();
5566
+ copyStep.start("Copying local Claude config to workspace\u2026");
5567
+ try {
5568
+ await provider.uploadDirectory(
5569
+ workspaceId,
5570
+ localClaudeDir,
5571
+ "/home/codespace/.claude",
5572
+ {
5573
+ exclude: [
5574
+ "./projects",
5575
+ // per-project conversation history (often 700MB+)
5576
+ "./file-history",
5577
+ // per-project file diffs
5578
+ "./downloads",
5579
+ // downloaded artifacts
5580
+ "./image-cache",
5581
+ // cached images
5582
+ "./paste-cache",
5583
+ // clipboard/paste cache
5584
+ "./backups",
5585
+ // local backups
5586
+ "./shell-snapshots",
5587
+ // shell history snapshots
5588
+ "./telemetry",
5589
+ // analytics dumps
5590
+ "./statsig",
5591
+ // feature-flag cache
5592
+ "./cache",
5593
+ // generic cache dir
5594
+ "./history.jsonl",
5595
+ // global REPL history
5596
+ "./ide",
5597
+ // local IDE bridge state
5598
+ "./todos",
5599
+ // local todo state
5600
+ "./tasks",
5601
+ // local task state
5602
+ // Don't overwrite the credentials we already staged in
5603
+ // step 4 — the local dir on macOS doesn't have a flat
5604
+ // credentials file anyway, but on Linux it would, and a
5605
+ // re-write here would be redundant.
5606
+ "./.credentials.json"
5607
+ ]
5608
+ }
5609
+ );
5610
+ copyStep.stop("\u2713 Claude config uploaded");
5611
+ } catch (err) {
5612
+ copyStep.stop("\u26A0 Could not upload Claude config (continuing)");
5613
+ void err;
5614
+ }
5002
5615
  }
5003
- if (/^[●⏺]\s/.test(t2)) skipEchoContinuation = false;
5004
- if (/^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/.test(t2)) continue;
5005
- if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) continue;
5006
- if (/high\s*[·•]\s*\/effort/i.test(t2)) continue;
5007
- if (/^[❯>]\s*$/.test(t2)) continue;
5008
- if (/^\(thinking\)\s*$/.test(t2)) continue;
5009
- if (/^\?\s.*shortcut/i.test(t2)) continue;
5010
- if (/spending limit|usage limit/i.test(t2) && t2.length < 80) continue;
5011
- if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) continue;
5012
- if (t2.replace(/\s/g, "").length === 1) continue;
5013
- if ((t2.match(/─/g)?.length ?? 0) >= 6) continue;
5014
- if (/ctrl\+?o\s+to\s+expand/i.test(t2)) continue;
5015
- 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(
5016
- t2
5017
- ))
5018
- continue;
5019
- if (/^└\s/.test(t2)) continue;
5020
- if (/^\+\s/.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) continue;
5021
- if (/^↓\s*\d+\s*tokens/i.test(t2)) continue;
5022
- if (/^\bthought\s+for\s+\d+/i.test(t2)) continue;
5023
- const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
5024
- if (/^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) {
5025
- skipEchoContinuation = true;
5026
- continue;
5616
+ if (opts.bridged !== "none") {
5617
+ const localClaudeJson = path11.join(os9.homedir(), ".claude.json");
5618
+ if (fs8.existsSync(localClaudeJson)) {
5619
+ try {
5620
+ const contents = fs8.readFileSync(localClaudeJson);
5621
+ await provider.uploadFile(
5622
+ workspaceId,
5623
+ "/home/codespace/.claude.json",
5624
+ contents,
5625
+ { mode: 384 }
5626
+ );
5627
+ } catch (err) {
5628
+ void err;
5629
+ }
5630
+ }
5631
+ }
5632
+ const verifyStep = fe();
5633
+ verifyStep.start("Verifying Claude auth on workspace\u2026");
5634
+ const verified = await verifyClaudeAuth(provider, workspaceId);
5635
+ if (verified) {
5636
+ verifyStep.stop("\u2713 Claude is logged in \u2014 no re-auth needed");
5637
+ } else {
5638
+ verifyStep.stop("\xB7 Claude not yet authenticated \u2014 running login flow");
5639
+ await runRemoteClaudeLogin(provider, workspaceId);
5640
+ const reverified = await verifyClaudeAuth(provider, workspaceId);
5641
+ if (!reverified) {
5642
+ wt(
5643
+ "Claude auth could not be confirmed. You may need to run `claude /login` manually inside the codespace.",
5644
+ "Heads up"
5645
+ );
5646
+ }
5027
5647
  }
5028
- if (skipEchoContinuation) continue;
5029
- result.push(line);
5030
5648
  }
5031
- return result;
5032
- }
5649
+ };
5033
5650
 
5034
- // ../../packages/shared/src/models/pricing.ts
5035
- var MODEL_PRICING = {
5036
- "claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
5037
- "claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
5038
- "claude-3-5-sonnet": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
5039
- "claude-3-5-haiku": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
5040
- "claude-3-haiku": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 }
5651
+ // src/agents/registry.ts
5652
+ var runtimeBuilders = {
5653
+ claude: () => new ClaudeRuntimeStrategy()
5654
+ // codex and copilot added in Phase 2 / later
5041
5655
  };
5042
- var MODEL_CONTEXT_WINDOW = {
5043
- "claude-opus-4": 1e6,
5044
- "claude-sonnet-4": 1e6,
5045
- "claude-3-5-sonnet": 2e5,
5046
- "claude-3-5-haiku": 2e5,
5047
- "claude-3-haiku": 2e5
5656
+ var deployBuilders = {
5657
+ claude: () => new ClaudeDeployStrategy()
5048
5658
  };
5049
- var DEFAULT_CONTEXT_WINDOW = 2e5;
5050
- function getPricing(model) {
5051
- for (const [prefix, pricing] of Object.entries(MODEL_PRICING)) {
5052
- if (model.startsWith(prefix)) return pricing;
5659
+ function createRuntimeStrategy(agent) {
5660
+ if (!AGENT_REGISTRY[agent]?.enabled) {
5661
+ throw new Error(
5662
+ `Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
5663
+ );
5053
5664
  }
5054
- return MODEL_PRICING["claude-sonnet-4"];
5665
+ const build = runtimeBuilders[agent];
5666
+ if (!build) {
5667
+ throw new Error(`No runtime strategy registered for agent "${agent}"`);
5668
+ }
5669
+ return build();
5055
5670
  }
5056
- function getContextWindow(model) {
5057
- if (!model) return DEFAULT_CONTEXT_WINDOW;
5058
- for (const [prefix, size] of Object.entries(MODEL_CONTEXT_WINDOW)) {
5059
- if (model.startsWith(prefix)) return size;
5671
+ function createDeployStrategy(agent) {
5672
+ if (!AGENT_REGISTRY[agent]?.enabled) {
5673
+ throw new Error(`Agent "${agent}" is not supported in this codeam-cli version.`);
5060
5674
  }
5061
- return DEFAULT_CONTEXT_WINDOW;
5675
+ const build = deployBuilders[agent];
5676
+ if (!build) {
5677
+ throw new Error(`No deploy strategy registered for agent "${agent}"`);
5678
+ }
5679
+ return build();
5062
5680
  }
5063
5681
 
5064
5682
  // src/services/output/chrome-tracker.ts
@@ -5531,9 +6149,9 @@ var OutputService = class _OutputService {
5531
6149
  };
5532
6150
 
5533
6151
  // src/services/history.service.ts
5534
- var fs5 = __toESM(require("fs"));
5535
- var path8 = __toESM(require("path"));
5536
- var os6 = __toESM(require("os"));
6152
+ var fs9 = __toESM(require("fs"));
6153
+ var path12 = __toESM(require("path"));
6154
+ var os10 = __toESM(require("os"));
5537
6155
  var https4 = __toESM(require("https"));
5538
6156
  var http4 = __toESM(require("http"));
5539
6157
  var import_zod = require("zod");
@@ -5548,28 +6166,7 @@ var historyRecordSchema = import_zod.z.object({
5548
6166
  }).passthrough().optional()
5549
6167
  }).passthrough();
5550
6168
  var API_BASE4 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
5551
- function encodeCwd(cwd) {
5552
- return cwd.replace(/[\\/:]/g, "-");
5553
- }
5554
- function findProjectDir(cwd) {
5555
- const projectsRoot = path8.join(os6.homedir(), ".claude", "projects");
5556
- const primary = path8.join(projectsRoot, encodeCwd(cwd));
5557
- if (fs5.existsSync(primary)) return primary;
5558
- try {
5559
- const entries = fs5.readdirSync(projectsRoot, { withFileTypes: true });
5560
- const wanted = encodeCwd(cwd);
5561
- for (const e of entries) {
5562
- if (!e.isDirectory()) continue;
5563
- const candidate = e.name.replace(/-+/g, "-");
5564
- if (candidate === wanted.replace(/-+/g, "-")) {
5565
- return path8.join(projectsRoot, e.name);
5566
- }
5567
- }
5568
- } catch {
5569
- }
5570
- return null;
5571
- }
5572
- function extractText(content) {
6169
+ function extractText2(content) {
5573
6170
  if (typeof content === "string") return content;
5574
6171
  if (Array.isArray(content)) {
5575
6172
  return content.filter((b) => b["type"] === "text").map((b) => b["text"]).join("\n");
@@ -5581,7 +6178,7 @@ function parseJsonl(filePath) {
5581
6178
  const messages = [];
5582
6179
  let raw;
5583
6180
  try {
5584
- raw = fs5.readFileSync(filePath, "utf8");
6181
+ raw = fs9.readFileSync(filePath, "utf8");
5585
6182
  } catch (err) {
5586
6183
  if (err.code !== "ENOENT") {
5587
6184
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -5608,10 +6205,10 @@ function parseJsonl(filePath) {
5608
6205
  const uuid = record.uuid ?? `${Date.now()}-${Math.random()}`;
5609
6206
  const msg = record.message;
5610
6207
  if (record.type === "user" && msg) {
5611
- const text = extractText(msg.content).trim();
6208
+ const text = extractText2(msg.content).trim();
5612
6209
  if (text) messages.push({ id: uuid, role: "user", text, timestamp });
5613
6210
  } else if (record.type === "assistant" && msg) {
5614
- const text = extractText(msg.content).trim();
6211
+ const text = extractText2(msg.content).trim();
5615
6212
  if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
5616
6213
  }
5617
6214
  }
@@ -5655,9 +6252,10 @@ function post(endpoint, body) {
5655
6252
  });
5656
6253
  }
5657
6254
  var HistoryService = class _HistoryService {
5658
- constructor(pluginId, cwd, options) {
6255
+ constructor(runtime, pluginId, cwd, options) {
5659
6256
  this.pluginId = pluginId;
5660
6257
  this.cwd = cwd;
6258
+ this.runtime = runtime;
5661
6259
  this.bootTimeMs = options?.bootTimeMs ?? Date.now();
5662
6260
  }
5663
6261
  pluginId;
@@ -5693,6 +6291,7 @@ var HistoryService = class _HistoryService {
5693
6291
  * previous pair / previous Claude run.
5694
6292
  */
5695
6293
  static BIRTHTIME_GRACE_MS = 5e3;
6294
+ runtime;
5696
6295
  /** Store rate limit reset info detected from Claude Code output */
5697
6296
  setRateLimitReset(reset) {
5698
6297
  this._rateLimitReset = reset;
@@ -5713,7 +6312,7 @@ var HistoryService = class _HistoryService {
5713
6312
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
5714
6313
  }
5715
6314
  get projectDir() {
5716
- return findProjectDir(this.cwd) ?? path8.join(os6.homedir(), ".claude", "projects", encodeCwd(this.cwd));
6315
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path12.join(os10.homedir(), ".claude", "projects", encodeCwd(this.cwd));
5717
6316
  }
5718
6317
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
5719
6318
  setCurrentConversationId(id) {
@@ -5725,7 +6324,7 @@ var HistoryService = class _HistoryService {
5725
6324
  /** Return the current message count in the active conversation. */
5726
6325
  getCurrentMessageCount() {
5727
6326
  if (!this.currentConversationId) return 0;
5728
- const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
6327
+ const filePath = path12.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5729
6328
  return parseJsonl(filePath).length;
5730
6329
  }
5731
6330
  /**
@@ -5736,7 +6335,7 @@ var HistoryService = class _HistoryService {
5736
6335
  const deadline = Date.now() + timeoutMs;
5737
6336
  while (Date.now() < deadline) {
5738
6337
  if (!this.currentConversationId) return null;
5739
- const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
6338
+ const filePath = path12.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5740
6339
  const messages = parseJsonl(filePath);
5741
6340
  if (messages.length > previousCount) {
5742
6341
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -5762,16 +6361,16 @@ var HistoryService = class _HistoryService {
5762
6361
  const dir = this.projectDir;
5763
6362
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
5764
6363
  try {
5765
- const files = fs5.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
6364
+ const files = fs9.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5766
6365
  try {
5767
- const stat3 = fs5.statSync(path8.join(dir, e.name));
6366
+ const stat3 = fs9.statSync(path12.join(dir, e.name));
5768
6367
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
5769
6368
  } catch {
5770
6369
  return { name: e.name, mtime: 0, birthtime: 0 };
5771
6370
  }
5772
6371
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
5773
6372
  if (files.length > 0) {
5774
- this.currentConversationId = path8.basename(files[0].name, ".jsonl");
6373
+ this.currentConversationId = path12.basename(files[0].name, ".jsonl");
5775
6374
  }
5776
6375
  } catch {
5777
6376
  }
@@ -5805,13 +6404,13 @@ var HistoryService = class _HistoryService {
5805
6404
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
5806
6405
  let entries;
5807
6406
  try {
5808
- entries = fs5.readdirSync(dir, { withFileTypes: true });
6407
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
5809
6408
  } catch {
5810
6409
  return null;
5811
6410
  }
5812
6411
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5813
6412
  try {
5814
- const stat3 = fs5.statSync(path8.join(dir, e.name));
6413
+ const stat3 = fs9.statSync(path12.join(dir, e.name));
5815
6414
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
5816
6415
  } catch {
5817
6416
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -5820,12 +6419,12 @@ var HistoryService = class _HistoryService {
5820
6419
  if (files.length === 0) return null;
5821
6420
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
5822
6421
  if (!files.some((f) => f.name === targetFile)) return null;
5823
- return this.extractUsageFromFile(path8.join(dir, targetFile));
6422
+ return this.extractUsageFromFile(path12.join(dir, targetFile));
5824
6423
  }
5825
6424
  extractUsageFromFile(filePath) {
5826
6425
  let raw;
5827
6426
  try {
5828
- raw = fs5.readFileSync(filePath, "utf8");
6427
+ raw = fs9.readFileSync(filePath, "utf8");
5829
6428
  } catch {
5830
6429
  return null;
5831
6430
  }
@@ -5870,9 +6469,9 @@ var HistoryService = class _HistoryService {
5870
6469
  let totalCost = 0;
5871
6470
  let files;
5872
6471
  try {
5873
- files = fs5.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
6472
+ files = fs9.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
5874
6473
  try {
5875
- return fs5.statSync(path8.join(projectDir, f)).mtimeMs >= monthStartMs;
6474
+ return fs9.statSync(path12.join(projectDir, f)).mtimeMs >= monthStartMs;
5876
6475
  } catch {
5877
6476
  return false;
5878
6477
  }
@@ -5883,7 +6482,7 @@ var HistoryService = class _HistoryService {
5883
6482
  for (const file of files) {
5884
6483
  let raw;
5885
6484
  try {
5886
- raw = fs5.readFileSync(path8.join(projectDir, file), "utf8");
6485
+ raw = fs9.readFileSync(path12.join(projectDir, file), "utf8");
5887
6486
  } catch {
5888
6487
  continue;
5889
6488
  }
@@ -5918,30 +6517,30 @@ var HistoryService = class _HistoryService {
5918
6517
  const dir = this.projectDir;
5919
6518
  let entries;
5920
6519
  try {
5921
- entries = fs5.readdirSync(dir, { withFileTypes: true });
6520
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
5922
6521
  } catch {
5923
6522
  return;
5924
6523
  }
5925
6524
  const sessions2 = [];
5926
6525
  for (const entry of entries) {
5927
6526
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
5928
- const id = path8.basename(entry.name, ".jsonl");
5929
- const filePath = path8.join(dir, entry.name);
6527
+ const id = path12.basename(entry.name, ".jsonl");
6528
+ const filePath = path12.join(dir, entry.name);
5930
6529
  let mtime = Date.now();
5931
6530
  try {
5932
- mtime = fs5.statSync(filePath).mtimeMs;
6531
+ mtime = fs9.statSync(filePath).mtimeMs;
5933
6532
  } catch {
5934
6533
  }
5935
6534
  let summary = "";
5936
6535
  try {
5937
- const raw = fs5.readFileSync(filePath, "utf8");
6536
+ const raw = fs9.readFileSync(filePath, "utf8");
5938
6537
  for (const line of raw.split("\n")) {
5939
6538
  if (!line.trim()) continue;
5940
6539
  try {
5941
6540
  const record = JSON.parse(line);
5942
6541
  if (record["type"] === "user") {
5943
6542
  const msg = record["message"];
5944
- const text = extractText(msg?.["content"]).trim();
6543
+ const text = extractText2(msg?.["content"]).trim();
5945
6544
  if (text) {
5946
6545
  summary = text.slice(0, 120);
5947
6546
  break;
@@ -5967,7 +6566,7 @@ var HistoryService = class _HistoryService {
5967
6566
  * showing an empty conversation.
5968
6567
  */
5969
6568
  async loadConversation(sessionId) {
5970
- const filePath = path8.join(this.projectDir, `${sessionId}.jsonl`);
6569
+ const filePath = path12.join(this.projectDir, `${sessionId}.jsonl`);
5971
6570
  const messages = parseJsonl(filePath);
5972
6571
  if (messages.length === 0) return;
5973
6572
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -6010,7 +6609,7 @@ var HistoryService = class _HistoryService {
6010
6609
  if (!this.currentConversationId) return 0;
6011
6610
  }
6012
6611
  const sessionId = this.currentConversationId;
6013
- const filePath = path8.join(this.projectDir, `${sessionId}.jsonl`);
6612
+ const filePath = path12.join(this.projectDir, `${sessionId}.jsonl`);
6014
6613
  const messages = parseJsonl(filePath);
6015
6614
  if (messages.length === 0) return 0;
6016
6615
  const marker = this.lastUploadedUuid.get(sessionId);
@@ -6038,119 +6637,28 @@ var HistoryService = class _HistoryService {
6038
6637
  }
6039
6638
  };
6040
6639
 
6041
- // src/commands/start/quota-fetcher.ts
6042
- var fs6 = __toESM(require("fs"));
6043
- var os7 = __toESM(require("os"));
6044
- var path9 = __toESM(require("path"));
6045
- var import_child_process4 = require("child_process");
6046
- var inProgress = false;
6047
- var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
6048
- m,s=pty.openpty()
6049
- try:
6050
- fcntl.ioctl(s,termios.TIOCSWINSZ,struct.pack('HHHH',30,120,0,0))
6051
- except Exception:pass
6052
- pid=os.fork()
6053
- if pid==0:
6054
- os.close(m);os.setsid()
6055
- try:fcntl.ioctl(s,termios.TIOCSCTTY,0)
6056
- except Exception:pass
6057
- for fd in[0,1,2]:os.dup2(s,fd)
6058
- if s>2:os.close(s)
6059
- os.execvp(sys.argv[1],sys.argv[1:])
6060
- sys.exit(127)
6061
- os.close(s)
6062
- done=[False]
6063
- def onchld(n,f):
6064
- try:os.waitpid(pid,os.WNOHANG)
6065
- except Exception:pass
6066
- done[0]=True
6067
- signal.signal(signal.SIGCHLD,onchld)
6068
- i=sys.stdin.fileno();o=sys.stdout.fileno()
6069
- while not done[0]:
6070
- try:r,_,_=select.select([i,m],[],[],0.1)
6071
- except OSError as e:
6072
- if e.errno==errno.EINTR:continue
6073
- break
6074
- if i in r:
6075
- try:
6076
- d=os.read(i,4096)
6077
- if d:os.write(m,d)
6078
- else:break
6079
- except OSError:break
6080
- if m in r:
6081
- try:
6082
- d=os.read(m,4096)
6083
- if d:os.write(o,d)
6084
- except OSError:done[0]=True
6085
- try:os.kill(pid,signal.SIGTERM)
6086
- except Exception:pass
6087
- try:
6088
- _,st=os.waitpid(pid,0)
6089
- sys.exit((st>>8)&0xFF)
6090
- except Exception:sys.exit(0)
6091
- `;
6092
- function fetchQuotaUsage(historySvc) {
6640
+ // src/commands/start/quota-fetcher.ts
6641
+ var inProgress = false;
6642
+ function fetchQuotaUsage(runtime, historySvc) {
6093
6643
  if (inProgress) return;
6094
6644
  inProgress = true;
6095
- const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
6096
- if (!claudeCmd) {
6097
- inProgress = false;
6098
- return;
6099
- }
6100
- const helperPath = path9.join(os7.tmpdir(), "codeam-quota-helper.py");
6101
- fs6.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
6102
- const python = findInPath("python3") ?? findInPath("python");
6103
- if (!python) {
6104
- inProgress = false;
6105
- return;
6106
- }
6107
- const proc = (0, import_child_process4.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
6108
- stdio: ["pipe", "pipe", "ignore"],
6109
- cwd: process.cwd(),
6110
- env: { ...process.env, TERM: "dumb", COLUMNS: "120", LINES: "30" }
6111
- });
6112
- let output = "";
6113
- proc.stdout?.on("data", (chunk) => {
6114
- output += chunk.toString("utf8");
6115
- });
6116
- setTimeout(() => {
6117
- proc.stdin?.write("/usage\r");
6118
- setTimeout(() => {
6119
- const clean = output.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, " ").replace(/\s+/g, " ");
6120
- const weekMatch = clean.match(/(\d+)%\s*used/i) || clean.match(/(\d+)\s*%/);
6121
- if (weekMatch) historySvc.setQuotaPercent(parseInt(weekMatch[1], 10));
6122
- const resetMatch = clean.match(/resets\s+(.+?)(?:\s*\(|$)/im);
6123
- if (resetMatch) historySvc.setRateLimitReset(resetMatch[1].trim());
6124
- try {
6125
- proc.kill();
6126
- } catch {
6127
- }
6128
- try {
6129
- fs6.unlinkSync(helperPath);
6130
- } catch {
6131
- }
6132
- inProgress = false;
6133
- }, 5e3);
6134
- }, 8e3);
6135
- proc.on("exit", () => {
6645
+ runtime.fetchWeeklyUsage().then((result) => {
6646
+ if (!result) return;
6647
+ historySvc.setQuotaPercent(result.percent);
6648
+ if (result.resetAt) historySvc.setRateLimitReset(result.resetAt);
6649
+ }).finally(() => {
6136
6650
  inProgress = false;
6137
6651
  });
6138
- setTimeout(() => {
6139
- try {
6140
- proc.kill();
6141
- } catch {
6142
- }
6143
- }, 2e4);
6144
6652
  }
6145
6653
 
6146
6654
  // src/commands/start/keep-alive.ts
6147
- var import_child_process5 = require("child_process");
6655
+ var import_child_process6 = require("child_process");
6148
6656
  function buildKeepAlive(ctx) {
6149
6657
  let timer = null;
6150
6658
  async function setIdleTimeout(minutes) {
6151
6659
  if (!ctx.inCodespace || !ctx.codespaceName) return;
6152
6660
  await new Promise((resolve2) => {
6153
- const proc = (0, import_child_process5.spawn)(
6661
+ const proc = (0, import_child_process6.spawn)(
6154
6662
  "gh",
6155
6663
  [
6156
6664
  "api",
@@ -6187,11 +6695,11 @@ function buildKeepAlive(ctx) {
6187
6695
  }
6188
6696
 
6189
6697
  // src/commands/start/handlers.ts
6190
- var fs9 = __toESM(require("fs"));
6191
- var os8 = __toESM(require("os"));
6192
- var path12 = __toESM(require("path"));
6698
+ var fs12 = __toESM(require("fs"));
6699
+ var os11 = __toESM(require("os"));
6700
+ var path15 = __toESM(require("path"));
6193
6701
  var import_crypto = require("crypto");
6194
- var import_child_process7 = require("child_process");
6702
+ var import_child_process8 = require("child_process");
6195
6703
 
6196
6704
  // src/lib/payload.ts
6197
6705
  var import_zod2 = require("zod");
@@ -6228,8 +6736,8 @@ function parsePayload(schema, raw) {
6228
6736
  }
6229
6737
 
6230
6738
  // src/services/file-ops.service.ts
6231
- var fs7 = __toESM(require("fs/promises"));
6232
- var path10 = __toESM(require("path"));
6739
+ var fs10 = __toESM(require("fs/promises"));
6740
+ var path13 = __toESM(require("path"));
6233
6741
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
6234
6742
  var MAX_WALK_DEPTH = 6;
6235
6743
  var MAX_VISITED_DIRS = 5e3;
@@ -6264,12 +6772,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
6264
6772
  "__pycache__"
6265
6773
  ]);
6266
6774
  function isUnder(parent, candidate) {
6267
- const rel = path10.relative(parent, candidate);
6268
- return rel === "" || !rel.startsWith("..") && !path10.isAbsolute(rel);
6775
+ const rel = path13.relative(parent, candidate);
6776
+ return rel === "" || !rel.startsWith("..") && !path13.isAbsolute(rel);
6269
6777
  }
6270
6778
  async function isExistingFile(absPath) {
6271
6779
  try {
6272
- const stat3 = await fs7.stat(absPath);
6780
+ const stat3 = await fs10.stat(absPath);
6273
6781
  return stat3.isFile();
6274
6782
  } catch {
6275
6783
  return false;
@@ -6282,13 +6790,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
6282
6790
  ctx.visited++;
6283
6791
  let entries = [];
6284
6792
  try {
6285
- entries = await fs7.readdir(dir, { withFileTypes: true });
6793
+ entries = await fs10.readdir(dir, { withFileTypes: true });
6286
6794
  } catch {
6287
6795
  return;
6288
6796
  }
6289
6797
  for (const e of entries) {
6290
6798
  if (!e.isFile()) continue;
6291
- const full = path10.join(dir, e.name);
6799
+ const full = path13.join(dir, e.name);
6292
6800
  if (needleVariants.some((needle) => full.endsWith(needle))) {
6293
6801
  ctx.matches.push(full);
6294
6802
  if (ctx.matches.length >= ctx.cap) return;
@@ -6298,21 +6806,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
6298
6806
  if (!e.isDirectory()) continue;
6299
6807
  if (SUBDIR_IGNORE.has(e.name)) continue;
6300
6808
  if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
6301
- await walkForSuffix(path10.join(dir, e.name), needleVariants, depth + 1, ctx);
6809
+ await walkForSuffix(path13.join(dir, e.name), needleVariants, depth + 1, ctx);
6302
6810
  if (ctx.matches.length >= ctx.cap) return;
6303
6811
  }
6304
6812
  }
6305
6813
  async function findFile(rawPath) {
6306
6814
  const cwd = process.cwd();
6307
- if (path10.isAbsolute(rawPath)) {
6308
- const abs = path10.normalize(rawPath);
6815
+ if (path13.isAbsolute(rawPath)) {
6816
+ const abs = path13.normalize(rawPath);
6309
6817
  if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
6310
6818
  }
6311
- const direct = path10.resolve(cwd, rawPath);
6819
+ const direct = path13.resolve(cwd, rawPath);
6312
6820
  if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
6313
- const normalized = path10.normalize(rawPath).replace(/^[./\\]+/, "");
6821
+ const normalized = path13.normalize(rawPath).replace(/^[./\\]+/, "");
6314
6822
  const needles = [
6315
- `${path10.sep}${normalized}`,
6823
+ `${path13.sep}${normalized}`,
6316
6824
  `/${normalized}`
6317
6825
  ].filter((v, i, a) => a.indexOf(v) === i);
6318
6826
  const ctx = { visited: 0, matches: [], cap: 16 };
@@ -6326,7 +6834,7 @@ async function findWriteTarget(rawPath) {
6326
6834
  const found = await findFile(rawPath);
6327
6835
  if (found) return found;
6328
6836
  const cwd = process.cwd();
6329
- const fallback = path10.isAbsolute(rawPath) ? path10.normalize(rawPath) : path10.resolve(cwd, rawPath);
6837
+ const fallback = path13.isAbsolute(rawPath) ? path13.normalize(rawPath) : path13.resolve(cwd, rawPath);
6330
6838
  if (!isUnder(cwd, fallback)) return null;
6331
6839
  return fallback;
6332
6840
  }
@@ -6343,11 +6851,11 @@ async function readProjectFile(rawPath) {
6343
6851
  if (!abs) {
6344
6852
  return { error: `File not found in the project tree: ${rawPath}` };
6345
6853
  }
6346
- const stat3 = await fs7.stat(abs);
6854
+ const stat3 = await fs10.stat(abs);
6347
6855
  if (stat3.size > MAX_FILE_BYTES) {
6348
6856
  return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
6349
6857
  }
6350
- const buf = await fs7.readFile(abs);
6858
+ const buf = await fs10.readFile(abs);
6351
6859
  if (looksBinary(buf)) {
6352
6860
  return { error: "Binary file \u2014 refusing to open in a code editor." };
6353
6861
  }
@@ -6366,8 +6874,8 @@ async function writeProjectFile(rawPath, content) {
6366
6874
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
6367
6875
  return { error: "Content too large." };
6368
6876
  }
6369
- await fs7.mkdir(path10.dirname(abs), { recursive: true });
6370
- await fs7.writeFile(abs, content, "utf-8");
6877
+ await fs10.mkdir(path13.dirname(abs), { recursive: true });
6878
+ await fs10.writeFile(abs, content, "utf-8");
6371
6879
  return { ok: true };
6372
6880
  } catch (e) {
6373
6881
  const msg = e instanceof Error ? e.message : "Write failed";
@@ -6376,11 +6884,11 @@ async function writeProjectFile(rawPath, content) {
6376
6884
  }
6377
6885
 
6378
6886
  // src/services/project-ops.service.ts
6379
- var import_child_process6 = require("child_process");
6380
- var import_util = require("util");
6381
- var fs8 = __toESM(require("fs/promises"));
6382
- var path11 = __toESM(require("path"));
6383
- var execFileP = (0, import_util.promisify)(import_child_process6.execFile);
6887
+ var import_child_process7 = require("child_process");
6888
+ var import_util2 = require("util");
6889
+ var fs11 = __toESM(require("fs/promises"));
6890
+ var path14 = __toESM(require("path"));
6891
+ var execFileP2 = (0, import_util2.promisify)(import_child_process7.execFile);
6384
6892
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
6385
6893
  "node_modules",
6386
6894
  ".git",
@@ -6427,7 +6935,7 @@ async function listProjectFiles(opts = {}) {
6427
6935
  }
6428
6936
  let entries = [];
6429
6937
  try {
6430
- entries = await fs8.readdir(dir, { withFileTypes: true });
6938
+ entries = await fs11.readdir(dir, { withFileTypes: true });
6431
6939
  } catch {
6432
6940
  return;
6433
6941
  }
@@ -6437,18 +6945,18 @@ async function listProjectFiles(opts = {}) {
6437
6945
  return;
6438
6946
  }
6439
6947
  if (PROJECT_IGNORE.has(e.name)) continue;
6440
- const full = path11.join(dir, e.name);
6948
+ const full = path14.join(dir, e.name);
6441
6949
  if (e.isDirectory()) {
6442
6950
  if (depth >= 12) continue;
6443
6951
  await walk(full, depth + 1);
6444
6952
  } else if (e.isFile()) {
6445
- const rel = path11.relative(root, full);
6953
+ const rel = path14.relative(root, full);
6446
6954
  if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
6447
6955
  continue;
6448
6956
  }
6449
6957
  let size = 0;
6450
6958
  try {
6451
- const st3 = await fs8.stat(full);
6959
+ const st3 = await fs11.stat(full);
6452
6960
  size = st3.size;
6453
6961
  } catch {
6454
6962
  }
@@ -6462,7 +6970,7 @@ async function listProjectFiles(opts = {}) {
6462
6970
  }
6463
6971
  async function git(args2, cwd) {
6464
6972
  try {
6465
- const { stdout, stderr } = await execFileP("git", args2, {
6973
+ const { stdout, stderr } = await execFileP2("git", args2, {
6466
6974
  cwd: cwd ?? process.cwd(),
6467
6975
  maxBuffer: MAX_GIT_OUTPUT,
6468
6976
  timeout: 3e4
@@ -6550,8 +7058,8 @@ async function gitStatus(cwd) {
6550
7058
  let hasMergeInProgress = false;
6551
7059
  try {
6552
7060
  const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
6553
- const mergeHead = path11.isAbsolute(gitDir) ? path11.join(gitDir, "MERGE_HEAD") : path11.join(root, gitDir, "MERGE_HEAD");
6554
- await fs8.access(mergeHead);
7061
+ const mergeHead = path14.isAbsolute(gitDir) ? path14.join(gitDir, "MERGE_HEAD") : path14.join(root, gitDir, "MERGE_HEAD");
7062
+ await fs11.access(mergeHead);
6555
7063
  hasMergeInProgress = true;
6556
7064
  } catch {
6557
7065
  }
@@ -6628,8 +7136,8 @@ async function gitResolve(file, side, cwd) {
6628
7136
  function saveFilesTemp(files) {
6629
7137
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
6630
7138
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
6631
- const tmpPath = path12.join(os8.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
6632
- fs9.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
7139
+ const tmpPath = path15.join(os11.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
7140
+ fs12.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
6633
7141
  return tmpPath;
6634
7142
  });
6635
7143
  }
@@ -6648,7 +7156,7 @@ var startTask = (ctx, _cmd, parsed) => {
6648
7156
  setTimeout(() => {
6649
7157
  for (const p2 of paths) {
6650
7158
  try {
6651
- fs9.unlinkSync(p2);
7159
+ fs12.unlinkSync(p2);
6652
7160
  } catch {
6653
7161
  }
6654
7162
  }
@@ -6708,14 +7216,35 @@ var getConversation = async (ctx, cmd) => {
6708
7216
  }
6709
7217
  };
6710
7218
  var listModels = async (ctx, cmd) => {
6711
- const models = [
6712
- { id: "claude-opus-4-7", label: "Claude Opus 4.7", description: "Most capable", family: "claude", vendor: "anthropic", isDefault: false },
6713
- { id: "claude-opus-4-6", label: "Claude Opus 4.6", description: "Top tier", family: "claude", vendor: "anthropic", isDefault: false },
6714
- { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", description: "Balanced", family: "claude", vendor: "anthropic", isDefault: true },
6715
- { id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", description: "Fastest", family: "claude", vendor: "anthropic", isDefault: false }
6716
- ];
7219
+ const models = await ctx.runtime.listModels();
6717
7220
  await ctx.relay.sendResult(cmd.id, "completed", { models });
6718
7221
  };
7222
+ var changeModel = async (ctx, cmd) => {
7223
+ const params = cmd.payload;
7224
+ if (typeof params.modelId !== "string" || !params.modelId) {
7225
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "modelId required" });
7226
+ return;
7227
+ }
7228
+ const instr = ctx.runtime.changeModelInstruction(params.modelId);
7229
+ if (instr.type === "pty") {
7230
+ if (!instr.ptyInput) {
7231
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "no pty input for this agent" });
7232
+ return;
7233
+ }
7234
+ ctx.claude.sendRawPtyInput(instr.ptyInput);
7235
+ } else if (instr.type === "restart") {
7236
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "restart-mode change_model not supported in Phase 1" });
7237
+ return;
7238
+ }
7239
+ await ctx.relay.sendResult(cmd.id, "completed", {});
7240
+ };
7241
+ var summarize = async (ctx, cmd) => {
7242
+ const params = cmd.payload;
7243
+ const mode = params.mode === "auto" ? "auto" : "normal";
7244
+ const instr = ctx.runtime.summarizeInstruction(mode);
7245
+ ctx.claude.sendRawPtyInput(instr.ptyInput);
7246
+ await ctx.relay.sendResult(cmd.id, "completed", {});
7247
+ };
6719
7248
  var setKeepAlive = async (ctx, cmd) => {
6720
7249
  const enabled = !!cmd.payload.enabled;
6721
7250
  ctx.setKeepAlive(enabled);
@@ -6739,7 +7268,7 @@ var sessionTerminated = (ctx) => {
6739
7268
  } catch {
6740
7269
  }
6741
7270
  try {
6742
- const proc = (0, import_child_process7.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
7271
+ const proc = (0, import_child_process8.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
6743
7272
  detached: true,
6744
7273
  stdio: "ignore"
6745
7274
  });
@@ -6761,7 +7290,7 @@ var shutdownSession = async (ctx, cmd) => {
6761
7290
  }
6762
7291
  if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
6763
7292
  try {
6764
- const stopProc = (0, import_child_process7.spawn)(
7293
+ const stopProc = (0, import_child_process8.spawn)(
6765
7294
  "bash",
6766
7295
  ["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
6767
7296
  { detached: true, stdio: "ignore" }
@@ -6771,7 +7300,7 @@ var shutdownSession = async (ctx, cmd) => {
6771
7300
  }
6772
7301
  }
6773
7302
  try {
6774
- const proc = (0, import_child_process7.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
7303
+ const proc = (0, import_child_process8.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
6775
7304
  detached: true,
6776
7305
  stdio: "ignore"
6777
7306
  });
@@ -6852,6 +7381,8 @@ var handlers = {
6852
7381
  get_context: getContext,
6853
7382
  get_conversation: getConversation,
6854
7383
  list_models: listModels,
7384
+ change_model: changeModel,
7385
+ summarize,
6855
7386
  set_keep_alive: setKeepAlive,
6856
7387
  session_terminated: sessionTerminated,
6857
7388
  shutdown_session: shutdownSession,
@@ -6888,11 +7419,16 @@ async function start() {
6888
7419
  `);
6889
7420
  process.exit(0);
6890
7421
  }
7422
+ if (!session.agent) {
7423
+ throw new Error("Active session has no agent \u2014 re-pair with `codeam pair`.");
7424
+ }
6891
7425
  const pluginId = session.pluginId ?? ensurePluginId();
6892
7426
  showInfo(`${session.userName} \xB7 ${import_picocolors2.default.cyan(session.plan)}`);
6893
- showInfo("Launching Claude Code...\n");
7427
+ showInfo(`Launching ${AGENT_REGISTRY[session.agent].displayName}...
7428
+ `);
6894
7429
  const cwd = process.cwd();
6895
- const historySvc = new HistoryService(pluginId, cwd);
7430
+ const runtime = createRuntimeStrategy(session.agent);
7431
+ const historySvc = new HistoryService(runtime, pluginId, cwd);
6896
7432
  const keepAliveCtx = {
6897
7433
  inCodespace: process.env.CODESPACES === "true",
6898
7434
  codespaceName: process.env.CODESPACE_NAME
@@ -6904,7 +7440,7 @@ async function start() {
6904
7440
  (conversationId) => historySvc.setCurrentConversationId(conversationId),
6905
7441
  (reset) => historySvc.setRateLimitReset(reset),
6906
7442
  () => {
6907
- if (historySvc.isQuotaStale()) fetchQuotaUsage(historySvc);
7443
+ if (historySvc.isQuotaStale()) fetchQuotaUsage(runtime, historySvc);
6908
7444
  setTimeout(() => {
6909
7445
  historySvc.uploadDelta().catch(() => {
6910
7446
  });
@@ -6916,22 +7452,26 @@ async function start() {
6916
7452
  },
6917
7453
  session.pluginAuthToken
6918
7454
  );
6919
- const claude = new ClaudeService({
6920
- cwd,
6921
- onData(raw) {
6922
- outputSvc.push(raw);
6923
- },
6924
- onExit(code) {
6925
- process.removeListener("SIGINT", sigintHandler);
6926
- outputSvc.dispose();
6927
- relay.stop();
6928
- process.exit(code);
7455
+ const claude = new AgentService(
7456
+ runtime,
7457
+ {
7458
+ cwd,
7459
+ onData(raw) {
7460
+ outputSvc.push(raw);
7461
+ },
7462
+ onExit(code) {
7463
+ process.removeListener("SIGINT", sigintHandler);
7464
+ outputSvc.dispose();
7465
+ relay.stop();
7466
+ process.exit(code);
7467
+ }
6929
7468
  }
6930
- });
7469
+ );
6931
7470
  const ctx = {
6932
7471
  outputSvc,
6933
7472
  claude,
6934
7473
  historySvc,
7474
+ runtime,
6935
7475
  relay: void 0,
6936
7476
  setKeepAlive: setKeepAlive2,
6937
7477
  keepAliveCtx
@@ -6954,7 +7494,7 @@ async function start() {
6954
7494
  historySvc.load().catch(() => {
6955
7495
  });
6956
7496
  }, 2e3);
6957
- setTimeout(() => fetchQuotaUsage(historySvc), 5e3);
7497
+ setTimeout(() => fetchQuotaUsage(runtime, historySvc), 5e3);
6958
7498
  }
6959
7499
 
6960
7500
  // src/commands/pair.ts
@@ -6981,8 +7521,45 @@ async function selectSession(sessions2, activeId) {
6981
7521
  return result;
6982
7522
  }
6983
7523
 
7524
+ // src/utils/agent-prompt.ts
7525
+ function parseAgentFlag(args2) {
7526
+ const flag = args2.find((a) => a.startsWith("--agent="));
7527
+ if (!flag) return null;
7528
+ const value = flag.slice("--agent=".length);
7529
+ if (!isKnownAgentId(value)) {
7530
+ throw new Error(
7531
+ `invalid agent "${value}"; valid: ${Object.keys(AGENT_REGISTRY).join(", ")}`
7532
+ );
7533
+ }
7534
+ if (!AGENT_REGISTRY[value].enabled) {
7535
+ throw new Error(
7536
+ `${AGENT_REGISTRY[value].displayName} is not available in this codeam-cli version`
7537
+ );
7538
+ }
7539
+ return value;
7540
+ }
7541
+ async function promptForAgent(initialValue) {
7542
+ const enabled = getEnabledAgents();
7543
+ if (enabled.length === 1) {
7544
+ return enabled[0].id;
7545
+ }
7546
+ const chosen = await _t({
7547
+ message: "Pick an agent:",
7548
+ options: enabled.map((m) => ({ value: m.id, label: m.displayName })),
7549
+ initialValue: initialValue ?? enabled[0].id
7550
+ });
7551
+ if (q(chosen)) {
7552
+ pt("Cancelled.");
7553
+ process.exit(0);
7554
+ }
7555
+ return chosen;
7556
+ }
7557
+
6984
7558
  // src/commands/pair.ts
6985
- async function pair() {
7559
+ async function pair(args2 = []) {
7560
+ const config = loadCliConfig();
7561
+ const flagAgent = parseAgentFlag(args2);
7562
+ const agentId = flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
6986
7563
  showIntro();
6987
7564
  const pluginId = (0, import_crypto2.randomUUID)();
6988
7565
  const spin = dist_exports.spinner();
@@ -7018,8 +7595,10 @@ async function pair() {
7018
7595
  userEmail: info.userEmail,
7019
7596
  plan: info.plan,
7020
7597
  pairedAt: Date.now(),
7021
- pluginAuthToken: info.pluginAuthToken
7598
+ pluginAuthToken: info.pluginAuthToken,
7599
+ agent: agentId
7022
7600
  });
7601
+ saveCliConfig({ ...loadCliConfig(), preferredAgent: agentId });
7023
7602
  showSuccess(`Paired with ${info.userName} (${info.plan})`);
7024
7603
  console.log("");
7025
7604
  resolve2();
@@ -7036,8 +7615,8 @@ async function pair() {
7036
7615
  }
7037
7616
 
7038
7617
  // src/commands/pair-auto.ts
7039
- var fs10 = __toESM(require("fs"));
7040
- var os9 = __toESM(require("os"));
7618
+ var fs13 = __toESM(require("fs"));
7619
+ var os12 = __toESM(require("os"));
7041
7620
  var import_crypto3 = require("crypto");
7042
7621
  var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
7043
7622
  function fail(msg) {
@@ -7054,12 +7633,12 @@ function readTokenFromArgs(args2) {
7054
7633
  }
7055
7634
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
7056
7635
  if (fileFlag) {
7057
- const path19 = fileFlag.slice("--token-file=".length);
7636
+ const path21 = fileFlag.slice("--token-file=".length);
7058
7637
  try {
7059
- const content = fs10.readFileSync(path19, "utf8").trim();
7060
- if (content.length === 0) fail(`--token-file ${path19} is empty`);
7638
+ const content = fs13.readFileSync(path21, "utf8").trim();
7639
+ if (content.length === 0) fail(`--token-file ${path21} is empty`);
7061
7640
  try {
7062
- fs10.unlinkSync(path19);
7641
+ fs13.unlinkSync(path21);
7063
7642
  } catch {
7064
7643
  }
7065
7644
  return content;
@@ -7077,7 +7656,7 @@ async function claim(token, pluginId) {
7077
7656
  pluginId,
7078
7657
  ideName: "codeam-cli (codespace)",
7079
7658
  ideVersion: process.env.npm_package_version ?? "unknown",
7080
- hostname: os9.hostname(),
7659
+ hostname: os12.hostname(),
7081
7660
  codespaceName: process.env.CODESPACE_NAME ?? ""
7082
7661
  };
7083
7662
  const res = await fetch(url, {
@@ -7103,6 +7682,11 @@ async function pairAuto(args2) {
7103
7682
  const pluginId = (0, import_crypto3.randomUUID)();
7104
7683
  console.log(" Claiming pairing token\u2026");
7105
7684
  const claimed = await claim(token, pluginId);
7685
+ if (!isKnownAgentId(claimed.agent)) {
7686
+ fail(
7687
+ `agent "${claimed.agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
7688
+ );
7689
+ }
7106
7690
  addSession({
7107
7691
  id: claimed.sessionId,
7108
7692
  pluginId,
@@ -7110,7 +7694,8 @@ async function pairAuto(args2) {
7110
7694
  userEmail: claimed.user.email,
7111
7695
  plan: claimed.user.plan,
7112
7696
  pairedAt: Date.now(),
7113
- pluginAuthToken: claimed.pluginAuthToken
7697
+ pluginAuthToken: claimed.pluginAuthToken,
7698
+ agent: claimed.agent
7114
7699
  });
7115
7700
  console.log(` Paired with ${claimed.user.name} (${claimed.user.plan})`);
7116
7701
  console.log(" Starting agent loop\u2026");
@@ -7217,19 +7802,14 @@ async function logout() {
7217
7802
  }
7218
7803
 
7219
7804
  // src/commands/deploy.ts
7220
- var import_child_process12 = require("child_process");
7221
- var fs11 = __toESM(require("fs"));
7222
- var os10 = __toESM(require("os"));
7223
- var path17 = __toESM(require("path"));
7224
- var import_util6 = require("util");
7225
7805
  var import_picocolors9 = __toESM(require("picocolors"));
7226
7806
 
7227
7807
  // src/services/providers/github-codespaces.ts
7228
- var import_child_process8 = require("child_process");
7229
- var import_util2 = require("util");
7808
+ var import_child_process9 = require("child_process");
7809
+ var import_util3 = require("util");
7230
7810
  var import_picocolors7 = __toESM(require("picocolors"));
7231
- var path13 = __toESM(require("path"));
7232
- var execFileP2 = (0, import_util2.promisify)(import_child_process8.execFile);
7811
+ var path16 = __toESM(require("path"));
7812
+ var execFileP3 = (0, import_util3.promisify)(import_child_process9.execFile);
7233
7813
  var MAX_BUFFER = 8 * 1024 * 1024;
7234
7814
  function resetStdinForChild() {
7235
7815
  if (process.stdin.isTTY) {
@@ -7246,11 +7826,11 @@ var GitHubCodespacesProvider = class {
7246
7826
  available = true;
7247
7827
  async authorize() {
7248
7828
  try {
7249
- await execFileP2("gh", ["--version"], { maxBuffer: MAX_BUFFER });
7829
+ await execFileP3("gh", ["--version"], { maxBuffer: MAX_BUFFER });
7250
7830
  } catch {
7251
7831
  await this.tryInstallGh();
7252
7832
  try {
7253
- await execFileP2("gh", ["--version"], { maxBuffer: MAX_BUFFER });
7833
+ await execFileP3("gh", ["--version"], { maxBuffer: MAX_BUFFER });
7254
7834
  } catch {
7255
7835
  throw new Error(
7256
7836
  [
@@ -7266,14 +7846,14 @@ var GitHubCodespacesProvider = class {
7266
7846
  }
7267
7847
  let isAuthed = false;
7268
7848
  try {
7269
- await execFileP2("gh", ["auth", "status"], { maxBuffer: MAX_BUFFER });
7849
+ await execFileP3("gh", ["auth", "status"], { maxBuffer: MAX_BUFFER });
7270
7850
  isAuthed = true;
7271
7851
  } catch {
7272
7852
  }
7273
7853
  if (!isAuthed) {
7274
7854
  resetStdinForChild();
7275
7855
  await new Promise((resolve2, reject) => {
7276
- const proc = (0, import_child_process8.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
7856
+ const proc = (0, import_child_process9.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
7277
7857
  stdio: "inherit"
7278
7858
  });
7279
7859
  proc.on("exit", (code) => {
@@ -7307,7 +7887,7 @@ var GitHubCodespacesProvider = class {
7307
7887
  wt(noteLines.join("\n"), "One more permission needed");
7308
7888
  resetStdinForChild();
7309
7889
  const refreshCode = await new Promise((resolve2, reject) => {
7310
- const proc = (0, import_child_process8.spawn)(
7890
+ const proc = (0, import_child_process9.spawn)(
7311
7891
  "gh",
7312
7892
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
7313
7893
  { stdio: "inherit" }
@@ -7342,7 +7922,7 @@ var GitHubCodespacesProvider = class {
7342
7922
  */
7343
7923
  async getActiveGhUser() {
7344
7924
  try {
7345
- const { stdout } = await execFileP2(
7925
+ const { stdout } = await execFileP3(
7346
7926
  "gh",
7347
7927
  ["api", "user", "--jq", ".login"],
7348
7928
  { maxBuffer: MAX_BUFFER }
@@ -7362,7 +7942,7 @@ var GitHubCodespacesProvider = class {
7362
7942
  */
7363
7943
  async hasCodespaceScope() {
7364
7944
  try {
7365
- const { stdout } = await execFileP2(
7945
+ const { stdout } = await execFileP3(
7366
7946
  "gh",
7367
7947
  ["api", "-i", "user"],
7368
7948
  { maxBuffer: MAX_BUFFER }
@@ -7392,12 +7972,12 @@ var GitHubCodespacesProvider = class {
7392
7972
  * install error if it's still missing.
7393
7973
  */
7394
7974
  async tryInstallGh() {
7395
- const platform = process.platform;
7975
+ const platform2 = process.platform;
7396
7976
  wt(
7397
7977
  `GitHub CLI (${import_picocolors7.default.cyan("gh")}) is required for Codespaces deploys but isn't on your PATH.`,
7398
7978
  "Heads up"
7399
7979
  );
7400
- if (platform === "linux") {
7980
+ if (platform2 === "linux") {
7401
7981
  wt(
7402
7982
  [
7403
7983
  "On Linux, please install gh from the official guide:",
@@ -7409,9 +7989,9 @@ var GitHubCodespacesProvider = class {
7409
7989
  return;
7410
7990
  }
7411
7991
  let installCmd = null;
7412
- if (platform === "darwin") {
7992
+ if (platform2 === "darwin") {
7413
7993
  try {
7414
- await execFileP2("brew", ["--version"], { maxBuffer: MAX_BUFFER });
7994
+ await execFileP3("brew", ["--version"], { maxBuffer: MAX_BUFFER });
7415
7995
  } catch {
7416
7996
  wt(
7417
7997
  [
@@ -7428,9 +8008,9 @@ var GitHubCodespacesProvider = class {
7428
8008
  args: ["install", "gh"],
7429
8009
  describe: "brew install gh"
7430
8010
  };
7431
- } else if (platform === "win32") {
8011
+ } else if (platform2 === "win32") {
7432
8012
  try {
7433
- await execFileP2("winget", ["--version"], { maxBuffer: MAX_BUFFER });
8013
+ await execFileP3("winget", ["--version"], { maxBuffer: MAX_BUFFER });
7434
8014
  } catch {
7435
8015
  wt(
7436
8016
  [
@@ -7457,7 +8037,7 @@ var GitHubCodespacesProvider = class {
7457
8037
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
7458
8038
  resetStdinForChild();
7459
8039
  const ok = await new Promise((resolve2) => {
7460
- const proc = (0, import_child_process8.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
8040
+ const proc = (0, import_child_process9.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
7461
8041
  proc.on("exit", (code) => resolve2(code === 0));
7462
8042
  proc.on("error", () => resolve2(false));
7463
8043
  });
@@ -7484,7 +8064,7 @@ var GitHubCodespacesProvider = class {
7484
8064
  );
7485
8065
  resetStdinForChild();
7486
8066
  await new Promise((resolve2, reject) => {
7487
- const proc = (0, import_child_process8.spawn)(
8067
+ const proc = (0, import_child_process9.spawn)(
7488
8068
  "gh",
7489
8069
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
7490
8070
  { stdio: "inherit" }
@@ -7509,7 +8089,7 @@ var GitHubCodespacesProvider = class {
7509
8089
  "200"
7510
8090
  );
7511
8091
  try {
7512
- const { stdout } = await execFileP2("gh", args2, { maxBuffer: MAX_BUFFER });
8092
+ const { stdout } = await execFileP3("gh", args2, { maxBuffer: MAX_BUFFER });
7513
8093
  return JSON.parse(stdout);
7514
8094
  } catch {
7515
8095
  return [];
@@ -7518,7 +8098,7 @@ var GitHubCodespacesProvider = class {
7518
8098
  const own = await fetchRepos();
7519
8099
  let orgRepos = [];
7520
8100
  try {
7521
- const { stdout } = await execFileP2(
8101
+ const { stdout } = await execFileP3(
7522
8102
  "gh",
7523
8103
  ["api", "--paginate", "user/orgs", "--jq", ".[].login"],
7524
8104
  { maxBuffer: MAX_BUFFER }
@@ -7555,7 +8135,7 @@ var GitHubCodespacesProvider = class {
7555
8135
  */
7556
8136
  async listMachineTypes(projectId) {
7557
8137
  try {
7558
- const { stdout } = await execFileP2(
8138
+ const { stdout } = await execFileP3(
7559
8139
  "gh",
7560
8140
  ["api", `/repos/${projectId}/codespaces/machines`],
7561
8141
  { maxBuffer: MAX_BUFFER }
@@ -7586,7 +8166,7 @@ var GitHubCodespacesProvider = class {
7586
8166
  const machine = machineTypeId ?? await this.pickDefaultMachine(projectId);
7587
8167
  const args2 = ["codespace", "create", "-R", projectId, "--default-permissions"];
7588
8168
  if (machine) args2.push("-m", machine);
7589
- const { stdout } = await execFileP2(
8169
+ const { stdout } = await execFileP3(
7590
8170
  "gh",
7591
8171
  args2,
7592
8172
  { maxBuffer: MAX_BUFFER, timeout: 12e4 }
@@ -7626,7 +8206,7 @@ var GitHubCodespacesProvider = class {
7626
8206
  async waitUntilAvailable(name) {
7627
8207
  const deadline = Date.now() + 5 * 60 * 1e3;
7628
8208
  while (Date.now() < deadline) {
7629
- const { stdout } = await execFileP2(
8209
+ const { stdout } = await execFileP3(
7630
8210
  "gh",
7631
8211
  ["codespace", "list", "--json", "name,state"],
7632
8212
  { maxBuffer: MAX_BUFFER }
@@ -7644,7 +8224,7 @@ var GitHubCodespacesProvider = class {
7644
8224
  }
7645
8225
  async exec(workspaceId, command2) {
7646
8226
  try {
7647
- const { stdout, stderr } = await execFileP2(
8227
+ const { stdout, stderr } = await execFileP3(
7648
8228
  "gh",
7649
8229
  ["codespace", "ssh", "-c", workspaceId, "--", command2],
7650
8230
  { maxBuffer: MAX_BUFFER, timeout: 6e5 }
@@ -7662,7 +8242,7 @@ var GitHubCodespacesProvider = class {
7662
8242
  async streamCommand(workspaceId, command2) {
7663
8243
  resetStdinForChild();
7664
8244
  return new Promise((resolve2, reject) => {
7665
- const proc = (0, import_child_process8.spawn)(
8245
+ const proc = (0, import_child_process9.spawn)(
7666
8246
  "gh",
7667
8247
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
7668
8248
  { stdio: "inherit" }
@@ -7689,11 +8269,11 @@ var GitHubCodespacesProvider = class {
7689
8269
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
7690
8270
  ];
7691
8271
  await new Promise((resolve2, reject) => {
7692
- const tar = (0, import_child_process8.spawn)("tar", tarArgs, {
8272
+ const tar = (0, import_child_process9.spawn)("tar", tarArgs, {
7693
8273
  stdio: ["ignore", "pipe", "pipe"],
7694
8274
  env: tarEnv
7695
8275
  });
7696
- const ssh = (0, import_child_process8.spawn)("gh", sshArgs, {
8276
+ const ssh = (0, import_child_process9.spawn)("gh", sshArgs, {
7697
8277
  stdio: [tar.stdout, "pipe", "pipe"]
7698
8278
  });
7699
8279
  let tarErr = "";
@@ -7717,7 +8297,7 @@ var GitHubCodespacesProvider = class {
7717
8297
  });
7718
8298
  }
7719
8299
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
7720
- const remoteDir = path13.posix.dirname(remotePath);
8300
+ const remoteDir = path16.posix.dirname(remotePath);
7721
8301
  const parts = [
7722
8302
  `mkdir -p ${shellQuote(remoteDir)}`,
7723
8303
  `cat > ${shellQuote(remotePath)}`
@@ -7727,7 +8307,7 @@ var GitHubCodespacesProvider = class {
7727
8307
  }
7728
8308
  const cmd = parts.join(" && ");
7729
8309
  await new Promise((resolve2, reject) => {
7730
- const proc = (0, import_child_process8.spawn)(
8310
+ const proc = (0, import_child_process9.spawn)(
7731
8311
  "gh",
7732
8312
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
7733
8313
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -7749,7 +8329,7 @@ var GitHubCodespacesProvider = class {
7749
8329
  try {
7750
8330
  const args2 = ["codespace", "list", "--json", "name,displayName,state,lastUsedAt"];
7751
8331
  if (projectId) args2.push("--repo", projectId);
7752
- const { stdout } = await execFileP2("gh", args2, { maxBuffer: MAX_BUFFER });
8332
+ const { stdout } = await execFileP3("gh", args2, { maxBuffer: MAX_BUFFER });
7753
8333
  const list = JSON.parse(stdout);
7754
8334
  return list.map((c2) => ({
7755
8335
  id: c2.name,
@@ -7764,7 +8344,7 @@ var GitHubCodespacesProvider = class {
7764
8344
  }
7765
8345
  async startWorkspace(workspaceId) {
7766
8346
  try {
7767
- await execFileP2(
8347
+ await execFileP3(
7768
8348
  "gh",
7769
8349
  ["api", "-X", "POST", `/user/codespaces/${workspaceId}/start`],
7770
8350
  { maxBuffer: MAX_BUFFER, timeout: 6e4 }
@@ -7785,11 +8365,11 @@ function shellQuote(s) {
7785
8365
  }
7786
8366
 
7787
8367
  // src/services/providers/gitpod.ts
7788
- var import_child_process9 = require("child_process");
7789
- var import_util3 = require("util");
7790
- var path14 = __toESM(require("path"));
8368
+ var import_child_process10 = require("child_process");
8369
+ var import_util4 = require("util");
8370
+ var path17 = __toESM(require("path"));
7791
8371
  var import_picocolors8 = __toESM(require("picocolors"));
7792
- var execFileP3 = (0, import_util3.promisify)(import_child_process9.execFile);
8372
+ var execFileP4 = (0, import_util4.promisify)(import_child_process10.execFile);
7793
8373
  var MAX_BUFFER2 = 8 * 1024 * 1024;
7794
8374
  function resetStdinForChild2() {
7795
8375
  if (process.stdin.isTTY) {
@@ -7806,7 +8386,7 @@ var GitpodProvider = class {
7806
8386
  available = true;
7807
8387
  async authorize() {
7808
8388
  try {
7809
- await execFileP3("gitpod", ["--version"], { maxBuffer: MAX_BUFFER2 });
8389
+ await execFileP4("gitpod", ["--version"], { maxBuffer: MAX_BUFFER2 });
7810
8390
  } catch {
7811
8391
  throw new Error(
7812
8392
  [
@@ -7819,7 +8399,7 @@ var GitpodProvider = class {
7819
8399
  );
7820
8400
  }
7821
8401
  try {
7822
- await execFileP3("gitpod", ["whoami"], { maxBuffer: MAX_BUFFER2 });
8402
+ await execFileP4("gitpod", ["whoami"], { maxBuffer: MAX_BUFFER2 });
7823
8403
  return;
7824
8404
  } catch {
7825
8405
  }
@@ -7829,7 +8409,7 @@ var GitpodProvider = class {
7829
8409
  );
7830
8410
  resetStdinForChild2();
7831
8411
  await new Promise((resolve2, reject) => {
7832
- const proc = (0, import_child_process9.spawn)("gitpod", ["login"], { stdio: "inherit" });
8412
+ const proc = (0, import_child_process10.spawn)("gitpod", ["login"], { stdio: "inherit" });
7833
8413
  proc.on("exit", (code) => {
7834
8414
  if (code === 0) resolve2();
7835
8415
  else reject(new Error("gitpod login failed."));
@@ -7839,7 +8419,7 @@ var GitpodProvider = class {
7839
8419
  }
7840
8420
  async listProjects() {
7841
8421
  try {
7842
- const { stdout } = await execFileP3(
8422
+ const { stdout } = await execFileP4(
7843
8423
  "gitpod",
7844
8424
  ["workspace", "list", "--output", "json", "--limit", "200"],
7845
8425
  { maxBuffer: MAX_BUFFER2 }
@@ -7872,7 +8452,7 @@ var GitpodProvider = class {
7872
8452
  async createWorkspace(projectId, machineTypeId) {
7873
8453
  const args2 = ["workspace", "create", projectId, "--start", "--output", "json"];
7874
8454
  if (machineTypeId) args2.push("--class", machineTypeId);
7875
- const { stdout } = await execFileP3("gitpod", args2, {
8455
+ const { stdout } = await execFileP4("gitpod", args2, {
7876
8456
  maxBuffer: MAX_BUFFER2,
7877
8457
  timeout: 3e5
7878
8458
  });
@@ -7896,7 +8476,7 @@ var GitpodProvider = class {
7896
8476
  const deadline = Date.now() + 5 * 60 * 1e3;
7897
8477
  while (Date.now() < deadline) {
7898
8478
  try {
7899
- const { stdout } = await execFileP3(
8479
+ const { stdout } = await execFileP4(
7900
8480
  "gitpod",
7901
8481
  ["workspace", "get", workspaceId, "--output", "json"],
7902
8482
  { maxBuffer: MAX_BUFFER2 }
@@ -7914,7 +8494,7 @@ var GitpodProvider = class {
7914
8494
  }
7915
8495
  async listMachineTypes(_projectId) {
7916
8496
  try {
7917
- const { stdout } = await execFileP3(
8497
+ const { stdout } = await execFileP4(
7918
8498
  "gitpod",
7919
8499
  ["organization", "list-classes", "--output", "json"],
7920
8500
  { maxBuffer: MAX_BUFFER2 }
@@ -7932,7 +8512,7 @@ var GitpodProvider = class {
7932
8512
  async listExistingWorkspaces(projectId) {
7933
8513
  try {
7934
8514
  const args2 = ["workspace", "list", "--output", "json", "--limit", "200"];
7935
- const { stdout } = await execFileP3("gitpod", args2, { maxBuffer: MAX_BUFFER2 });
8515
+ const { stdout } = await execFileP4("gitpod", args2, { maxBuffer: MAX_BUFFER2 });
7936
8516
  const list = JSON.parse(stdout);
7937
8517
  return list.filter((w3) => !projectId || w3.contextUrl === projectId).map((w3) => ({
7938
8518
  id: w3.id,
@@ -7947,7 +8527,7 @@ var GitpodProvider = class {
7947
8527
  }
7948
8528
  async startWorkspace(workspaceId) {
7949
8529
  try {
7950
- await execFileP3(
8530
+ await execFileP4(
7951
8531
  "gitpod",
7952
8532
  ["workspace", "start", workspaceId],
7953
8533
  { maxBuffer: MAX_BUFFER2, timeout: 6e4 }
@@ -7963,7 +8543,7 @@ var GitpodProvider = class {
7963
8543
  }
7964
8544
  async exec(workspaceId, command2) {
7965
8545
  try {
7966
- const { stdout, stderr } = await execFileP3(
8546
+ const { stdout, stderr } = await execFileP4(
7967
8547
  "gitpod",
7968
8548
  ["workspace", "ssh", workspaceId, "--", command2],
7969
8549
  { maxBuffer: MAX_BUFFER2, timeout: 6e5 }
@@ -7981,7 +8561,7 @@ var GitpodProvider = class {
7981
8561
  async streamCommand(workspaceId, command2) {
7982
8562
  resetStdinForChild2();
7983
8563
  return new Promise((resolve2, reject) => {
7984
- const proc = (0, import_child_process9.spawn)(
8564
+ const proc = (0, import_child_process10.spawn)(
7985
8565
  "gitpod",
7986
8566
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
7987
8567
  { stdio: "inherit" }
@@ -8001,11 +8581,11 @@ var GitpodProvider = class {
8001
8581
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
8002
8582
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
8003
8583
  await new Promise((resolve2, reject) => {
8004
- const tar = (0, import_child_process9.spawn)("tar", tarArgs, {
8584
+ const tar = (0, import_child_process10.spawn)("tar", tarArgs, {
8005
8585
  stdio: ["ignore", "pipe", "pipe"],
8006
8586
  env: tarEnv
8007
8587
  });
8008
- const ssh = (0, import_child_process9.spawn)(
8588
+ const ssh = (0, import_child_process10.spawn)(
8009
8589
  "gitpod",
8010
8590
  ["workspace", "ssh", workspaceId, "--", remoteCmd],
8011
8591
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -8027,7 +8607,7 @@ var GitpodProvider = class {
8027
8607
  });
8028
8608
  }
8029
8609
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
8030
- const remoteDir = path14.posix.dirname(remotePath);
8610
+ const remoteDir = path17.posix.dirname(remotePath);
8031
8611
  const parts = [
8032
8612
  `mkdir -p ${shellQuote2(remoteDir)}`,
8033
8613
  `cat > ${shellQuote2(remotePath)}`
@@ -8037,7 +8617,7 @@ var GitpodProvider = class {
8037
8617
  }
8038
8618
  const cmd = parts.join(" && ");
8039
8619
  await new Promise((resolve2, reject) => {
8040
- const proc = (0, import_child_process9.spawn)(
8620
+ const proc = (0, import_child_process10.spawn)(
8041
8621
  "gitpod",
8042
8622
  ["workspace", "ssh", workspaceId, "--", cmd],
8043
8623
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -8061,10 +8641,10 @@ function shellQuote2(s) {
8061
8641
  }
8062
8642
 
8063
8643
  // src/services/providers/gitlab-workspaces.ts
8064
- var import_child_process10 = require("child_process");
8065
- var import_util4 = require("util");
8066
- var path15 = __toESM(require("path"));
8067
- var execFileP4 = (0, import_util4.promisify)(import_child_process10.execFile);
8644
+ var import_child_process11 = require("child_process");
8645
+ var import_util5 = require("util");
8646
+ var path18 = __toESM(require("path"));
8647
+ var execFileP5 = (0, import_util5.promisify)(import_child_process11.execFile);
8068
8648
  var MAX_BUFFER3 = 8 * 1024 * 1024;
8069
8649
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
8070
8650
  function resetStdinForChild3() {
@@ -8082,7 +8662,7 @@ var GitLabWorkspacesProvider = class {
8082
8662
  available = true;
8083
8663
  async authorize() {
8084
8664
  try {
8085
- await execFileP4("glab", ["--version"], { maxBuffer: MAX_BUFFER3 });
8665
+ await execFileP5("glab", ["--version"], { maxBuffer: MAX_BUFFER3 });
8086
8666
  } catch {
8087
8667
  throw new Error(
8088
8668
  [
@@ -8096,7 +8676,7 @@ var GitLabWorkspacesProvider = class {
8096
8676
  );
8097
8677
  }
8098
8678
  try {
8099
- await execFileP4("glab", ["auth", "status"], { maxBuffer: MAX_BUFFER3 });
8679
+ await execFileP5("glab", ["auth", "status"], { maxBuffer: MAX_BUFFER3 });
8100
8680
  return;
8101
8681
  } catch {
8102
8682
  }
@@ -8106,7 +8686,7 @@ var GitLabWorkspacesProvider = class {
8106
8686
  );
8107
8687
  resetStdinForChild3();
8108
8688
  await new Promise((resolve2, reject) => {
8109
- const proc = (0, import_child_process10.spawn)(
8689
+ const proc = (0, import_child_process11.spawn)(
8110
8690
  "glab",
8111
8691
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
8112
8692
  { stdio: "inherit" }
@@ -8120,7 +8700,7 @@ var GitLabWorkspacesProvider = class {
8120
8700
  }
8121
8701
  async listProjects() {
8122
8702
  try {
8123
- const { stdout } = await execFileP4(
8703
+ const { stdout } = await execFileP5(
8124
8704
  "glab",
8125
8705
  ["repo", "list", "--per-page", "200", "--output", "json"],
8126
8706
  { maxBuffer: MAX_BUFFER3 }
@@ -8252,7 +8832,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
8252
8832
  async exec(workspaceId, command2) {
8253
8833
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
8254
8834
  try {
8255
- const { stdout, stderr } = await execFileP4(
8835
+ const { stdout, stderr } = await execFileP5(
8256
8836
  "ssh",
8257
8837
  [
8258
8838
  "-o",
@@ -8278,7 +8858,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
8278
8858
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
8279
8859
  resetStdinForChild3();
8280
8860
  return new Promise((resolve2, reject) => {
8281
- const proc = (0, import_child_process10.spawn)(
8861
+ const proc = (0, import_child_process11.spawn)(
8282
8862
  "ssh",
8283
8863
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
8284
8864
  { stdio: "inherit" }
@@ -8299,8 +8879,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
8299
8879
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
8300
8880
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
8301
8881
  await new Promise((resolve2, reject) => {
8302
- const tar = (0, import_child_process10.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
8303
- const ssh = (0, import_child_process10.spawn)(
8882
+ const tar = (0, import_child_process11.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
8883
+ const ssh = (0, import_child_process11.spawn)(
8304
8884
  "ssh",
8305
8885
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
8306
8886
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -8323,14 +8903,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
8323
8903
  }
8324
8904
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
8325
8905
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
8326
- const remoteDir = path15.posix.dirname(remotePath);
8906
+ const remoteDir = path18.posix.dirname(remotePath);
8327
8907
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
8328
8908
  if (options.mode != null) {
8329
8909
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
8330
8910
  }
8331
8911
  const cmd = parts.join(" && ");
8332
8912
  await new Promise((resolve2, reject) => {
8333
- const proc = (0, import_child_process10.spawn)(
8913
+ const proc = (0, import_child_process11.spawn)(
8334
8914
  "ssh",
8335
8915
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
8336
8916
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -8355,7 +8935,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
8355
8935
  */
8356
8936
  async getGlabToken() {
8357
8937
  try {
8358
- const { stdout, stderr } = await execFileP4(
8938
+ const { stdout, stderr } = await execFileP5(
8359
8939
  "glab",
8360
8940
  ["auth", "status", "--show-token"],
8361
8941
  { maxBuffer: MAX_BUFFER3 }
@@ -8389,10 +8969,10 @@ function shellQuote3(s) {
8389
8969
  }
8390
8970
 
8391
8971
  // src/services/providers/railway.ts
8392
- var import_child_process11 = require("child_process");
8393
- var import_util5 = require("util");
8394
- var path16 = __toESM(require("path"));
8395
- var execFileP5 = (0, import_util5.promisify)(import_child_process11.execFile);
8972
+ var import_child_process12 = require("child_process");
8973
+ var import_util6 = require("util");
8974
+ var path19 = __toESM(require("path"));
8975
+ var execFileP6 = (0, import_util6.promisify)(import_child_process12.execFile);
8396
8976
  var MAX_BUFFER4 = 8 * 1024 * 1024;
8397
8977
  function resetStdinForChild4() {
8398
8978
  if (process.stdin.isTTY) {
@@ -8409,7 +8989,7 @@ var RailwayProvider = class {
8409
8989
  available = true;
8410
8990
  async authorize() {
8411
8991
  try {
8412
- await execFileP5("railway", ["--version"], { maxBuffer: MAX_BUFFER4 });
8992
+ await execFileP6("railway", ["--version"], { maxBuffer: MAX_BUFFER4 });
8413
8993
  } catch {
8414
8994
  throw new Error(
8415
8995
  [
@@ -8423,7 +9003,7 @@ var RailwayProvider = class {
8423
9003
  );
8424
9004
  }
8425
9005
  try {
8426
- await execFileP5("railway", ["whoami"], { maxBuffer: MAX_BUFFER4 });
9006
+ await execFileP6("railway", ["whoami"], { maxBuffer: MAX_BUFFER4 });
8427
9007
  return;
8428
9008
  } catch {
8429
9009
  }
@@ -8433,7 +9013,7 @@ var RailwayProvider = class {
8433
9013
  );
8434
9014
  resetStdinForChild4();
8435
9015
  await new Promise((resolve2, reject) => {
8436
- const proc = (0, import_child_process11.spawn)("railway", ["login"], { stdio: "inherit" });
9016
+ const proc = (0, import_child_process12.spawn)("railway", ["login"], { stdio: "inherit" });
8437
9017
  proc.on("exit", (code) => {
8438
9018
  if (code === 0) resolve2();
8439
9019
  else reject(new Error("railway login failed."));
@@ -8443,7 +9023,7 @@ var RailwayProvider = class {
8443
9023
  }
8444
9024
  async listProjects() {
8445
9025
  try {
8446
- const { stdout } = await execFileP5(
9026
+ const { stdout } = await execFileP6(
8447
9027
  "railway",
8448
9028
  ["list", "--json"],
8449
9029
  { maxBuffer: MAX_BUFFER4 }
@@ -8459,7 +9039,7 @@ var RailwayProvider = class {
8459
9039
  }));
8460
9040
  } catch {
8461
9041
  try {
8462
- const { stdout } = await execFileP5("railway", ["list"], { maxBuffer: MAX_BUFFER4 });
9042
+ const { stdout } = await execFileP6("railway", ["list"], { maxBuffer: MAX_BUFFER4 });
8463
9043
  const projects = [];
8464
9044
  for (const line of stdout.split("\n")) {
8465
9045
  const trimmed = line.trim();
@@ -8510,7 +9090,7 @@ var RailwayProvider = class {
8510
9090
  async listExistingWorkspaces(projectId) {
8511
9091
  if (!projectId) return [];
8512
9092
  try {
8513
- const { stdout } = await execFileP5(
9093
+ const { stdout } = await execFileP6(
8514
9094
  "railway",
8515
9095
  ["service", "list", "--project", projectId, "--json"],
8516
9096
  { maxBuffer: MAX_BUFFER4 }
@@ -8535,7 +9115,7 @@ var RailwayProvider = class {
8535
9115
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
8536
9116
  }
8537
9117
  try {
8538
- await execFileP5(
9118
+ await execFileP6(
8539
9119
  "railway",
8540
9120
  ["service", "restart", "--service", serviceId, "--project", projectId],
8541
9121
  { maxBuffer: MAX_BUFFER4, timeout: 6e4 }
@@ -8554,7 +9134,7 @@ var RailwayProvider = class {
8554
9134
  };
8555
9135
  }
8556
9136
  try {
8557
- const { stdout, stderr } = await execFileP5(
9137
+ const { stdout, stderr } = await execFileP6(
8558
9138
  "railway",
8559
9139
  ["run", "--project", projectId, "--service", serviceId, "--", "bash", "-lc", command2],
8560
9140
  { maxBuffer: MAX_BUFFER4, timeout: 6e5 }
@@ -8576,7 +9156,7 @@ var RailwayProvider = class {
8576
9156
  }
8577
9157
  resetStdinForChild4();
8578
9158
  return new Promise((resolve2, reject) => {
8579
- const proc = (0, import_child_process11.spawn)(
9159
+ const proc = (0, import_child_process12.spawn)(
8580
9160
  "railway",
8581
9161
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
8582
9162
  { stdio: "inherit" }
@@ -8600,8 +9180,8 @@ var RailwayProvider = class {
8600
9180
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
8601
9181
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
8602
9182
  await new Promise((resolve2, reject) => {
8603
- const tar = (0, import_child_process11.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
8604
- const sh = (0, import_child_process11.spawn)(
9183
+ const tar = (0, import_child_process12.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
9184
+ const sh = (0, import_child_process12.spawn)(
8605
9185
  "railway",
8606
9186
  ["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
8607
9187
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -8627,14 +9207,14 @@ var RailwayProvider = class {
8627
9207
  if (!projectId || !serviceId) {
8628
9208
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
8629
9209
  }
8630
- const remoteDir = path16.posix.dirname(remotePath);
9210
+ const remoteDir = path19.posix.dirname(remotePath);
8631
9211
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
8632
9212
  if (options.mode != null) {
8633
9213
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
8634
9214
  }
8635
9215
  const cmd = parts.join(" && ");
8636
9216
  await new Promise((resolve2, reject) => {
8637
- const proc = (0, import_child_process11.spawn)(
9217
+ const proc = (0, import_child_process12.spawn)(
8638
9218
  "railway",
8639
9219
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
8640
9220
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -8666,8 +9246,7 @@ var PROVIDERS = [
8666
9246
  ];
8667
9247
 
8668
9248
  // src/commands/deploy.ts
8669
- var execFileP6 = (0, import_util6.promisify)(import_child_process12.execFile);
8670
- async function deploy() {
9249
+ async function deploy(args2 = []) {
8671
9250
  console.log();
8672
9251
  mt(import_picocolors9.default.bgMagenta(import_picocolors9.default.white(" codeam deploy ")));
8673
9252
  const provider = await pickProvider();
@@ -8819,13 +9398,14 @@ async function deploy() {
8819
9398
  process.exit(1);
8820
9399
  }
8821
9400
  }
8822
- const localClaudeDir = path17.join(os10.homedir(), ".claude");
8823
- const localCredsKind = await detectLocalClaudeCredentials(localClaudeDir);
9401
+ const cfg = loadCliConfig();
9402
+ const agentId = parseAgentFlag(args2) ?? await promptForAgent(cfg.preferredAgent ?? "claude");
9403
+ const strategy = createDeployStrategy(agentId);
9404
+ const localCreds = await strategy.detectLocalCredentials();
8824
9405
  let bridged = "none";
8825
- if (localCredsKind !== "none") {
8826
- const sourceLabel = localCredsKind === "flat-file" ? "~/.claude/.credentials.json" : "macOS Keychain";
9406
+ if (localCreds.source !== "none") {
8827
9407
  const useLocal = await ot2({
8828
- message: `Copy your local Claude credentials (${sourceLabel}) to the workspace?`,
9408
+ message: `Copy your local ${AGENT_REGISTRY[agentId].displayName} credentials (${localCreds.description}) to the workspace?`,
8829
9409
  active: "Yes \u2014 same account, no re-auth",
8830
9410
  inactive: "No \u2014 log in with a different account",
8831
9411
  initialValue: true
@@ -8836,118 +9416,13 @@ async function deploy() {
8836
9416
  }
8837
9417
  if (useLocal) {
8838
9418
  const credStep = fe();
8839
- credStep.start("Bridging Claude credentials\u2026");
8840
- bridged = await bridgeClaudeCredentials(provider, workspace.id, localClaudeDir);
8841
- switch (bridged) {
8842
- case "flat-file":
8843
- credStep.stop("\u2713 Local credentials staged");
8844
- break;
8845
- case "macos-keychain":
8846
- credStep.stop("\u2713 Credentials extracted from macOS Keychain and staged");
8847
- break;
8848
- case "none":
8849
- credStep.stop("\u26A0 Could not extract local credentials \u2014 falling back to remote login");
8850
- break;
8851
- }
8852
- }
8853
- }
8854
- const claudeStep = fe();
8855
- claudeStep.start("Installing Claude CLI on workspace\u2026");
8856
- const installResult = await provider.exec(
8857
- workspace.id,
8858
- "curl -fsSL https://claude.ai/install.sh | bash"
8859
- );
8860
- if (installResult.code !== 0) {
8861
- claudeStep.stop("\u2717 Claude CLI install failed");
8862
- pt(installResult.stderr.slice(0, 1e3));
8863
- process.exit(1);
8864
- }
8865
- claudeStep.stop("\u2713 Claude CLI installed");
8866
- const haveLocalClaude = fs11.existsSync(localClaudeDir) && fs11.statSync(localClaudeDir).isDirectory();
8867
- if (haveLocalClaude) {
8868
- const copyStep = fe();
8869
- copyStep.start("Copying local Claude config to workspace\u2026");
8870
- try {
8871
- await provider.uploadDirectory(
8872
- workspace.id,
8873
- localClaudeDir,
8874
- "/home/codespace/.claude",
8875
- {
8876
- exclude: [
8877
- "./projects",
8878
- // per-project conversation history (often 700MB+)
8879
- "./file-history",
8880
- // per-project file diffs
8881
- "./downloads",
8882
- // downloaded artifacts
8883
- "./image-cache",
8884
- // cached images
8885
- "./paste-cache",
8886
- // clipboard/paste cache
8887
- "./backups",
8888
- // local backups
8889
- "./shell-snapshots",
8890
- // shell history snapshots
8891
- "./telemetry",
8892
- // analytics dumps
8893
- "./statsig",
8894
- // feature-flag cache
8895
- "./cache",
8896
- // generic cache dir
8897
- "./history.jsonl",
8898
- // global REPL history
8899
- "./ide",
8900
- // local IDE bridge state
8901
- "./todos",
8902
- // local todo state
8903
- "./tasks",
8904
- // local task state
8905
- // Don't overwrite the credentials we already staged in
8906
- // step 4 — the local dir on macOS doesn't have a flat
8907
- // credentials file anyway, but on Linux it would, and a
8908
- // re-write here would be redundant.
8909
- "./.credentials.json"
8910
- ]
8911
- }
8912
- );
8913
- copyStep.stop("\u2713 Claude config uploaded");
8914
- } catch (err) {
8915
- copyStep.stop("\u26A0 Could not upload Claude config (continuing)");
8916
- void err;
8917
- }
8918
- }
8919
- if (bridged !== "none") {
8920
- const localClaudeJson = path17.join(os10.homedir(), ".claude.json");
8921
- if (fs11.existsSync(localClaudeJson)) {
8922
- try {
8923
- const contents = fs11.readFileSync(localClaudeJson);
8924
- await provider.uploadFile(
8925
- workspace.id,
8926
- "/home/codespace/.claude.json",
8927
- contents,
8928
- { mode: 384 }
8929
- );
8930
- } catch (err) {
8931
- void err;
8932
- }
8933
- }
8934
- }
8935
- const verifyStep = fe();
8936
- verifyStep.start("Verifying Claude auth on workspace\u2026");
8937
- const verified = await verifyClaudeAuth(provider, workspace.id);
8938
- if (verified) {
8939
- verifyStep.stop("\u2713 Claude is logged in \u2014 no re-auth needed");
8940
- } else {
8941
- verifyStep.stop("\xB7 Claude not yet authenticated \u2014 running login flow");
8942
- await runRemoteClaudeLogin(provider, workspace.id);
8943
- const reverified = await verifyClaudeAuth(provider, workspace.id);
8944
- if (!reverified) {
8945
- wt(
8946
- "Claude auth could not be confirmed. You may need to run `claude /login` manually inside the codespace.",
8947
- "Heads up"
8948
- );
9419
+ credStep.start(`Bridging ${AGENT_REGISTRY[agentId].displayName} credentials\u2026`);
9420
+ const result = await strategy.bridgeLocalCredentials(provider, workspace.id);
9421
+ bridged = result.source;
9422
+ credStep.stop(`\u2713 Credentials staged (${bridged})`);
8949
9423
  }
8950
9424
  }
9425
+ await strategy.setupOnWorkspace(provider, workspace.id, { bridged });
8951
9426
  const cliStep = fe();
8952
9427
  cliStep.start("Installing codeam-cli on workspace\u2026");
8953
9428
  const cliInstall = await provider.exec(workspace.id, "npm install -g codeam-cli@latest");
@@ -8962,8 +9437,8 @@ async function deploy() {
8962
9437
  `Workspace: ${import_picocolors9.default.cyan(workspace.displayName ?? workspace.id)}`,
8963
9438
  workspace.webUrl ? `Web: ${import_picocolors9.default.cyan(workspace.webUrl)}` : "",
8964
9439
  "",
8965
- "Starting `codeam pair` on the workspace.",
8966
- "Scan the QR code below with the CodeAgent Mobile app to finish pairing.",
9440
+ `Starting \`codeam pair\` on the workspace (agent: ${AGENT_REGISTRY[agentId].displayName}).`,
9441
+ "Scan the QR code from your phone to pair.",
8967
9442
  import_picocolors9.default.dim("(Once paired, this terminal disconnects automatically; the session stays alive on the codespace.)")
8968
9443
  ].filter(Boolean).join("\n"),
8969
9444
  "Almost there"
@@ -8973,10 +9448,9 @@ async function deploy() {
8973
9448
  "LOG=~/.codeam-deploy/session.log",
8974
9449
  ': > "$LOG"',
8975
9450
  // The default `gh codespace ssh` cwd is the repo root
8976
- // (/workspaces/<repo>), which is exactly where Claude needs to
9451
+ // (/workspaces/<repo>), which is exactly where the agent needs to
8977
9452
  // run so it can read/edit project files. Pass that to PM2 via
8978
- // --cwd so the relay's child Claude inherits the right
8979
- // working directory.
9453
+ // --cwd so the relay's child agent inherits the right directory.
8980
9454
  'PROJECT_DIR="$(pwd)"',
8981
9455
  // Install PM2 if it isn't already on PATH. Idempotent.
8982
9456
  "if ! command -v pm2 >/dev/null 2>&1; then",
@@ -8988,11 +9462,11 @@ async function deploy() {
8988
9462
  // Start codeam pair under PM2. `--merge-logs` writes stdout
8989
9463
  // and stderr to the same file so we only need one tail.
8990
9464
  // --max-restarts 3 keeps PM2 from looping forever if codeam pair
8991
- // can't start (e.g. backend unreachable) — three attempts is
8992
- // enough for transient flakes, anything more wastes time.
8993
- // No `--time` (would prefix every line with a timestamp and
8994
- // break the QR rendering); no `--no-pmx` either (default off).
8995
- 'pm2 start codeam --name codeam-pair --cwd "$PROJECT_DIR" --max-restarts 3 -o "$LOG" -e "$LOG" --merge-logs -- pair >/dev/null 2>&1',
9465
+ // can't start (e.g. backend unreachable) — three attempts is enough
9466
+ // for transient flakes.
9467
+ // No `--time` (would prefix every line with a timestamp); no
9468
+ // `--no-pmx` either (default off).
9469
+ `pm2 start codeam --name codeam-pair --cwd "$PROJECT_DIR" --max-restarts 3 -o "$LOG" -e "$LOG" --merge-logs -- pair --agent=${agentId} >/dev/null 2>&1`,
8996
9470
  // Give PM2 a moment to spawn the process before we start polling
8997
9471
  // status — otherwise the very first jlist can race the spawn.
8998
9472
  "sleep 2",
@@ -9090,83 +9564,6 @@ async function deploy() {
9090
9564
  function shellQuoteSingle(s) {
9091
9565
  return `'${s.replace(/'/g, `'\\''`)}'`;
9092
9566
  }
9093
- async function runRemoteClaudeLogin(provider, workspaceId) {
9094
- wt(
9095
- [
9096
- "A login URL will print below. Open it in your local browser, sign in,",
9097
- "and paste any code Claude asks for back into this terminal."
9098
- ].join("\n"),
9099
- "Authenticating Claude on workspace"
9100
- );
9101
- const result = await provider.streamCommand(
9102
- workspaceId,
9103
- 'bash -lc "claude login || claude /login || true"'
9104
- );
9105
- if (result.code !== 0) {
9106
- wt(
9107
- "claude login exited non-zero. You can re-run it manually inside the codespace later.",
9108
- "Heads up"
9109
- );
9110
- }
9111
- }
9112
- async function detectLocalClaudeCredentials(localClaudeDir) {
9113
- if (fs11.existsSync(path17.join(localClaudeDir, ".credentials.json"))) {
9114
- return "flat-file";
9115
- }
9116
- if (process.platform === "darwin") {
9117
- try {
9118
- await execFileP6(
9119
- "security",
9120
- ["find-generic-password", "-s", "Claude Code-credentials"],
9121
- { maxBuffer: 1024 * 1024 }
9122
- );
9123
- return "macos-keychain";
9124
- } catch {
9125
- return "none";
9126
- }
9127
- }
9128
- return "none";
9129
- }
9130
- async function verifyClaudeAuth(provider, workspaceId) {
9131
- const result = await provider.exec(
9132
- workspaceId,
9133
- 'bash -lc "claude auth status 2>/dev/null || true"'
9134
- );
9135
- if (result.code !== 0) return false;
9136
- const jsonStart = result.stdout.indexOf("{");
9137
- if (jsonStart < 0) return false;
9138
- try {
9139
- const parsed = JSON.parse(result.stdout.slice(jsonStart));
9140
- return parsed.loggedIn === true;
9141
- } catch {
9142
- return false;
9143
- }
9144
- }
9145
- async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
9146
- const fileBased = path17.join(localClaudeDir, ".credentials.json");
9147
- if (fs11.existsSync(fileBased)) return "flat-file";
9148
- if (process.platform === "darwin") {
9149
- try {
9150
- const { stdout } = await execFileP6(
9151
- "security",
9152
- ["find-generic-password", "-s", "Claude Code-credentials", "-w"],
9153
- { maxBuffer: 1024 * 1024 }
9154
- );
9155
- const json = stdout.trim();
9156
- if (json.length === 0) return "none";
9157
- await provider.uploadFile(
9158
- workspaceId,
9159
- "/home/codespace/.claude/.credentials.json",
9160
- json,
9161
- { mode: 384 }
9162
- );
9163
- return "macos-keychain";
9164
- } catch {
9165
- return "none";
9166
- }
9167
- }
9168
- return "none";
9169
- }
9170
9567
  function formatLastUsed(iso) {
9171
9568
  if (!iso) return "";
9172
9569
  const t2 = Date.parse(iso);
@@ -9352,7 +9749,7 @@ async function stopWorkspaceFromLocal(target) {
9352
9749
  // src/commands/version.ts
9353
9750
  var import_picocolors11 = __toESM(require("picocolors"));
9354
9751
  function version() {
9355
- const v = true ? "2.10.8" : "unknown";
9752
+ const v = true ? "2.11.0" : "unknown";
9356
9753
  console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
9357
9754
  }
9358
9755
 
@@ -9389,9 +9786,9 @@ function help() {
9389
9786
  }
9390
9787
 
9391
9788
  // src/lib/updateNotifier.ts
9392
- var fs12 = __toESM(require("fs"));
9393
- var os11 = __toESM(require("os"));
9394
- var path18 = __toESM(require("path"));
9789
+ var fs14 = __toESM(require("fs"));
9790
+ var os13 = __toESM(require("os"));
9791
+ var path20 = __toESM(require("path"));
9395
9792
  var https5 = __toESM(require("https"));
9396
9793
  var import_picocolors13 = __toESM(require("picocolors"));
9397
9794
  var PKG_NAME = "codeam-cli";
@@ -9399,12 +9796,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
9399
9796
  var TTL_MS = 24 * 60 * 60 * 1e3;
9400
9797
  var REQUEST_TIMEOUT_MS = 1500;
9401
9798
  function cachePath() {
9402
- const dir = path18.join(os11.homedir(), ".codeam");
9403
- return path18.join(dir, "update-check.json");
9799
+ const dir = path20.join(os13.homedir(), ".codeam");
9800
+ return path20.join(dir, "update-check.json");
9404
9801
  }
9405
9802
  function readCache() {
9406
9803
  try {
9407
- const raw = fs12.readFileSync(cachePath(), "utf8");
9804
+ const raw = fs14.readFileSync(cachePath(), "utf8");
9408
9805
  const parsed = JSON.parse(raw);
9409
9806
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
9410
9807
  return parsed;
@@ -9415,8 +9812,8 @@ function readCache() {
9415
9812
  function writeCache(cache) {
9416
9813
  try {
9417
9814
  const file = cachePath();
9418
- fs12.mkdirSync(path18.dirname(file), { recursive: true });
9419
- fs12.writeFileSync(file, JSON.stringify(cache));
9815
+ fs14.mkdirSync(path20.dirname(file), { recursive: true });
9816
+ fs14.writeFileSync(file, JSON.stringify(cache));
9420
9817
  } catch {
9421
9818
  }
9422
9819
  }
@@ -9487,7 +9884,7 @@ function checkForUpdates() {
9487
9884
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
9488
9885
  if (process.env.CI) return;
9489
9886
  if (!process.stdout.isTTY) return;
9490
- const current = true ? "2.10.8" : null;
9887
+ const current = true ? "2.11.0" : null;
9491
9888
  if (!current) return;
9492
9889
  const cache = readCache();
9493
9890
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -9516,7 +9913,7 @@ async function main() {
9516
9913
  case "help":
9517
9914
  return help();
9518
9915
  case "pair":
9519
- return pair();
9916
+ return pair(args);
9520
9917
  case "pair-auto":
9521
9918
  return pairAuto(args);
9522
9919
  case "sessions":
@@ -9528,7 +9925,7 @@ async function main() {
9528
9925
  case "deploy":
9529
9926
  if (args[0] === "ls" || args[0] === "list") return deployList();
9530
9927
  if (args[0] === "stop" || args[0] === "remove") return deployStop();
9531
- return deploy();
9928
+ return deploy(args);
9532
9929
  default:
9533
9930
  return start();
9534
9931
  }