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