codeam-cli 2.10.7 → 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.
- package/CHANGELOG.md +22 -0
- package/dist/index.js +1358 -924
- 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
|
|
523
|
-
var
|
|
524
|
-
var
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
1016
|
-
var
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|
|
@@ -1848,6 +2271,13 @@ var CommandRelayService = class {
|
|
|
1848
2271
|
/** Polling backoff state (only used on the fallback). */
|
|
1849
2272
|
pollTimer = null;
|
|
1850
2273
|
pollFailures = 0;
|
|
2274
|
+
// Successive polls that returned no commands. Drives an idle
|
|
2275
|
+
// backoff so a CLI sitting on the polling fallback (SSE was
|
|
2276
|
+
// unavailable) doesn't keep hammering the API every 2 s when
|
|
2277
|
+
// there's nothing to do. Reset to 0 whenever a poll DELIVERS a
|
|
2278
|
+
// command, so the first real work after a quiet period still
|
|
2279
|
+
// reaches the CLI quickly.
|
|
2280
|
+
pollEmptyStreak = 0;
|
|
1851
2281
|
/** Reconnect backoff state for the SSE stream. */
|
|
1852
2282
|
sseFailures = 0;
|
|
1853
2283
|
sseReconnectTimer = null;
|
|
@@ -1976,7 +2406,9 @@ var CommandRelayService = class {
|
|
|
1976
2406
|
if (!this._running) return;
|
|
1977
2407
|
await this.pollOnce();
|
|
1978
2408
|
if (this._running) {
|
|
1979
|
-
const
|
|
2409
|
+
const idleExp = Math.min(this.pollEmptyStreak, 4);
|
|
2410
|
+
const effectiveFailures = Math.max(this.pollFailures, idleExp);
|
|
2411
|
+
const delay = computePollDelay({ baseMs: 2e3, failures: effectiveFailures });
|
|
1980
2412
|
this.pollTimer = setTimeout(() => this.pollLoop(), delay);
|
|
1981
2413
|
}
|
|
1982
2414
|
}
|
|
@@ -1985,7 +2417,11 @@ var CommandRelayService = class {
|
|
|
1985
2417
|
const data = await _getJson(`${API_BASE2}/api/commands/pending?pluginId=${this.pluginId}`);
|
|
1986
2418
|
const commands = data?.data;
|
|
1987
2419
|
this.pollFailures = 0;
|
|
1988
|
-
if (!Array.isArray(commands) || commands.length === 0)
|
|
2420
|
+
if (!Array.isArray(commands) || commands.length === 0) {
|
|
2421
|
+
this.pollEmptyStreak += 1;
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
this.pollEmptyStreak = 0;
|
|
1989
2425
|
log.trace("relay", `poll received ${commands.length} command(s)`);
|
|
1990
2426
|
await this.dispatchCommands(commands);
|
|
1991
2427
|
} catch (err) {
|
|
@@ -2380,7 +2816,7 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2380
2816
|
resizeHandler = null;
|
|
2381
2817
|
/**
|
|
2382
2818
|
* Factory that returns a working ConPTY strategy or `null` if
|
|
2383
|
-
* node-pty can't load. The caller (
|
|
2819
|
+
* node-pty can't load. The caller (agent.service.ts) decides
|
|
2384
2820
|
* whether to fall back to the legacy pipe strategy.
|
|
2385
2821
|
*/
|
|
2386
2822
|
static tryCreate(opts) {
|
|
@@ -4485,9 +4921,10 @@ function buildClaudeLaunch(extraArgs = []) {
|
|
|
4485
4921
|
return { cmd: found, args: extraArgs };
|
|
4486
4922
|
}
|
|
4487
4923
|
|
|
4488
|
-
// src/services/
|
|
4489
|
-
var
|
|
4490
|
-
constructor(opts) {
|
|
4924
|
+
// src/services/agent.service.ts
|
|
4925
|
+
var AgentService = class {
|
|
4926
|
+
constructor(runtime, opts) {
|
|
4927
|
+
this.runtime = runtime;
|
|
4491
4928
|
this.opts = opts;
|
|
4492
4929
|
this.strategyOpts = {
|
|
4493
4930
|
onData: (d3) => {
|
|
@@ -4501,6 +4938,7 @@ var ClaudeService = class {
|
|
|
4501
4938
|
onExit: opts.onExit
|
|
4502
4939
|
};
|
|
4503
4940
|
}
|
|
4941
|
+
runtime;
|
|
4504
4942
|
opts;
|
|
4505
4943
|
// Strategy is selected lazily inside spawn() so we can fall back from
|
|
4506
4944
|
// ConPTY → legacy pipe at runtime if the native binding fails to load.
|
|
@@ -4532,10 +4970,20 @@ var ClaudeService = class {
|
|
|
4532
4970
|
this.pendingInputs.length = 0;
|
|
4533
4971
|
}
|
|
4534
4972
|
async spawn() {
|
|
4535
|
-
let launch
|
|
4536
|
-
|
|
4973
|
+
let launch;
|
|
4974
|
+
try {
|
|
4975
|
+
launch = await this.runtime.prepareLaunch();
|
|
4976
|
+
} catch {
|
|
4537
4977
|
const installed = await ensureClaudeInstalled();
|
|
4538
|
-
if (installed)
|
|
4978
|
+
if (installed) {
|
|
4979
|
+
try {
|
|
4980
|
+
launch = await this.runtime.prepareLaunch();
|
|
4981
|
+
} catch {
|
|
4982
|
+
launch = null;
|
|
4983
|
+
}
|
|
4984
|
+
} else {
|
|
4985
|
+
launch = null;
|
|
4986
|
+
}
|
|
4539
4987
|
if (!launch) {
|
|
4540
4988
|
const cmd = process.platform === "win32" ? "irm https://claude.ai/install.ps1 | iex" : "curl -fsSL https://claude.ai/install.sh | bash";
|
|
4541
4989
|
console.error(
|
|
@@ -4648,6 +5096,20 @@ var ClaudeService = class {
|
|
|
4648
5096
|
s.write("\r");
|
|
4649
5097
|
}, steps * ARROW_MS + ENTER_MS);
|
|
4650
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
|
+
}
|
|
4651
5113
|
/** Send Escape key to Claude (cancels interactive prompts). */
|
|
4652
5114
|
sendEscape() {
|
|
4653
5115
|
this.strategy?.write("\x1B");
|
|
@@ -4662,390 +5124,559 @@ var ClaudeService = class {
|
|
|
4662
5124
|
/**
|
|
4663
5125
|
* Kill the current Claude process and relaunch it resuming the given session.
|
|
4664
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.
|
|
4665
5132
|
*/
|
|
4666
5133
|
restart(sessionId, auto = false) {
|
|
4667
5134
|
if (!this.strategy) return;
|
|
4668
|
-
const
|
|
4669
|
-
|
|
4670
|
-
const launch = buildClaudeLaunch(extraArgs);
|
|
5135
|
+
const resumeArgs = auto ? this.runtime.resumeLaunchArgs(sessionId) : ["--resume", sessionId];
|
|
5136
|
+
const launch = buildClaudeLaunch(resumeArgs);
|
|
4671
5137
|
if (!launch) return;
|
|
4672
5138
|
this.strategy.kill();
|
|
4673
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
|
+
}
|
|
4674
5146
|
}
|
|
4675
5147
|
};
|
|
4676
5148
|
|
|
4677
|
-
//
|
|
4678
|
-
var
|
|
4679
|
-
var
|
|
4680
|
-
var
|
|
4681
|
-
var
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
if (/^Edit(?:ing)?\s+|^Writ(?:e|ing|ing to)\s+|^Creat(?:e|ing)\s+/i.test(text)) {
|
|
4743
|
-
const label2 = text.replace(/^(?:Edit(?:ing)?|Writ(?:e|ing(?: to)?)|Creat(?:e|ing))\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
4744
|
-
return { tool: "edit", label: label2, status: "running" };
|
|
4745
|
-
}
|
|
4746
|
-
if (/^Runn(?:ing)?\s+|^Execut(?:e|ing)\s+|^Bash(?:ing)?\s*:|^\$\s+/i.test(text)) {
|
|
4747
|
-
const label2 = text.replace(/^(?:Runn(?:ing)?|Execut(?:e|ing)|Bash(?:ing)?:|\$)\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
4748
|
-
return { tool: "bash", label: label2, status: "running" };
|
|
4749
|
-
}
|
|
4750
|
-
if (/^Search(?:ing)?\s+for\s+|^Grep(?:ping)?\s*:/i.test(text)) {
|
|
4751
|
-
const label2 = text.replace(/^(?:Search(?:ing)?\s+for|Grep(?:ping)?:)\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
4752
|
-
return { tool: "search", label: label2, status: "running" };
|
|
4753
|
-
}
|
|
4754
|
-
const label = text.replace(/\.\.\.$/, "").trim();
|
|
4755
|
-
return { tool: "other", label, status: "running" };
|
|
4756
|
-
}
|
|
4757
|
-
|
|
4758
|
-
// ../../packages/shared/src/protocol/renderToLines.ts
|
|
4759
|
-
function renderToLines(raw) {
|
|
4760
|
-
const screen = [""];
|
|
4761
|
-
let row = 0;
|
|
4762
|
-
let col = 0;
|
|
4763
|
-
function ensureRow() {
|
|
4764
|
-
while (screen.length <= row) screen.push("");
|
|
4765
|
-
}
|
|
4766
|
-
function writeChar(ch) {
|
|
4767
|
-
ensureRow();
|
|
4768
|
-
if (col < screen[row].length) {
|
|
4769
|
-
screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);
|
|
4770
|
-
} else {
|
|
4771
|
-
while (screen[row].length < col) screen[row] += " ";
|
|
4772
|
-
screen[row] += ch;
|
|
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;
|
|
4773
5214
|
}
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
}
|
|
4800
|
-
const p2 = param.split(";");
|
|
4801
|
-
row = Math.max(0, (parseInt(p2[0] ?? "1") || 1) - 1);
|
|
4802
|
-
col = Math.max(0, (parseInt(p2[1] ?? "1") || 1) - 1);
|
|
4803
|
-
ensureRow();
|
|
4804
|
-
} else if (cmd === "J") {
|
|
4805
|
-
if (param === "2" || param === "3") {
|
|
4806
|
-
screen.length = 1;
|
|
4807
|
-
screen[0] = "";
|
|
4808
|
-
row = 0;
|
|
4809
|
-
col = 0;
|
|
4810
|
-
} else if (param === "1") {
|
|
4811
|
-
for (let r = 0; r < row; r++) screen[r] = "";
|
|
4812
|
-
screen[row] = " ".repeat(col) + screen[row].slice(col);
|
|
4813
|
-
} else {
|
|
4814
|
-
screen[row] = screen[row].slice(0, col);
|
|
4815
|
-
screen.splice(row + 1);
|
|
4816
|
-
}
|
|
4817
|
-
} else if (cmd === "K") {
|
|
4818
|
-
ensureRow();
|
|
4819
|
-
if (param === "" || param === "0") screen[row] = screen[row].slice(0, col);
|
|
4820
|
-
else if (param === "1") screen[row] = " ".repeat(col) + screen[row].slice(col);
|
|
4821
|
-
else if (param === "2") screen[row] = "";
|
|
4822
|
-
} else if (cmd === "h" && (param === "?1049" || param === "?47")) {
|
|
4823
|
-
screen.length = 1;
|
|
4824
|
-
screen[0] = "";
|
|
4825
|
-
row = 0;
|
|
4826
|
-
col = 0;
|
|
4827
|
-
} else if (cmd === "l" && (param === "?1049" || param === "?47")) {
|
|
4828
|
-
screen.length = 1;
|
|
4829
|
-
screen[0] = "";
|
|
4830
|
-
row = 0;
|
|
4831
|
-
col = 0;
|
|
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 {
|
|
4832
5241
|
}
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
if (raw[i] === "\x07") break;
|
|
4837
|
-
if (raw[i] === "\x1B" && i + 1 < raw.length && raw[i + 1] === "\\") {
|
|
4838
|
-
i++;
|
|
4839
|
-
break;
|
|
4840
|
-
}
|
|
4841
|
-
i++;
|
|
5242
|
+
try {
|
|
5243
|
+
fs5.unlinkSync(helperPath);
|
|
5244
|
+
} catch {
|
|
4842
5245
|
}
|
|
5246
|
+
resolve2(result);
|
|
5247
|
+
}, 5e3);
|
|
5248
|
+
}, 8e3);
|
|
5249
|
+
setTimeout(() => {
|
|
5250
|
+
if (!resolved) {
|
|
5251
|
+
resolved = true;
|
|
5252
|
+
resolve2(null);
|
|
4843
5253
|
}
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
col = 0;
|
|
4848
|
-
ensureRow();
|
|
4849
|
-
i++;
|
|
4850
|
-
} else {
|
|
4851
|
-
col = 0;
|
|
5254
|
+
try {
|
|
5255
|
+
proc.kill();
|
|
5256
|
+
} catch {
|
|
4852
5257
|
}
|
|
4853
|
-
}
|
|
4854
|
-
|
|
4855
|
-
col = 0;
|
|
4856
|
-
ensureRow();
|
|
4857
|
-
} else if (ch >= " " || ch === " ") {
|
|
4858
|
-
writeChar(ch);
|
|
4859
|
-
}
|
|
4860
|
-
i++;
|
|
4861
|
-
}
|
|
4862
|
-
return screen;
|
|
5258
|
+
}, 2e4);
|
|
5259
|
+
});
|
|
4863
5260
|
}
|
|
4864
5261
|
|
|
4865
|
-
//
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
);
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
);
|
|
4875
|
-
if (
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
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
|
+
}
|
|
4881
5282
|
}
|
|
5283
|
+
} catch {
|
|
4882
5284
|
}
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
5285
|
+
return null;
|
|
5286
|
+
}
|
|
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;
|
|
4893
5295
|
}
|
|
4894
|
-
const
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
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;
|
|
5311
|
+
}
|
|
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 {
|
|
4911
5326
|
}
|
|
4912
5327
|
}
|
|
4913
|
-
const
|
|
4914
|
-
if (
|
|
5328
|
+
const total = getContextWindow(lastModel);
|
|
5329
|
+
if (!lastUsage) {
|
|
5330
|
+
if (!lastModel) return null;
|
|
5331
|
+
return { used: 0, total, percent: 0, model: lastModel };
|
|
5332
|
+
}
|
|
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));
|
|
4915
5335
|
return {
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
5336
|
+
used: inputTokens,
|
|
5337
|
+
total,
|
|
5338
|
+
percent,
|
|
5339
|
+
model: lastModel ?? void 0
|
|
4920
5340
|
};
|
|
4921
5341
|
}
|
|
4922
|
-
function
|
|
4923
|
-
if (
|
|
4924
|
-
if (
|
|
4925
|
-
|
|
4926
|
-
const isSelected = (line) => /^\s+❯\s+\S/.test(line);
|
|
4927
|
-
const isUnselected = (line) => /^ \S/.test(line);
|
|
4928
|
-
const isItem = (line) => isSelected(line) || isUnselected(line);
|
|
4929
|
-
let optionStartIdx = -1;
|
|
4930
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4931
|
-
if (isItem(lines[i])) {
|
|
4932
|
-
optionStartIdx = i;
|
|
4933
|
-
break;
|
|
4934
|
-
}
|
|
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");
|
|
4935
5346
|
}
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
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;
|
|
5356
|
+
}
|
|
5357
|
+
for (const line of raw.split("\n").filter(Boolean)) {
|
|
5358
|
+
let rec;
|
|
5359
|
+
try {
|
|
5360
|
+
rec = JSON.parse(line);
|
|
5361
|
+
} catch {
|
|
4945
5362
|
continue;
|
|
4946
5363
|
}
|
|
4947
|
-
if (
|
|
4948
|
-
|
|
4949
|
-
if (
|
|
4950
|
-
|
|
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
|
+
});
|
|
5390
|
+
}
|
|
5391
|
+
return out;
|
|
5392
|
+
}
|
|
5393
|
+
|
|
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 };
|
|
5402
|
+
}
|
|
5403
|
+
resumeLaunchArgs(sessionId) {
|
|
5404
|
+
return ["--resume", sessionId, "--dangerously-skip-permissions"];
|
|
5405
|
+
}
|
|
5406
|
+
resolveHistoryDir(cwd) {
|
|
5407
|
+
return resolveHistoryDir(cwd);
|
|
5408
|
+
}
|
|
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: "" };
|
|
5463
|
+
}
|
|
4951
5464
|
}
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
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" };
|
|
5472
|
+
}
|
|
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: "" };
|
|
4965
5491
|
}
|
|
4966
5492
|
}
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
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
|
+
}
|
|
4974
5528
|
}
|
|
4975
5529
|
|
|
4976
|
-
//
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
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);
|
|
4985
5560
|
}
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
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
|
+
}
|
|
4989
5615
|
}
|
|
4990
|
-
if (
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
if (
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
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
|
+
}
|
|
5014
5647
|
}
|
|
5015
|
-
if (skipEchoContinuation) continue;
|
|
5016
|
-
result.push(line);
|
|
5017
5648
|
}
|
|
5018
|
-
|
|
5019
|
-
}
|
|
5649
|
+
};
|
|
5020
5650
|
|
|
5021
|
-
//
|
|
5022
|
-
var
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
"claude-3-5-sonnet": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
5026
|
-
"claude-3-5-haiku": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
|
|
5027
|
-
"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
|
|
5028
5655
|
};
|
|
5029
|
-
var
|
|
5030
|
-
|
|
5031
|
-
"claude-sonnet-4": 1e6,
|
|
5032
|
-
"claude-3-5-sonnet": 2e5,
|
|
5033
|
-
"claude-3-5-haiku": 2e5,
|
|
5034
|
-
"claude-3-haiku": 2e5
|
|
5656
|
+
var deployBuilders = {
|
|
5657
|
+
claude: () => new ClaudeDeployStrategy()
|
|
5035
5658
|
};
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
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
|
+
);
|
|
5040
5664
|
}
|
|
5041
|
-
|
|
5665
|
+
const build = runtimeBuilders[agent];
|
|
5666
|
+
if (!build) {
|
|
5667
|
+
throw new Error(`No runtime strategy registered for agent "${agent}"`);
|
|
5668
|
+
}
|
|
5669
|
+
return build();
|
|
5042
5670
|
}
|
|
5043
|
-
function
|
|
5044
|
-
if (!
|
|
5045
|
-
|
|
5046
|
-
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.`);
|
|
5047
5674
|
}
|
|
5048
|
-
|
|
5675
|
+
const build = deployBuilders[agent];
|
|
5676
|
+
if (!build) {
|
|
5677
|
+
throw new Error(`No deploy strategy registered for agent "${agent}"`);
|
|
5678
|
+
}
|
|
5679
|
+
return build();
|
|
5049
5680
|
}
|
|
5050
5681
|
|
|
5051
5682
|
// src/services/output/chrome-tracker.ts
|
|
@@ -5504,19 +6135,8 @@ var OutputService = class _OutputService {
|
|
|
5504
6135
|
tryExtractSessionId(text) {
|
|
5505
6136
|
if (!this.onSessionIdDetected) return;
|
|
5506
6137
|
const printable = text.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
|
|
5507
|
-
const
|
|
5508
|
-
|
|
5509
|
-
/Session[:\s]+([a-f0-9-]{36})/i,
|
|
5510
|
-
/Conversation[:\s]+([a-f0-9-]{36})/i,
|
|
5511
|
-
/Session\s+ID[:\s]+([a-f0-9-]{36})/i
|
|
5512
|
-
];
|
|
5513
|
-
for (const pattern of patterns) {
|
|
5514
|
-
const match = printable.match(pattern);
|
|
5515
|
-
if (match) {
|
|
5516
|
-
this.onSessionIdDetected(match[1]);
|
|
5517
|
-
return;
|
|
5518
|
-
}
|
|
5519
|
-
}
|
|
6138
|
+
const match = printable.match(/Resuming session[:\s]+([a-f0-9-]{36})/i);
|
|
6139
|
+
if (match) this.onSessionIdDetected(match[1]);
|
|
5520
6140
|
}
|
|
5521
6141
|
tryDetectRateLimit(text) {
|
|
5522
6142
|
if (!this.onRateLimitDetected) return;
|
|
@@ -5529,9 +6149,9 @@ var OutputService = class _OutputService {
|
|
|
5529
6149
|
};
|
|
5530
6150
|
|
|
5531
6151
|
// src/services/history.service.ts
|
|
5532
|
-
var
|
|
5533
|
-
var
|
|
5534
|
-
var
|
|
6152
|
+
var fs9 = __toESM(require("fs"));
|
|
6153
|
+
var path12 = __toESM(require("path"));
|
|
6154
|
+
var os10 = __toESM(require("os"));
|
|
5535
6155
|
var https4 = __toESM(require("https"));
|
|
5536
6156
|
var http4 = __toESM(require("http"));
|
|
5537
6157
|
var import_zod = require("zod");
|
|
@@ -5546,28 +6166,7 @@ var historyRecordSchema = import_zod.z.object({
|
|
|
5546
6166
|
}).passthrough().optional()
|
|
5547
6167
|
}).passthrough();
|
|
5548
6168
|
var API_BASE4 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
5549
|
-
function
|
|
5550
|
-
return cwd.replace(/[\\/:]/g, "-");
|
|
5551
|
-
}
|
|
5552
|
-
function findProjectDir(cwd) {
|
|
5553
|
-
const projectsRoot = path8.join(os6.homedir(), ".claude", "projects");
|
|
5554
|
-
const primary = path8.join(projectsRoot, encodeCwd(cwd));
|
|
5555
|
-
if (fs5.existsSync(primary)) return primary;
|
|
5556
|
-
try {
|
|
5557
|
-
const entries = fs5.readdirSync(projectsRoot, { withFileTypes: true });
|
|
5558
|
-
const wanted = encodeCwd(cwd);
|
|
5559
|
-
for (const e of entries) {
|
|
5560
|
-
if (!e.isDirectory()) continue;
|
|
5561
|
-
const candidate = e.name.replace(/-+/g, "-");
|
|
5562
|
-
if (candidate === wanted.replace(/-+/g, "-")) {
|
|
5563
|
-
return path8.join(projectsRoot, e.name);
|
|
5564
|
-
}
|
|
5565
|
-
}
|
|
5566
|
-
} catch {
|
|
5567
|
-
}
|
|
5568
|
-
return null;
|
|
5569
|
-
}
|
|
5570
|
-
function extractText(content) {
|
|
6169
|
+
function extractText2(content) {
|
|
5571
6170
|
if (typeof content === "string") return content;
|
|
5572
6171
|
if (Array.isArray(content)) {
|
|
5573
6172
|
return content.filter((b) => b["type"] === "text").map((b) => b["text"]).join("\n");
|
|
@@ -5579,7 +6178,7 @@ function parseJsonl(filePath) {
|
|
|
5579
6178
|
const messages = [];
|
|
5580
6179
|
let raw;
|
|
5581
6180
|
try {
|
|
5582
|
-
raw =
|
|
6181
|
+
raw = fs9.readFileSync(filePath, "utf8");
|
|
5583
6182
|
} catch (err) {
|
|
5584
6183
|
if (err.code !== "ENOENT") {
|
|
5585
6184
|
log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
|
|
@@ -5606,10 +6205,10 @@ function parseJsonl(filePath) {
|
|
|
5606
6205
|
const uuid = record.uuid ?? `${Date.now()}-${Math.random()}`;
|
|
5607
6206
|
const msg = record.message;
|
|
5608
6207
|
if (record.type === "user" && msg) {
|
|
5609
|
-
const text =
|
|
6208
|
+
const text = extractText2(msg.content).trim();
|
|
5610
6209
|
if (text) messages.push({ id: uuid, role: "user", text, timestamp });
|
|
5611
6210
|
} else if (record.type === "assistant" && msg) {
|
|
5612
|
-
const text =
|
|
6211
|
+
const text = extractText2(msg.content).trim();
|
|
5613
6212
|
if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
|
|
5614
6213
|
}
|
|
5615
6214
|
}
|
|
@@ -5652,10 +6251,12 @@ function post(endpoint, body) {
|
|
|
5652
6251
|
req.end();
|
|
5653
6252
|
});
|
|
5654
6253
|
}
|
|
5655
|
-
var HistoryService = class {
|
|
5656
|
-
constructor(pluginId, cwd) {
|
|
6254
|
+
var HistoryService = class _HistoryService {
|
|
6255
|
+
constructor(runtime, pluginId, cwd, options) {
|
|
5657
6256
|
this.pluginId = pluginId;
|
|
5658
6257
|
this.cwd = cwd;
|
|
6258
|
+
this.runtime = runtime;
|
|
6259
|
+
this.bootTimeMs = options?.bootTimeMs ?? Date.now();
|
|
5659
6260
|
}
|
|
5660
6261
|
pluginId;
|
|
5661
6262
|
cwd;
|
|
@@ -5671,6 +6272,26 @@ var HistoryService = class {
|
|
|
5671
6272
|
* resume re-uploads the full transcript on first call.
|
|
5672
6273
|
*/
|
|
5673
6274
|
lastUploadedUuid = /* @__PURE__ */ new Map();
|
|
6275
|
+
/**
|
|
6276
|
+
* Captured at construction time so every JSONL discovery (detect,
|
|
6277
|
+
* usage stats) can ignore files that already existed in the
|
|
6278
|
+
* project's `~/.claude/projects/<cwd>/` dir *before* this CLI
|
|
6279
|
+
* run started. Without this filter, an old conversation — or
|
|
6280
|
+
* a *parallel* Claude session actively writing to the same
|
|
6281
|
+
* project — wins the mtime sort and we publish its content
|
|
6282
|
+
* to the mobile app as if it were the fresh pair's chat.
|
|
6283
|
+
*/
|
|
6284
|
+
bootTimeMs;
|
|
6285
|
+
/**
|
|
6286
|
+
* Small grace window subtracted from `bootTimeMs` when filtering
|
|
6287
|
+
* by `birthtime`. Covers clock skew + filesystem timestamp
|
|
6288
|
+
* rounding (HFS+ floors to 1 s; some Linux filesystems round to
|
|
6289
|
+
* the nearest second). 5 s is comfortably wider than any
|
|
6290
|
+
* filesystem rounding while still excluding everything from a
|
|
6291
|
+
* previous pair / previous Claude run.
|
|
6292
|
+
*/
|
|
6293
|
+
static BIRTHTIME_GRACE_MS = 5e3;
|
|
6294
|
+
runtime;
|
|
5674
6295
|
/** Store rate limit reset info detected from Claude Code output */
|
|
5675
6296
|
setRateLimitReset(reset) {
|
|
5676
6297
|
this._rateLimitReset = reset;
|
|
@@ -5691,7 +6312,7 @@ var HistoryService = class {
|
|
|
5691
6312
|
return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
|
|
5692
6313
|
}
|
|
5693
6314
|
get projectDir() {
|
|
5694
|
-
return
|
|
6315
|
+
return this.runtime.resolveHistoryDir(this.cwd) ?? path12.join(os10.homedir(), ".claude", "projects", encodeCwd(this.cwd));
|
|
5695
6316
|
}
|
|
5696
6317
|
/** Set the current Claude conversation ID (extracted from /cost command or session start) */
|
|
5697
6318
|
setCurrentConversationId(id) {
|
|
@@ -5703,7 +6324,7 @@ var HistoryService = class {
|
|
|
5703
6324
|
/** Return the current message count in the active conversation. */
|
|
5704
6325
|
getCurrentMessageCount() {
|
|
5705
6326
|
if (!this.currentConversationId) return 0;
|
|
5706
|
-
const filePath =
|
|
6327
|
+
const filePath = path12.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
5707
6328
|
return parseJsonl(filePath).length;
|
|
5708
6329
|
}
|
|
5709
6330
|
/**
|
|
@@ -5714,7 +6335,7 @@ var HistoryService = class {
|
|
|
5714
6335
|
const deadline = Date.now() + timeoutMs;
|
|
5715
6336
|
while (Date.now() < deadline) {
|
|
5716
6337
|
if (!this.currentConversationId) return null;
|
|
5717
|
-
const filePath =
|
|
6338
|
+
const filePath = path12.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
5718
6339
|
const messages = parseJsonl(filePath);
|
|
5719
6340
|
if (messages.length > previousCount) {
|
|
5720
6341
|
for (let i = messages.length - 1; i >= previousCount; i--) {
|
|
@@ -5725,37 +6346,50 @@ var HistoryService = class {
|
|
|
5725
6346
|
}
|
|
5726
6347
|
return null;
|
|
5727
6348
|
}
|
|
5728
|
-
/**
|
|
6349
|
+
/**
|
|
6350
|
+
* Detect the active conversation by finding the most recently
|
|
6351
|
+
* modified JSONL file that was **created during this CLI run**.
|
|
6352
|
+
* The birthtime filter is critical: without it, an old
|
|
6353
|
+
* conversation in the same project dir — or a parallel Claude
|
|
6354
|
+
* session actively writing to a sibling JSONL — wins the mtime
|
|
6355
|
+
* sort, and we publish that other run's content to mobile as if
|
|
6356
|
+
* it were the fresh pair's chat. With the filter, only files
|
|
6357
|
+
* Claude created on or after `bootTimeMs` are eligible, so a
|
|
6358
|
+
* fresh pair stays empty until the user actually types a turn.
|
|
6359
|
+
*/
|
|
5729
6360
|
detectCurrentConversation() {
|
|
5730
6361
|
const dir = this.projectDir;
|
|
6362
|
+
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
5731
6363
|
try {
|
|
5732
|
-
const files =
|
|
6364
|
+
const files = fs9.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5733
6365
|
try {
|
|
5734
|
-
|
|
6366
|
+
const stat3 = fs9.statSync(path12.join(dir, e.name));
|
|
6367
|
+
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
5735
6368
|
} catch {
|
|
5736
|
-
return { name: e.name, mtime: 0 };
|
|
6369
|
+
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
5737
6370
|
}
|
|
5738
|
-
}).sort((a, b) => b.mtime - a.mtime);
|
|
6371
|
+
}).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
|
|
5739
6372
|
if (files.length > 0) {
|
|
5740
|
-
this.currentConversationId =
|
|
6373
|
+
this.currentConversationId = path12.basename(files[0].name, ".jsonl");
|
|
5741
6374
|
}
|
|
5742
6375
|
} catch {
|
|
5743
6376
|
}
|
|
5744
6377
|
}
|
|
5745
|
-
/**
|
|
6378
|
+
/**
|
|
6379
|
+
* Extract conversation ID from Claude output. Limited to the
|
|
6380
|
+
* unambiguous "Resuming session: <uuid>" pattern — the older
|
|
6381
|
+
* generic `Session: <uuid>` / `Conversation: <uuid>` patterns
|
|
6382
|
+
* were too greedy and matched any incidental UUID-bearing line
|
|
6383
|
+
* Claude printed (debug logs, status info, etc.), causing the
|
|
6384
|
+
* CLI to "detect" the wrong conversation on a fresh pair.
|
|
6385
|
+
* Resume is the only flow that legitimately needs to bind via
|
|
6386
|
+
* output text; everything else sets `currentConversationId`
|
|
6387
|
+
* via `setCurrentConversationId()` or the birthtime-filtered
|
|
6388
|
+
* `detectCurrentConversation()`.
|
|
6389
|
+
*/
|
|
5746
6390
|
tryExtractConversationIdFromOutput(output) {
|
|
5747
|
-
const
|
|
5748
|
-
|
|
5749
|
-
/session[:\s]+([a-f0-9-]{36})/i,
|
|
5750
|
-
/conversation[:\s]+([a-f0-9-]{36})/i
|
|
5751
|
-
];
|
|
5752
|
-
for (const pattern of patterns) {
|
|
5753
|
-
const match = output.match(pattern);
|
|
5754
|
-
if (match) {
|
|
5755
|
-
this.currentConversationId = match[1];
|
|
5756
|
-
return;
|
|
5757
|
-
}
|
|
5758
|
-
}
|
|
6391
|
+
const match = output.match(/Resuming session[:\s]+([a-f0-9-]{36})/i);
|
|
6392
|
+
if (match) this.currentConversationId = match[1];
|
|
5759
6393
|
}
|
|
5760
6394
|
/**
|
|
5761
6395
|
* Read the most recently modified JSONL session file and extract the
|
|
@@ -5767,28 +6401,30 @@ var HistoryService = class {
|
|
|
5767
6401
|
*/
|
|
5768
6402
|
getCurrentUsage() {
|
|
5769
6403
|
const dir = this.projectDir;
|
|
6404
|
+
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
5770
6405
|
let entries;
|
|
5771
6406
|
try {
|
|
5772
|
-
entries =
|
|
6407
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
5773
6408
|
} catch {
|
|
5774
6409
|
return null;
|
|
5775
6410
|
}
|
|
5776
6411
|
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5777
6412
|
try {
|
|
5778
|
-
|
|
6413
|
+
const stat3 = fs9.statSync(path12.join(dir, e.name));
|
|
6414
|
+
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
5779
6415
|
} catch {
|
|
5780
|
-
return { name: e.name, mtime: 0 };
|
|
6416
|
+
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
5781
6417
|
}
|
|
5782
|
-
}).sort((a, b) => b.mtime - a.mtime);
|
|
6418
|
+
}).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
|
|
5783
6419
|
if (files.length === 0) return null;
|
|
5784
6420
|
const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
|
|
5785
6421
|
if (!files.some((f) => f.name === targetFile)) return null;
|
|
5786
|
-
return this.extractUsageFromFile(
|
|
6422
|
+
return this.extractUsageFromFile(path12.join(dir, targetFile));
|
|
5787
6423
|
}
|
|
5788
6424
|
extractUsageFromFile(filePath) {
|
|
5789
6425
|
let raw;
|
|
5790
6426
|
try {
|
|
5791
|
-
raw =
|
|
6427
|
+
raw = fs9.readFileSync(filePath, "utf8");
|
|
5792
6428
|
} catch {
|
|
5793
6429
|
return null;
|
|
5794
6430
|
}
|
|
@@ -5833,9 +6469,9 @@ var HistoryService = class {
|
|
|
5833
6469
|
let totalCost = 0;
|
|
5834
6470
|
let files;
|
|
5835
6471
|
try {
|
|
5836
|
-
files =
|
|
6472
|
+
files = fs9.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
|
|
5837
6473
|
try {
|
|
5838
|
-
return
|
|
6474
|
+
return fs9.statSync(path12.join(projectDir, f)).mtimeMs >= monthStartMs;
|
|
5839
6475
|
} catch {
|
|
5840
6476
|
return false;
|
|
5841
6477
|
}
|
|
@@ -5846,7 +6482,7 @@ var HistoryService = class {
|
|
|
5846
6482
|
for (const file of files) {
|
|
5847
6483
|
let raw;
|
|
5848
6484
|
try {
|
|
5849
|
-
raw =
|
|
6485
|
+
raw = fs9.readFileSync(path12.join(projectDir, file), "utf8");
|
|
5850
6486
|
} catch {
|
|
5851
6487
|
continue;
|
|
5852
6488
|
}
|
|
@@ -5881,30 +6517,30 @@ var HistoryService = class {
|
|
|
5881
6517
|
const dir = this.projectDir;
|
|
5882
6518
|
let entries;
|
|
5883
6519
|
try {
|
|
5884
|
-
entries =
|
|
6520
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
5885
6521
|
} catch {
|
|
5886
6522
|
return;
|
|
5887
6523
|
}
|
|
5888
6524
|
const sessions2 = [];
|
|
5889
6525
|
for (const entry of entries) {
|
|
5890
6526
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
5891
|
-
const id =
|
|
5892
|
-
const filePath =
|
|
6527
|
+
const id = path12.basename(entry.name, ".jsonl");
|
|
6528
|
+
const filePath = path12.join(dir, entry.name);
|
|
5893
6529
|
let mtime = Date.now();
|
|
5894
6530
|
try {
|
|
5895
|
-
mtime =
|
|
6531
|
+
mtime = fs9.statSync(filePath).mtimeMs;
|
|
5896
6532
|
} catch {
|
|
5897
6533
|
}
|
|
5898
6534
|
let summary = "";
|
|
5899
6535
|
try {
|
|
5900
|
-
const raw =
|
|
6536
|
+
const raw = fs9.readFileSync(filePath, "utf8");
|
|
5901
6537
|
for (const line of raw.split("\n")) {
|
|
5902
6538
|
if (!line.trim()) continue;
|
|
5903
6539
|
try {
|
|
5904
6540
|
const record = JSON.parse(line);
|
|
5905
6541
|
if (record["type"] === "user") {
|
|
5906
6542
|
const msg = record["message"];
|
|
5907
|
-
const text =
|
|
6543
|
+
const text = extractText2(msg?.["content"]).trim();
|
|
5908
6544
|
if (text) {
|
|
5909
6545
|
summary = text.slice(0, 120);
|
|
5910
6546
|
break;
|
|
@@ -5930,7 +6566,7 @@ var HistoryService = class {
|
|
|
5930
6566
|
* showing an empty conversation.
|
|
5931
6567
|
*/
|
|
5932
6568
|
async loadConversation(sessionId) {
|
|
5933
|
-
const filePath =
|
|
6569
|
+
const filePath = path12.join(this.projectDir, `${sessionId}.jsonl`);
|
|
5934
6570
|
const messages = parseJsonl(filePath);
|
|
5935
6571
|
if (messages.length === 0) return;
|
|
5936
6572
|
const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
|
|
@@ -5973,7 +6609,7 @@ var HistoryService = class {
|
|
|
5973
6609
|
if (!this.currentConversationId) return 0;
|
|
5974
6610
|
}
|
|
5975
6611
|
const sessionId = this.currentConversationId;
|
|
5976
|
-
const filePath =
|
|
6612
|
+
const filePath = path12.join(this.projectDir, `${sessionId}.jsonl`);
|
|
5977
6613
|
const messages = parseJsonl(filePath);
|
|
5978
6614
|
if (messages.length === 0) return 0;
|
|
5979
6615
|
const marker = this.lastUploadedUuid.get(sessionId);
|
|
@@ -6001,119 +6637,28 @@ var HistoryService = class {
|
|
|
6001
6637
|
}
|
|
6002
6638
|
};
|
|
6003
6639
|
|
|
6004
|
-
// src/commands/start/quota-fetcher.ts
|
|
6005
|
-
var
|
|
6006
|
-
|
|
6007
|
-
var path9 = __toESM(require("path"));
|
|
6008
|
-
var import_child_process4 = require("child_process");
|
|
6009
|
-
var inProgress = false;
|
|
6010
|
-
var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
|
|
6011
|
-
m,s=pty.openpty()
|
|
6012
|
-
try:
|
|
6013
|
-
fcntl.ioctl(s,termios.TIOCSWINSZ,struct.pack('HHHH',30,120,0,0))
|
|
6014
|
-
except Exception:pass
|
|
6015
|
-
pid=os.fork()
|
|
6016
|
-
if pid==0:
|
|
6017
|
-
os.close(m);os.setsid()
|
|
6018
|
-
try:fcntl.ioctl(s,termios.TIOCSCTTY,0)
|
|
6019
|
-
except Exception:pass
|
|
6020
|
-
for fd in[0,1,2]:os.dup2(s,fd)
|
|
6021
|
-
if s>2:os.close(s)
|
|
6022
|
-
os.execvp(sys.argv[1],sys.argv[1:])
|
|
6023
|
-
sys.exit(127)
|
|
6024
|
-
os.close(s)
|
|
6025
|
-
done=[False]
|
|
6026
|
-
def onchld(n,f):
|
|
6027
|
-
try:os.waitpid(pid,os.WNOHANG)
|
|
6028
|
-
except Exception:pass
|
|
6029
|
-
done[0]=True
|
|
6030
|
-
signal.signal(signal.SIGCHLD,onchld)
|
|
6031
|
-
i=sys.stdin.fileno();o=sys.stdout.fileno()
|
|
6032
|
-
while not done[0]:
|
|
6033
|
-
try:r,_,_=select.select([i,m],[],[],0.1)
|
|
6034
|
-
except OSError as e:
|
|
6035
|
-
if e.errno==errno.EINTR:continue
|
|
6036
|
-
break
|
|
6037
|
-
if i in r:
|
|
6038
|
-
try:
|
|
6039
|
-
d=os.read(i,4096)
|
|
6040
|
-
if d:os.write(m,d)
|
|
6041
|
-
else:break
|
|
6042
|
-
except OSError:break
|
|
6043
|
-
if m in r:
|
|
6044
|
-
try:
|
|
6045
|
-
d=os.read(m,4096)
|
|
6046
|
-
if d:os.write(o,d)
|
|
6047
|
-
except OSError:done[0]=True
|
|
6048
|
-
try:os.kill(pid,signal.SIGTERM)
|
|
6049
|
-
except Exception:pass
|
|
6050
|
-
try:
|
|
6051
|
-
_,st=os.waitpid(pid,0)
|
|
6052
|
-
sys.exit((st>>8)&0xFF)
|
|
6053
|
-
except Exception:sys.exit(0)
|
|
6054
|
-
`;
|
|
6055
|
-
function fetchQuotaUsage(historySvc) {
|
|
6640
|
+
// src/commands/start/quota-fetcher.ts
|
|
6641
|
+
var inProgress = false;
|
|
6642
|
+
function fetchQuotaUsage(runtime, historySvc) {
|
|
6056
6643
|
if (inProgress) return;
|
|
6057
6644
|
inProgress = true;
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
}
|
|
6063
|
-
const helperPath = path9.join(os7.tmpdir(), "codeam-quota-helper.py");
|
|
6064
|
-
fs6.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
|
|
6065
|
-
const python = findInPath("python3") ?? findInPath("python");
|
|
6066
|
-
if (!python) {
|
|
6067
|
-
inProgress = false;
|
|
6068
|
-
return;
|
|
6069
|
-
}
|
|
6070
|
-
const proc = (0, import_child_process4.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
|
|
6071
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
6072
|
-
cwd: process.cwd(),
|
|
6073
|
-
env: { ...process.env, TERM: "dumb", COLUMNS: "120", LINES: "30" }
|
|
6074
|
-
});
|
|
6075
|
-
let output = "";
|
|
6076
|
-
proc.stdout?.on("data", (chunk) => {
|
|
6077
|
-
output += chunk.toString("utf8");
|
|
6078
|
-
});
|
|
6079
|
-
setTimeout(() => {
|
|
6080
|
-
proc.stdin?.write("/usage\r");
|
|
6081
|
-
setTimeout(() => {
|
|
6082
|
-
const clean = output.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, " ").replace(/\s+/g, " ");
|
|
6083
|
-
const weekMatch = clean.match(/(\d+)%\s*used/i) || clean.match(/(\d+)\s*%/);
|
|
6084
|
-
if (weekMatch) historySvc.setQuotaPercent(parseInt(weekMatch[1], 10));
|
|
6085
|
-
const resetMatch = clean.match(/resets\s+(.+?)(?:\s*\(|$)/im);
|
|
6086
|
-
if (resetMatch) historySvc.setRateLimitReset(resetMatch[1].trim());
|
|
6087
|
-
try {
|
|
6088
|
-
proc.kill();
|
|
6089
|
-
} catch {
|
|
6090
|
-
}
|
|
6091
|
-
try {
|
|
6092
|
-
fs6.unlinkSync(helperPath);
|
|
6093
|
-
} catch {
|
|
6094
|
-
}
|
|
6095
|
-
inProgress = false;
|
|
6096
|
-
}, 5e3);
|
|
6097
|
-
}, 8e3);
|
|
6098
|
-
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(() => {
|
|
6099
6650
|
inProgress = false;
|
|
6100
6651
|
});
|
|
6101
|
-
setTimeout(() => {
|
|
6102
|
-
try {
|
|
6103
|
-
proc.kill();
|
|
6104
|
-
} catch {
|
|
6105
|
-
}
|
|
6106
|
-
}, 2e4);
|
|
6107
6652
|
}
|
|
6108
6653
|
|
|
6109
6654
|
// src/commands/start/keep-alive.ts
|
|
6110
|
-
var
|
|
6655
|
+
var import_child_process6 = require("child_process");
|
|
6111
6656
|
function buildKeepAlive(ctx) {
|
|
6112
6657
|
let timer = null;
|
|
6113
6658
|
async function setIdleTimeout(minutes) {
|
|
6114
6659
|
if (!ctx.inCodespace || !ctx.codespaceName) return;
|
|
6115
6660
|
await new Promise((resolve2) => {
|
|
6116
|
-
const proc = (0,
|
|
6661
|
+
const proc = (0, import_child_process6.spawn)(
|
|
6117
6662
|
"gh",
|
|
6118
6663
|
[
|
|
6119
6664
|
"api",
|
|
@@ -6150,11 +6695,11 @@ function buildKeepAlive(ctx) {
|
|
|
6150
6695
|
}
|
|
6151
6696
|
|
|
6152
6697
|
// src/commands/start/handlers.ts
|
|
6153
|
-
var
|
|
6154
|
-
var
|
|
6155
|
-
var
|
|
6698
|
+
var fs12 = __toESM(require("fs"));
|
|
6699
|
+
var os11 = __toESM(require("os"));
|
|
6700
|
+
var path15 = __toESM(require("path"));
|
|
6156
6701
|
var import_crypto = require("crypto");
|
|
6157
|
-
var
|
|
6702
|
+
var import_child_process8 = require("child_process");
|
|
6158
6703
|
|
|
6159
6704
|
// src/lib/payload.ts
|
|
6160
6705
|
var import_zod2 = require("zod");
|
|
@@ -6191,8 +6736,8 @@ function parsePayload(schema, raw) {
|
|
|
6191
6736
|
}
|
|
6192
6737
|
|
|
6193
6738
|
// src/services/file-ops.service.ts
|
|
6194
|
-
var
|
|
6195
|
-
var
|
|
6739
|
+
var fs10 = __toESM(require("fs/promises"));
|
|
6740
|
+
var path13 = __toESM(require("path"));
|
|
6196
6741
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
6197
6742
|
var MAX_WALK_DEPTH = 6;
|
|
6198
6743
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -6227,12 +6772,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
6227
6772
|
"__pycache__"
|
|
6228
6773
|
]);
|
|
6229
6774
|
function isUnder(parent, candidate) {
|
|
6230
|
-
const rel =
|
|
6231
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
6775
|
+
const rel = path13.relative(parent, candidate);
|
|
6776
|
+
return rel === "" || !rel.startsWith("..") && !path13.isAbsolute(rel);
|
|
6232
6777
|
}
|
|
6233
6778
|
async function isExistingFile(absPath) {
|
|
6234
6779
|
try {
|
|
6235
|
-
const stat3 = await
|
|
6780
|
+
const stat3 = await fs10.stat(absPath);
|
|
6236
6781
|
return stat3.isFile();
|
|
6237
6782
|
} catch {
|
|
6238
6783
|
return false;
|
|
@@ -6245,13 +6790,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
6245
6790
|
ctx.visited++;
|
|
6246
6791
|
let entries = [];
|
|
6247
6792
|
try {
|
|
6248
|
-
entries = await
|
|
6793
|
+
entries = await fs10.readdir(dir, { withFileTypes: true });
|
|
6249
6794
|
} catch {
|
|
6250
6795
|
return;
|
|
6251
6796
|
}
|
|
6252
6797
|
for (const e of entries) {
|
|
6253
6798
|
if (!e.isFile()) continue;
|
|
6254
|
-
const full =
|
|
6799
|
+
const full = path13.join(dir, e.name);
|
|
6255
6800
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
6256
6801
|
ctx.matches.push(full);
|
|
6257
6802
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -6261,21 +6806,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
6261
6806
|
if (!e.isDirectory()) continue;
|
|
6262
6807
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
6263
6808
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
6264
|
-
await walkForSuffix(
|
|
6809
|
+
await walkForSuffix(path13.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
6265
6810
|
if (ctx.matches.length >= ctx.cap) return;
|
|
6266
6811
|
}
|
|
6267
6812
|
}
|
|
6268
6813
|
async function findFile(rawPath) {
|
|
6269
6814
|
const cwd = process.cwd();
|
|
6270
|
-
if (
|
|
6271
|
-
const abs =
|
|
6815
|
+
if (path13.isAbsolute(rawPath)) {
|
|
6816
|
+
const abs = path13.normalize(rawPath);
|
|
6272
6817
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
6273
6818
|
}
|
|
6274
|
-
const direct =
|
|
6819
|
+
const direct = path13.resolve(cwd, rawPath);
|
|
6275
6820
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
6276
|
-
const normalized =
|
|
6821
|
+
const normalized = path13.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
6277
6822
|
const needles = [
|
|
6278
|
-
`${
|
|
6823
|
+
`${path13.sep}${normalized}`,
|
|
6279
6824
|
`/${normalized}`
|
|
6280
6825
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
6281
6826
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -6289,7 +6834,7 @@ async function findWriteTarget(rawPath) {
|
|
|
6289
6834
|
const found = await findFile(rawPath);
|
|
6290
6835
|
if (found) return found;
|
|
6291
6836
|
const cwd = process.cwd();
|
|
6292
|
-
const fallback =
|
|
6837
|
+
const fallback = path13.isAbsolute(rawPath) ? path13.normalize(rawPath) : path13.resolve(cwd, rawPath);
|
|
6293
6838
|
if (!isUnder(cwd, fallback)) return null;
|
|
6294
6839
|
return fallback;
|
|
6295
6840
|
}
|
|
@@ -6306,11 +6851,11 @@ async function readProjectFile(rawPath) {
|
|
|
6306
6851
|
if (!abs) {
|
|
6307
6852
|
return { error: `File not found in the project tree: ${rawPath}` };
|
|
6308
6853
|
}
|
|
6309
|
-
const stat3 = await
|
|
6854
|
+
const stat3 = await fs10.stat(abs);
|
|
6310
6855
|
if (stat3.size > MAX_FILE_BYTES) {
|
|
6311
6856
|
return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
6312
6857
|
}
|
|
6313
|
-
const buf = await
|
|
6858
|
+
const buf = await fs10.readFile(abs);
|
|
6314
6859
|
if (looksBinary(buf)) {
|
|
6315
6860
|
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
6316
6861
|
}
|
|
@@ -6329,8 +6874,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
6329
6874
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
6330
6875
|
return { error: "Content too large." };
|
|
6331
6876
|
}
|
|
6332
|
-
await
|
|
6333
|
-
await
|
|
6877
|
+
await fs10.mkdir(path13.dirname(abs), { recursive: true });
|
|
6878
|
+
await fs10.writeFile(abs, content, "utf-8");
|
|
6334
6879
|
return { ok: true };
|
|
6335
6880
|
} catch (e) {
|
|
6336
6881
|
const msg = e instanceof Error ? e.message : "Write failed";
|
|
@@ -6339,11 +6884,11 @@ async function writeProjectFile(rawPath, content) {
|
|
|
6339
6884
|
}
|
|
6340
6885
|
|
|
6341
6886
|
// src/services/project-ops.service.ts
|
|
6342
|
-
var
|
|
6343
|
-
var
|
|
6344
|
-
var
|
|
6345
|
-
var
|
|
6346
|
-
var
|
|
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);
|
|
6347
6892
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
6348
6893
|
"node_modules",
|
|
6349
6894
|
".git",
|
|
@@ -6390,7 +6935,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
6390
6935
|
}
|
|
6391
6936
|
let entries = [];
|
|
6392
6937
|
try {
|
|
6393
|
-
entries = await
|
|
6938
|
+
entries = await fs11.readdir(dir, { withFileTypes: true });
|
|
6394
6939
|
} catch {
|
|
6395
6940
|
return;
|
|
6396
6941
|
}
|
|
@@ -6400,18 +6945,18 @@ async function listProjectFiles(opts = {}) {
|
|
|
6400
6945
|
return;
|
|
6401
6946
|
}
|
|
6402
6947
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
6403
|
-
const full =
|
|
6948
|
+
const full = path14.join(dir, e.name);
|
|
6404
6949
|
if (e.isDirectory()) {
|
|
6405
6950
|
if (depth >= 12) continue;
|
|
6406
6951
|
await walk(full, depth + 1);
|
|
6407
6952
|
} else if (e.isFile()) {
|
|
6408
|
-
const rel =
|
|
6953
|
+
const rel = path14.relative(root, full);
|
|
6409
6954
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
6410
6955
|
continue;
|
|
6411
6956
|
}
|
|
6412
6957
|
let size = 0;
|
|
6413
6958
|
try {
|
|
6414
|
-
const st3 = await
|
|
6959
|
+
const st3 = await fs11.stat(full);
|
|
6415
6960
|
size = st3.size;
|
|
6416
6961
|
} catch {
|
|
6417
6962
|
}
|
|
@@ -6425,7 +6970,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
6425
6970
|
}
|
|
6426
6971
|
async function git(args2, cwd) {
|
|
6427
6972
|
try {
|
|
6428
|
-
const { stdout, stderr } = await
|
|
6973
|
+
const { stdout, stderr } = await execFileP2("git", args2, {
|
|
6429
6974
|
cwd: cwd ?? process.cwd(),
|
|
6430
6975
|
maxBuffer: MAX_GIT_OUTPUT,
|
|
6431
6976
|
timeout: 3e4
|
|
@@ -6513,8 +7058,8 @@ async function gitStatus(cwd) {
|
|
|
6513
7058
|
let hasMergeInProgress = false;
|
|
6514
7059
|
try {
|
|
6515
7060
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
6516
|
-
const mergeHead =
|
|
6517
|
-
await
|
|
7061
|
+
const mergeHead = path14.isAbsolute(gitDir) ? path14.join(gitDir, "MERGE_HEAD") : path14.join(root, gitDir, "MERGE_HEAD");
|
|
7062
|
+
await fs11.access(mergeHead);
|
|
6518
7063
|
hasMergeInProgress = true;
|
|
6519
7064
|
} catch {
|
|
6520
7065
|
}
|
|
@@ -6591,8 +7136,8 @@ async function gitResolve(file, side, cwd) {
|
|
|
6591
7136
|
function saveFilesTemp(files) {
|
|
6592
7137
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
6593
7138
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
6594
|
-
const tmpPath =
|
|
6595
|
-
|
|
7139
|
+
const tmpPath = path15.join(os11.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
|
|
7140
|
+
fs12.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
6596
7141
|
return tmpPath;
|
|
6597
7142
|
});
|
|
6598
7143
|
}
|
|
@@ -6611,7 +7156,7 @@ var startTask = (ctx, _cmd, parsed) => {
|
|
|
6611
7156
|
setTimeout(() => {
|
|
6612
7157
|
for (const p2 of paths) {
|
|
6613
7158
|
try {
|
|
6614
|
-
|
|
7159
|
+
fs12.unlinkSync(p2);
|
|
6615
7160
|
} catch {
|
|
6616
7161
|
}
|
|
6617
7162
|
}
|
|
@@ -6671,14 +7216,35 @@ var getConversation = async (ctx, cmd) => {
|
|
|
6671
7216
|
}
|
|
6672
7217
|
};
|
|
6673
7218
|
var listModels = async (ctx, cmd) => {
|
|
6674
|
-
const models =
|
|
6675
|
-
{ id: "claude-opus-4-7", label: "Claude Opus 4.7", description: "Most capable", family: "claude", vendor: "anthropic", isDefault: false },
|
|
6676
|
-
{ id: "claude-opus-4-6", label: "Claude Opus 4.6", description: "Top tier", family: "claude", vendor: "anthropic", isDefault: false },
|
|
6677
|
-
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", description: "Balanced", family: "claude", vendor: "anthropic", isDefault: true },
|
|
6678
|
-
{ id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", description: "Fastest", family: "claude", vendor: "anthropic", isDefault: false }
|
|
6679
|
-
];
|
|
7219
|
+
const models = await ctx.runtime.listModels();
|
|
6680
7220
|
await ctx.relay.sendResult(cmd.id, "completed", { models });
|
|
6681
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
|
+
};
|
|
6682
7248
|
var setKeepAlive = async (ctx, cmd) => {
|
|
6683
7249
|
const enabled = !!cmd.payload.enabled;
|
|
6684
7250
|
ctx.setKeepAlive(enabled);
|
|
@@ -6702,7 +7268,7 @@ var sessionTerminated = (ctx) => {
|
|
|
6702
7268
|
} catch {
|
|
6703
7269
|
}
|
|
6704
7270
|
try {
|
|
6705
|
-
const proc = (0,
|
|
7271
|
+
const proc = (0, import_child_process8.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6706
7272
|
detached: true,
|
|
6707
7273
|
stdio: "ignore"
|
|
6708
7274
|
});
|
|
@@ -6724,7 +7290,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
6724
7290
|
}
|
|
6725
7291
|
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
6726
7292
|
try {
|
|
6727
|
-
const stopProc = (0,
|
|
7293
|
+
const stopProc = (0, import_child_process8.spawn)(
|
|
6728
7294
|
"bash",
|
|
6729
7295
|
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
6730
7296
|
{ detached: true, stdio: "ignore" }
|
|
@@ -6734,7 +7300,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
6734
7300
|
}
|
|
6735
7301
|
}
|
|
6736
7302
|
try {
|
|
6737
|
-
const proc = (0,
|
|
7303
|
+
const proc = (0, import_child_process8.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6738
7304
|
detached: true,
|
|
6739
7305
|
stdio: "ignore"
|
|
6740
7306
|
});
|
|
@@ -6815,6 +7381,8 @@ var handlers = {
|
|
|
6815
7381
|
get_context: getContext,
|
|
6816
7382
|
get_conversation: getConversation,
|
|
6817
7383
|
list_models: listModels,
|
|
7384
|
+
change_model: changeModel,
|
|
7385
|
+
summarize,
|
|
6818
7386
|
set_keep_alive: setKeepAlive,
|
|
6819
7387
|
session_terminated: sessionTerminated,
|
|
6820
7388
|
shutdown_session: shutdownSession,
|
|
@@ -6851,11 +7419,16 @@ async function start() {
|
|
|
6851
7419
|
`);
|
|
6852
7420
|
process.exit(0);
|
|
6853
7421
|
}
|
|
7422
|
+
if (!session.agent) {
|
|
7423
|
+
throw new Error("Active session has no agent \u2014 re-pair with `codeam pair`.");
|
|
7424
|
+
}
|
|
6854
7425
|
const pluginId = session.pluginId ?? ensurePluginId();
|
|
6855
7426
|
showInfo(`${session.userName} \xB7 ${import_picocolors2.default.cyan(session.plan)}`);
|
|
6856
|
-
showInfo(
|
|
7427
|
+
showInfo(`Launching ${AGENT_REGISTRY[session.agent].displayName}...
|
|
7428
|
+
`);
|
|
6857
7429
|
const cwd = process.cwd();
|
|
6858
|
-
const
|
|
7430
|
+
const runtime = createRuntimeStrategy(session.agent);
|
|
7431
|
+
const historySvc = new HistoryService(runtime, pluginId, cwd);
|
|
6859
7432
|
const keepAliveCtx = {
|
|
6860
7433
|
inCodespace: process.env.CODESPACES === "true",
|
|
6861
7434
|
codespaceName: process.env.CODESPACE_NAME
|
|
@@ -6867,7 +7440,7 @@ async function start() {
|
|
|
6867
7440
|
(conversationId) => historySvc.setCurrentConversationId(conversationId),
|
|
6868
7441
|
(reset) => historySvc.setRateLimitReset(reset),
|
|
6869
7442
|
() => {
|
|
6870
|
-
if (historySvc.isQuotaStale()) fetchQuotaUsage(historySvc);
|
|
7443
|
+
if (historySvc.isQuotaStale()) fetchQuotaUsage(runtime, historySvc);
|
|
6871
7444
|
setTimeout(() => {
|
|
6872
7445
|
historySvc.uploadDelta().catch(() => {
|
|
6873
7446
|
});
|
|
@@ -6879,22 +7452,26 @@ async function start() {
|
|
|
6879
7452
|
},
|
|
6880
7453
|
session.pluginAuthToken
|
|
6881
7454
|
);
|
|
6882
|
-
const claude = new
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
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
|
+
}
|
|
6892
7468
|
}
|
|
6893
|
-
|
|
7469
|
+
);
|
|
6894
7470
|
const ctx = {
|
|
6895
7471
|
outputSvc,
|
|
6896
7472
|
claude,
|
|
6897
7473
|
historySvc,
|
|
7474
|
+
runtime,
|
|
6898
7475
|
relay: void 0,
|
|
6899
7476
|
setKeepAlive: setKeepAlive2,
|
|
6900
7477
|
keepAliveCtx
|
|
@@ -6917,7 +7494,7 @@ async function start() {
|
|
|
6917
7494
|
historySvc.load().catch(() => {
|
|
6918
7495
|
});
|
|
6919
7496
|
}, 2e3);
|
|
6920
|
-
setTimeout(() => fetchQuotaUsage(historySvc), 5e3);
|
|
7497
|
+
setTimeout(() => fetchQuotaUsage(runtime, historySvc), 5e3);
|
|
6921
7498
|
}
|
|
6922
7499
|
|
|
6923
7500
|
// src/commands/pair.ts
|
|
@@ -6944,8 +7521,45 @@ async function selectSession(sessions2, activeId) {
|
|
|
6944
7521
|
return result;
|
|
6945
7522
|
}
|
|
6946
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
|
+
|
|
6947
7558
|
// src/commands/pair.ts
|
|
6948
|
-
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");
|
|
6949
7563
|
showIntro();
|
|
6950
7564
|
const pluginId = (0, import_crypto2.randomUUID)();
|
|
6951
7565
|
const spin = dist_exports.spinner();
|
|
@@ -6981,8 +7595,10 @@ async function pair() {
|
|
|
6981
7595
|
userEmail: info.userEmail,
|
|
6982
7596
|
plan: info.plan,
|
|
6983
7597
|
pairedAt: Date.now(),
|
|
6984
|
-
pluginAuthToken: info.pluginAuthToken
|
|
7598
|
+
pluginAuthToken: info.pluginAuthToken,
|
|
7599
|
+
agent: agentId
|
|
6985
7600
|
});
|
|
7601
|
+
saveCliConfig({ ...loadCliConfig(), preferredAgent: agentId });
|
|
6986
7602
|
showSuccess(`Paired with ${info.userName} (${info.plan})`);
|
|
6987
7603
|
console.log("");
|
|
6988
7604
|
resolve2();
|
|
@@ -6999,8 +7615,8 @@ async function pair() {
|
|
|
6999
7615
|
}
|
|
7000
7616
|
|
|
7001
7617
|
// src/commands/pair-auto.ts
|
|
7002
|
-
var
|
|
7003
|
-
var
|
|
7618
|
+
var fs13 = __toESM(require("fs"));
|
|
7619
|
+
var os12 = __toESM(require("os"));
|
|
7004
7620
|
var import_crypto3 = require("crypto");
|
|
7005
7621
|
var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
7006
7622
|
function fail(msg) {
|
|
@@ -7017,12 +7633,12 @@ function readTokenFromArgs(args2) {
|
|
|
7017
7633
|
}
|
|
7018
7634
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
7019
7635
|
if (fileFlag) {
|
|
7020
|
-
const
|
|
7636
|
+
const path21 = fileFlag.slice("--token-file=".length);
|
|
7021
7637
|
try {
|
|
7022
|
-
const content =
|
|
7023
|
-
if (content.length === 0) fail(`--token-file ${
|
|
7638
|
+
const content = fs13.readFileSync(path21, "utf8").trim();
|
|
7639
|
+
if (content.length === 0) fail(`--token-file ${path21} is empty`);
|
|
7024
7640
|
try {
|
|
7025
|
-
|
|
7641
|
+
fs13.unlinkSync(path21);
|
|
7026
7642
|
} catch {
|
|
7027
7643
|
}
|
|
7028
7644
|
return content;
|
|
@@ -7040,7 +7656,7 @@ async function claim(token, pluginId) {
|
|
|
7040
7656
|
pluginId,
|
|
7041
7657
|
ideName: "codeam-cli (codespace)",
|
|
7042
7658
|
ideVersion: process.env.npm_package_version ?? "unknown",
|
|
7043
|
-
hostname:
|
|
7659
|
+
hostname: os12.hostname(),
|
|
7044
7660
|
codespaceName: process.env.CODESPACE_NAME ?? ""
|
|
7045
7661
|
};
|
|
7046
7662
|
const res = await fetch(url, {
|
|
@@ -7066,6 +7682,11 @@ async function pairAuto(args2) {
|
|
|
7066
7682
|
const pluginId = (0, import_crypto3.randomUUID)();
|
|
7067
7683
|
console.log(" Claiming pairing token\u2026");
|
|
7068
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
|
+
}
|
|
7069
7690
|
addSession({
|
|
7070
7691
|
id: claimed.sessionId,
|
|
7071
7692
|
pluginId,
|
|
@@ -7073,7 +7694,8 @@ async function pairAuto(args2) {
|
|
|
7073
7694
|
userEmail: claimed.user.email,
|
|
7074
7695
|
plan: claimed.user.plan,
|
|
7075
7696
|
pairedAt: Date.now(),
|
|
7076
|
-
pluginAuthToken: claimed.pluginAuthToken
|
|
7697
|
+
pluginAuthToken: claimed.pluginAuthToken,
|
|
7698
|
+
agent: claimed.agent
|
|
7077
7699
|
});
|
|
7078
7700
|
console.log(` Paired with ${claimed.user.name} (${claimed.user.plan})`);
|
|
7079
7701
|
console.log(" Starting agent loop\u2026");
|
|
@@ -7180,19 +7802,14 @@ async function logout() {
|
|
|
7180
7802
|
}
|
|
7181
7803
|
|
|
7182
7804
|
// src/commands/deploy.ts
|
|
7183
|
-
var import_child_process12 = require("child_process");
|
|
7184
|
-
var fs11 = __toESM(require("fs"));
|
|
7185
|
-
var os10 = __toESM(require("os"));
|
|
7186
|
-
var path17 = __toESM(require("path"));
|
|
7187
|
-
var import_util6 = require("util");
|
|
7188
7805
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
7189
7806
|
|
|
7190
7807
|
// src/services/providers/github-codespaces.ts
|
|
7191
|
-
var
|
|
7192
|
-
var
|
|
7808
|
+
var import_child_process9 = require("child_process");
|
|
7809
|
+
var import_util3 = require("util");
|
|
7193
7810
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
7194
|
-
var
|
|
7195
|
-
var
|
|
7811
|
+
var path16 = __toESM(require("path"));
|
|
7812
|
+
var execFileP3 = (0, import_util3.promisify)(import_child_process9.execFile);
|
|
7196
7813
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
7197
7814
|
function resetStdinForChild() {
|
|
7198
7815
|
if (process.stdin.isTTY) {
|
|
@@ -7209,11 +7826,11 @@ var GitHubCodespacesProvider = class {
|
|
|
7209
7826
|
available = true;
|
|
7210
7827
|
async authorize() {
|
|
7211
7828
|
try {
|
|
7212
|
-
await
|
|
7829
|
+
await execFileP3("gh", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
7213
7830
|
} catch {
|
|
7214
7831
|
await this.tryInstallGh();
|
|
7215
7832
|
try {
|
|
7216
|
-
await
|
|
7833
|
+
await execFileP3("gh", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
7217
7834
|
} catch {
|
|
7218
7835
|
throw new Error(
|
|
7219
7836
|
[
|
|
@@ -7229,14 +7846,14 @@ var GitHubCodespacesProvider = class {
|
|
|
7229
7846
|
}
|
|
7230
7847
|
let isAuthed = false;
|
|
7231
7848
|
try {
|
|
7232
|
-
await
|
|
7849
|
+
await execFileP3("gh", ["auth", "status"], { maxBuffer: MAX_BUFFER });
|
|
7233
7850
|
isAuthed = true;
|
|
7234
7851
|
} catch {
|
|
7235
7852
|
}
|
|
7236
7853
|
if (!isAuthed) {
|
|
7237
7854
|
resetStdinForChild();
|
|
7238
7855
|
await new Promise((resolve2, reject) => {
|
|
7239
|
-
const proc = (0,
|
|
7856
|
+
const proc = (0, import_child_process9.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
7240
7857
|
stdio: "inherit"
|
|
7241
7858
|
});
|
|
7242
7859
|
proc.on("exit", (code) => {
|
|
@@ -7270,7 +7887,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7270
7887
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
7271
7888
|
resetStdinForChild();
|
|
7272
7889
|
const refreshCode = await new Promise((resolve2, reject) => {
|
|
7273
|
-
const proc = (0,
|
|
7890
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7274
7891
|
"gh",
|
|
7275
7892
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
7276
7893
|
{ stdio: "inherit" }
|
|
@@ -7305,7 +7922,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7305
7922
|
*/
|
|
7306
7923
|
async getActiveGhUser() {
|
|
7307
7924
|
try {
|
|
7308
|
-
const { stdout } = await
|
|
7925
|
+
const { stdout } = await execFileP3(
|
|
7309
7926
|
"gh",
|
|
7310
7927
|
["api", "user", "--jq", ".login"],
|
|
7311
7928
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7325,7 +7942,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7325
7942
|
*/
|
|
7326
7943
|
async hasCodespaceScope() {
|
|
7327
7944
|
try {
|
|
7328
|
-
const { stdout } = await
|
|
7945
|
+
const { stdout } = await execFileP3(
|
|
7329
7946
|
"gh",
|
|
7330
7947
|
["api", "-i", "user"],
|
|
7331
7948
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7355,12 +7972,12 @@ var GitHubCodespacesProvider = class {
|
|
|
7355
7972
|
* install error if it's still missing.
|
|
7356
7973
|
*/
|
|
7357
7974
|
async tryInstallGh() {
|
|
7358
|
-
const
|
|
7975
|
+
const platform2 = process.platform;
|
|
7359
7976
|
wt(
|
|
7360
7977
|
`GitHub CLI (${import_picocolors7.default.cyan("gh")}) is required for Codespaces deploys but isn't on your PATH.`,
|
|
7361
7978
|
"Heads up"
|
|
7362
7979
|
);
|
|
7363
|
-
if (
|
|
7980
|
+
if (platform2 === "linux") {
|
|
7364
7981
|
wt(
|
|
7365
7982
|
[
|
|
7366
7983
|
"On Linux, please install gh from the official guide:",
|
|
@@ -7372,9 +7989,9 @@ var GitHubCodespacesProvider = class {
|
|
|
7372
7989
|
return;
|
|
7373
7990
|
}
|
|
7374
7991
|
let installCmd = null;
|
|
7375
|
-
if (
|
|
7992
|
+
if (platform2 === "darwin") {
|
|
7376
7993
|
try {
|
|
7377
|
-
await
|
|
7994
|
+
await execFileP3("brew", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
7378
7995
|
} catch {
|
|
7379
7996
|
wt(
|
|
7380
7997
|
[
|
|
@@ -7391,9 +8008,9 @@ var GitHubCodespacesProvider = class {
|
|
|
7391
8008
|
args: ["install", "gh"],
|
|
7392
8009
|
describe: "brew install gh"
|
|
7393
8010
|
};
|
|
7394
|
-
} else if (
|
|
8011
|
+
} else if (platform2 === "win32") {
|
|
7395
8012
|
try {
|
|
7396
|
-
await
|
|
8013
|
+
await execFileP3("winget", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
7397
8014
|
} catch {
|
|
7398
8015
|
wt(
|
|
7399
8016
|
[
|
|
@@ -7420,7 +8037,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7420
8037
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
7421
8038
|
resetStdinForChild();
|
|
7422
8039
|
const ok = await new Promise((resolve2) => {
|
|
7423
|
-
const proc = (0,
|
|
8040
|
+
const proc = (0, import_child_process9.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
7424
8041
|
proc.on("exit", (code) => resolve2(code === 0));
|
|
7425
8042
|
proc.on("error", () => resolve2(false));
|
|
7426
8043
|
});
|
|
@@ -7447,7 +8064,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7447
8064
|
);
|
|
7448
8065
|
resetStdinForChild();
|
|
7449
8066
|
await new Promise((resolve2, reject) => {
|
|
7450
|
-
const proc = (0,
|
|
8067
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7451
8068
|
"gh",
|
|
7452
8069
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
7453
8070
|
{ stdio: "inherit" }
|
|
@@ -7472,7 +8089,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7472
8089
|
"200"
|
|
7473
8090
|
);
|
|
7474
8091
|
try {
|
|
7475
|
-
const { stdout } = await
|
|
8092
|
+
const { stdout } = await execFileP3("gh", args2, { maxBuffer: MAX_BUFFER });
|
|
7476
8093
|
return JSON.parse(stdout);
|
|
7477
8094
|
} catch {
|
|
7478
8095
|
return [];
|
|
@@ -7481,7 +8098,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7481
8098
|
const own = await fetchRepos();
|
|
7482
8099
|
let orgRepos = [];
|
|
7483
8100
|
try {
|
|
7484
|
-
const { stdout } = await
|
|
8101
|
+
const { stdout } = await execFileP3(
|
|
7485
8102
|
"gh",
|
|
7486
8103
|
["api", "--paginate", "user/orgs", "--jq", ".[].login"],
|
|
7487
8104
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7518,7 +8135,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7518
8135
|
*/
|
|
7519
8136
|
async listMachineTypes(projectId) {
|
|
7520
8137
|
try {
|
|
7521
|
-
const { stdout } = await
|
|
8138
|
+
const { stdout } = await execFileP3(
|
|
7522
8139
|
"gh",
|
|
7523
8140
|
["api", `/repos/${projectId}/codespaces/machines`],
|
|
7524
8141
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7549,7 +8166,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7549
8166
|
const machine = machineTypeId ?? await this.pickDefaultMachine(projectId);
|
|
7550
8167
|
const args2 = ["codespace", "create", "-R", projectId, "--default-permissions"];
|
|
7551
8168
|
if (machine) args2.push("-m", machine);
|
|
7552
|
-
const { stdout } = await
|
|
8169
|
+
const { stdout } = await execFileP3(
|
|
7553
8170
|
"gh",
|
|
7554
8171
|
args2,
|
|
7555
8172
|
{ maxBuffer: MAX_BUFFER, timeout: 12e4 }
|
|
@@ -7589,7 +8206,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7589
8206
|
async waitUntilAvailable(name) {
|
|
7590
8207
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
7591
8208
|
while (Date.now() < deadline) {
|
|
7592
|
-
const { stdout } = await
|
|
8209
|
+
const { stdout } = await execFileP3(
|
|
7593
8210
|
"gh",
|
|
7594
8211
|
["codespace", "list", "--json", "name,state"],
|
|
7595
8212
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7607,7 +8224,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7607
8224
|
}
|
|
7608
8225
|
async exec(workspaceId, command2) {
|
|
7609
8226
|
try {
|
|
7610
|
-
const { stdout, stderr } = await
|
|
8227
|
+
const { stdout, stderr } = await execFileP3(
|
|
7611
8228
|
"gh",
|
|
7612
8229
|
["codespace", "ssh", "-c", workspaceId, "--", command2],
|
|
7613
8230
|
{ maxBuffer: MAX_BUFFER, timeout: 6e5 }
|
|
@@ -7625,7 +8242,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7625
8242
|
async streamCommand(workspaceId, command2) {
|
|
7626
8243
|
resetStdinForChild();
|
|
7627
8244
|
return new Promise((resolve2, reject) => {
|
|
7628
|
-
const proc = (0,
|
|
8245
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7629
8246
|
"gh",
|
|
7630
8247
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
7631
8248
|
{ stdio: "inherit" }
|
|
@@ -7652,11 +8269,11 @@ var GitHubCodespacesProvider = class {
|
|
|
7652
8269
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
7653
8270
|
];
|
|
7654
8271
|
await new Promise((resolve2, reject) => {
|
|
7655
|
-
const tar = (0,
|
|
8272
|
+
const tar = (0, import_child_process9.spawn)("tar", tarArgs, {
|
|
7656
8273
|
stdio: ["ignore", "pipe", "pipe"],
|
|
7657
8274
|
env: tarEnv
|
|
7658
8275
|
});
|
|
7659
|
-
const ssh = (0,
|
|
8276
|
+
const ssh = (0, import_child_process9.spawn)("gh", sshArgs, {
|
|
7660
8277
|
stdio: [tar.stdout, "pipe", "pipe"]
|
|
7661
8278
|
});
|
|
7662
8279
|
let tarErr = "";
|
|
@@ -7680,7 +8297,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7680
8297
|
});
|
|
7681
8298
|
}
|
|
7682
8299
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7683
|
-
const remoteDir =
|
|
8300
|
+
const remoteDir = path16.posix.dirname(remotePath);
|
|
7684
8301
|
const parts = [
|
|
7685
8302
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
7686
8303
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -7690,7 +8307,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7690
8307
|
}
|
|
7691
8308
|
const cmd = parts.join(" && ");
|
|
7692
8309
|
await new Promise((resolve2, reject) => {
|
|
7693
|
-
const proc = (0,
|
|
8310
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7694
8311
|
"gh",
|
|
7695
8312
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
7696
8313
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -7712,7 +8329,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7712
8329
|
try {
|
|
7713
8330
|
const args2 = ["codespace", "list", "--json", "name,displayName,state,lastUsedAt"];
|
|
7714
8331
|
if (projectId) args2.push("--repo", projectId);
|
|
7715
|
-
const { stdout } = await
|
|
8332
|
+
const { stdout } = await execFileP3("gh", args2, { maxBuffer: MAX_BUFFER });
|
|
7716
8333
|
const list = JSON.parse(stdout);
|
|
7717
8334
|
return list.map((c2) => ({
|
|
7718
8335
|
id: c2.name,
|
|
@@ -7727,7 +8344,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7727
8344
|
}
|
|
7728
8345
|
async startWorkspace(workspaceId) {
|
|
7729
8346
|
try {
|
|
7730
|
-
await
|
|
8347
|
+
await execFileP3(
|
|
7731
8348
|
"gh",
|
|
7732
8349
|
["api", "-X", "POST", `/user/codespaces/${workspaceId}/start`],
|
|
7733
8350
|
{ maxBuffer: MAX_BUFFER, timeout: 6e4 }
|
|
@@ -7748,11 +8365,11 @@ function shellQuote(s) {
|
|
|
7748
8365
|
}
|
|
7749
8366
|
|
|
7750
8367
|
// src/services/providers/gitpod.ts
|
|
7751
|
-
var
|
|
7752
|
-
var
|
|
7753
|
-
var
|
|
8368
|
+
var import_child_process10 = require("child_process");
|
|
8369
|
+
var import_util4 = require("util");
|
|
8370
|
+
var path17 = __toESM(require("path"));
|
|
7754
8371
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
7755
|
-
var
|
|
8372
|
+
var execFileP4 = (0, import_util4.promisify)(import_child_process10.execFile);
|
|
7756
8373
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
7757
8374
|
function resetStdinForChild2() {
|
|
7758
8375
|
if (process.stdin.isTTY) {
|
|
@@ -7769,7 +8386,7 @@ var GitpodProvider = class {
|
|
|
7769
8386
|
available = true;
|
|
7770
8387
|
async authorize() {
|
|
7771
8388
|
try {
|
|
7772
|
-
await
|
|
8389
|
+
await execFileP4("gitpod", ["--version"], { maxBuffer: MAX_BUFFER2 });
|
|
7773
8390
|
} catch {
|
|
7774
8391
|
throw new Error(
|
|
7775
8392
|
[
|
|
@@ -7782,7 +8399,7 @@ var GitpodProvider = class {
|
|
|
7782
8399
|
);
|
|
7783
8400
|
}
|
|
7784
8401
|
try {
|
|
7785
|
-
await
|
|
8402
|
+
await execFileP4("gitpod", ["whoami"], { maxBuffer: MAX_BUFFER2 });
|
|
7786
8403
|
return;
|
|
7787
8404
|
} catch {
|
|
7788
8405
|
}
|
|
@@ -7792,7 +8409,7 @@ var GitpodProvider = class {
|
|
|
7792
8409
|
);
|
|
7793
8410
|
resetStdinForChild2();
|
|
7794
8411
|
await new Promise((resolve2, reject) => {
|
|
7795
|
-
const proc = (0,
|
|
8412
|
+
const proc = (0, import_child_process10.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
7796
8413
|
proc.on("exit", (code) => {
|
|
7797
8414
|
if (code === 0) resolve2();
|
|
7798
8415
|
else reject(new Error("gitpod login failed."));
|
|
@@ -7802,7 +8419,7 @@ var GitpodProvider = class {
|
|
|
7802
8419
|
}
|
|
7803
8420
|
async listProjects() {
|
|
7804
8421
|
try {
|
|
7805
|
-
const { stdout } = await
|
|
8422
|
+
const { stdout } = await execFileP4(
|
|
7806
8423
|
"gitpod",
|
|
7807
8424
|
["workspace", "list", "--output", "json", "--limit", "200"],
|
|
7808
8425
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -7835,7 +8452,7 @@ var GitpodProvider = class {
|
|
|
7835
8452
|
async createWorkspace(projectId, machineTypeId) {
|
|
7836
8453
|
const args2 = ["workspace", "create", projectId, "--start", "--output", "json"];
|
|
7837
8454
|
if (machineTypeId) args2.push("--class", machineTypeId);
|
|
7838
|
-
const { stdout } = await
|
|
8455
|
+
const { stdout } = await execFileP4("gitpod", args2, {
|
|
7839
8456
|
maxBuffer: MAX_BUFFER2,
|
|
7840
8457
|
timeout: 3e5
|
|
7841
8458
|
});
|
|
@@ -7859,7 +8476,7 @@ var GitpodProvider = class {
|
|
|
7859
8476
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
7860
8477
|
while (Date.now() < deadline) {
|
|
7861
8478
|
try {
|
|
7862
|
-
const { stdout } = await
|
|
8479
|
+
const { stdout } = await execFileP4(
|
|
7863
8480
|
"gitpod",
|
|
7864
8481
|
["workspace", "get", workspaceId, "--output", "json"],
|
|
7865
8482
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -7877,7 +8494,7 @@ var GitpodProvider = class {
|
|
|
7877
8494
|
}
|
|
7878
8495
|
async listMachineTypes(_projectId) {
|
|
7879
8496
|
try {
|
|
7880
|
-
const { stdout } = await
|
|
8497
|
+
const { stdout } = await execFileP4(
|
|
7881
8498
|
"gitpod",
|
|
7882
8499
|
["organization", "list-classes", "--output", "json"],
|
|
7883
8500
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -7895,7 +8512,7 @@ var GitpodProvider = class {
|
|
|
7895
8512
|
async listExistingWorkspaces(projectId) {
|
|
7896
8513
|
try {
|
|
7897
8514
|
const args2 = ["workspace", "list", "--output", "json", "--limit", "200"];
|
|
7898
|
-
const { stdout } = await
|
|
8515
|
+
const { stdout } = await execFileP4("gitpod", args2, { maxBuffer: MAX_BUFFER2 });
|
|
7899
8516
|
const list = JSON.parse(stdout);
|
|
7900
8517
|
return list.filter((w3) => !projectId || w3.contextUrl === projectId).map((w3) => ({
|
|
7901
8518
|
id: w3.id,
|
|
@@ -7910,7 +8527,7 @@ var GitpodProvider = class {
|
|
|
7910
8527
|
}
|
|
7911
8528
|
async startWorkspace(workspaceId) {
|
|
7912
8529
|
try {
|
|
7913
|
-
await
|
|
8530
|
+
await execFileP4(
|
|
7914
8531
|
"gitpod",
|
|
7915
8532
|
["workspace", "start", workspaceId],
|
|
7916
8533
|
{ maxBuffer: MAX_BUFFER2, timeout: 6e4 }
|
|
@@ -7926,7 +8543,7 @@ var GitpodProvider = class {
|
|
|
7926
8543
|
}
|
|
7927
8544
|
async exec(workspaceId, command2) {
|
|
7928
8545
|
try {
|
|
7929
|
-
const { stdout, stderr } = await
|
|
8546
|
+
const { stdout, stderr } = await execFileP4(
|
|
7930
8547
|
"gitpod",
|
|
7931
8548
|
["workspace", "ssh", workspaceId, "--", command2],
|
|
7932
8549
|
{ maxBuffer: MAX_BUFFER2, timeout: 6e5 }
|
|
@@ -7944,7 +8561,7 @@ var GitpodProvider = class {
|
|
|
7944
8561
|
async streamCommand(workspaceId, command2) {
|
|
7945
8562
|
resetStdinForChild2();
|
|
7946
8563
|
return new Promise((resolve2, reject) => {
|
|
7947
|
-
const proc = (0,
|
|
8564
|
+
const proc = (0, import_child_process10.spawn)(
|
|
7948
8565
|
"gitpod",
|
|
7949
8566
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
7950
8567
|
{ stdio: "inherit" }
|
|
@@ -7964,11 +8581,11 @@ var GitpodProvider = class {
|
|
|
7964
8581
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
7965
8582
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
7966
8583
|
await new Promise((resolve2, reject) => {
|
|
7967
|
-
const tar = (0,
|
|
8584
|
+
const tar = (0, import_child_process10.spawn)("tar", tarArgs, {
|
|
7968
8585
|
stdio: ["ignore", "pipe", "pipe"],
|
|
7969
8586
|
env: tarEnv
|
|
7970
8587
|
});
|
|
7971
|
-
const ssh = (0,
|
|
8588
|
+
const ssh = (0, import_child_process10.spawn)(
|
|
7972
8589
|
"gitpod",
|
|
7973
8590
|
["workspace", "ssh", workspaceId, "--", remoteCmd],
|
|
7974
8591
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -7990,7 +8607,7 @@ var GitpodProvider = class {
|
|
|
7990
8607
|
});
|
|
7991
8608
|
}
|
|
7992
8609
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7993
|
-
const remoteDir =
|
|
8610
|
+
const remoteDir = path17.posix.dirname(remotePath);
|
|
7994
8611
|
const parts = [
|
|
7995
8612
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
7996
8613
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -8000,7 +8617,7 @@ var GitpodProvider = class {
|
|
|
8000
8617
|
}
|
|
8001
8618
|
const cmd = parts.join(" && ");
|
|
8002
8619
|
await new Promise((resolve2, reject) => {
|
|
8003
|
-
const proc = (0,
|
|
8620
|
+
const proc = (0, import_child_process10.spawn)(
|
|
8004
8621
|
"gitpod",
|
|
8005
8622
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
8006
8623
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8024,10 +8641,10 @@ function shellQuote2(s) {
|
|
|
8024
8641
|
}
|
|
8025
8642
|
|
|
8026
8643
|
// src/services/providers/gitlab-workspaces.ts
|
|
8027
|
-
var
|
|
8028
|
-
var
|
|
8029
|
-
var
|
|
8030
|
-
var
|
|
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);
|
|
8031
8648
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
8032
8649
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
8033
8650
|
function resetStdinForChild3() {
|
|
@@ -8045,7 +8662,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8045
8662
|
available = true;
|
|
8046
8663
|
async authorize() {
|
|
8047
8664
|
try {
|
|
8048
|
-
await
|
|
8665
|
+
await execFileP5("glab", ["--version"], { maxBuffer: MAX_BUFFER3 });
|
|
8049
8666
|
} catch {
|
|
8050
8667
|
throw new Error(
|
|
8051
8668
|
[
|
|
@@ -8059,7 +8676,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8059
8676
|
);
|
|
8060
8677
|
}
|
|
8061
8678
|
try {
|
|
8062
|
-
await
|
|
8679
|
+
await execFileP5("glab", ["auth", "status"], { maxBuffer: MAX_BUFFER3 });
|
|
8063
8680
|
return;
|
|
8064
8681
|
} catch {
|
|
8065
8682
|
}
|
|
@@ -8069,7 +8686,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8069
8686
|
);
|
|
8070
8687
|
resetStdinForChild3();
|
|
8071
8688
|
await new Promise((resolve2, reject) => {
|
|
8072
|
-
const proc = (0,
|
|
8689
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8073
8690
|
"glab",
|
|
8074
8691
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
8075
8692
|
{ stdio: "inherit" }
|
|
@@ -8083,7 +8700,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8083
8700
|
}
|
|
8084
8701
|
async listProjects() {
|
|
8085
8702
|
try {
|
|
8086
|
-
const { stdout } = await
|
|
8703
|
+
const { stdout } = await execFileP5(
|
|
8087
8704
|
"glab",
|
|
8088
8705
|
["repo", "list", "--per-page", "200", "--output", "json"],
|
|
8089
8706
|
{ maxBuffer: MAX_BUFFER3 }
|
|
@@ -8215,7 +8832,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8215
8832
|
async exec(workspaceId, command2) {
|
|
8216
8833
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8217
8834
|
try {
|
|
8218
|
-
const { stdout, stderr } = await
|
|
8835
|
+
const { stdout, stderr } = await execFileP5(
|
|
8219
8836
|
"ssh",
|
|
8220
8837
|
[
|
|
8221
8838
|
"-o",
|
|
@@ -8241,7 +8858,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8241
8858
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8242
8859
|
resetStdinForChild3();
|
|
8243
8860
|
return new Promise((resolve2, reject) => {
|
|
8244
|
-
const proc = (0,
|
|
8861
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8245
8862
|
"ssh",
|
|
8246
8863
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
8247
8864
|
{ stdio: "inherit" }
|
|
@@ -8262,8 +8879,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8262
8879
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8263
8880
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
8264
8881
|
await new Promise((resolve2, reject) => {
|
|
8265
|
-
const tar = (0,
|
|
8266
|
-
const ssh = (0,
|
|
8882
|
+
const tar = (0, import_child_process11.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
8883
|
+
const ssh = (0, import_child_process11.spawn)(
|
|
8267
8884
|
"ssh",
|
|
8268
8885
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
|
|
8269
8886
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8286,14 +8903,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8286
8903
|
}
|
|
8287
8904
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8288
8905
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8289
|
-
const remoteDir =
|
|
8906
|
+
const remoteDir = path18.posix.dirname(remotePath);
|
|
8290
8907
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
8291
8908
|
if (options.mode != null) {
|
|
8292
8909
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
8293
8910
|
}
|
|
8294
8911
|
const cmd = parts.join(" && ");
|
|
8295
8912
|
await new Promise((resolve2, reject) => {
|
|
8296
|
-
const proc = (0,
|
|
8913
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8297
8914
|
"ssh",
|
|
8298
8915
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
8299
8916
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8318,7 +8935,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8318
8935
|
*/
|
|
8319
8936
|
async getGlabToken() {
|
|
8320
8937
|
try {
|
|
8321
|
-
const { stdout, stderr } = await
|
|
8938
|
+
const { stdout, stderr } = await execFileP5(
|
|
8322
8939
|
"glab",
|
|
8323
8940
|
["auth", "status", "--show-token"],
|
|
8324
8941
|
{ maxBuffer: MAX_BUFFER3 }
|
|
@@ -8352,10 +8969,10 @@ function shellQuote3(s) {
|
|
|
8352
8969
|
}
|
|
8353
8970
|
|
|
8354
8971
|
// src/services/providers/railway.ts
|
|
8355
|
-
var
|
|
8356
|
-
var
|
|
8357
|
-
var
|
|
8358
|
-
var
|
|
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);
|
|
8359
8976
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
8360
8977
|
function resetStdinForChild4() {
|
|
8361
8978
|
if (process.stdin.isTTY) {
|
|
@@ -8372,7 +8989,7 @@ var RailwayProvider = class {
|
|
|
8372
8989
|
available = true;
|
|
8373
8990
|
async authorize() {
|
|
8374
8991
|
try {
|
|
8375
|
-
await
|
|
8992
|
+
await execFileP6("railway", ["--version"], { maxBuffer: MAX_BUFFER4 });
|
|
8376
8993
|
} catch {
|
|
8377
8994
|
throw new Error(
|
|
8378
8995
|
[
|
|
@@ -8386,7 +9003,7 @@ var RailwayProvider = class {
|
|
|
8386
9003
|
);
|
|
8387
9004
|
}
|
|
8388
9005
|
try {
|
|
8389
|
-
await
|
|
9006
|
+
await execFileP6("railway", ["whoami"], { maxBuffer: MAX_BUFFER4 });
|
|
8390
9007
|
return;
|
|
8391
9008
|
} catch {
|
|
8392
9009
|
}
|
|
@@ -8396,7 +9013,7 @@ var RailwayProvider = class {
|
|
|
8396
9013
|
);
|
|
8397
9014
|
resetStdinForChild4();
|
|
8398
9015
|
await new Promise((resolve2, reject) => {
|
|
8399
|
-
const proc = (0,
|
|
9016
|
+
const proc = (0, import_child_process12.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
8400
9017
|
proc.on("exit", (code) => {
|
|
8401
9018
|
if (code === 0) resolve2();
|
|
8402
9019
|
else reject(new Error("railway login failed."));
|
|
@@ -8406,7 +9023,7 @@ var RailwayProvider = class {
|
|
|
8406
9023
|
}
|
|
8407
9024
|
async listProjects() {
|
|
8408
9025
|
try {
|
|
8409
|
-
const { stdout } = await
|
|
9026
|
+
const { stdout } = await execFileP6(
|
|
8410
9027
|
"railway",
|
|
8411
9028
|
["list", "--json"],
|
|
8412
9029
|
{ maxBuffer: MAX_BUFFER4 }
|
|
@@ -8422,7 +9039,7 @@ var RailwayProvider = class {
|
|
|
8422
9039
|
}));
|
|
8423
9040
|
} catch {
|
|
8424
9041
|
try {
|
|
8425
|
-
const { stdout } = await
|
|
9042
|
+
const { stdout } = await execFileP6("railway", ["list"], { maxBuffer: MAX_BUFFER4 });
|
|
8426
9043
|
const projects = [];
|
|
8427
9044
|
for (const line of stdout.split("\n")) {
|
|
8428
9045
|
const trimmed = line.trim();
|
|
@@ -8473,7 +9090,7 @@ var RailwayProvider = class {
|
|
|
8473
9090
|
async listExistingWorkspaces(projectId) {
|
|
8474
9091
|
if (!projectId) return [];
|
|
8475
9092
|
try {
|
|
8476
|
-
const { stdout } = await
|
|
9093
|
+
const { stdout } = await execFileP6(
|
|
8477
9094
|
"railway",
|
|
8478
9095
|
["service", "list", "--project", projectId, "--json"],
|
|
8479
9096
|
{ maxBuffer: MAX_BUFFER4 }
|
|
@@ -8498,7 +9115,7 @@ var RailwayProvider = class {
|
|
|
8498
9115
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
8499
9116
|
}
|
|
8500
9117
|
try {
|
|
8501
|
-
await
|
|
9118
|
+
await execFileP6(
|
|
8502
9119
|
"railway",
|
|
8503
9120
|
["service", "restart", "--service", serviceId, "--project", projectId],
|
|
8504
9121
|
{ maxBuffer: MAX_BUFFER4, timeout: 6e4 }
|
|
@@ -8517,7 +9134,7 @@ var RailwayProvider = class {
|
|
|
8517
9134
|
};
|
|
8518
9135
|
}
|
|
8519
9136
|
try {
|
|
8520
|
-
const { stdout, stderr } = await
|
|
9137
|
+
const { stdout, stderr } = await execFileP6(
|
|
8521
9138
|
"railway",
|
|
8522
9139
|
["run", "--project", projectId, "--service", serviceId, "--", "bash", "-lc", command2],
|
|
8523
9140
|
{ maxBuffer: MAX_BUFFER4, timeout: 6e5 }
|
|
@@ -8539,7 +9156,7 @@ var RailwayProvider = class {
|
|
|
8539
9156
|
}
|
|
8540
9157
|
resetStdinForChild4();
|
|
8541
9158
|
return new Promise((resolve2, reject) => {
|
|
8542
|
-
const proc = (0,
|
|
9159
|
+
const proc = (0, import_child_process12.spawn)(
|
|
8543
9160
|
"railway",
|
|
8544
9161
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
8545
9162
|
{ stdio: "inherit" }
|
|
@@ -8563,8 +9180,8 @@ var RailwayProvider = class {
|
|
|
8563
9180
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8564
9181
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
8565
9182
|
await new Promise((resolve2, reject) => {
|
|
8566
|
-
const tar = (0,
|
|
8567
|
-
const sh = (0,
|
|
9183
|
+
const tar = (0, import_child_process12.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
9184
|
+
const sh = (0, import_child_process12.spawn)(
|
|
8568
9185
|
"railway",
|
|
8569
9186
|
["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
|
|
8570
9187
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8590,14 +9207,14 @@ var RailwayProvider = class {
|
|
|
8590
9207
|
if (!projectId || !serviceId) {
|
|
8591
9208
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
8592
9209
|
}
|
|
8593
|
-
const remoteDir =
|
|
9210
|
+
const remoteDir = path19.posix.dirname(remotePath);
|
|
8594
9211
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
8595
9212
|
if (options.mode != null) {
|
|
8596
9213
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
8597
9214
|
}
|
|
8598
9215
|
const cmd = parts.join(" && ");
|
|
8599
9216
|
await new Promise((resolve2, reject) => {
|
|
8600
|
-
const proc = (0,
|
|
9217
|
+
const proc = (0, import_child_process12.spawn)(
|
|
8601
9218
|
"railway",
|
|
8602
9219
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
8603
9220
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8629,8 +9246,7 @@ var PROVIDERS = [
|
|
|
8629
9246
|
];
|
|
8630
9247
|
|
|
8631
9248
|
// src/commands/deploy.ts
|
|
8632
|
-
|
|
8633
|
-
async function deploy() {
|
|
9249
|
+
async function deploy(args2 = []) {
|
|
8634
9250
|
console.log();
|
|
8635
9251
|
mt(import_picocolors9.default.bgMagenta(import_picocolors9.default.white(" codeam deploy ")));
|
|
8636
9252
|
const provider = await pickProvider();
|
|
@@ -8782,13 +9398,14 @@ async function deploy() {
|
|
|
8782
9398
|
process.exit(1);
|
|
8783
9399
|
}
|
|
8784
9400
|
}
|
|
8785
|
-
const
|
|
8786
|
-
const
|
|
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();
|
|
8787
9405
|
let bridged = "none";
|
|
8788
|
-
if (
|
|
8789
|
-
const sourceLabel = localCredsKind === "flat-file" ? "~/.claude/.credentials.json" : "macOS Keychain";
|
|
9406
|
+
if (localCreds.source !== "none") {
|
|
8790
9407
|
const useLocal = await ot2({
|
|
8791
|
-
message: `Copy your local
|
|
9408
|
+
message: `Copy your local ${AGENT_REGISTRY[agentId].displayName} credentials (${localCreds.description}) to the workspace?`,
|
|
8792
9409
|
active: "Yes \u2014 same account, no re-auth",
|
|
8793
9410
|
inactive: "No \u2014 log in with a different account",
|
|
8794
9411
|
initialValue: true
|
|
@@ -8799,118 +9416,13 @@ async function deploy() {
|
|
|
8799
9416
|
}
|
|
8800
9417
|
if (useLocal) {
|
|
8801
9418
|
const credStep = fe();
|
|
8802
|
-
credStep.start(
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
credStep.stop("\u2713 Local credentials staged");
|
|
8807
|
-
break;
|
|
8808
|
-
case "macos-keychain":
|
|
8809
|
-
credStep.stop("\u2713 Credentials extracted from macOS Keychain and staged");
|
|
8810
|
-
break;
|
|
8811
|
-
case "none":
|
|
8812
|
-
credStep.stop("\u26A0 Could not extract local credentials \u2014 falling back to remote login");
|
|
8813
|
-
break;
|
|
8814
|
-
}
|
|
8815
|
-
}
|
|
8816
|
-
}
|
|
8817
|
-
const claudeStep = fe();
|
|
8818
|
-
claudeStep.start("Installing Claude CLI on workspace\u2026");
|
|
8819
|
-
const installResult = await provider.exec(
|
|
8820
|
-
workspace.id,
|
|
8821
|
-
"curl -fsSL https://claude.ai/install.sh | bash"
|
|
8822
|
-
);
|
|
8823
|
-
if (installResult.code !== 0) {
|
|
8824
|
-
claudeStep.stop("\u2717 Claude CLI install failed");
|
|
8825
|
-
pt(installResult.stderr.slice(0, 1e3));
|
|
8826
|
-
process.exit(1);
|
|
8827
|
-
}
|
|
8828
|
-
claudeStep.stop("\u2713 Claude CLI installed");
|
|
8829
|
-
const haveLocalClaude = fs11.existsSync(localClaudeDir) && fs11.statSync(localClaudeDir).isDirectory();
|
|
8830
|
-
if (haveLocalClaude) {
|
|
8831
|
-
const copyStep = fe();
|
|
8832
|
-
copyStep.start("Copying local Claude config to workspace\u2026");
|
|
8833
|
-
try {
|
|
8834
|
-
await provider.uploadDirectory(
|
|
8835
|
-
workspace.id,
|
|
8836
|
-
localClaudeDir,
|
|
8837
|
-
"/home/codespace/.claude",
|
|
8838
|
-
{
|
|
8839
|
-
exclude: [
|
|
8840
|
-
"./projects",
|
|
8841
|
-
// per-project conversation history (often 700MB+)
|
|
8842
|
-
"./file-history",
|
|
8843
|
-
// per-project file diffs
|
|
8844
|
-
"./downloads",
|
|
8845
|
-
// downloaded artifacts
|
|
8846
|
-
"./image-cache",
|
|
8847
|
-
// cached images
|
|
8848
|
-
"./paste-cache",
|
|
8849
|
-
// clipboard/paste cache
|
|
8850
|
-
"./backups",
|
|
8851
|
-
// local backups
|
|
8852
|
-
"./shell-snapshots",
|
|
8853
|
-
// shell history snapshots
|
|
8854
|
-
"./telemetry",
|
|
8855
|
-
// analytics dumps
|
|
8856
|
-
"./statsig",
|
|
8857
|
-
// feature-flag cache
|
|
8858
|
-
"./cache",
|
|
8859
|
-
// generic cache dir
|
|
8860
|
-
"./history.jsonl",
|
|
8861
|
-
// global REPL history
|
|
8862
|
-
"./ide",
|
|
8863
|
-
// local IDE bridge state
|
|
8864
|
-
"./todos",
|
|
8865
|
-
// local todo state
|
|
8866
|
-
"./tasks",
|
|
8867
|
-
// local task state
|
|
8868
|
-
// Don't overwrite the credentials we already staged in
|
|
8869
|
-
// step 4 — the local dir on macOS doesn't have a flat
|
|
8870
|
-
// credentials file anyway, but on Linux it would, and a
|
|
8871
|
-
// re-write here would be redundant.
|
|
8872
|
-
"./.credentials.json"
|
|
8873
|
-
]
|
|
8874
|
-
}
|
|
8875
|
-
);
|
|
8876
|
-
copyStep.stop("\u2713 Claude config uploaded");
|
|
8877
|
-
} catch (err) {
|
|
8878
|
-
copyStep.stop("\u26A0 Could not upload Claude config (continuing)");
|
|
8879
|
-
void err;
|
|
8880
|
-
}
|
|
8881
|
-
}
|
|
8882
|
-
if (bridged !== "none") {
|
|
8883
|
-
const localClaudeJson = path17.join(os10.homedir(), ".claude.json");
|
|
8884
|
-
if (fs11.existsSync(localClaudeJson)) {
|
|
8885
|
-
try {
|
|
8886
|
-
const contents = fs11.readFileSync(localClaudeJson);
|
|
8887
|
-
await provider.uploadFile(
|
|
8888
|
-
workspace.id,
|
|
8889
|
-
"/home/codespace/.claude.json",
|
|
8890
|
-
contents,
|
|
8891
|
-
{ mode: 384 }
|
|
8892
|
-
);
|
|
8893
|
-
} catch (err) {
|
|
8894
|
-
void err;
|
|
8895
|
-
}
|
|
8896
|
-
}
|
|
8897
|
-
}
|
|
8898
|
-
const verifyStep = fe();
|
|
8899
|
-
verifyStep.start("Verifying Claude auth on workspace\u2026");
|
|
8900
|
-
const verified = await verifyClaudeAuth(provider, workspace.id);
|
|
8901
|
-
if (verified) {
|
|
8902
|
-
verifyStep.stop("\u2713 Claude is logged in \u2014 no re-auth needed");
|
|
8903
|
-
} else {
|
|
8904
|
-
verifyStep.stop("\xB7 Claude not yet authenticated \u2014 running login flow");
|
|
8905
|
-
await runRemoteClaudeLogin(provider, workspace.id);
|
|
8906
|
-
const reverified = await verifyClaudeAuth(provider, workspace.id);
|
|
8907
|
-
if (!reverified) {
|
|
8908
|
-
wt(
|
|
8909
|
-
"Claude auth could not be confirmed. You may need to run `claude /login` manually inside the codespace.",
|
|
8910
|
-
"Heads up"
|
|
8911
|
-
);
|
|
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})`);
|
|
8912
9423
|
}
|
|
8913
9424
|
}
|
|
9425
|
+
await strategy.setupOnWorkspace(provider, workspace.id, { bridged });
|
|
8914
9426
|
const cliStep = fe();
|
|
8915
9427
|
cliStep.start("Installing codeam-cli on workspace\u2026");
|
|
8916
9428
|
const cliInstall = await provider.exec(workspace.id, "npm install -g codeam-cli@latest");
|
|
@@ -8925,8 +9437,8 @@ async function deploy() {
|
|
|
8925
9437
|
`Workspace: ${import_picocolors9.default.cyan(workspace.displayName ?? workspace.id)}`,
|
|
8926
9438
|
workspace.webUrl ? `Web: ${import_picocolors9.default.cyan(workspace.webUrl)}` : "",
|
|
8927
9439
|
"",
|
|
8928
|
-
|
|
8929
|
-
"Scan the QR code
|
|
9440
|
+
`Starting \`codeam pair\` on the workspace (agent: ${AGENT_REGISTRY[agentId].displayName}).`,
|
|
9441
|
+
"Scan the QR code from your phone to pair.",
|
|
8930
9442
|
import_picocolors9.default.dim("(Once paired, this terminal disconnects automatically; the session stays alive on the codespace.)")
|
|
8931
9443
|
].filter(Boolean).join("\n"),
|
|
8932
9444
|
"Almost there"
|
|
@@ -8936,10 +9448,9 @@ async function deploy() {
|
|
|
8936
9448
|
"LOG=~/.codeam-deploy/session.log",
|
|
8937
9449
|
': > "$LOG"',
|
|
8938
9450
|
// The default `gh codespace ssh` cwd is the repo root
|
|
8939
|
-
// (/workspaces/<repo>), which is exactly where
|
|
9451
|
+
// (/workspaces/<repo>), which is exactly where the agent needs to
|
|
8940
9452
|
// run so it can read/edit project files. Pass that to PM2 via
|
|
8941
|
-
// --cwd so the relay's child
|
|
8942
|
-
// working directory.
|
|
9453
|
+
// --cwd so the relay's child agent inherits the right directory.
|
|
8943
9454
|
'PROJECT_DIR="$(pwd)"',
|
|
8944
9455
|
// Install PM2 if it isn't already on PATH. Idempotent.
|
|
8945
9456
|
"if ! command -v pm2 >/dev/null 2>&1; then",
|
|
@@ -8951,11 +9462,11 @@ async function deploy() {
|
|
|
8951
9462
|
// Start codeam pair under PM2. `--merge-logs` writes stdout
|
|
8952
9463
|
// and stderr to the same file so we only need one tail.
|
|
8953
9464
|
// --max-restarts 3 keeps PM2 from looping forever if codeam pair
|
|
8954
|
-
// can't start (e.g. backend unreachable) — three attempts is
|
|
8955
|
-
//
|
|
8956
|
-
// No `--time` (would prefix every line with a timestamp
|
|
8957
|
-
//
|
|
8958
|
-
|
|
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`,
|
|
8959
9470
|
// Give PM2 a moment to spawn the process before we start polling
|
|
8960
9471
|
// status — otherwise the very first jlist can race the spawn.
|
|
8961
9472
|
"sleep 2",
|
|
@@ -9053,83 +9564,6 @@ async function deploy() {
|
|
|
9053
9564
|
function shellQuoteSingle(s) {
|
|
9054
9565
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
9055
9566
|
}
|
|
9056
|
-
async function runRemoteClaudeLogin(provider, workspaceId) {
|
|
9057
|
-
wt(
|
|
9058
|
-
[
|
|
9059
|
-
"A login URL will print below. Open it in your local browser, sign in,",
|
|
9060
|
-
"and paste any code Claude asks for back into this terminal."
|
|
9061
|
-
].join("\n"),
|
|
9062
|
-
"Authenticating Claude on workspace"
|
|
9063
|
-
);
|
|
9064
|
-
const result = await provider.streamCommand(
|
|
9065
|
-
workspaceId,
|
|
9066
|
-
'bash -lc "claude login || claude /login || true"'
|
|
9067
|
-
);
|
|
9068
|
-
if (result.code !== 0) {
|
|
9069
|
-
wt(
|
|
9070
|
-
"claude login exited non-zero. You can re-run it manually inside the codespace later.",
|
|
9071
|
-
"Heads up"
|
|
9072
|
-
);
|
|
9073
|
-
}
|
|
9074
|
-
}
|
|
9075
|
-
async function detectLocalClaudeCredentials(localClaudeDir) {
|
|
9076
|
-
if (fs11.existsSync(path17.join(localClaudeDir, ".credentials.json"))) {
|
|
9077
|
-
return "flat-file";
|
|
9078
|
-
}
|
|
9079
|
-
if (process.platform === "darwin") {
|
|
9080
|
-
try {
|
|
9081
|
-
await execFileP6(
|
|
9082
|
-
"security",
|
|
9083
|
-
["find-generic-password", "-s", "Claude Code-credentials"],
|
|
9084
|
-
{ maxBuffer: 1024 * 1024 }
|
|
9085
|
-
);
|
|
9086
|
-
return "macos-keychain";
|
|
9087
|
-
} catch {
|
|
9088
|
-
return "none";
|
|
9089
|
-
}
|
|
9090
|
-
}
|
|
9091
|
-
return "none";
|
|
9092
|
-
}
|
|
9093
|
-
async function verifyClaudeAuth(provider, workspaceId) {
|
|
9094
|
-
const result = await provider.exec(
|
|
9095
|
-
workspaceId,
|
|
9096
|
-
'bash -lc "claude auth status 2>/dev/null || true"'
|
|
9097
|
-
);
|
|
9098
|
-
if (result.code !== 0) return false;
|
|
9099
|
-
const jsonStart = result.stdout.indexOf("{");
|
|
9100
|
-
if (jsonStart < 0) return false;
|
|
9101
|
-
try {
|
|
9102
|
-
const parsed = JSON.parse(result.stdout.slice(jsonStart));
|
|
9103
|
-
return parsed.loggedIn === true;
|
|
9104
|
-
} catch {
|
|
9105
|
-
return false;
|
|
9106
|
-
}
|
|
9107
|
-
}
|
|
9108
|
-
async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
|
|
9109
|
-
const fileBased = path17.join(localClaudeDir, ".credentials.json");
|
|
9110
|
-
if (fs11.existsSync(fileBased)) return "flat-file";
|
|
9111
|
-
if (process.platform === "darwin") {
|
|
9112
|
-
try {
|
|
9113
|
-
const { stdout } = await execFileP6(
|
|
9114
|
-
"security",
|
|
9115
|
-
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
|
9116
|
-
{ maxBuffer: 1024 * 1024 }
|
|
9117
|
-
);
|
|
9118
|
-
const json = stdout.trim();
|
|
9119
|
-
if (json.length === 0) return "none";
|
|
9120
|
-
await provider.uploadFile(
|
|
9121
|
-
workspaceId,
|
|
9122
|
-
"/home/codespace/.claude/.credentials.json",
|
|
9123
|
-
json,
|
|
9124
|
-
{ mode: 384 }
|
|
9125
|
-
);
|
|
9126
|
-
return "macos-keychain";
|
|
9127
|
-
} catch {
|
|
9128
|
-
return "none";
|
|
9129
|
-
}
|
|
9130
|
-
}
|
|
9131
|
-
return "none";
|
|
9132
|
-
}
|
|
9133
9567
|
function formatLastUsed(iso) {
|
|
9134
9568
|
if (!iso) return "";
|
|
9135
9569
|
const t2 = Date.parse(iso);
|
|
@@ -9315,7 +9749,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
9315
9749
|
// src/commands/version.ts
|
|
9316
9750
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
9317
9751
|
function version() {
|
|
9318
|
-
const v = true ? "2.
|
|
9752
|
+
const v = true ? "2.11.0" : "unknown";
|
|
9319
9753
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
9320
9754
|
}
|
|
9321
9755
|
|
|
@@ -9352,9 +9786,9 @@ function help() {
|
|
|
9352
9786
|
}
|
|
9353
9787
|
|
|
9354
9788
|
// src/lib/updateNotifier.ts
|
|
9355
|
-
var
|
|
9356
|
-
var
|
|
9357
|
-
var
|
|
9789
|
+
var fs14 = __toESM(require("fs"));
|
|
9790
|
+
var os13 = __toESM(require("os"));
|
|
9791
|
+
var path20 = __toESM(require("path"));
|
|
9358
9792
|
var https5 = __toESM(require("https"));
|
|
9359
9793
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
9360
9794
|
var PKG_NAME = "codeam-cli";
|
|
@@ -9362,12 +9796,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
|
9362
9796
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9363
9797
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
9364
9798
|
function cachePath() {
|
|
9365
|
-
const dir =
|
|
9366
|
-
return
|
|
9799
|
+
const dir = path20.join(os13.homedir(), ".codeam");
|
|
9800
|
+
return path20.join(dir, "update-check.json");
|
|
9367
9801
|
}
|
|
9368
9802
|
function readCache() {
|
|
9369
9803
|
try {
|
|
9370
|
-
const raw =
|
|
9804
|
+
const raw = fs14.readFileSync(cachePath(), "utf8");
|
|
9371
9805
|
const parsed = JSON.parse(raw);
|
|
9372
9806
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
9373
9807
|
return parsed;
|
|
@@ -9378,8 +9812,8 @@ function readCache() {
|
|
|
9378
9812
|
function writeCache(cache) {
|
|
9379
9813
|
try {
|
|
9380
9814
|
const file = cachePath();
|
|
9381
|
-
|
|
9382
|
-
|
|
9815
|
+
fs14.mkdirSync(path20.dirname(file), { recursive: true });
|
|
9816
|
+
fs14.writeFileSync(file, JSON.stringify(cache));
|
|
9383
9817
|
} catch {
|
|
9384
9818
|
}
|
|
9385
9819
|
}
|
|
@@ -9450,7 +9884,7 @@ function checkForUpdates() {
|
|
|
9450
9884
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
9451
9885
|
if (process.env.CI) return;
|
|
9452
9886
|
if (!process.stdout.isTTY) return;
|
|
9453
|
-
const current = true ? "2.
|
|
9887
|
+
const current = true ? "2.11.0" : null;
|
|
9454
9888
|
if (!current) return;
|
|
9455
9889
|
const cache = readCache();
|
|
9456
9890
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -9479,7 +9913,7 @@ async function main() {
|
|
|
9479
9913
|
case "help":
|
|
9480
9914
|
return help();
|
|
9481
9915
|
case "pair":
|
|
9482
|
-
return pair();
|
|
9916
|
+
return pair(args);
|
|
9483
9917
|
case "pair-auto":
|
|
9484
9918
|
return pairAuto(args);
|
|
9485
9919
|
case "sessions":
|
|
@@ -9491,7 +9925,7 @@ async function main() {
|
|
|
9491
9925
|
case "deploy":
|
|
9492
9926
|
if (args[0] === "ls" || args[0] === "list") return deployList();
|
|
9493
9927
|
if (args[0] === "stop" || args[0] === "remove") return deployStop();
|
|
9494
|
-
return deploy();
|
|
9928
|
+
return deploy(args);
|
|
9495
9929
|
default:
|
|
9496
9930
|
return start();
|
|
9497
9931
|
}
|