codeam-cli 2.10.8 → 2.12.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 +53 -0
- package/dist/index.js +1509 -898
- 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 fs17 = require("fs");
|
|
523
|
+
var os16 = require("os");
|
|
524
|
+
var path23 = 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 = path23.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 = fs17.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(path23.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(os16.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 fs17 = require("fs");
|
|
1016
|
+
var path23 = 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 = path23.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
|
+
fs17.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() {
|
|
@@ -1313,10 +1313,10 @@ var require_lib = __commonJS({
|
|
|
1313
1313
|
} else {
|
|
1314
1314
|
terminalCtor = require_unixTerminal().UnixTerminal;
|
|
1315
1315
|
}
|
|
1316
|
-
function
|
|
1316
|
+
function spawn11(file, args2, opt) {
|
|
1317
1317
|
return new terminalCtor(file, args2, opt);
|
|
1318
1318
|
}
|
|
1319
|
-
exports2.spawn =
|
|
1319
|
+
exports2.spawn = spawn11;
|
|
1320
1320
|
function fork(file, args2, opt) {
|
|
1321
1321
|
return new terminalCtor(file, args2, opt);
|
|
1322
1322
|
}
|
|
@@ -1392,6 +1392,438 @@ 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
|
+
// ── Anthropic / Claude ────────────────────────────────────
|
|
1742
|
+
"claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
1743
|
+
"claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
|
|
1744
|
+
"claude-3-5-sonnet": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
1745
|
+
"claude-3-5-haiku": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
|
|
1746
|
+
"claude-3-haiku": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 },
|
|
1747
|
+
// ── Codex / OpenAI ────────────────────────────────────────
|
|
1748
|
+
// Phase 2 placeholder pricing: 0 across the board until OpenAI publishes
|
|
1749
|
+
// confirmed rates for the GPT-5.x catalog. Sync from
|
|
1750
|
+
// developers.openai.com/pricing when available.
|
|
1751
|
+
"gpt-5.5": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1752
|
+
"gpt-5.4": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1753
|
+
"gpt-5.4-mini": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1754
|
+
"gpt-5.3-codex": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1755
|
+
"gpt-5.2": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1756
|
+
"codex-auto-review": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
1757
|
+
};
|
|
1758
|
+
var MODEL_CONTEXT_WINDOW = {
|
|
1759
|
+
// ── Anthropic / Claude ────────────────────────────────────
|
|
1760
|
+
"claude-opus-4": 1e6,
|
|
1761
|
+
"claude-sonnet-4": 1e6,
|
|
1762
|
+
"claude-3-5-sonnet": 2e5,
|
|
1763
|
+
"claude-3-5-haiku": 2e5,
|
|
1764
|
+
"claude-3-haiku": 2e5,
|
|
1765
|
+
// ── Codex / OpenAI ────────────────────────────────────────
|
|
1766
|
+
"gpt-5.5": 272e3,
|
|
1767
|
+
"gpt-5.4": 272e3,
|
|
1768
|
+
"gpt-5.4-mini": 272e3,
|
|
1769
|
+
"gpt-5.3-codex": 272e3,
|
|
1770
|
+
"gpt-5.2": 272e3,
|
|
1771
|
+
"codex-auto-review": 272e3
|
|
1772
|
+
};
|
|
1773
|
+
var DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
1774
|
+
function getPricing(model) {
|
|
1775
|
+
for (const [prefix, pricing] of Object.entries(MODEL_PRICING)) {
|
|
1776
|
+
if (model.startsWith(prefix)) return pricing;
|
|
1777
|
+
}
|
|
1778
|
+
return MODEL_PRICING["claude-sonnet-4"];
|
|
1779
|
+
}
|
|
1780
|
+
function getContextWindow(model) {
|
|
1781
|
+
if (!model) return DEFAULT_CONTEXT_WINDOW;
|
|
1782
|
+
for (const [prefix, size] of Object.entries(MODEL_CONTEXT_WINDOW)) {
|
|
1783
|
+
if (model.startsWith(prefix)) return size;
|
|
1784
|
+
}
|
|
1785
|
+
return DEFAULT_CONTEXT_WINDOW;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// ../../packages/shared/src/agents/registry.ts
|
|
1789
|
+
var AGENT_REGISTRY = {
|
|
1790
|
+
claude: {
|
|
1791
|
+
id: "claude",
|
|
1792
|
+
displayName: "Claude Code",
|
|
1793
|
+
binaryName: "claude",
|
|
1794
|
+
enabled: true,
|
|
1795
|
+
supportedAuthKinds: ["oauth_token", "api_key"],
|
|
1796
|
+
preferredAuthKind: "oauth_token"
|
|
1797
|
+
},
|
|
1798
|
+
codex: {
|
|
1799
|
+
id: "codex",
|
|
1800
|
+
displayName: "Codex CLI",
|
|
1801
|
+
binaryName: "codex",
|
|
1802
|
+
enabled: true,
|
|
1803
|
+
supportedAuthKinds: ["oauth_token", "api_key"],
|
|
1804
|
+
preferredAuthKind: "oauth_token"
|
|
1805
|
+
},
|
|
1806
|
+
copilot: {
|
|
1807
|
+
id: "copilot",
|
|
1808
|
+
displayName: "GitHub Copilot CLI",
|
|
1809
|
+
binaryName: "gh",
|
|
1810
|
+
enabled: false,
|
|
1811
|
+
supportedAuthKinds: ["oauth_token"],
|
|
1812
|
+
preferredAuthKind: "oauth_token"
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
function getEnabledAgents() {
|
|
1816
|
+
return Object.values(AGENT_REGISTRY).filter((m) => m.enabled);
|
|
1817
|
+
}
|
|
1818
|
+
function getAgent(id) {
|
|
1819
|
+
const meta = AGENT_REGISTRY[id];
|
|
1820
|
+
if (!meta) throw new Error(`Unknown agent id: ${id}`);
|
|
1821
|
+
return meta;
|
|
1822
|
+
}
|
|
1823
|
+
function isKnownAgentId(id) {
|
|
1824
|
+
return id === "claude" || id === "codex" || id === "copilot";
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1395
1827
|
// src/config.ts
|
|
1396
1828
|
var fs = __toESM(require("fs"));
|
|
1397
1829
|
var os = __toESM(require("os"));
|
|
@@ -1402,6 +1834,9 @@ var EMPTY_CONFIG = () => ({
|
|
|
1402
1834
|
activeSessionId: null,
|
|
1403
1835
|
sessions: []
|
|
1404
1836
|
});
|
|
1837
|
+
function migrateSession(s) {
|
|
1838
|
+
return { ...s, agent: s.agent ?? "claude" };
|
|
1839
|
+
}
|
|
1405
1840
|
function makeConfig(baseDir) {
|
|
1406
1841
|
const dir = path.join(baseDir ?? os.homedir(), ".codeam");
|
|
1407
1842
|
const file = path.join(dir, "config.json");
|
|
@@ -1411,7 +1846,8 @@ function makeConfig(baseDir) {
|
|
|
1411
1846
|
return {
|
|
1412
1847
|
pluginId: typeof raw.pluginId === "string" ? raw.pluginId : crypto.randomUUID(),
|
|
1413
1848
|
activeSessionId: typeof raw.activeSessionId === "string" ? raw.activeSessionId : null,
|
|
1414
|
-
sessions: Array.isArray(raw.sessions) ? raw.sessions : []
|
|
1849
|
+
sessions: Array.isArray(raw.sessions) ? raw.sessions.map(migrateSession) : [],
|
|
1850
|
+
preferredAgent: typeof raw.preferredAgent === "string" ? raw.preferredAgent : void 0
|
|
1415
1851
|
};
|
|
1416
1852
|
} catch {
|
|
1417
1853
|
return EMPTY_CONFIG();
|
|
@@ -1465,10 +1901,16 @@ function makeConfig(baseDir) {
|
|
|
1465
1901
|
} catch {
|
|
1466
1902
|
}
|
|
1467
1903
|
}
|
|
1468
|
-
|
|
1904
|
+
function saveCliConfig2(c2) {
|
|
1905
|
+
save(c2);
|
|
1906
|
+
}
|
|
1907
|
+
function loadCliConfig2() {
|
|
1908
|
+
return load();
|
|
1909
|
+
}
|
|
1910
|
+
return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, clearAll: clearAll2, saveCliConfig: saveCliConfig2, loadCliConfig: loadCliConfig2 };
|
|
1469
1911
|
}
|
|
1470
1912
|
var _default = makeConfig();
|
|
1471
|
-
var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, clearAll } = _default;
|
|
1913
|
+
var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, clearAll, saveCliConfig, loadCliConfig } = _default;
|
|
1472
1914
|
|
|
1473
1915
|
// src/ui/banner.ts
|
|
1474
1916
|
var import_picocolors = __toESM(require("picocolors"));
|
|
@@ -1477,7 +1919,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
1477
1919
|
// package.json
|
|
1478
1920
|
var package_default = {
|
|
1479
1921
|
name: "codeam-cli",
|
|
1480
|
-
version: "2.
|
|
1922
|
+
version: "2.12.0",
|
|
1481
1923
|
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
1924
|
type: "commonjs",
|
|
1483
1925
|
main: "dist/index.js",
|
|
@@ -2393,7 +2835,7 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2393
2835
|
resizeHandler = null;
|
|
2394
2836
|
/**
|
|
2395
2837
|
* Factory that returns a working ConPTY strategy or `null` if
|
|
2396
|
-
* node-pty can't load. The caller (
|
|
2838
|
+
* node-pty can't load. The caller (agent.service.ts) decides
|
|
2397
2839
|
* whether to fall back to the legacy pipe strategy.
|
|
2398
2840
|
*/
|
|
2399
2841
|
static tryCreate(opts) {
|
|
@@ -4498,9 +4940,10 @@ function buildClaudeLaunch(extraArgs = []) {
|
|
|
4498
4940
|
return { cmd: found, args: extraArgs };
|
|
4499
4941
|
}
|
|
4500
4942
|
|
|
4501
|
-
// src/services/
|
|
4502
|
-
var
|
|
4503
|
-
constructor(opts) {
|
|
4943
|
+
// src/services/agent.service.ts
|
|
4944
|
+
var AgentService = class {
|
|
4945
|
+
constructor(runtime, opts) {
|
|
4946
|
+
this.runtime = runtime;
|
|
4504
4947
|
this.opts = opts;
|
|
4505
4948
|
this.strategyOpts = {
|
|
4506
4949
|
onData: (d3) => {
|
|
@@ -4514,6 +4957,7 @@ var ClaudeService = class {
|
|
|
4514
4957
|
onExit: opts.onExit
|
|
4515
4958
|
};
|
|
4516
4959
|
}
|
|
4960
|
+
runtime;
|
|
4517
4961
|
opts;
|
|
4518
4962
|
// Strategy is selected lazily inside spawn() so we can fall back from
|
|
4519
4963
|
// ConPTY → legacy pipe at runtime if the native binding fails to load.
|
|
@@ -4545,10 +4989,20 @@ var ClaudeService = class {
|
|
|
4545
4989
|
this.pendingInputs.length = 0;
|
|
4546
4990
|
}
|
|
4547
4991
|
async spawn() {
|
|
4548
|
-
let launch
|
|
4549
|
-
|
|
4992
|
+
let launch;
|
|
4993
|
+
try {
|
|
4994
|
+
launch = await this.runtime.prepareLaunch();
|
|
4995
|
+
} catch {
|
|
4550
4996
|
const installed = await ensureClaudeInstalled();
|
|
4551
|
-
if (installed)
|
|
4997
|
+
if (installed) {
|
|
4998
|
+
try {
|
|
4999
|
+
launch = await this.runtime.prepareLaunch();
|
|
5000
|
+
} catch {
|
|
5001
|
+
launch = null;
|
|
5002
|
+
}
|
|
5003
|
+
} else {
|
|
5004
|
+
launch = null;
|
|
5005
|
+
}
|
|
4552
5006
|
if (!launch) {
|
|
4553
5007
|
const cmd = process.platform === "win32" ? "irm https://claude.ai/install.ps1 | iex" : "curl -fsSL https://claude.ai/install.sh | bash";
|
|
4554
5008
|
console.error(
|
|
@@ -4661,6 +5115,20 @@ var ClaudeService = class {
|
|
|
4661
5115
|
s.write("\r");
|
|
4662
5116
|
}, steps * ARROW_MS + ENTER_MS);
|
|
4663
5117
|
}
|
|
5118
|
+
/**
|
|
5119
|
+
* Write raw bytes to the PTY without any auto-appended `\r` or delay.
|
|
5120
|
+
* Use this when the caller already owns the full input (e.g. the
|
|
5121
|
+
* `ptyInput` returned by `RuntimeStrategy.changeModelInstruction()`
|
|
5122
|
+
* already contains the trailing `\r`).
|
|
5123
|
+
*/
|
|
5124
|
+
sendRawPtyInput(text) {
|
|
5125
|
+
if (!this.strategy) {
|
|
5126
|
+
log.trace("claude", "sendRawPtyInput dropped (no strategy)");
|
|
5127
|
+
return;
|
|
5128
|
+
}
|
|
5129
|
+
log.trace("claude", `sendRawPtyInput len=${text.length}`);
|
|
5130
|
+
this.strategy.write(text);
|
|
5131
|
+
}
|
|
4664
5132
|
/** Send Escape key to Claude (cancels interactive prompts). */
|
|
4665
5133
|
sendEscape() {
|
|
4666
5134
|
this.strategy?.write("\x1B");
|
|
@@ -4675,390 +5143,754 @@ var ClaudeService = class {
|
|
|
4675
5143
|
/**
|
|
4676
5144
|
* Kill the current Claude process and relaunch it resuming the given session.
|
|
4677
5145
|
* Pass auto=true to add --dangerously-skip-permissions (no confirmation prompts).
|
|
5146
|
+
*
|
|
5147
|
+
* For agents that use CLI flags (Claude: --resume <id>), `resumeLaunchArgs`
|
|
5148
|
+
* returns a non-empty array and we pass those directly to the binary.
|
|
5149
|
+
* For agents that use a post-spawn PTY instruction (e.g. Codex), `resumeLaunchArgs`
|
|
5150
|
+
* returns [] and `postSpawnInstruction` types the resume command into the PTY.
|
|
4678
5151
|
*/
|
|
4679
5152
|
restart(sessionId, auto = false) {
|
|
4680
5153
|
if (!this.strategy) return;
|
|
4681
|
-
const
|
|
4682
|
-
|
|
4683
|
-
const launch = buildClaudeLaunch(extraArgs);
|
|
5154
|
+
const resumeArgs = auto ? this.runtime.resumeLaunchArgs(sessionId) : ["--resume", sessionId];
|
|
5155
|
+
const launch = buildClaudeLaunch(resumeArgs);
|
|
4684
5156
|
if (!launch) return;
|
|
4685
5157
|
this.strategy.kill();
|
|
4686
5158
|
this.strategy.spawn(launch.cmd, this.opts.cwd, launch.args);
|
|
5159
|
+
if (resumeArgs.length === 0 && this.runtime.postSpawnInstruction) {
|
|
5160
|
+
const { ptyInput } = this.runtime.postSpawnInstruction(sessionId);
|
|
5161
|
+
setTimeout(() => {
|
|
5162
|
+
this.strategy?.write(ptyInput);
|
|
5163
|
+
}, 500);
|
|
5164
|
+
}
|
|
4687
5165
|
}
|
|
4688
5166
|
};
|
|
4689
5167
|
|
|
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
|
-
|
|
5168
|
+
// src/agents/claude/quota.ts
|
|
5169
|
+
var fs5 = __toESM(require("fs"));
|
|
5170
|
+
var os6 = __toESM(require("os"));
|
|
5171
|
+
var path8 = __toESM(require("path"));
|
|
5172
|
+
var import_child_process4 = require("child_process");
|
|
5173
|
+
var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
|
|
5174
|
+
m,s=pty.openpty()
|
|
5175
|
+
try:
|
|
5176
|
+
fcntl.ioctl(s,termios.TIOCSWINSZ,struct.pack('HHHH',30,120,0,0))
|
|
5177
|
+
except Exception:pass
|
|
5178
|
+
pid=os.fork()
|
|
5179
|
+
if pid==0:
|
|
5180
|
+
os.close(m);os.setsid()
|
|
5181
|
+
try:fcntl.ioctl(s,termios.TIOCSCTTY,0)
|
|
5182
|
+
except Exception:pass
|
|
5183
|
+
for fd in[0,1,2]:os.dup2(s,fd)
|
|
5184
|
+
if s>2:os.close(s)
|
|
5185
|
+
os.execvp(sys.argv[1],sys.argv[1:])
|
|
5186
|
+
sys.exit(127)
|
|
5187
|
+
os.close(s)
|
|
5188
|
+
done=[False]
|
|
5189
|
+
def onchld(n,f):
|
|
5190
|
+
try:os.waitpid(pid,os.WNOHANG)
|
|
5191
|
+
except Exception:pass
|
|
5192
|
+
done[0]=True
|
|
5193
|
+
signal.signal(signal.SIGCHLD,onchld)
|
|
5194
|
+
i=sys.stdin.fileno();o=sys.stdout.fileno()
|
|
5195
|
+
while not done[0]:
|
|
5196
|
+
try:r,_,_=select.select([i,m],[],[],0.1)
|
|
5197
|
+
except OSError as e:
|
|
5198
|
+
if e.errno==errno.EINTR:continue
|
|
5199
|
+
break
|
|
5200
|
+
if i in r:
|
|
5201
|
+
try:
|
|
5202
|
+
d=os.read(i,4096)
|
|
5203
|
+
if d:os.write(m,d)
|
|
5204
|
+
else:break
|
|
5205
|
+
except OSError:break
|
|
5206
|
+
if m in r:
|
|
5207
|
+
try:
|
|
5208
|
+
d=os.read(m,4096)
|
|
5209
|
+
if d:os.write(o,d)
|
|
5210
|
+
except OSError:done[0]=True
|
|
5211
|
+
try:os.kill(pid,signal.SIGTERM)
|
|
5212
|
+
except Exception:pass
|
|
5213
|
+
try:
|
|
5214
|
+
_,st=os.waitpid(pid,0)
|
|
5215
|
+
sys.exit((st>>8)&0xFF)
|
|
5216
|
+
except Exception:sys.exit(0)
|
|
5217
|
+
`;
|
|
5218
|
+
function parseUsageOutput(raw) {
|
|
5219
|
+
const clean = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, " ");
|
|
5220
|
+
const weekMatch = clean.match(/(\d+)%\s*used/i) || clean.match(/(\d+)\s*%/);
|
|
5221
|
+
if (!weekMatch) return null;
|
|
5222
|
+
const percent = parseInt(weekMatch[1], 10);
|
|
5223
|
+
const resetMatch = clean.match(/resets\s+(.+?)(?:\s*[\(\)]|$)/im);
|
|
5224
|
+
const resetAt = resetMatch?.[1]?.trim() || void 0;
|
|
5225
|
+
return { percent, resetAt };
|
|
5226
|
+
}
|
|
5227
|
+
async function fetchClaudeQuota() {
|
|
5228
|
+
return new Promise((resolve2) => {
|
|
5229
|
+
const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
|
|
5230
|
+
if (!claudeCmd) {
|
|
5231
|
+
resolve2(null);
|
|
5232
|
+
return;
|
|
5233
|
+
}
|
|
5234
|
+
const helperPath = path8.join(os6.tmpdir(), "codeam-quota-helper.py");
|
|
5235
|
+
fs5.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
|
|
5236
|
+
const python = findInPath("python3") ?? findInPath("python");
|
|
5237
|
+
if (!python) {
|
|
5238
|
+
resolve2(null);
|
|
5239
|
+
return;
|
|
5240
|
+
}
|
|
5241
|
+
const proc = (0, import_child_process4.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
|
|
5242
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
5243
|
+
cwd: process.cwd(),
|
|
5244
|
+
env: { ...process.env, TERM: "dumb", COLUMNS: "120", LINES: "30" }
|
|
5245
|
+
});
|
|
5246
|
+
let output = "";
|
|
5247
|
+
let resolved = false;
|
|
5248
|
+
proc.stdout?.on("data", (chunk) => {
|
|
5249
|
+
output += chunk.toString("utf8");
|
|
5250
|
+
});
|
|
5251
|
+
setTimeout(() => {
|
|
5252
|
+
proc.stdin?.write("/usage\r");
|
|
5253
|
+
setTimeout(() => {
|
|
5254
|
+
if (resolved) return;
|
|
5255
|
+
resolved = true;
|
|
5256
|
+
const result = parseUsageOutput(output);
|
|
5257
|
+
try {
|
|
5258
|
+
proc.kill();
|
|
5259
|
+
} catch {
|
|
5260
|
+
}
|
|
5261
|
+
try {
|
|
5262
|
+
fs5.unlinkSync(helperPath);
|
|
5263
|
+
} catch {
|
|
5264
|
+
}
|
|
5265
|
+
resolve2(result);
|
|
5266
|
+
}, 5e3);
|
|
5267
|
+
}, 8e3);
|
|
5268
|
+
setTimeout(() => {
|
|
5269
|
+
if (!resolved) {
|
|
5270
|
+
resolved = true;
|
|
5271
|
+
resolve2(null);
|
|
5272
|
+
}
|
|
5273
|
+
try {
|
|
5274
|
+
proc.kill();
|
|
5275
|
+
} catch {
|
|
5276
|
+
}
|
|
5277
|
+
}, 2e4);
|
|
5278
|
+
});
|
|
4719
5279
|
}
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
if (
|
|
4732
|
-
|
|
4733
|
-
|
|
5280
|
+
|
|
5281
|
+
// src/agents/claude/history.ts
|
|
5282
|
+
var fs6 = __toESM(require("fs"));
|
|
5283
|
+
var path9 = __toESM(require("path"));
|
|
5284
|
+
var os7 = __toESM(require("os"));
|
|
5285
|
+
function encodeCwd(cwd) {
|
|
5286
|
+
return cwd.replace(/[\\/:]/g, "-");
|
|
5287
|
+
}
|
|
5288
|
+
function resolveHistoryDir(cwd, projectsRoot) {
|
|
5289
|
+
const root = projectsRoot ?? path9.join(os7.homedir(), ".claude", "projects");
|
|
5290
|
+
const primary = path9.join(root, encodeCwd(cwd));
|
|
5291
|
+
if (fs6.existsSync(primary)) return primary;
|
|
5292
|
+
try {
|
|
5293
|
+
const entries = fs6.readdirSync(root, { withFileTypes: true });
|
|
5294
|
+
const wanted = encodeCwd(cwd);
|
|
5295
|
+
for (const e of entries) {
|
|
5296
|
+
if (!e.isDirectory()) continue;
|
|
5297
|
+
const candidate = e.name.replace(/-+/g, "-");
|
|
5298
|
+
if (candidate === wanted.replace(/-+/g, "-")) {
|
|
5299
|
+
return path9.join(root, e.name);
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
} catch {
|
|
5303
|
+
}
|
|
5304
|
+
return null;
|
|
5305
|
+
}
|
|
5306
|
+
function getCurrentUsage(historyDir, bootTimeMs = 0) {
|
|
5307
|
+
const GRACE_MS = 5e3;
|
|
5308
|
+
const cutoff = bootTimeMs > 0 ? bootTimeMs - GRACE_MS : 0;
|
|
5309
|
+
let entries;
|
|
5310
|
+
try {
|
|
5311
|
+
entries = fs6.readdirSync(historyDir, { withFileTypes: true });
|
|
5312
|
+
} catch {
|
|
5313
|
+
return null;
|
|
5314
|
+
}
|
|
5315
|
+
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5316
|
+
try {
|
|
5317
|
+
const stat3 = fs6.statSync(path9.join(historyDir, e.name));
|
|
5318
|
+
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
5319
|
+
} catch {
|
|
5320
|
+
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
5321
|
+
}
|
|
5322
|
+
}).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
|
|
5323
|
+
if (files.length === 0) return null;
|
|
5324
|
+
const filePath = path9.join(historyDir, files[0].name);
|
|
5325
|
+
let raw;
|
|
5326
|
+
try {
|
|
5327
|
+
raw = fs6.readFileSync(filePath, "utf8");
|
|
5328
|
+
} catch {
|
|
5329
|
+
return null;
|
|
5330
|
+
}
|
|
5331
|
+
let lastUsage = null;
|
|
5332
|
+
let lastModel = null;
|
|
5333
|
+
for (const line of raw.split("\n").filter(Boolean)) {
|
|
5334
|
+
try {
|
|
5335
|
+
const record = JSON.parse(line);
|
|
5336
|
+
if (record["type"] !== "assistant") continue;
|
|
5337
|
+
const msg = record["message"];
|
|
5338
|
+
if (msg?.["model"] === "<synthetic>") continue;
|
|
5339
|
+
const usage = msg?.["usage"];
|
|
5340
|
+
if (usage && (usage["input_tokens"] !== void 0 || usage["prompt_tokens"] !== void 0)) {
|
|
5341
|
+
lastUsage = usage;
|
|
5342
|
+
}
|
|
5343
|
+
if (msg?.["model"]) lastModel = msg["model"];
|
|
5344
|
+
} catch {
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
const total = getContextWindow(lastModel);
|
|
5348
|
+
if (!lastUsage) {
|
|
5349
|
+
if (!lastModel) return null;
|
|
5350
|
+
return { used: 0, total, percent: 0, model: lastModel };
|
|
5351
|
+
}
|
|
5352
|
+
const inputTokens = (lastUsage["input_tokens"] ?? lastUsage["prompt_tokens"] ?? 0) + (lastUsage["cache_read_input_tokens"] ?? 0) + (lastUsage["cache_creation_input_tokens"] ?? 0);
|
|
5353
|
+
const percent = Math.min(100, Math.round(inputTokens / total * 100));
|
|
5354
|
+
return {
|
|
5355
|
+
used: inputTokens,
|
|
5356
|
+
total,
|
|
5357
|
+
percent,
|
|
5358
|
+
model: lastModel ?? void 0
|
|
5359
|
+
};
|
|
5360
|
+
}
|
|
5361
|
+
function extractText(content) {
|
|
5362
|
+
if (typeof content === "string") return content;
|
|
5363
|
+
if (Array.isArray(content)) {
|
|
5364
|
+
return content.filter((b) => b["type"] === "text").map((b) => b["text"]).join("\n");
|
|
5365
|
+
}
|
|
5366
|
+
return "";
|
|
5367
|
+
}
|
|
5368
|
+
function parseHistoryFile(filePath) {
|
|
5369
|
+
const out = [];
|
|
5370
|
+
let raw;
|
|
5371
|
+
try {
|
|
5372
|
+
raw = fs6.readFileSync(filePath, "utf8");
|
|
5373
|
+
} catch {
|
|
5374
|
+
return out;
|
|
5375
|
+
}
|
|
5376
|
+
for (const line of raw.split("\n").filter(Boolean)) {
|
|
5377
|
+
let rec;
|
|
5378
|
+
try {
|
|
5379
|
+
rec = JSON.parse(line);
|
|
5380
|
+
} catch {
|
|
5381
|
+
continue;
|
|
5382
|
+
}
|
|
5383
|
+
if (typeof rec !== "object" || rec === null) continue;
|
|
5384
|
+
const r = rec;
|
|
5385
|
+
if (r["isMeta"]) continue;
|
|
5386
|
+
const type = r["type"];
|
|
5387
|
+
if (type !== "user" && type !== "assistant") continue;
|
|
5388
|
+
const msg = r["message"];
|
|
5389
|
+
if (!msg) continue;
|
|
5390
|
+
const text = extractText(msg["content"]).trim();
|
|
5391
|
+
if (!text) continue;
|
|
5392
|
+
const ts = r["timestamp"];
|
|
5393
|
+
const timestamp = typeof ts === "string" ? ts : typeof ts === "number" ? new Date(ts).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
5394
|
+
const uuid = typeof r["uuid"] === "string" ? r["uuid"] : `${Date.now()}-${Math.random()}`;
|
|
5395
|
+
const usage = msg["usage"];
|
|
5396
|
+
out.push({
|
|
5397
|
+
id: uuid,
|
|
5398
|
+
role: type === "user" ? "user" : "agent",
|
|
5399
|
+
text,
|
|
5400
|
+
timestamp,
|
|
5401
|
+
modelId: typeof msg["model"] === "string" ? msg["model"] : void 0,
|
|
5402
|
+
usage: usage ? {
|
|
5403
|
+
input: usage["input_tokens"] ?? 0,
|
|
5404
|
+
output: usage["output_tokens"] ?? 0,
|
|
5405
|
+
cacheRead: usage["cache_read_input_tokens"],
|
|
5406
|
+
cacheCreation: usage["cache_creation_input_tokens"]
|
|
5407
|
+
} : void 0
|
|
5408
|
+
});
|
|
4734
5409
|
}
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
5410
|
+
return out;
|
|
5411
|
+
}
|
|
5412
|
+
|
|
5413
|
+
// src/agents/claude/runtime.ts
|
|
5414
|
+
var ClaudeRuntimeStrategy = class {
|
|
5415
|
+
id = "claude";
|
|
5416
|
+
meta = getAgent("claude");
|
|
5417
|
+
async prepareLaunch() {
|
|
5418
|
+
const launch = buildClaudeLaunch();
|
|
5419
|
+
if (!launch) throw new Error("claude binary not found in PATH");
|
|
5420
|
+
return { cmd: launch.cmd, args: launch.args };
|
|
4739
5421
|
}
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
text = t2.slice(2).trim().replace(/….*/s, "").trim();
|
|
4743
|
-
} else if (BULLET_TOOL_RE.test(t2)) {
|
|
4744
|
-
text = t2.slice(2).trim();
|
|
4745
|
-
text = text.replace(/\s*\(ctrl\+?o[^)]*\)/gi, "").replace(/,\s*reading\s+\d+\s+files?\s*…?/gi, "").replace(/,\s*\d+\s+files?\s*…?/gi, "").replace(/…$/, "").trim();
|
|
5422
|
+
resumeLaunchArgs(sessionId) {
|
|
5423
|
+
return ["--resume", sessionId, "--dangerously-skip-permissions"];
|
|
4746
5424
|
}
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
}
|
|
4750
|
-
function classifyStep(text) {
|
|
4751
|
-
if (/^Read(?:ing)?\s+/i.test(text)) {
|
|
4752
|
-
const label2 = text.replace(/^Read(?:ing)?\s+/i, "").replace(/\.\.\.$/, "").trim();
|
|
4753
|
-
return { tool: "read", label: label2, status: "running" };
|
|
5425
|
+
resolveHistoryDir(cwd) {
|
|
5426
|
+
return resolveHistoryDir(cwd);
|
|
4754
5427
|
}
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
return { tool: "edit", label: label2, status: "running" };
|
|
5428
|
+
parseHistoryFile(filePath) {
|
|
5429
|
+
return parseHistoryFile(filePath);
|
|
4758
5430
|
}
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
return { tool: "bash", label: label2, status: "running" };
|
|
5431
|
+
getCurrentUsage(historyDir) {
|
|
5432
|
+
return getCurrentUsage(historyDir);
|
|
4762
5433
|
}
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
return { tool: "search", label: label2, status: "running" };
|
|
5434
|
+
async fetchWeeklyUsage() {
|
|
5435
|
+
return fetchClaudeQuota();
|
|
4766
5436
|
}
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
}
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
let row = 0;
|
|
4775
|
-
let col = 0;
|
|
4776
|
-
function ensureRow() {
|
|
4777
|
-
while (screen.length <= row) screen.push("");
|
|
5437
|
+
async listModels() {
|
|
5438
|
+
return [
|
|
5439
|
+
{ id: "claude-opus-4-7", label: "Claude Opus 4.7", contextWindow: 2e5 },
|
|
5440
|
+
{ id: "claude-opus-4-6", label: "Claude Opus 4.6", contextWindow: 2e5 },
|
|
5441
|
+
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", contextWindow: 2e5 },
|
|
5442
|
+
{ id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", contextWindow: 2e5 }
|
|
5443
|
+
];
|
|
4778
5444
|
}
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
5445
|
+
changeModelInstruction(modelId) {
|
|
5446
|
+
return { type: "pty", ptyInput: `/model ${modelId}\r` };
|
|
5447
|
+
}
|
|
5448
|
+
summarizeInstruction(mode) {
|
|
5449
|
+
if (mode === "normal") return { ptyInput: "/compact\r" };
|
|
5450
|
+
return { ptyInput: "/compact\r" };
|
|
5451
|
+
}
|
|
5452
|
+
};
|
|
5453
|
+
|
|
5454
|
+
// src/agents/claude/deploy.ts
|
|
5455
|
+
var fs8 = __toESM(require("fs"));
|
|
5456
|
+
var os9 = __toESM(require("os"));
|
|
5457
|
+
var path11 = __toESM(require("path"));
|
|
5458
|
+
|
|
5459
|
+
// src/agents/claude/credentials.ts
|
|
5460
|
+
var import_child_process5 = require("child_process");
|
|
5461
|
+
var fs7 = __toESM(require("fs"));
|
|
5462
|
+
var os8 = __toESM(require("os"));
|
|
5463
|
+
var path10 = __toESM(require("path"));
|
|
5464
|
+
var import_util = require("util");
|
|
5465
|
+
var execFileP = (0, import_util.promisify)(import_child_process5.execFile);
|
|
5466
|
+
async function detectLocalClaudeCredentials() {
|
|
5467
|
+
const localClaudeDir = path10.join(os8.homedir(), ".claude");
|
|
5468
|
+
const flat = path10.join(localClaudeDir, ".credentials.json");
|
|
5469
|
+
if (fs7.existsSync(flat)) {
|
|
5470
|
+
return { source: "flat-file", description: "~/.claude/.credentials.json" };
|
|
5471
|
+
}
|
|
5472
|
+
if (os8.platform() === "darwin") {
|
|
5473
|
+
try {
|
|
5474
|
+
await execFileP(
|
|
5475
|
+
"security",
|
|
5476
|
+
["find-generic-password", "-s", "Claude Code-credentials"],
|
|
5477
|
+
{ maxBuffer: 1024 * 1024 }
|
|
5478
|
+
);
|
|
5479
|
+
return { source: "macos-keychain", description: "macOS Keychain" };
|
|
5480
|
+
} catch {
|
|
5481
|
+
return { source: "none", description: "" };
|
|
4786
5482
|
}
|
|
4787
|
-
col++;
|
|
4788
5483
|
}
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
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);
|
|
5484
|
+
return { source: "none", description: "" };
|
|
5485
|
+
}
|
|
5486
|
+
async function bridgeClaudeCredentials(provider, workspaceId) {
|
|
5487
|
+
const localClaudeDir = path10.join(os8.homedir(), ".claude");
|
|
5488
|
+
const fileBased = path10.join(localClaudeDir, ".credentials.json");
|
|
5489
|
+
if (fs7.existsSync(fileBased)) {
|
|
5490
|
+
return { source: "flat-file", description: "~/.claude/.credentials.json" };
|
|
5491
|
+
}
|
|
5492
|
+
if (process.platform === "darwin") {
|
|
5493
|
+
try {
|
|
5494
|
+
const { stdout } = await execFileP(
|
|
5495
|
+
"security",
|
|
5496
|
+
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
|
5497
|
+
{ maxBuffer: 1024 * 1024 }
|
|
5498
|
+
);
|
|
5499
|
+
const json = stdout.trim();
|
|
5500
|
+
if (json.length === 0) return { source: "none", description: "" };
|
|
5501
|
+
await provider.uploadFile(
|
|
5502
|
+
workspaceId,
|
|
5503
|
+
"/home/codespace/.claude/.credentials.json",
|
|
5504
|
+
json,
|
|
5505
|
+
{ mode: 384 }
|
|
5506
|
+
);
|
|
5507
|
+
return { source: "macos-keychain", description: "macOS Keychain" };
|
|
5508
|
+
} catch {
|
|
5509
|
+
return { source: "none", description: "" };
|
|
4872
5510
|
}
|
|
4873
|
-
i++;
|
|
4874
5511
|
}
|
|
4875
|
-
return
|
|
5512
|
+
return { source: "none", description: "" };
|
|
4876
5513
|
}
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
5514
|
+
async function runRemoteClaudeLogin(provider, workspaceId) {
|
|
5515
|
+
wt(
|
|
5516
|
+
[
|
|
5517
|
+
"A login URL will print below. Open it in your local browser, sign in,",
|
|
5518
|
+
"and paste any code Claude asks for back into this terminal."
|
|
5519
|
+
].join("\n"),
|
|
5520
|
+
"Authenticating Claude on workspace"
|
|
4883
5521
|
);
|
|
4884
|
-
const
|
|
4885
|
-
|
|
4886
|
-
|
|
5522
|
+
const result = await provider.streamCommand(
|
|
5523
|
+
workspaceId,
|
|
5524
|
+
'bash -lc "claude login || claude /login || true"'
|
|
4887
5525
|
);
|
|
4888
|
-
if (
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
break;
|
|
4894
|
-
}
|
|
5526
|
+
if (result.code !== 0) {
|
|
5527
|
+
wt(
|
|
5528
|
+
"claude login exited non-zero. You can re-run it manually inside the codespace later.",
|
|
5529
|
+
"Heads up"
|
|
5530
|
+
);
|
|
4895
5531
|
}
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
5532
|
+
}
|
|
5533
|
+
async function verifyClaudeAuth(provider, workspaceId) {
|
|
5534
|
+
const result = await provider.exec(
|
|
5535
|
+
workspaceId,
|
|
5536
|
+
'bash -lc "claude auth status 2>/dev/null || true"'
|
|
5537
|
+
);
|
|
5538
|
+
if (result.code !== 0) return false;
|
|
5539
|
+
const jsonStart = result.stdout.indexOf("{");
|
|
5540
|
+
if (jsonStart < 0) return false;
|
|
5541
|
+
try {
|
|
5542
|
+
const parsed = JSON.parse(result.stdout.slice(jsonStart));
|
|
5543
|
+
return parsed.loggedIn === true;
|
|
5544
|
+
} catch {
|
|
5545
|
+
return false;
|
|
4906
5546
|
}
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
5547
|
+
}
|
|
5548
|
+
|
|
5549
|
+
// src/agents/claude/deploy.ts
|
|
5550
|
+
var ClaudeDeployStrategy = class {
|
|
5551
|
+
id = "claude";
|
|
5552
|
+
async detectLocalCredentials() {
|
|
5553
|
+
return detectLocalClaudeCredentials();
|
|
5554
|
+
}
|
|
5555
|
+
async bridgeLocalCredentials(provider, workspaceId) {
|
|
5556
|
+
return bridgeClaudeCredentials(provider, workspaceId);
|
|
5557
|
+
}
|
|
5558
|
+
async runRemoteLogin(provider, workspaceId) {
|
|
5559
|
+
return runRemoteClaudeLogin(provider, workspaceId);
|
|
5560
|
+
}
|
|
5561
|
+
/**
|
|
5562
|
+
* Steps 5-9 of the deploy flow:
|
|
5563
|
+
* - Install Claude CLI on the workspace
|
|
5564
|
+
* - Copy local ~/.claude config dir (excluding heavy state)
|
|
5565
|
+
* - Ship ~/.claude.json (only when creds were bridged — privacy)
|
|
5566
|
+
* - Verify auth, fallback to runRemoteLogin
|
|
5567
|
+
*/
|
|
5568
|
+
async setupOnWorkspace(provider, workspaceId, opts) {
|
|
5569
|
+
const claudeStep = fe();
|
|
5570
|
+
claudeStep.start("Installing Claude CLI on workspace\u2026");
|
|
5571
|
+
const installResult = await provider.exec(
|
|
5572
|
+
workspaceId,
|
|
5573
|
+
"curl -fsSL https://claude.ai/install.sh | bash"
|
|
5574
|
+
);
|
|
5575
|
+
if (installResult.code !== 0) {
|
|
5576
|
+
claudeStep.stop("\u2717 Claude CLI install failed");
|
|
5577
|
+
pt(installResult.stderr.slice(0, 1e3));
|
|
5578
|
+
process.exit(1);
|
|
5579
|
+
}
|
|
5580
|
+
claudeStep.stop("\u2713 Claude CLI installed");
|
|
5581
|
+
const localClaudeDir = path11.join(os9.homedir(), ".claude");
|
|
5582
|
+
const haveLocalClaude = fs8.existsSync(localClaudeDir) && fs8.statSync(localClaudeDir).isDirectory();
|
|
5583
|
+
if (haveLocalClaude) {
|
|
5584
|
+
const copyStep = fe();
|
|
5585
|
+
copyStep.start("Copying local Claude config to workspace\u2026");
|
|
5586
|
+
try {
|
|
5587
|
+
await provider.uploadDirectory(
|
|
5588
|
+
workspaceId,
|
|
5589
|
+
localClaudeDir,
|
|
5590
|
+
"/home/codespace/.claude",
|
|
5591
|
+
{
|
|
5592
|
+
exclude: [
|
|
5593
|
+
"./projects",
|
|
5594
|
+
// per-project conversation history (often 700MB+)
|
|
5595
|
+
"./file-history",
|
|
5596
|
+
// per-project file diffs
|
|
5597
|
+
"./downloads",
|
|
5598
|
+
// downloaded artifacts
|
|
5599
|
+
"./image-cache",
|
|
5600
|
+
// cached images
|
|
5601
|
+
"./paste-cache",
|
|
5602
|
+
// clipboard/paste cache
|
|
5603
|
+
"./backups",
|
|
5604
|
+
// local backups
|
|
5605
|
+
"./shell-snapshots",
|
|
5606
|
+
// shell history snapshots
|
|
5607
|
+
"./telemetry",
|
|
5608
|
+
// analytics dumps
|
|
5609
|
+
"./statsig",
|
|
5610
|
+
// feature-flag cache
|
|
5611
|
+
"./cache",
|
|
5612
|
+
// generic cache dir
|
|
5613
|
+
"./history.jsonl",
|
|
5614
|
+
// global REPL history
|
|
5615
|
+
"./ide",
|
|
5616
|
+
// local IDE bridge state
|
|
5617
|
+
"./todos",
|
|
5618
|
+
// local todo state
|
|
5619
|
+
"./tasks",
|
|
5620
|
+
// local task state
|
|
5621
|
+
// Don't overwrite the credentials we already staged in
|
|
5622
|
+
// step 4 — the local dir on macOS doesn't have a flat
|
|
5623
|
+
// credentials file anyway, but on Linux it would, and a
|
|
5624
|
+
// re-write here would be redundant.
|
|
5625
|
+
"./.credentials.json"
|
|
5626
|
+
]
|
|
5627
|
+
}
|
|
5628
|
+
);
|
|
5629
|
+
copyStep.stop("\u2713 Claude config uploaded");
|
|
5630
|
+
} catch (err) {
|
|
5631
|
+
copyStep.stop("\u26A0 Could not upload Claude config (continuing)");
|
|
5632
|
+
void err;
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
if (opts.bridged !== "none") {
|
|
5636
|
+
const localClaudeJson = path11.join(os9.homedir(), ".claude.json");
|
|
5637
|
+
if (fs8.existsSync(localClaudeJson)) {
|
|
5638
|
+
try {
|
|
5639
|
+
const contents = fs8.readFileSync(localClaudeJson);
|
|
5640
|
+
await provider.uploadFile(
|
|
5641
|
+
workspaceId,
|
|
5642
|
+
"/home/codespace/.claude.json",
|
|
5643
|
+
contents,
|
|
5644
|
+
{ mode: 384 }
|
|
5645
|
+
);
|
|
5646
|
+
} catch (err) {
|
|
5647
|
+
void err;
|
|
5648
|
+
}
|
|
4920
5649
|
}
|
|
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
5650
|
}
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
const isUnselected = (line) => /^ \S/.test(line);
|
|
4941
|
-
const isItem = (line) => isSelected(line) || isUnselected(line);
|
|
4942
|
-
let optionStartIdx = -1;
|
|
4943
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4944
|
-
if (isItem(lines[i])) {
|
|
4945
|
-
optionStartIdx = i;
|
|
4946
|
-
break;
|
|
5651
|
+
const verifyStep = fe();
|
|
5652
|
+
verifyStep.start("Verifying Claude auth on workspace\u2026");
|
|
5653
|
+
const verified = await verifyClaudeAuth(provider, workspaceId);
|
|
5654
|
+
if (verified) {
|
|
5655
|
+
verifyStep.stop("\u2713 Claude is logged in \u2014 no re-auth needed");
|
|
5656
|
+
} else {
|
|
5657
|
+
verifyStep.stop("\xB7 Claude not yet authenticated \u2014 running login flow");
|
|
5658
|
+
await runRemoteClaudeLogin(provider, workspaceId);
|
|
5659
|
+
const reverified = await verifyClaudeAuth(provider, workspaceId);
|
|
5660
|
+
if (!reverified) {
|
|
5661
|
+
wt(
|
|
5662
|
+
"Claude auth could not be confirmed. You may need to run `claude /login` manually inside the codespace.",
|
|
5663
|
+
"Heads up"
|
|
5664
|
+
);
|
|
5665
|
+
}
|
|
4947
5666
|
}
|
|
4948
5667
|
}
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
5668
|
+
};
|
|
5669
|
+
|
|
5670
|
+
// src/agents/codex/runtime.ts
|
|
5671
|
+
var import_node_child_process = require("child_process");
|
|
5672
|
+
|
|
5673
|
+
// src/agents/codex/history.ts
|
|
5674
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
5675
|
+
var import_node_path2 = __toESM(require("path"));
|
|
5676
|
+
var import_node_os = __toESM(require("os"));
|
|
5677
|
+
function resolveHistoryDir2(_cwd, homeOverride) {
|
|
5678
|
+
const home = homeOverride ?? import_node_os.default.homedir();
|
|
5679
|
+
const codexDir = import_node_path2.default.join(home, ".codex");
|
|
5680
|
+
if (!import_node_fs2.default.existsSync(import_node_path2.default.join(codexDir, "history.jsonl"))) return null;
|
|
5681
|
+
return codexDir;
|
|
5682
|
+
}
|
|
5683
|
+
function isCodexRecord(v) {
|
|
5684
|
+
if (!v || typeof v !== "object") return false;
|
|
5685
|
+
const r = v;
|
|
5686
|
+
return typeof r.session_id === "string" && typeof r.ts === "number" && typeof r.text === "string";
|
|
5687
|
+
}
|
|
5688
|
+
function parseHistoryFile2(filePath) {
|
|
5689
|
+
const raw = import_node_fs2.default.readFileSync(filePath, "utf8");
|
|
5690
|
+
const out = [];
|
|
5691
|
+
let idx = 0;
|
|
5692
|
+
for (const line of raw.split("\n").filter(Boolean)) {
|
|
5693
|
+
let rec;
|
|
5694
|
+
try {
|
|
5695
|
+
rec = JSON.parse(line);
|
|
5696
|
+
} catch {
|
|
4958
5697
|
continue;
|
|
4959
5698
|
}
|
|
4960
|
-
if (
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
5699
|
+
if (!isCodexRecord(rec)) continue;
|
|
5700
|
+
out.push({
|
|
5701
|
+
id: `${rec.session_id}:${idx}`,
|
|
5702
|
+
role: "user",
|
|
5703
|
+
text: rec.text,
|
|
5704
|
+
timestamp: new Date(rec.ts * 1e3).toISOString()
|
|
5705
|
+
});
|
|
5706
|
+
idx++;
|
|
4964
5707
|
}
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
5708
|
+
return out;
|
|
5709
|
+
}
|
|
5710
|
+
function getCurrentUsage2(_historyDir) {
|
|
5711
|
+
return null;
|
|
5712
|
+
}
|
|
5713
|
+
|
|
5714
|
+
// src/agents/codex/runtime.ts
|
|
5715
|
+
var CODEX_CONTEXT_WINDOW = 272e3;
|
|
5716
|
+
var CODEX_MODELS = [
|
|
5717
|
+
{ id: "gpt-5.5", label: "GPT-5.5", contextWindow: CODEX_CONTEXT_WINDOW },
|
|
5718
|
+
{ id: "gpt-5.4", label: "GPT-5.4", contextWindow: CODEX_CONTEXT_WINDOW },
|
|
5719
|
+
{ id: "gpt-5.4-mini", label: "GPT-5.4 Mini", contextWindow: CODEX_CONTEXT_WINDOW },
|
|
5720
|
+
{ id: "gpt-5.3-codex", label: "GPT-5.3 Codex", contextWindow: CODEX_CONTEXT_WINDOW },
|
|
5721
|
+
{ id: "gpt-5.2", label: "GPT-5.2", contextWindow: CODEX_CONTEXT_WINDOW },
|
|
5722
|
+
{ id: "codex-auto-review", label: "Codex Auto Review", contextWindow: CODEX_CONTEXT_WINDOW }
|
|
5723
|
+
];
|
|
5724
|
+
var CodexRuntimeStrategy = class {
|
|
5725
|
+
id = "codex";
|
|
5726
|
+
meta = getAgent("codex");
|
|
5727
|
+
async prepareLaunch() {
|
|
5728
|
+
const binary = findInPath("codex");
|
|
5729
|
+
if (!binary) {
|
|
5730
|
+
await installCodexViaNpm();
|
|
5731
|
+
const afterInstall = findInPath("codex");
|
|
5732
|
+
if (!afterInstall) {
|
|
5733
|
+
throw new Error(
|
|
5734
|
+
"Could not find 'codex' on PATH after running 'npm install -g @openai/codex'. Install it manually and retry."
|
|
5735
|
+
);
|
|
5736
|
+
}
|
|
5737
|
+
return { cmd: afterInstall, args: [] };
|
|
4978
5738
|
}
|
|
5739
|
+
return { cmd: binary, args: [] };
|
|
4979
5740
|
}
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
}
|
|
5741
|
+
/** `codex resume <SESSION_ID>` — subcommand, not flag. */
|
|
5742
|
+
resumeLaunchArgs(sessionId) {
|
|
5743
|
+
return ["resume", sessionId];
|
|
5744
|
+
}
|
|
5745
|
+
resolveHistoryDir(cwd) {
|
|
5746
|
+
return resolveHistoryDir2(cwd);
|
|
5747
|
+
}
|
|
5748
|
+
parseHistoryFile(filePath) {
|
|
5749
|
+
return parseHistoryFile2(filePath);
|
|
5750
|
+
}
|
|
5751
|
+
getCurrentUsage(historyDir) {
|
|
5752
|
+
return getCurrentUsage2(historyDir);
|
|
5753
|
+
}
|
|
5754
|
+
/**
|
|
5755
|
+
* Codex's quota lives behind the `account/get_account_rate_limits` RPC,
|
|
5756
|
+
* not a TUI slash command. Phase 2 ships with this stubbed to null so the
|
|
5757
|
+
* mobile shows "—" for weekly usage on Codex sessions. A follow-up will
|
|
5758
|
+
* invoke the RPC directly.
|
|
5759
|
+
*/
|
|
5760
|
+
async fetchWeeklyUsage() {
|
|
5761
|
+
return null;
|
|
5762
|
+
}
|
|
5763
|
+
async listModels() {
|
|
5764
|
+
return CODEX_MODELS;
|
|
5765
|
+
}
|
|
5766
|
+
changeModelInstruction(modelId) {
|
|
5767
|
+
return { type: "pty", ptyInput: `/model ${modelId}\r` };
|
|
5768
|
+
}
|
|
5769
|
+
/**
|
|
5770
|
+
* Codex has no auto-compact (`auto_compact_token_limit: null` for every
|
|
5771
|
+
* model). Both modes fall through to the manual `/compact` slash command.
|
|
5772
|
+
*/
|
|
5773
|
+
summarizeInstruction(_mode) {
|
|
5774
|
+
return { ptyInput: "/compact\r" };
|
|
5775
|
+
}
|
|
5776
|
+
};
|
|
5777
|
+
async function installCodexViaNpm() {
|
|
5778
|
+
return new Promise((resolve2, reject) => {
|
|
5779
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
5780
|
+
const proc = (0, import_node_child_process.spawn)(npm, ["install", "-g", "@openai/codex"], {
|
|
5781
|
+
stdio: "inherit"
|
|
5782
|
+
});
|
|
5783
|
+
proc.on("close", (code) => {
|
|
5784
|
+
if (code === 0) resolve2();
|
|
5785
|
+
else reject(new Error(`npm install -g @openai/codex exited ${code}`));
|
|
5786
|
+
});
|
|
5787
|
+
proc.on("error", reject);
|
|
5788
|
+
});
|
|
4987
5789
|
}
|
|
4988
5790
|
|
|
4989
|
-
//
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5791
|
+
// src/agents/codex/credentials.ts
|
|
5792
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
5793
|
+
var import_node_path3 = __toESM(require("path"));
|
|
5794
|
+
var import_node_os2 = __toESM(require("os"));
|
|
5795
|
+
async function detectLocalCodexCredentials() {
|
|
5796
|
+
const home = process.env.HOME ?? import_node_os2.default.homedir();
|
|
5797
|
+
const authJson = import_node_path3.default.join(home, ".codex", "auth.json");
|
|
5798
|
+
if (import_node_fs3.default.existsSync(authJson)) {
|
|
5799
|
+
return { source: "flat-file", description: "~/.codex/auth.json" };
|
|
5800
|
+
}
|
|
5801
|
+
if (process.env.OPENAI_API_KEY) {
|
|
5802
|
+
return { source: "env-var", description: "OPENAI_API_KEY env var" };
|
|
5803
|
+
}
|
|
5804
|
+
return { source: "none", description: "" };
|
|
5805
|
+
}
|
|
5806
|
+
async function bridgeLocalCodexCredentials(provider, workspaceId) {
|
|
5807
|
+
const home = process.env.HOME ?? import_node_os2.default.homedir();
|
|
5808
|
+
const authJson = import_node_path3.default.join(home, ".codex", "auth.json");
|
|
5809
|
+
if (import_node_fs3.default.existsSync(authJson)) {
|
|
5810
|
+
const contents = import_node_fs3.default.readFileSync(authJson);
|
|
5811
|
+
await provider.exec(workspaceId, "mkdir -p ~/.codex && chmod 700 ~/.codex");
|
|
5812
|
+
await provider.uploadFile(workspaceId, "/home/codespace/.codex/auth.json", contents, { mode: 384 });
|
|
5813
|
+
return { source: "flat-file", description: "~/.codex/auth.json" };
|
|
5814
|
+
}
|
|
5815
|
+
const key = process.env.OPENAI_API_KEY;
|
|
5816
|
+
if (key) {
|
|
5817
|
+
const escaped = key.replace(/'/g, "'\\''");
|
|
5818
|
+
const line = `export OPENAI_API_KEY='${escaped}'`;
|
|
5819
|
+
await provider.exec(workspaceId, `grep -qxF "${line}" ~/.bashrc || echo "${line}" >> ~/.bashrc`);
|
|
5820
|
+
return { source: "env-var", description: "OPENAI_API_KEY env var" };
|
|
5821
|
+
}
|
|
5822
|
+
return { source: "none", description: "" };
|
|
5823
|
+
}
|
|
5824
|
+
async function runRemoteCodexLogin(provider, workspaceId) {
|
|
5825
|
+
await provider.streamCommand(workspaceId, 'bash -lc "codex login"');
|
|
5826
|
+
}
|
|
5827
|
+
async function verifyCodexAuth(provider, workspaceId) {
|
|
5828
|
+
const r = await provider.exec(workspaceId, 'bash -lc "codex --version"');
|
|
5829
|
+
return r.code === 0;
|
|
5830
|
+
}
|
|
5831
|
+
|
|
5832
|
+
// src/agents/codex/deploy.ts
|
|
5833
|
+
var CodexDeployStrategy = class {
|
|
5834
|
+
id = "codex";
|
|
5835
|
+
async detectLocalCredentials() {
|
|
5836
|
+
return detectLocalCodexCredentials();
|
|
5837
|
+
}
|
|
5838
|
+
async bridgeLocalCredentials(provider, workspaceId) {
|
|
5839
|
+
return bridgeLocalCodexCredentials(provider, workspaceId);
|
|
5840
|
+
}
|
|
5841
|
+
async runRemoteLogin(provider, workspaceId) {
|
|
5842
|
+
return runRemoteCodexLogin(provider, workspaceId);
|
|
5843
|
+
}
|
|
5844
|
+
/**
|
|
5845
|
+
* Steps 5–7 of the existing local-deploy flow, adapted for Codex:
|
|
5846
|
+
* 1. Install codex via npm
|
|
5847
|
+
* 2. (skipped — bridge already happened before this method runs, or the
|
|
5848
|
+
* fallback runs via runRemoteLogin)
|
|
5849
|
+
* 3. Verify auth; fallback to remote login if verify fails AND no
|
|
5850
|
+
* credentials were bridged.
|
|
5851
|
+
*/
|
|
5852
|
+
async setupOnWorkspace(provider, workspaceId, opts) {
|
|
5853
|
+
const installResult = await provider.exec(workspaceId, "npm install -g @openai/codex");
|
|
5854
|
+
if (installResult.code !== 0) {
|
|
5855
|
+
throw new Error(`codex install failed: ${installResult.stderr.slice(0, 500)}`);
|
|
5002
5856
|
}
|
|
5003
|
-
|
|
5004
|
-
if (
|
|
5005
|
-
|
|
5006
|
-
if (/high\s*[·•]\s*\/effort/i.test(t2)) continue;
|
|
5007
|
-
if (/^[❯>]\s*$/.test(t2)) continue;
|
|
5008
|
-
if (/^\(thinking\)\s*$/.test(t2)) continue;
|
|
5009
|
-
if (/^\?\s.*shortcut/i.test(t2)) continue;
|
|
5010
|
-
if (/spending limit|usage limit/i.test(t2) && t2.length < 80) continue;
|
|
5011
|
-
if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) continue;
|
|
5012
|
-
if (t2.replace(/\s/g, "").length === 1) continue;
|
|
5013
|
-
if ((t2.match(/─/g)?.length ?? 0) >= 6) continue;
|
|
5014
|
-
if (/ctrl\+?o\s+to\s+expand/i.test(t2)) continue;
|
|
5015
|
-
if (/^•\s+(?:Read(?:ing)?|Edit(?:ing)?|Writ(?:e|ing)|Bash|Runn(?:ing)?|Search(?:ing)?|Glob(?:bing)?|Grep(?:ping)?|Creat(?:e|ing)|Execut(?:e|ing)|Task|Agent|NotebookEdit)\b/i.test(
|
|
5016
|
-
t2
|
|
5017
|
-
))
|
|
5018
|
-
continue;
|
|
5019
|
-
if (/^└\s/.test(t2)) continue;
|
|
5020
|
-
if (/^\+\s/.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) continue;
|
|
5021
|
-
if (/^↓\s*\d+\s*tokens/i.test(t2)) continue;
|
|
5022
|
-
if (/^\bthought\s+for\s+\d+/i.test(t2)) continue;
|
|
5023
|
-
const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
|
|
5024
|
-
if (/^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) {
|
|
5025
|
-
skipEchoContinuation = true;
|
|
5026
|
-
continue;
|
|
5857
|
+
const verified = await verifyCodexAuth(provider, workspaceId);
|
|
5858
|
+
if (!verified && opts.bridged === "none") {
|
|
5859
|
+
await runRemoteCodexLogin(provider, workspaceId);
|
|
5027
5860
|
}
|
|
5028
|
-
if (skipEchoContinuation) continue;
|
|
5029
|
-
result.push(line);
|
|
5030
5861
|
}
|
|
5031
|
-
|
|
5032
|
-
}
|
|
5862
|
+
};
|
|
5033
5863
|
|
|
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 }
|
|
5864
|
+
// src/agents/registry.ts
|
|
5865
|
+
var runtimeBuilders = {
|
|
5866
|
+
claude: () => new ClaudeRuntimeStrategy(),
|
|
5867
|
+
codex: () => new CodexRuntimeStrategy()
|
|
5041
5868
|
};
|
|
5042
|
-
var
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
"claude-3-5-sonnet": 2e5,
|
|
5046
|
-
"claude-3-5-haiku": 2e5,
|
|
5047
|
-
"claude-3-haiku": 2e5
|
|
5869
|
+
var deployBuilders = {
|
|
5870
|
+
claude: () => new ClaudeDeployStrategy(),
|
|
5871
|
+
codex: () => new CodexDeployStrategy()
|
|
5048
5872
|
};
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5873
|
+
function createRuntimeStrategy(agent) {
|
|
5874
|
+
if (!AGENT_REGISTRY[agent]?.enabled) {
|
|
5875
|
+
throw new Error(
|
|
5876
|
+
`Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
|
|
5877
|
+
);
|
|
5053
5878
|
}
|
|
5054
|
-
|
|
5879
|
+
const build = runtimeBuilders[agent];
|
|
5880
|
+
if (!build) {
|
|
5881
|
+
throw new Error(`No runtime strategy registered for agent "${agent}"`);
|
|
5882
|
+
}
|
|
5883
|
+
return build();
|
|
5055
5884
|
}
|
|
5056
|
-
function
|
|
5057
|
-
if (!
|
|
5058
|
-
|
|
5059
|
-
if (model.startsWith(prefix)) return size;
|
|
5885
|
+
function createDeployStrategy(agent) {
|
|
5886
|
+
if (!AGENT_REGISTRY[agent]?.enabled) {
|
|
5887
|
+
throw new Error(`Agent "${agent}" is not supported in this codeam-cli version.`);
|
|
5060
5888
|
}
|
|
5061
|
-
|
|
5889
|
+
const build = deployBuilders[agent];
|
|
5890
|
+
if (!build) {
|
|
5891
|
+
throw new Error(`No deploy strategy registered for agent "${agent}"`);
|
|
5892
|
+
}
|
|
5893
|
+
return build();
|
|
5062
5894
|
}
|
|
5063
5895
|
|
|
5064
5896
|
// src/services/output/chrome-tracker.ts
|
|
@@ -5531,9 +6363,9 @@ var OutputService = class _OutputService {
|
|
|
5531
6363
|
};
|
|
5532
6364
|
|
|
5533
6365
|
// src/services/history.service.ts
|
|
5534
|
-
var
|
|
5535
|
-
var
|
|
5536
|
-
var
|
|
6366
|
+
var fs11 = __toESM(require("fs"));
|
|
6367
|
+
var path14 = __toESM(require("path"));
|
|
6368
|
+
var os12 = __toESM(require("os"));
|
|
5537
6369
|
var https4 = __toESM(require("https"));
|
|
5538
6370
|
var http4 = __toESM(require("http"));
|
|
5539
6371
|
var import_zod = require("zod");
|
|
@@ -5548,28 +6380,7 @@ var historyRecordSchema = import_zod.z.object({
|
|
|
5548
6380
|
}).passthrough().optional()
|
|
5549
6381
|
}).passthrough();
|
|
5550
6382
|
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) {
|
|
6383
|
+
function extractText2(content) {
|
|
5573
6384
|
if (typeof content === "string") return content;
|
|
5574
6385
|
if (Array.isArray(content)) {
|
|
5575
6386
|
return content.filter((b) => b["type"] === "text").map((b) => b["text"]).join("\n");
|
|
@@ -5581,7 +6392,7 @@ function parseJsonl(filePath) {
|
|
|
5581
6392
|
const messages = [];
|
|
5582
6393
|
let raw;
|
|
5583
6394
|
try {
|
|
5584
|
-
raw =
|
|
6395
|
+
raw = fs11.readFileSync(filePath, "utf8");
|
|
5585
6396
|
} catch (err) {
|
|
5586
6397
|
if (err.code !== "ENOENT") {
|
|
5587
6398
|
log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
|
|
@@ -5608,10 +6419,10 @@ function parseJsonl(filePath) {
|
|
|
5608
6419
|
const uuid = record.uuid ?? `${Date.now()}-${Math.random()}`;
|
|
5609
6420
|
const msg = record.message;
|
|
5610
6421
|
if (record.type === "user" && msg) {
|
|
5611
|
-
const text =
|
|
6422
|
+
const text = extractText2(msg.content).trim();
|
|
5612
6423
|
if (text) messages.push({ id: uuid, role: "user", text, timestamp });
|
|
5613
6424
|
} else if (record.type === "assistant" && msg) {
|
|
5614
|
-
const text =
|
|
6425
|
+
const text = extractText2(msg.content).trim();
|
|
5615
6426
|
if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
|
|
5616
6427
|
}
|
|
5617
6428
|
}
|
|
@@ -5655,9 +6466,10 @@ function post(endpoint, body) {
|
|
|
5655
6466
|
});
|
|
5656
6467
|
}
|
|
5657
6468
|
var HistoryService = class _HistoryService {
|
|
5658
|
-
constructor(pluginId, cwd, options) {
|
|
6469
|
+
constructor(runtime, pluginId, cwd, options) {
|
|
5659
6470
|
this.pluginId = pluginId;
|
|
5660
6471
|
this.cwd = cwd;
|
|
6472
|
+
this.runtime = runtime;
|
|
5661
6473
|
this.bootTimeMs = options?.bootTimeMs ?? Date.now();
|
|
5662
6474
|
}
|
|
5663
6475
|
pluginId;
|
|
@@ -5693,6 +6505,7 @@ var HistoryService = class _HistoryService {
|
|
|
5693
6505
|
* previous pair / previous Claude run.
|
|
5694
6506
|
*/
|
|
5695
6507
|
static BIRTHTIME_GRACE_MS = 5e3;
|
|
6508
|
+
runtime;
|
|
5696
6509
|
/** Store rate limit reset info detected from Claude Code output */
|
|
5697
6510
|
setRateLimitReset(reset) {
|
|
5698
6511
|
this._rateLimitReset = reset;
|
|
@@ -5713,7 +6526,7 @@ var HistoryService = class _HistoryService {
|
|
|
5713
6526
|
return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
|
|
5714
6527
|
}
|
|
5715
6528
|
get projectDir() {
|
|
5716
|
-
return
|
|
6529
|
+
return this.runtime.resolveHistoryDir(this.cwd) ?? path14.join(os12.homedir(), ".claude", "projects", encodeCwd(this.cwd));
|
|
5717
6530
|
}
|
|
5718
6531
|
/** Set the current Claude conversation ID (extracted from /cost command or session start) */
|
|
5719
6532
|
setCurrentConversationId(id) {
|
|
@@ -5725,7 +6538,7 @@ var HistoryService = class _HistoryService {
|
|
|
5725
6538
|
/** Return the current message count in the active conversation. */
|
|
5726
6539
|
getCurrentMessageCount() {
|
|
5727
6540
|
if (!this.currentConversationId) return 0;
|
|
5728
|
-
const filePath =
|
|
6541
|
+
const filePath = path14.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
5729
6542
|
return parseJsonl(filePath).length;
|
|
5730
6543
|
}
|
|
5731
6544
|
/**
|
|
@@ -5736,7 +6549,7 @@ var HistoryService = class _HistoryService {
|
|
|
5736
6549
|
const deadline = Date.now() + timeoutMs;
|
|
5737
6550
|
while (Date.now() < deadline) {
|
|
5738
6551
|
if (!this.currentConversationId) return null;
|
|
5739
|
-
const filePath =
|
|
6552
|
+
const filePath = path14.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
5740
6553
|
const messages = parseJsonl(filePath);
|
|
5741
6554
|
if (messages.length > previousCount) {
|
|
5742
6555
|
for (let i = messages.length - 1; i >= previousCount; i--) {
|
|
@@ -5762,16 +6575,16 @@ var HistoryService = class _HistoryService {
|
|
|
5762
6575
|
const dir = this.projectDir;
|
|
5763
6576
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
5764
6577
|
try {
|
|
5765
|
-
const files =
|
|
6578
|
+
const files = fs11.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5766
6579
|
try {
|
|
5767
|
-
const stat3 =
|
|
6580
|
+
const stat3 = fs11.statSync(path14.join(dir, e.name));
|
|
5768
6581
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
5769
6582
|
} catch {
|
|
5770
6583
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
5771
6584
|
}
|
|
5772
6585
|
}).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
|
|
5773
6586
|
if (files.length > 0) {
|
|
5774
|
-
this.currentConversationId =
|
|
6587
|
+
this.currentConversationId = path14.basename(files[0].name, ".jsonl");
|
|
5775
6588
|
}
|
|
5776
6589
|
} catch {
|
|
5777
6590
|
}
|
|
@@ -5805,13 +6618,13 @@ var HistoryService = class _HistoryService {
|
|
|
5805
6618
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
5806
6619
|
let entries;
|
|
5807
6620
|
try {
|
|
5808
|
-
entries =
|
|
6621
|
+
entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
5809
6622
|
} catch {
|
|
5810
6623
|
return null;
|
|
5811
6624
|
}
|
|
5812
6625
|
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5813
6626
|
try {
|
|
5814
|
-
const stat3 =
|
|
6627
|
+
const stat3 = fs11.statSync(path14.join(dir, e.name));
|
|
5815
6628
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
5816
6629
|
} catch {
|
|
5817
6630
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
@@ -5820,12 +6633,12 @@ var HistoryService = class _HistoryService {
|
|
|
5820
6633
|
if (files.length === 0) return null;
|
|
5821
6634
|
const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
|
|
5822
6635
|
if (!files.some((f) => f.name === targetFile)) return null;
|
|
5823
|
-
return this.extractUsageFromFile(
|
|
6636
|
+
return this.extractUsageFromFile(path14.join(dir, targetFile));
|
|
5824
6637
|
}
|
|
5825
6638
|
extractUsageFromFile(filePath) {
|
|
5826
6639
|
let raw;
|
|
5827
6640
|
try {
|
|
5828
|
-
raw =
|
|
6641
|
+
raw = fs11.readFileSync(filePath, "utf8");
|
|
5829
6642
|
} catch {
|
|
5830
6643
|
return null;
|
|
5831
6644
|
}
|
|
@@ -5870,9 +6683,9 @@ var HistoryService = class _HistoryService {
|
|
|
5870
6683
|
let totalCost = 0;
|
|
5871
6684
|
let files;
|
|
5872
6685
|
try {
|
|
5873
|
-
files =
|
|
6686
|
+
files = fs11.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
|
|
5874
6687
|
try {
|
|
5875
|
-
return
|
|
6688
|
+
return fs11.statSync(path14.join(projectDir, f)).mtimeMs >= monthStartMs;
|
|
5876
6689
|
} catch {
|
|
5877
6690
|
return false;
|
|
5878
6691
|
}
|
|
@@ -5883,7 +6696,7 @@ var HistoryService = class _HistoryService {
|
|
|
5883
6696
|
for (const file of files) {
|
|
5884
6697
|
let raw;
|
|
5885
6698
|
try {
|
|
5886
|
-
raw =
|
|
6699
|
+
raw = fs11.readFileSync(path14.join(projectDir, file), "utf8");
|
|
5887
6700
|
} catch {
|
|
5888
6701
|
continue;
|
|
5889
6702
|
}
|
|
@@ -5918,30 +6731,30 @@ var HistoryService = class _HistoryService {
|
|
|
5918
6731
|
const dir = this.projectDir;
|
|
5919
6732
|
let entries;
|
|
5920
6733
|
try {
|
|
5921
|
-
entries =
|
|
6734
|
+
entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
5922
6735
|
} catch {
|
|
5923
6736
|
return;
|
|
5924
6737
|
}
|
|
5925
6738
|
const sessions2 = [];
|
|
5926
6739
|
for (const entry of entries) {
|
|
5927
6740
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
5928
|
-
const id =
|
|
5929
|
-
const filePath =
|
|
6741
|
+
const id = path14.basename(entry.name, ".jsonl");
|
|
6742
|
+
const filePath = path14.join(dir, entry.name);
|
|
5930
6743
|
let mtime = Date.now();
|
|
5931
6744
|
try {
|
|
5932
|
-
mtime =
|
|
6745
|
+
mtime = fs11.statSync(filePath).mtimeMs;
|
|
5933
6746
|
} catch {
|
|
5934
6747
|
}
|
|
5935
6748
|
let summary = "";
|
|
5936
6749
|
try {
|
|
5937
|
-
const raw =
|
|
6750
|
+
const raw = fs11.readFileSync(filePath, "utf8");
|
|
5938
6751
|
for (const line of raw.split("\n")) {
|
|
5939
6752
|
if (!line.trim()) continue;
|
|
5940
6753
|
try {
|
|
5941
6754
|
const record = JSON.parse(line);
|
|
5942
6755
|
if (record["type"] === "user") {
|
|
5943
6756
|
const msg = record["message"];
|
|
5944
|
-
const text =
|
|
6757
|
+
const text = extractText2(msg?.["content"]).trim();
|
|
5945
6758
|
if (text) {
|
|
5946
6759
|
summary = text.slice(0, 120);
|
|
5947
6760
|
break;
|
|
@@ -5967,7 +6780,7 @@ var HistoryService = class _HistoryService {
|
|
|
5967
6780
|
* showing an empty conversation.
|
|
5968
6781
|
*/
|
|
5969
6782
|
async loadConversation(sessionId) {
|
|
5970
|
-
const filePath =
|
|
6783
|
+
const filePath = path14.join(this.projectDir, `${sessionId}.jsonl`);
|
|
5971
6784
|
const messages = parseJsonl(filePath);
|
|
5972
6785
|
if (messages.length === 0) return;
|
|
5973
6786
|
const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
|
|
@@ -6010,7 +6823,7 @@ var HistoryService = class _HistoryService {
|
|
|
6010
6823
|
if (!this.currentConversationId) return 0;
|
|
6011
6824
|
}
|
|
6012
6825
|
const sessionId = this.currentConversationId;
|
|
6013
|
-
const filePath =
|
|
6826
|
+
const filePath = path14.join(this.projectDir, `${sessionId}.jsonl`);
|
|
6014
6827
|
const messages = parseJsonl(filePath);
|
|
6015
6828
|
if (messages.length === 0) return 0;
|
|
6016
6829
|
const marker = this.lastUploadedUuid.get(sessionId);
|
|
@@ -6027,130 +6840,39 @@ var HistoryService = class _HistoryService {
|
|
|
6027
6840
|
sessionId,
|
|
6028
6841
|
messages: newMessages,
|
|
6029
6842
|
mode: "append"
|
|
6030
|
-
};
|
|
6031
|
-
const ok = await post("/api/sessions/claude-conversation", body);
|
|
6032
|
-
if (ok) {
|
|
6033
|
-
const last = newMessages[newMessages.length - 1];
|
|
6034
|
-
this.lastUploadedUuid.set(sessionId, last.id);
|
|
6035
|
-
return newMessages.length;
|
|
6036
|
-
}
|
|
6037
|
-
return 0;
|
|
6038
|
-
}
|
|
6039
|
-
};
|
|
6040
|
-
|
|
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) {
|
|
6843
|
+
};
|
|
6844
|
+
const ok = await post("/api/sessions/claude-conversation", body);
|
|
6845
|
+
if (ok) {
|
|
6846
|
+
const last = newMessages[newMessages.length - 1];
|
|
6847
|
+
this.lastUploadedUuid.set(sessionId, last.id);
|
|
6848
|
+
return newMessages.length;
|
|
6849
|
+
}
|
|
6850
|
+
return 0;
|
|
6851
|
+
}
|
|
6852
|
+
};
|
|
6853
|
+
|
|
6854
|
+
// src/commands/start/quota-fetcher.ts
|
|
6855
|
+
var inProgress = false;
|
|
6856
|
+
function fetchQuotaUsage(runtime, historySvc) {
|
|
6093
6857
|
if (inProgress) return;
|
|
6094
6858
|
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", () => {
|
|
6859
|
+
runtime.fetchWeeklyUsage().then((result) => {
|
|
6860
|
+
if (!result) return;
|
|
6861
|
+
historySvc.setQuotaPercent(result.percent);
|
|
6862
|
+
if (result.resetAt) historySvc.setRateLimitReset(result.resetAt);
|
|
6863
|
+
}).finally(() => {
|
|
6136
6864
|
inProgress = false;
|
|
6137
6865
|
});
|
|
6138
|
-
setTimeout(() => {
|
|
6139
|
-
try {
|
|
6140
|
-
proc.kill();
|
|
6141
|
-
} catch {
|
|
6142
|
-
}
|
|
6143
|
-
}, 2e4);
|
|
6144
6866
|
}
|
|
6145
6867
|
|
|
6146
6868
|
// src/commands/start/keep-alive.ts
|
|
6147
|
-
var
|
|
6869
|
+
var import_child_process6 = require("child_process");
|
|
6148
6870
|
function buildKeepAlive(ctx) {
|
|
6149
6871
|
let timer = null;
|
|
6150
6872
|
async function setIdleTimeout(minutes) {
|
|
6151
6873
|
if (!ctx.inCodespace || !ctx.codespaceName) return;
|
|
6152
6874
|
await new Promise((resolve2) => {
|
|
6153
|
-
const proc = (0,
|
|
6875
|
+
const proc = (0, import_child_process6.spawn)(
|
|
6154
6876
|
"gh",
|
|
6155
6877
|
[
|
|
6156
6878
|
"api",
|
|
@@ -6187,11 +6909,11 @@ function buildKeepAlive(ctx) {
|
|
|
6187
6909
|
}
|
|
6188
6910
|
|
|
6189
6911
|
// src/commands/start/handlers.ts
|
|
6190
|
-
var
|
|
6191
|
-
var
|
|
6192
|
-
var
|
|
6912
|
+
var fs14 = __toESM(require("fs"));
|
|
6913
|
+
var os13 = __toESM(require("os"));
|
|
6914
|
+
var path17 = __toESM(require("path"));
|
|
6193
6915
|
var import_crypto = require("crypto");
|
|
6194
|
-
var
|
|
6916
|
+
var import_child_process8 = require("child_process");
|
|
6195
6917
|
|
|
6196
6918
|
// src/lib/payload.ts
|
|
6197
6919
|
var import_zod2 = require("zod");
|
|
@@ -6228,8 +6950,8 @@ function parsePayload(schema, raw) {
|
|
|
6228
6950
|
}
|
|
6229
6951
|
|
|
6230
6952
|
// src/services/file-ops.service.ts
|
|
6231
|
-
var
|
|
6232
|
-
var
|
|
6953
|
+
var fs12 = __toESM(require("fs/promises"));
|
|
6954
|
+
var path15 = __toESM(require("path"));
|
|
6233
6955
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
6234
6956
|
var MAX_WALK_DEPTH = 6;
|
|
6235
6957
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -6264,12 +6986,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
6264
6986
|
"__pycache__"
|
|
6265
6987
|
]);
|
|
6266
6988
|
function isUnder(parent, candidate) {
|
|
6267
|
-
const rel =
|
|
6268
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
6989
|
+
const rel = path15.relative(parent, candidate);
|
|
6990
|
+
return rel === "" || !rel.startsWith("..") && !path15.isAbsolute(rel);
|
|
6269
6991
|
}
|
|
6270
6992
|
async function isExistingFile(absPath) {
|
|
6271
6993
|
try {
|
|
6272
|
-
const stat3 = await
|
|
6994
|
+
const stat3 = await fs12.stat(absPath);
|
|
6273
6995
|
return stat3.isFile();
|
|
6274
6996
|
} catch {
|
|
6275
6997
|
return false;
|
|
@@ -6282,13 +7004,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
6282
7004
|
ctx.visited++;
|
|
6283
7005
|
let entries = [];
|
|
6284
7006
|
try {
|
|
6285
|
-
entries = await
|
|
7007
|
+
entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
6286
7008
|
} catch {
|
|
6287
7009
|
return;
|
|
6288
7010
|
}
|
|
6289
7011
|
for (const e of entries) {
|
|
6290
7012
|
if (!e.isFile()) continue;
|
|
6291
|
-
const full =
|
|
7013
|
+
const full = path15.join(dir, e.name);
|
|
6292
7014
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
6293
7015
|
ctx.matches.push(full);
|
|
6294
7016
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -6298,21 +7020,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
6298
7020
|
if (!e.isDirectory()) continue;
|
|
6299
7021
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
6300
7022
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
6301
|
-
await walkForSuffix(
|
|
7023
|
+
await walkForSuffix(path15.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
6302
7024
|
if (ctx.matches.length >= ctx.cap) return;
|
|
6303
7025
|
}
|
|
6304
7026
|
}
|
|
6305
7027
|
async function findFile(rawPath) {
|
|
6306
7028
|
const cwd = process.cwd();
|
|
6307
|
-
if (
|
|
6308
|
-
const abs =
|
|
7029
|
+
if (path15.isAbsolute(rawPath)) {
|
|
7030
|
+
const abs = path15.normalize(rawPath);
|
|
6309
7031
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
6310
7032
|
}
|
|
6311
|
-
const direct =
|
|
7033
|
+
const direct = path15.resolve(cwd, rawPath);
|
|
6312
7034
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
6313
|
-
const normalized =
|
|
7035
|
+
const normalized = path15.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
6314
7036
|
const needles = [
|
|
6315
|
-
`${
|
|
7037
|
+
`${path15.sep}${normalized}`,
|
|
6316
7038
|
`/${normalized}`
|
|
6317
7039
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
6318
7040
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -6326,7 +7048,7 @@ async function findWriteTarget(rawPath) {
|
|
|
6326
7048
|
const found = await findFile(rawPath);
|
|
6327
7049
|
if (found) return found;
|
|
6328
7050
|
const cwd = process.cwd();
|
|
6329
|
-
const fallback =
|
|
7051
|
+
const fallback = path15.isAbsolute(rawPath) ? path15.normalize(rawPath) : path15.resolve(cwd, rawPath);
|
|
6330
7052
|
if (!isUnder(cwd, fallback)) return null;
|
|
6331
7053
|
return fallback;
|
|
6332
7054
|
}
|
|
@@ -6343,11 +7065,11 @@ async function readProjectFile(rawPath) {
|
|
|
6343
7065
|
if (!abs) {
|
|
6344
7066
|
return { error: `File not found in the project tree: ${rawPath}` };
|
|
6345
7067
|
}
|
|
6346
|
-
const stat3 = await
|
|
7068
|
+
const stat3 = await fs12.stat(abs);
|
|
6347
7069
|
if (stat3.size > MAX_FILE_BYTES) {
|
|
6348
7070
|
return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
6349
7071
|
}
|
|
6350
|
-
const buf = await
|
|
7072
|
+
const buf = await fs12.readFile(abs);
|
|
6351
7073
|
if (looksBinary(buf)) {
|
|
6352
7074
|
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
6353
7075
|
}
|
|
@@ -6366,8 +7088,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
6366
7088
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
6367
7089
|
return { error: "Content too large." };
|
|
6368
7090
|
}
|
|
6369
|
-
await
|
|
6370
|
-
await
|
|
7091
|
+
await fs12.mkdir(path15.dirname(abs), { recursive: true });
|
|
7092
|
+
await fs12.writeFile(abs, content, "utf-8");
|
|
6371
7093
|
return { ok: true };
|
|
6372
7094
|
} catch (e) {
|
|
6373
7095
|
const msg = e instanceof Error ? e.message : "Write failed";
|
|
@@ -6376,11 +7098,11 @@ async function writeProjectFile(rawPath, content) {
|
|
|
6376
7098
|
}
|
|
6377
7099
|
|
|
6378
7100
|
// src/services/project-ops.service.ts
|
|
6379
|
-
var
|
|
6380
|
-
var
|
|
6381
|
-
var
|
|
6382
|
-
var
|
|
6383
|
-
var
|
|
7101
|
+
var import_child_process7 = require("child_process");
|
|
7102
|
+
var import_util2 = require("util");
|
|
7103
|
+
var fs13 = __toESM(require("fs/promises"));
|
|
7104
|
+
var path16 = __toESM(require("path"));
|
|
7105
|
+
var execFileP2 = (0, import_util2.promisify)(import_child_process7.execFile);
|
|
6384
7106
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
6385
7107
|
"node_modules",
|
|
6386
7108
|
".git",
|
|
@@ -6427,7 +7149,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
6427
7149
|
}
|
|
6428
7150
|
let entries = [];
|
|
6429
7151
|
try {
|
|
6430
|
-
entries = await
|
|
7152
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
6431
7153
|
} catch {
|
|
6432
7154
|
return;
|
|
6433
7155
|
}
|
|
@@ -6437,18 +7159,18 @@ async function listProjectFiles(opts = {}) {
|
|
|
6437
7159
|
return;
|
|
6438
7160
|
}
|
|
6439
7161
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
6440
|
-
const full =
|
|
7162
|
+
const full = path16.join(dir, e.name);
|
|
6441
7163
|
if (e.isDirectory()) {
|
|
6442
7164
|
if (depth >= 12) continue;
|
|
6443
7165
|
await walk(full, depth + 1);
|
|
6444
7166
|
} else if (e.isFile()) {
|
|
6445
|
-
const rel =
|
|
7167
|
+
const rel = path16.relative(root, full);
|
|
6446
7168
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
6447
7169
|
continue;
|
|
6448
7170
|
}
|
|
6449
7171
|
let size = 0;
|
|
6450
7172
|
try {
|
|
6451
|
-
const st3 = await
|
|
7173
|
+
const st3 = await fs13.stat(full);
|
|
6452
7174
|
size = st3.size;
|
|
6453
7175
|
} catch {
|
|
6454
7176
|
}
|
|
@@ -6462,7 +7184,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
6462
7184
|
}
|
|
6463
7185
|
async function git(args2, cwd) {
|
|
6464
7186
|
try {
|
|
6465
|
-
const { stdout, stderr } = await
|
|
7187
|
+
const { stdout, stderr } = await execFileP2("git", args2, {
|
|
6466
7188
|
cwd: cwd ?? process.cwd(),
|
|
6467
7189
|
maxBuffer: MAX_GIT_OUTPUT,
|
|
6468
7190
|
timeout: 3e4
|
|
@@ -6550,8 +7272,8 @@ async function gitStatus(cwd) {
|
|
|
6550
7272
|
let hasMergeInProgress = false;
|
|
6551
7273
|
try {
|
|
6552
7274
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
6553
|
-
const mergeHead =
|
|
6554
|
-
await
|
|
7275
|
+
const mergeHead = path16.isAbsolute(gitDir) ? path16.join(gitDir, "MERGE_HEAD") : path16.join(root, gitDir, "MERGE_HEAD");
|
|
7276
|
+
await fs13.access(mergeHead);
|
|
6555
7277
|
hasMergeInProgress = true;
|
|
6556
7278
|
} catch {
|
|
6557
7279
|
}
|
|
@@ -6628,8 +7350,8 @@ async function gitResolve(file, side, cwd) {
|
|
|
6628
7350
|
function saveFilesTemp(files) {
|
|
6629
7351
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
6630
7352
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
6631
|
-
const tmpPath =
|
|
6632
|
-
|
|
7353
|
+
const tmpPath = path17.join(os13.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
|
|
7354
|
+
fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
6633
7355
|
return tmpPath;
|
|
6634
7356
|
});
|
|
6635
7357
|
}
|
|
@@ -6648,7 +7370,7 @@ var startTask = (ctx, _cmd, parsed) => {
|
|
|
6648
7370
|
setTimeout(() => {
|
|
6649
7371
|
for (const p2 of paths) {
|
|
6650
7372
|
try {
|
|
6651
|
-
|
|
7373
|
+
fs14.unlinkSync(p2);
|
|
6652
7374
|
} catch {
|
|
6653
7375
|
}
|
|
6654
7376
|
}
|
|
@@ -6708,14 +7430,35 @@ var getConversation = async (ctx, cmd) => {
|
|
|
6708
7430
|
}
|
|
6709
7431
|
};
|
|
6710
7432
|
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
|
-
];
|
|
7433
|
+
const models = await ctx.runtime.listModels();
|
|
6717
7434
|
await ctx.relay.sendResult(cmd.id, "completed", { models });
|
|
6718
7435
|
};
|
|
7436
|
+
var changeModel = async (ctx, cmd) => {
|
|
7437
|
+
const params = cmd.payload;
|
|
7438
|
+
if (typeof params.modelId !== "string" || !params.modelId) {
|
|
7439
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "modelId required" });
|
|
7440
|
+
return;
|
|
7441
|
+
}
|
|
7442
|
+
const instr = ctx.runtime.changeModelInstruction(params.modelId);
|
|
7443
|
+
if (instr.type === "pty") {
|
|
7444
|
+
if (!instr.ptyInput) {
|
|
7445
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "no pty input for this agent" });
|
|
7446
|
+
return;
|
|
7447
|
+
}
|
|
7448
|
+
ctx.claude.sendRawPtyInput(instr.ptyInput);
|
|
7449
|
+
} else if (instr.type === "restart") {
|
|
7450
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "restart-mode change_model not supported in Phase 1" });
|
|
7451
|
+
return;
|
|
7452
|
+
}
|
|
7453
|
+
await ctx.relay.sendResult(cmd.id, "completed", {});
|
|
7454
|
+
};
|
|
7455
|
+
var summarize = async (ctx, cmd) => {
|
|
7456
|
+
const params = cmd.payload;
|
|
7457
|
+
const mode = params.mode === "auto" ? "auto" : "normal";
|
|
7458
|
+
const instr = ctx.runtime.summarizeInstruction(mode);
|
|
7459
|
+
ctx.claude.sendRawPtyInput(instr.ptyInput);
|
|
7460
|
+
await ctx.relay.sendResult(cmd.id, "completed", {});
|
|
7461
|
+
};
|
|
6719
7462
|
var setKeepAlive = async (ctx, cmd) => {
|
|
6720
7463
|
const enabled = !!cmd.payload.enabled;
|
|
6721
7464
|
ctx.setKeepAlive(enabled);
|
|
@@ -6739,7 +7482,7 @@ var sessionTerminated = (ctx) => {
|
|
|
6739
7482
|
} catch {
|
|
6740
7483
|
}
|
|
6741
7484
|
try {
|
|
6742
|
-
const proc = (0,
|
|
7485
|
+
const proc = (0, import_child_process8.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6743
7486
|
detached: true,
|
|
6744
7487
|
stdio: "ignore"
|
|
6745
7488
|
});
|
|
@@ -6761,7 +7504,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
6761
7504
|
}
|
|
6762
7505
|
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
6763
7506
|
try {
|
|
6764
|
-
const stopProc = (0,
|
|
7507
|
+
const stopProc = (0, import_child_process8.spawn)(
|
|
6765
7508
|
"bash",
|
|
6766
7509
|
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
6767
7510
|
{ detached: true, stdio: "ignore" }
|
|
@@ -6771,7 +7514,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
6771
7514
|
}
|
|
6772
7515
|
}
|
|
6773
7516
|
try {
|
|
6774
|
-
const proc = (0,
|
|
7517
|
+
const proc = (0, import_child_process8.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6775
7518
|
detached: true,
|
|
6776
7519
|
stdio: "ignore"
|
|
6777
7520
|
});
|
|
@@ -6852,6 +7595,8 @@ var handlers = {
|
|
|
6852
7595
|
get_context: getContext,
|
|
6853
7596
|
get_conversation: getConversation,
|
|
6854
7597
|
list_models: listModels,
|
|
7598
|
+
change_model: changeModel,
|
|
7599
|
+
summarize,
|
|
6855
7600
|
set_keep_alive: setKeepAlive,
|
|
6856
7601
|
session_terminated: sessionTerminated,
|
|
6857
7602
|
shutdown_session: shutdownSession,
|
|
@@ -6888,11 +7633,16 @@ async function start() {
|
|
|
6888
7633
|
`);
|
|
6889
7634
|
process.exit(0);
|
|
6890
7635
|
}
|
|
7636
|
+
if (!session.agent) {
|
|
7637
|
+
throw new Error("Active session has no agent \u2014 re-pair with `codeam pair`.");
|
|
7638
|
+
}
|
|
6891
7639
|
const pluginId = session.pluginId ?? ensurePluginId();
|
|
6892
7640
|
showInfo(`${session.userName} \xB7 ${import_picocolors2.default.cyan(session.plan)}`);
|
|
6893
|
-
showInfo(
|
|
7641
|
+
showInfo(`Launching ${AGENT_REGISTRY[session.agent].displayName}...
|
|
7642
|
+
`);
|
|
6894
7643
|
const cwd = process.cwd();
|
|
6895
|
-
const
|
|
7644
|
+
const runtime = createRuntimeStrategy(session.agent);
|
|
7645
|
+
const historySvc = new HistoryService(runtime, pluginId, cwd);
|
|
6896
7646
|
const keepAliveCtx = {
|
|
6897
7647
|
inCodespace: process.env.CODESPACES === "true",
|
|
6898
7648
|
codespaceName: process.env.CODESPACE_NAME
|
|
@@ -6904,7 +7654,7 @@ async function start() {
|
|
|
6904
7654
|
(conversationId) => historySvc.setCurrentConversationId(conversationId),
|
|
6905
7655
|
(reset) => historySvc.setRateLimitReset(reset),
|
|
6906
7656
|
() => {
|
|
6907
|
-
if (historySvc.isQuotaStale()) fetchQuotaUsage(historySvc);
|
|
7657
|
+
if (historySvc.isQuotaStale()) fetchQuotaUsage(runtime, historySvc);
|
|
6908
7658
|
setTimeout(() => {
|
|
6909
7659
|
historySvc.uploadDelta().catch(() => {
|
|
6910
7660
|
});
|
|
@@ -6916,22 +7666,26 @@ async function start() {
|
|
|
6916
7666
|
},
|
|
6917
7667
|
session.pluginAuthToken
|
|
6918
7668
|
);
|
|
6919
|
-
const claude = new
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
7669
|
+
const claude = new AgentService(
|
|
7670
|
+
runtime,
|
|
7671
|
+
{
|
|
7672
|
+
cwd,
|
|
7673
|
+
onData(raw) {
|
|
7674
|
+
outputSvc.push(raw);
|
|
7675
|
+
},
|
|
7676
|
+
onExit(code) {
|
|
7677
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
7678
|
+
outputSvc.dispose();
|
|
7679
|
+
relay.stop();
|
|
7680
|
+
process.exit(code);
|
|
7681
|
+
}
|
|
6929
7682
|
}
|
|
6930
|
-
|
|
7683
|
+
);
|
|
6931
7684
|
const ctx = {
|
|
6932
7685
|
outputSvc,
|
|
6933
7686
|
claude,
|
|
6934
7687
|
historySvc,
|
|
7688
|
+
runtime,
|
|
6935
7689
|
relay: void 0,
|
|
6936
7690
|
setKeepAlive: setKeepAlive2,
|
|
6937
7691
|
keepAliveCtx
|
|
@@ -6954,7 +7708,7 @@ async function start() {
|
|
|
6954
7708
|
historySvc.load().catch(() => {
|
|
6955
7709
|
});
|
|
6956
7710
|
}, 2e3);
|
|
6957
|
-
setTimeout(() => fetchQuotaUsage(historySvc), 5e3);
|
|
7711
|
+
setTimeout(() => fetchQuotaUsage(runtime, historySvc), 5e3);
|
|
6958
7712
|
}
|
|
6959
7713
|
|
|
6960
7714
|
// src/commands/pair.ts
|
|
@@ -6981,8 +7735,45 @@ async function selectSession(sessions2, activeId) {
|
|
|
6981
7735
|
return result;
|
|
6982
7736
|
}
|
|
6983
7737
|
|
|
7738
|
+
// src/utils/agent-prompt.ts
|
|
7739
|
+
function parseAgentFlag(args2) {
|
|
7740
|
+
const flag = args2.find((a) => a.startsWith("--agent="));
|
|
7741
|
+
if (!flag) return null;
|
|
7742
|
+
const value = flag.slice("--agent=".length);
|
|
7743
|
+
if (!isKnownAgentId(value)) {
|
|
7744
|
+
throw new Error(
|
|
7745
|
+
`invalid agent "${value}"; valid: ${Object.keys(AGENT_REGISTRY).join(", ")}`
|
|
7746
|
+
);
|
|
7747
|
+
}
|
|
7748
|
+
if (!AGENT_REGISTRY[value].enabled) {
|
|
7749
|
+
throw new Error(
|
|
7750
|
+
`${AGENT_REGISTRY[value].displayName} is not available in this codeam-cli version`
|
|
7751
|
+
);
|
|
7752
|
+
}
|
|
7753
|
+
return value;
|
|
7754
|
+
}
|
|
7755
|
+
async function promptForAgent(initialValue) {
|
|
7756
|
+
const enabled = getEnabledAgents();
|
|
7757
|
+
if (enabled.length === 1) {
|
|
7758
|
+
return enabled[0].id;
|
|
7759
|
+
}
|
|
7760
|
+
const chosen = await _t({
|
|
7761
|
+
message: "Pick an agent:",
|
|
7762
|
+
options: enabled.map((m) => ({ value: m.id, label: m.displayName })),
|
|
7763
|
+
initialValue: initialValue ?? enabled[0].id
|
|
7764
|
+
});
|
|
7765
|
+
if (q(chosen)) {
|
|
7766
|
+
pt("Cancelled.");
|
|
7767
|
+
process.exit(0);
|
|
7768
|
+
}
|
|
7769
|
+
return chosen;
|
|
7770
|
+
}
|
|
7771
|
+
|
|
6984
7772
|
// src/commands/pair.ts
|
|
6985
|
-
async function pair() {
|
|
7773
|
+
async function pair(args2 = []) {
|
|
7774
|
+
const config = loadCliConfig();
|
|
7775
|
+
const flagAgent = parseAgentFlag(args2);
|
|
7776
|
+
const agentId = flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
|
|
6986
7777
|
showIntro();
|
|
6987
7778
|
const pluginId = (0, import_crypto2.randomUUID)();
|
|
6988
7779
|
const spin = dist_exports.spinner();
|
|
@@ -7018,8 +7809,10 @@ async function pair() {
|
|
|
7018
7809
|
userEmail: info.userEmail,
|
|
7019
7810
|
plan: info.plan,
|
|
7020
7811
|
pairedAt: Date.now(),
|
|
7021
|
-
pluginAuthToken: info.pluginAuthToken
|
|
7812
|
+
pluginAuthToken: info.pluginAuthToken,
|
|
7813
|
+
agent: agentId
|
|
7022
7814
|
});
|
|
7815
|
+
saveCliConfig({ ...loadCliConfig(), preferredAgent: agentId });
|
|
7023
7816
|
showSuccess(`Paired with ${info.userName} (${info.plan})`);
|
|
7024
7817
|
console.log("");
|
|
7025
7818
|
resolve2();
|
|
@@ -7036,8 +7829,8 @@ async function pair() {
|
|
|
7036
7829
|
}
|
|
7037
7830
|
|
|
7038
7831
|
// src/commands/pair-auto.ts
|
|
7039
|
-
var
|
|
7040
|
-
var
|
|
7832
|
+
var fs15 = __toESM(require("fs"));
|
|
7833
|
+
var os14 = __toESM(require("os"));
|
|
7041
7834
|
var import_crypto3 = require("crypto");
|
|
7042
7835
|
var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
7043
7836
|
function fail(msg) {
|
|
@@ -7054,12 +7847,12 @@ function readTokenFromArgs(args2) {
|
|
|
7054
7847
|
}
|
|
7055
7848
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
7056
7849
|
if (fileFlag) {
|
|
7057
|
-
const
|
|
7850
|
+
const path23 = fileFlag.slice("--token-file=".length);
|
|
7058
7851
|
try {
|
|
7059
|
-
const content =
|
|
7060
|
-
if (content.length === 0) fail(`--token-file ${
|
|
7852
|
+
const content = fs15.readFileSync(path23, "utf8").trim();
|
|
7853
|
+
if (content.length === 0) fail(`--token-file ${path23} is empty`);
|
|
7061
7854
|
try {
|
|
7062
|
-
|
|
7855
|
+
fs15.unlinkSync(path23);
|
|
7063
7856
|
} catch {
|
|
7064
7857
|
}
|
|
7065
7858
|
return content;
|
|
@@ -7077,7 +7870,7 @@ async function claim(token, pluginId) {
|
|
|
7077
7870
|
pluginId,
|
|
7078
7871
|
ideName: "codeam-cli (codespace)",
|
|
7079
7872
|
ideVersion: process.env.npm_package_version ?? "unknown",
|
|
7080
|
-
hostname:
|
|
7873
|
+
hostname: os14.hostname(),
|
|
7081
7874
|
codespaceName: process.env.CODESPACE_NAME ?? ""
|
|
7082
7875
|
};
|
|
7083
7876
|
const res = await fetch(url, {
|
|
@@ -7103,6 +7896,11 @@ async function pairAuto(args2) {
|
|
|
7103
7896
|
const pluginId = (0, import_crypto3.randomUUID)();
|
|
7104
7897
|
console.log(" Claiming pairing token\u2026");
|
|
7105
7898
|
const claimed = await claim(token, pluginId);
|
|
7899
|
+
if (!isKnownAgentId(claimed.agent)) {
|
|
7900
|
+
fail(
|
|
7901
|
+
`agent "${claimed.agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
|
|
7902
|
+
);
|
|
7903
|
+
}
|
|
7106
7904
|
addSession({
|
|
7107
7905
|
id: claimed.sessionId,
|
|
7108
7906
|
pluginId,
|
|
@@ -7110,7 +7908,8 @@ async function pairAuto(args2) {
|
|
|
7110
7908
|
userEmail: claimed.user.email,
|
|
7111
7909
|
plan: claimed.user.plan,
|
|
7112
7910
|
pairedAt: Date.now(),
|
|
7113
|
-
pluginAuthToken: claimed.pluginAuthToken
|
|
7911
|
+
pluginAuthToken: claimed.pluginAuthToken,
|
|
7912
|
+
agent: claimed.agent
|
|
7114
7913
|
});
|
|
7115
7914
|
console.log(` Paired with ${claimed.user.name} (${claimed.user.plan})`);
|
|
7116
7915
|
console.log(" Starting agent loop\u2026");
|
|
@@ -7217,19 +8016,14 @@ async function logout() {
|
|
|
7217
8016
|
}
|
|
7218
8017
|
|
|
7219
8018
|
// 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
8019
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
7226
8020
|
|
|
7227
8021
|
// src/services/providers/github-codespaces.ts
|
|
7228
|
-
var
|
|
7229
|
-
var
|
|
8022
|
+
var import_child_process9 = require("child_process");
|
|
8023
|
+
var import_util3 = require("util");
|
|
7230
8024
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
7231
|
-
var
|
|
7232
|
-
var
|
|
8025
|
+
var path18 = __toESM(require("path"));
|
|
8026
|
+
var execFileP3 = (0, import_util3.promisify)(import_child_process9.execFile);
|
|
7233
8027
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
7234
8028
|
function resetStdinForChild() {
|
|
7235
8029
|
if (process.stdin.isTTY) {
|
|
@@ -7246,11 +8040,11 @@ var GitHubCodespacesProvider = class {
|
|
|
7246
8040
|
available = true;
|
|
7247
8041
|
async authorize() {
|
|
7248
8042
|
try {
|
|
7249
|
-
await
|
|
8043
|
+
await execFileP3("gh", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
7250
8044
|
} catch {
|
|
7251
8045
|
await this.tryInstallGh();
|
|
7252
8046
|
try {
|
|
7253
|
-
await
|
|
8047
|
+
await execFileP3("gh", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
7254
8048
|
} catch {
|
|
7255
8049
|
throw new Error(
|
|
7256
8050
|
[
|
|
@@ -7266,14 +8060,14 @@ var GitHubCodespacesProvider = class {
|
|
|
7266
8060
|
}
|
|
7267
8061
|
let isAuthed = false;
|
|
7268
8062
|
try {
|
|
7269
|
-
await
|
|
8063
|
+
await execFileP3("gh", ["auth", "status"], { maxBuffer: MAX_BUFFER });
|
|
7270
8064
|
isAuthed = true;
|
|
7271
8065
|
} catch {
|
|
7272
8066
|
}
|
|
7273
8067
|
if (!isAuthed) {
|
|
7274
8068
|
resetStdinForChild();
|
|
7275
8069
|
await new Promise((resolve2, reject) => {
|
|
7276
|
-
const proc = (0,
|
|
8070
|
+
const proc = (0, import_child_process9.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
7277
8071
|
stdio: "inherit"
|
|
7278
8072
|
});
|
|
7279
8073
|
proc.on("exit", (code) => {
|
|
@@ -7307,7 +8101,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7307
8101
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
7308
8102
|
resetStdinForChild();
|
|
7309
8103
|
const refreshCode = await new Promise((resolve2, reject) => {
|
|
7310
|
-
const proc = (0,
|
|
8104
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7311
8105
|
"gh",
|
|
7312
8106
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
7313
8107
|
{ stdio: "inherit" }
|
|
@@ -7342,7 +8136,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7342
8136
|
*/
|
|
7343
8137
|
async getActiveGhUser() {
|
|
7344
8138
|
try {
|
|
7345
|
-
const { stdout } = await
|
|
8139
|
+
const { stdout } = await execFileP3(
|
|
7346
8140
|
"gh",
|
|
7347
8141
|
["api", "user", "--jq", ".login"],
|
|
7348
8142
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7362,7 +8156,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7362
8156
|
*/
|
|
7363
8157
|
async hasCodespaceScope() {
|
|
7364
8158
|
try {
|
|
7365
|
-
const { stdout } = await
|
|
8159
|
+
const { stdout } = await execFileP3(
|
|
7366
8160
|
"gh",
|
|
7367
8161
|
["api", "-i", "user"],
|
|
7368
8162
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7392,12 +8186,12 @@ var GitHubCodespacesProvider = class {
|
|
|
7392
8186
|
* install error if it's still missing.
|
|
7393
8187
|
*/
|
|
7394
8188
|
async tryInstallGh() {
|
|
7395
|
-
const
|
|
8189
|
+
const platform2 = process.platform;
|
|
7396
8190
|
wt(
|
|
7397
8191
|
`GitHub CLI (${import_picocolors7.default.cyan("gh")}) is required for Codespaces deploys but isn't on your PATH.`,
|
|
7398
8192
|
"Heads up"
|
|
7399
8193
|
);
|
|
7400
|
-
if (
|
|
8194
|
+
if (platform2 === "linux") {
|
|
7401
8195
|
wt(
|
|
7402
8196
|
[
|
|
7403
8197
|
"On Linux, please install gh from the official guide:",
|
|
@@ -7409,9 +8203,9 @@ var GitHubCodespacesProvider = class {
|
|
|
7409
8203
|
return;
|
|
7410
8204
|
}
|
|
7411
8205
|
let installCmd = null;
|
|
7412
|
-
if (
|
|
8206
|
+
if (platform2 === "darwin") {
|
|
7413
8207
|
try {
|
|
7414
|
-
await
|
|
8208
|
+
await execFileP3("brew", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
7415
8209
|
} catch {
|
|
7416
8210
|
wt(
|
|
7417
8211
|
[
|
|
@@ -7428,9 +8222,9 @@ var GitHubCodespacesProvider = class {
|
|
|
7428
8222
|
args: ["install", "gh"],
|
|
7429
8223
|
describe: "brew install gh"
|
|
7430
8224
|
};
|
|
7431
|
-
} else if (
|
|
8225
|
+
} else if (platform2 === "win32") {
|
|
7432
8226
|
try {
|
|
7433
|
-
await
|
|
8227
|
+
await execFileP3("winget", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
7434
8228
|
} catch {
|
|
7435
8229
|
wt(
|
|
7436
8230
|
[
|
|
@@ -7457,7 +8251,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7457
8251
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
7458
8252
|
resetStdinForChild();
|
|
7459
8253
|
const ok = await new Promise((resolve2) => {
|
|
7460
|
-
const proc = (0,
|
|
8254
|
+
const proc = (0, import_child_process9.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
7461
8255
|
proc.on("exit", (code) => resolve2(code === 0));
|
|
7462
8256
|
proc.on("error", () => resolve2(false));
|
|
7463
8257
|
});
|
|
@@ -7484,7 +8278,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7484
8278
|
);
|
|
7485
8279
|
resetStdinForChild();
|
|
7486
8280
|
await new Promise((resolve2, reject) => {
|
|
7487
|
-
const proc = (0,
|
|
8281
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7488
8282
|
"gh",
|
|
7489
8283
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
7490
8284
|
{ stdio: "inherit" }
|
|
@@ -7509,7 +8303,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7509
8303
|
"200"
|
|
7510
8304
|
);
|
|
7511
8305
|
try {
|
|
7512
|
-
const { stdout } = await
|
|
8306
|
+
const { stdout } = await execFileP3("gh", args2, { maxBuffer: MAX_BUFFER });
|
|
7513
8307
|
return JSON.parse(stdout);
|
|
7514
8308
|
} catch {
|
|
7515
8309
|
return [];
|
|
@@ -7518,7 +8312,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7518
8312
|
const own = await fetchRepos();
|
|
7519
8313
|
let orgRepos = [];
|
|
7520
8314
|
try {
|
|
7521
|
-
const { stdout } = await
|
|
8315
|
+
const { stdout } = await execFileP3(
|
|
7522
8316
|
"gh",
|
|
7523
8317
|
["api", "--paginate", "user/orgs", "--jq", ".[].login"],
|
|
7524
8318
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7555,7 +8349,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7555
8349
|
*/
|
|
7556
8350
|
async listMachineTypes(projectId) {
|
|
7557
8351
|
try {
|
|
7558
|
-
const { stdout } = await
|
|
8352
|
+
const { stdout } = await execFileP3(
|
|
7559
8353
|
"gh",
|
|
7560
8354
|
["api", `/repos/${projectId}/codespaces/machines`],
|
|
7561
8355
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7586,7 +8380,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7586
8380
|
const machine = machineTypeId ?? await this.pickDefaultMachine(projectId);
|
|
7587
8381
|
const args2 = ["codespace", "create", "-R", projectId, "--default-permissions"];
|
|
7588
8382
|
if (machine) args2.push("-m", machine);
|
|
7589
|
-
const { stdout } = await
|
|
8383
|
+
const { stdout } = await execFileP3(
|
|
7590
8384
|
"gh",
|
|
7591
8385
|
args2,
|
|
7592
8386
|
{ maxBuffer: MAX_BUFFER, timeout: 12e4 }
|
|
@@ -7626,7 +8420,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7626
8420
|
async waitUntilAvailable(name) {
|
|
7627
8421
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
7628
8422
|
while (Date.now() < deadline) {
|
|
7629
|
-
const { stdout } = await
|
|
8423
|
+
const { stdout } = await execFileP3(
|
|
7630
8424
|
"gh",
|
|
7631
8425
|
["codespace", "list", "--json", "name,state"],
|
|
7632
8426
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -7644,7 +8438,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7644
8438
|
}
|
|
7645
8439
|
async exec(workspaceId, command2) {
|
|
7646
8440
|
try {
|
|
7647
|
-
const { stdout, stderr } = await
|
|
8441
|
+
const { stdout, stderr } = await execFileP3(
|
|
7648
8442
|
"gh",
|
|
7649
8443
|
["codespace", "ssh", "-c", workspaceId, "--", command2],
|
|
7650
8444
|
{ maxBuffer: MAX_BUFFER, timeout: 6e5 }
|
|
@@ -7662,7 +8456,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7662
8456
|
async streamCommand(workspaceId, command2) {
|
|
7663
8457
|
resetStdinForChild();
|
|
7664
8458
|
return new Promise((resolve2, reject) => {
|
|
7665
|
-
const proc = (0,
|
|
8459
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7666
8460
|
"gh",
|
|
7667
8461
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
7668
8462
|
{ stdio: "inherit" }
|
|
@@ -7689,11 +8483,11 @@ var GitHubCodespacesProvider = class {
|
|
|
7689
8483
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
7690
8484
|
];
|
|
7691
8485
|
await new Promise((resolve2, reject) => {
|
|
7692
|
-
const tar = (0,
|
|
8486
|
+
const tar = (0, import_child_process9.spawn)("tar", tarArgs, {
|
|
7693
8487
|
stdio: ["ignore", "pipe", "pipe"],
|
|
7694
8488
|
env: tarEnv
|
|
7695
8489
|
});
|
|
7696
|
-
const ssh = (0,
|
|
8490
|
+
const ssh = (0, import_child_process9.spawn)("gh", sshArgs, {
|
|
7697
8491
|
stdio: [tar.stdout, "pipe", "pipe"]
|
|
7698
8492
|
});
|
|
7699
8493
|
let tarErr = "";
|
|
@@ -7717,7 +8511,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7717
8511
|
});
|
|
7718
8512
|
}
|
|
7719
8513
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7720
|
-
const remoteDir =
|
|
8514
|
+
const remoteDir = path18.posix.dirname(remotePath);
|
|
7721
8515
|
const parts = [
|
|
7722
8516
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
7723
8517
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -7727,7 +8521,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7727
8521
|
}
|
|
7728
8522
|
const cmd = parts.join(" && ");
|
|
7729
8523
|
await new Promise((resolve2, reject) => {
|
|
7730
|
-
const proc = (0,
|
|
8524
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7731
8525
|
"gh",
|
|
7732
8526
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
7733
8527
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -7749,7 +8543,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7749
8543
|
try {
|
|
7750
8544
|
const args2 = ["codespace", "list", "--json", "name,displayName,state,lastUsedAt"];
|
|
7751
8545
|
if (projectId) args2.push("--repo", projectId);
|
|
7752
|
-
const { stdout } = await
|
|
8546
|
+
const { stdout } = await execFileP3("gh", args2, { maxBuffer: MAX_BUFFER });
|
|
7753
8547
|
const list = JSON.parse(stdout);
|
|
7754
8548
|
return list.map((c2) => ({
|
|
7755
8549
|
id: c2.name,
|
|
@@ -7764,7 +8558,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7764
8558
|
}
|
|
7765
8559
|
async startWorkspace(workspaceId) {
|
|
7766
8560
|
try {
|
|
7767
|
-
await
|
|
8561
|
+
await execFileP3(
|
|
7768
8562
|
"gh",
|
|
7769
8563
|
["api", "-X", "POST", `/user/codespaces/${workspaceId}/start`],
|
|
7770
8564
|
{ maxBuffer: MAX_BUFFER, timeout: 6e4 }
|
|
@@ -7785,11 +8579,11 @@ function shellQuote(s) {
|
|
|
7785
8579
|
}
|
|
7786
8580
|
|
|
7787
8581
|
// src/services/providers/gitpod.ts
|
|
7788
|
-
var
|
|
7789
|
-
var
|
|
7790
|
-
var
|
|
8582
|
+
var import_child_process10 = require("child_process");
|
|
8583
|
+
var import_util4 = require("util");
|
|
8584
|
+
var path19 = __toESM(require("path"));
|
|
7791
8585
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
7792
|
-
var
|
|
8586
|
+
var execFileP4 = (0, import_util4.promisify)(import_child_process10.execFile);
|
|
7793
8587
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
7794
8588
|
function resetStdinForChild2() {
|
|
7795
8589
|
if (process.stdin.isTTY) {
|
|
@@ -7806,7 +8600,7 @@ var GitpodProvider = class {
|
|
|
7806
8600
|
available = true;
|
|
7807
8601
|
async authorize() {
|
|
7808
8602
|
try {
|
|
7809
|
-
await
|
|
8603
|
+
await execFileP4("gitpod", ["--version"], { maxBuffer: MAX_BUFFER2 });
|
|
7810
8604
|
} catch {
|
|
7811
8605
|
throw new Error(
|
|
7812
8606
|
[
|
|
@@ -7819,7 +8613,7 @@ var GitpodProvider = class {
|
|
|
7819
8613
|
);
|
|
7820
8614
|
}
|
|
7821
8615
|
try {
|
|
7822
|
-
await
|
|
8616
|
+
await execFileP4("gitpod", ["whoami"], { maxBuffer: MAX_BUFFER2 });
|
|
7823
8617
|
return;
|
|
7824
8618
|
} catch {
|
|
7825
8619
|
}
|
|
@@ -7829,7 +8623,7 @@ var GitpodProvider = class {
|
|
|
7829
8623
|
);
|
|
7830
8624
|
resetStdinForChild2();
|
|
7831
8625
|
await new Promise((resolve2, reject) => {
|
|
7832
|
-
const proc = (0,
|
|
8626
|
+
const proc = (0, import_child_process10.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
7833
8627
|
proc.on("exit", (code) => {
|
|
7834
8628
|
if (code === 0) resolve2();
|
|
7835
8629
|
else reject(new Error("gitpod login failed."));
|
|
@@ -7839,7 +8633,7 @@ var GitpodProvider = class {
|
|
|
7839
8633
|
}
|
|
7840
8634
|
async listProjects() {
|
|
7841
8635
|
try {
|
|
7842
|
-
const { stdout } = await
|
|
8636
|
+
const { stdout } = await execFileP4(
|
|
7843
8637
|
"gitpod",
|
|
7844
8638
|
["workspace", "list", "--output", "json", "--limit", "200"],
|
|
7845
8639
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -7872,7 +8666,7 @@ var GitpodProvider = class {
|
|
|
7872
8666
|
async createWorkspace(projectId, machineTypeId) {
|
|
7873
8667
|
const args2 = ["workspace", "create", projectId, "--start", "--output", "json"];
|
|
7874
8668
|
if (machineTypeId) args2.push("--class", machineTypeId);
|
|
7875
|
-
const { stdout } = await
|
|
8669
|
+
const { stdout } = await execFileP4("gitpod", args2, {
|
|
7876
8670
|
maxBuffer: MAX_BUFFER2,
|
|
7877
8671
|
timeout: 3e5
|
|
7878
8672
|
});
|
|
@@ -7896,7 +8690,7 @@ var GitpodProvider = class {
|
|
|
7896
8690
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
7897
8691
|
while (Date.now() < deadline) {
|
|
7898
8692
|
try {
|
|
7899
|
-
const { stdout } = await
|
|
8693
|
+
const { stdout } = await execFileP4(
|
|
7900
8694
|
"gitpod",
|
|
7901
8695
|
["workspace", "get", workspaceId, "--output", "json"],
|
|
7902
8696
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -7914,7 +8708,7 @@ var GitpodProvider = class {
|
|
|
7914
8708
|
}
|
|
7915
8709
|
async listMachineTypes(_projectId) {
|
|
7916
8710
|
try {
|
|
7917
|
-
const { stdout } = await
|
|
8711
|
+
const { stdout } = await execFileP4(
|
|
7918
8712
|
"gitpod",
|
|
7919
8713
|
["organization", "list-classes", "--output", "json"],
|
|
7920
8714
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -7932,7 +8726,7 @@ var GitpodProvider = class {
|
|
|
7932
8726
|
async listExistingWorkspaces(projectId) {
|
|
7933
8727
|
try {
|
|
7934
8728
|
const args2 = ["workspace", "list", "--output", "json", "--limit", "200"];
|
|
7935
|
-
const { stdout } = await
|
|
8729
|
+
const { stdout } = await execFileP4("gitpod", args2, { maxBuffer: MAX_BUFFER2 });
|
|
7936
8730
|
const list = JSON.parse(stdout);
|
|
7937
8731
|
return list.filter((w3) => !projectId || w3.contextUrl === projectId).map((w3) => ({
|
|
7938
8732
|
id: w3.id,
|
|
@@ -7947,7 +8741,7 @@ var GitpodProvider = class {
|
|
|
7947
8741
|
}
|
|
7948
8742
|
async startWorkspace(workspaceId) {
|
|
7949
8743
|
try {
|
|
7950
|
-
await
|
|
8744
|
+
await execFileP4(
|
|
7951
8745
|
"gitpod",
|
|
7952
8746
|
["workspace", "start", workspaceId],
|
|
7953
8747
|
{ maxBuffer: MAX_BUFFER2, timeout: 6e4 }
|
|
@@ -7963,7 +8757,7 @@ var GitpodProvider = class {
|
|
|
7963
8757
|
}
|
|
7964
8758
|
async exec(workspaceId, command2) {
|
|
7965
8759
|
try {
|
|
7966
|
-
const { stdout, stderr } = await
|
|
8760
|
+
const { stdout, stderr } = await execFileP4(
|
|
7967
8761
|
"gitpod",
|
|
7968
8762
|
["workspace", "ssh", workspaceId, "--", command2],
|
|
7969
8763
|
{ maxBuffer: MAX_BUFFER2, timeout: 6e5 }
|
|
@@ -7981,7 +8775,7 @@ var GitpodProvider = class {
|
|
|
7981
8775
|
async streamCommand(workspaceId, command2) {
|
|
7982
8776
|
resetStdinForChild2();
|
|
7983
8777
|
return new Promise((resolve2, reject) => {
|
|
7984
|
-
const proc = (0,
|
|
8778
|
+
const proc = (0, import_child_process10.spawn)(
|
|
7985
8779
|
"gitpod",
|
|
7986
8780
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
7987
8781
|
{ stdio: "inherit" }
|
|
@@ -8001,11 +8795,11 @@ var GitpodProvider = class {
|
|
|
8001
8795
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8002
8796
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
8003
8797
|
await new Promise((resolve2, reject) => {
|
|
8004
|
-
const tar = (0,
|
|
8798
|
+
const tar = (0, import_child_process10.spawn)("tar", tarArgs, {
|
|
8005
8799
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8006
8800
|
env: tarEnv
|
|
8007
8801
|
});
|
|
8008
|
-
const ssh = (0,
|
|
8802
|
+
const ssh = (0, import_child_process10.spawn)(
|
|
8009
8803
|
"gitpod",
|
|
8010
8804
|
["workspace", "ssh", workspaceId, "--", remoteCmd],
|
|
8011
8805
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8027,7 +8821,7 @@ var GitpodProvider = class {
|
|
|
8027
8821
|
});
|
|
8028
8822
|
}
|
|
8029
8823
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8030
|
-
const remoteDir =
|
|
8824
|
+
const remoteDir = path19.posix.dirname(remotePath);
|
|
8031
8825
|
const parts = [
|
|
8032
8826
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
8033
8827
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -8037,7 +8831,7 @@ var GitpodProvider = class {
|
|
|
8037
8831
|
}
|
|
8038
8832
|
const cmd = parts.join(" && ");
|
|
8039
8833
|
await new Promise((resolve2, reject) => {
|
|
8040
|
-
const proc = (0,
|
|
8834
|
+
const proc = (0, import_child_process10.spawn)(
|
|
8041
8835
|
"gitpod",
|
|
8042
8836
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
8043
8837
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8061,10 +8855,10 @@ function shellQuote2(s) {
|
|
|
8061
8855
|
}
|
|
8062
8856
|
|
|
8063
8857
|
// src/services/providers/gitlab-workspaces.ts
|
|
8064
|
-
var
|
|
8065
|
-
var
|
|
8066
|
-
var
|
|
8067
|
-
var
|
|
8858
|
+
var import_child_process11 = require("child_process");
|
|
8859
|
+
var import_util5 = require("util");
|
|
8860
|
+
var path20 = __toESM(require("path"));
|
|
8861
|
+
var execFileP5 = (0, import_util5.promisify)(import_child_process11.execFile);
|
|
8068
8862
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
8069
8863
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
8070
8864
|
function resetStdinForChild3() {
|
|
@@ -8082,7 +8876,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8082
8876
|
available = true;
|
|
8083
8877
|
async authorize() {
|
|
8084
8878
|
try {
|
|
8085
|
-
await
|
|
8879
|
+
await execFileP5("glab", ["--version"], { maxBuffer: MAX_BUFFER3 });
|
|
8086
8880
|
} catch {
|
|
8087
8881
|
throw new Error(
|
|
8088
8882
|
[
|
|
@@ -8096,7 +8890,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8096
8890
|
);
|
|
8097
8891
|
}
|
|
8098
8892
|
try {
|
|
8099
|
-
await
|
|
8893
|
+
await execFileP5("glab", ["auth", "status"], { maxBuffer: MAX_BUFFER3 });
|
|
8100
8894
|
return;
|
|
8101
8895
|
} catch {
|
|
8102
8896
|
}
|
|
@@ -8106,7 +8900,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8106
8900
|
);
|
|
8107
8901
|
resetStdinForChild3();
|
|
8108
8902
|
await new Promise((resolve2, reject) => {
|
|
8109
|
-
const proc = (0,
|
|
8903
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8110
8904
|
"glab",
|
|
8111
8905
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
8112
8906
|
{ stdio: "inherit" }
|
|
@@ -8120,7 +8914,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8120
8914
|
}
|
|
8121
8915
|
async listProjects() {
|
|
8122
8916
|
try {
|
|
8123
|
-
const { stdout } = await
|
|
8917
|
+
const { stdout } = await execFileP5(
|
|
8124
8918
|
"glab",
|
|
8125
8919
|
["repo", "list", "--per-page", "200", "--output", "json"],
|
|
8126
8920
|
{ maxBuffer: MAX_BUFFER3 }
|
|
@@ -8252,7 +9046,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8252
9046
|
async exec(workspaceId, command2) {
|
|
8253
9047
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8254
9048
|
try {
|
|
8255
|
-
const { stdout, stderr } = await
|
|
9049
|
+
const { stdout, stderr } = await execFileP5(
|
|
8256
9050
|
"ssh",
|
|
8257
9051
|
[
|
|
8258
9052
|
"-o",
|
|
@@ -8278,7 +9072,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8278
9072
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8279
9073
|
resetStdinForChild3();
|
|
8280
9074
|
return new Promise((resolve2, reject) => {
|
|
8281
|
-
const proc = (0,
|
|
9075
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8282
9076
|
"ssh",
|
|
8283
9077
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
8284
9078
|
{ stdio: "inherit" }
|
|
@@ -8299,8 +9093,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8299
9093
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8300
9094
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
8301
9095
|
await new Promise((resolve2, reject) => {
|
|
8302
|
-
const tar = (0,
|
|
8303
|
-
const ssh = (0,
|
|
9096
|
+
const tar = (0, import_child_process11.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
9097
|
+
const ssh = (0, import_child_process11.spawn)(
|
|
8304
9098
|
"ssh",
|
|
8305
9099
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
|
|
8306
9100
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8323,14 +9117,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8323
9117
|
}
|
|
8324
9118
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8325
9119
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8326
|
-
const remoteDir =
|
|
9120
|
+
const remoteDir = path20.posix.dirname(remotePath);
|
|
8327
9121
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
8328
9122
|
if (options.mode != null) {
|
|
8329
9123
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
8330
9124
|
}
|
|
8331
9125
|
const cmd = parts.join(" && ");
|
|
8332
9126
|
await new Promise((resolve2, reject) => {
|
|
8333
|
-
const proc = (0,
|
|
9127
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8334
9128
|
"ssh",
|
|
8335
9129
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
8336
9130
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8355,7 +9149,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8355
9149
|
*/
|
|
8356
9150
|
async getGlabToken() {
|
|
8357
9151
|
try {
|
|
8358
|
-
const { stdout, stderr } = await
|
|
9152
|
+
const { stdout, stderr } = await execFileP5(
|
|
8359
9153
|
"glab",
|
|
8360
9154
|
["auth", "status", "--show-token"],
|
|
8361
9155
|
{ maxBuffer: MAX_BUFFER3 }
|
|
@@ -8389,10 +9183,10 @@ function shellQuote3(s) {
|
|
|
8389
9183
|
}
|
|
8390
9184
|
|
|
8391
9185
|
// src/services/providers/railway.ts
|
|
8392
|
-
var
|
|
8393
|
-
var
|
|
8394
|
-
var
|
|
8395
|
-
var
|
|
9186
|
+
var import_child_process12 = require("child_process");
|
|
9187
|
+
var import_util6 = require("util");
|
|
9188
|
+
var path21 = __toESM(require("path"));
|
|
9189
|
+
var execFileP6 = (0, import_util6.promisify)(import_child_process12.execFile);
|
|
8396
9190
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
8397
9191
|
function resetStdinForChild4() {
|
|
8398
9192
|
if (process.stdin.isTTY) {
|
|
@@ -8409,7 +9203,7 @@ var RailwayProvider = class {
|
|
|
8409
9203
|
available = true;
|
|
8410
9204
|
async authorize() {
|
|
8411
9205
|
try {
|
|
8412
|
-
await
|
|
9206
|
+
await execFileP6("railway", ["--version"], { maxBuffer: MAX_BUFFER4 });
|
|
8413
9207
|
} catch {
|
|
8414
9208
|
throw new Error(
|
|
8415
9209
|
[
|
|
@@ -8423,7 +9217,7 @@ var RailwayProvider = class {
|
|
|
8423
9217
|
);
|
|
8424
9218
|
}
|
|
8425
9219
|
try {
|
|
8426
|
-
await
|
|
9220
|
+
await execFileP6("railway", ["whoami"], { maxBuffer: MAX_BUFFER4 });
|
|
8427
9221
|
return;
|
|
8428
9222
|
} catch {
|
|
8429
9223
|
}
|
|
@@ -8433,7 +9227,7 @@ var RailwayProvider = class {
|
|
|
8433
9227
|
);
|
|
8434
9228
|
resetStdinForChild4();
|
|
8435
9229
|
await new Promise((resolve2, reject) => {
|
|
8436
|
-
const proc = (0,
|
|
9230
|
+
const proc = (0, import_child_process12.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
8437
9231
|
proc.on("exit", (code) => {
|
|
8438
9232
|
if (code === 0) resolve2();
|
|
8439
9233
|
else reject(new Error("railway login failed."));
|
|
@@ -8443,7 +9237,7 @@ var RailwayProvider = class {
|
|
|
8443
9237
|
}
|
|
8444
9238
|
async listProjects() {
|
|
8445
9239
|
try {
|
|
8446
|
-
const { stdout } = await
|
|
9240
|
+
const { stdout } = await execFileP6(
|
|
8447
9241
|
"railway",
|
|
8448
9242
|
["list", "--json"],
|
|
8449
9243
|
{ maxBuffer: MAX_BUFFER4 }
|
|
@@ -8459,7 +9253,7 @@ var RailwayProvider = class {
|
|
|
8459
9253
|
}));
|
|
8460
9254
|
} catch {
|
|
8461
9255
|
try {
|
|
8462
|
-
const { stdout } = await
|
|
9256
|
+
const { stdout } = await execFileP6("railway", ["list"], { maxBuffer: MAX_BUFFER4 });
|
|
8463
9257
|
const projects = [];
|
|
8464
9258
|
for (const line of stdout.split("\n")) {
|
|
8465
9259
|
const trimmed = line.trim();
|
|
@@ -8510,7 +9304,7 @@ var RailwayProvider = class {
|
|
|
8510
9304
|
async listExistingWorkspaces(projectId) {
|
|
8511
9305
|
if (!projectId) return [];
|
|
8512
9306
|
try {
|
|
8513
|
-
const { stdout } = await
|
|
9307
|
+
const { stdout } = await execFileP6(
|
|
8514
9308
|
"railway",
|
|
8515
9309
|
["service", "list", "--project", projectId, "--json"],
|
|
8516
9310
|
{ maxBuffer: MAX_BUFFER4 }
|
|
@@ -8535,7 +9329,7 @@ var RailwayProvider = class {
|
|
|
8535
9329
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
8536
9330
|
}
|
|
8537
9331
|
try {
|
|
8538
|
-
await
|
|
9332
|
+
await execFileP6(
|
|
8539
9333
|
"railway",
|
|
8540
9334
|
["service", "restart", "--service", serviceId, "--project", projectId],
|
|
8541
9335
|
{ maxBuffer: MAX_BUFFER4, timeout: 6e4 }
|
|
@@ -8554,7 +9348,7 @@ var RailwayProvider = class {
|
|
|
8554
9348
|
};
|
|
8555
9349
|
}
|
|
8556
9350
|
try {
|
|
8557
|
-
const { stdout, stderr } = await
|
|
9351
|
+
const { stdout, stderr } = await execFileP6(
|
|
8558
9352
|
"railway",
|
|
8559
9353
|
["run", "--project", projectId, "--service", serviceId, "--", "bash", "-lc", command2],
|
|
8560
9354
|
{ maxBuffer: MAX_BUFFER4, timeout: 6e5 }
|
|
@@ -8576,7 +9370,7 @@ var RailwayProvider = class {
|
|
|
8576
9370
|
}
|
|
8577
9371
|
resetStdinForChild4();
|
|
8578
9372
|
return new Promise((resolve2, reject) => {
|
|
8579
|
-
const proc = (0,
|
|
9373
|
+
const proc = (0, import_child_process12.spawn)(
|
|
8580
9374
|
"railway",
|
|
8581
9375
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
8582
9376
|
{ stdio: "inherit" }
|
|
@@ -8600,8 +9394,8 @@ var RailwayProvider = class {
|
|
|
8600
9394
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8601
9395
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
8602
9396
|
await new Promise((resolve2, reject) => {
|
|
8603
|
-
const tar = (0,
|
|
8604
|
-
const sh = (0,
|
|
9397
|
+
const tar = (0, import_child_process12.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
9398
|
+
const sh = (0, import_child_process12.spawn)(
|
|
8605
9399
|
"railway",
|
|
8606
9400
|
["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
|
|
8607
9401
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8627,14 +9421,14 @@ var RailwayProvider = class {
|
|
|
8627
9421
|
if (!projectId || !serviceId) {
|
|
8628
9422
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
8629
9423
|
}
|
|
8630
|
-
const remoteDir =
|
|
9424
|
+
const remoteDir = path21.posix.dirname(remotePath);
|
|
8631
9425
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
8632
9426
|
if (options.mode != null) {
|
|
8633
9427
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
8634
9428
|
}
|
|
8635
9429
|
const cmd = parts.join(" && ");
|
|
8636
9430
|
await new Promise((resolve2, reject) => {
|
|
8637
|
-
const proc = (0,
|
|
9431
|
+
const proc = (0, import_child_process12.spawn)(
|
|
8638
9432
|
"railway",
|
|
8639
9433
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
8640
9434
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8666,8 +9460,7 @@ var PROVIDERS = [
|
|
|
8666
9460
|
];
|
|
8667
9461
|
|
|
8668
9462
|
// src/commands/deploy.ts
|
|
8669
|
-
|
|
8670
|
-
async function deploy() {
|
|
9463
|
+
async function deploy(args2 = []) {
|
|
8671
9464
|
console.log();
|
|
8672
9465
|
mt(import_picocolors9.default.bgMagenta(import_picocolors9.default.white(" codeam deploy ")));
|
|
8673
9466
|
const provider = await pickProvider();
|
|
@@ -8819,13 +9612,14 @@ async function deploy() {
|
|
|
8819
9612
|
process.exit(1);
|
|
8820
9613
|
}
|
|
8821
9614
|
}
|
|
8822
|
-
const
|
|
8823
|
-
const
|
|
9615
|
+
const cfg = loadCliConfig();
|
|
9616
|
+
const agentId = parseAgentFlag(args2) ?? await promptForAgent(cfg.preferredAgent ?? "claude");
|
|
9617
|
+
const strategy = createDeployStrategy(agentId);
|
|
9618
|
+
const localCreds = await strategy.detectLocalCredentials();
|
|
8824
9619
|
let bridged = "none";
|
|
8825
|
-
if (
|
|
8826
|
-
const sourceLabel = localCredsKind === "flat-file" ? "~/.claude/.credentials.json" : "macOS Keychain";
|
|
9620
|
+
if (localCreds.source !== "none") {
|
|
8827
9621
|
const useLocal = await ot2({
|
|
8828
|
-
message: `Copy your local
|
|
9622
|
+
message: `Copy your local ${AGENT_REGISTRY[agentId].displayName} credentials (${localCreds.description}) to the workspace?`,
|
|
8829
9623
|
active: "Yes \u2014 same account, no re-auth",
|
|
8830
9624
|
inactive: "No \u2014 log in with a different account",
|
|
8831
9625
|
initialValue: true
|
|
@@ -8836,118 +9630,13 @@ async function deploy() {
|
|
|
8836
9630
|
}
|
|
8837
9631
|
if (useLocal) {
|
|
8838
9632
|
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
|
-
);
|
|
9633
|
+
credStep.start(`Bridging ${AGENT_REGISTRY[agentId].displayName} credentials\u2026`);
|
|
9634
|
+
const result = await strategy.bridgeLocalCredentials(provider, workspace.id);
|
|
9635
|
+
bridged = result.source;
|
|
9636
|
+
credStep.stop(`\u2713 Credentials staged (${bridged})`);
|
|
8949
9637
|
}
|
|
8950
9638
|
}
|
|
9639
|
+
await strategy.setupOnWorkspace(provider, workspace.id, { bridged });
|
|
8951
9640
|
const cliStep = fe();
|
|
8952
9641
|
cliStep.start("Installing codeam-cli on workspace\u2026");
|
|
8953
9642
|
const cliInstall = await provider.exec(workspace.id, "npm install -g codeam-cli@latest");
|
|
@@ -8962,8 +9651,8 @@ async function deploy() {
|
|
|
8962
9651
|
`Workspace: ${import_picocolors9.default.cyan(workspace.displayName ?? workspace.id)}`,
|
|
8963
9652
|
workspace.webUrl ? `Web: ${import_picocolors9.default.cyan(workspace.webUrl)}` : "",
|
|
8964
9653
|
"",
|
|
8965
|
-
|
|
8966
|
-
"Scan the QR code
|
|
9654
|
+
`Starting \`codeam pair\` on the workspace (agent: ${AGENT_REGISTRY[agentId].displayName}).`,
|
|
9655
|
+
"Scan the QR code from your phone to pair.",
|
|
8967
9656
|
import_picocolors9.default.dim("(Once paired, this terminal disconnects automatically; the session stays alive on the codespace.)")
|
|
8968
9657
|
].filter(Boolean).join("\n"),
|
|
8969
9658
|
"Almost there"
|
|
@@ -8973,10 +9662,9 @@ async function deploy() {
|
|
|
8973
9662
|
"LOG=~/.codeam-deploy/session.log",
|
|
8974
9663
|
': > "$LOG"',
|
|
8975
9664
|
// The default `gh codespace ssh` cwd is the repo root
|
|
8976
|
-
// (/workspaces/<repo>), which is exactly where
|
|
9665
|
+
// (/workspaces/<repo>), which is exactly where the agent needs to
|
|
8977
9666
|
// run so it can read/edit project files. Pass that to PM2 via
|
|
8978
|
-
// --cwd so the relay's child
|
|
8979
|
-
// working directory.
|
|
9667
|
+
// --cwd so the relay's child agent inherits the right directory.
|
|
8980
9668
|
'PROJECT_DIR="$(pwd)"',
|
|
8981
9669
|
// Install PM2 if it isn't already on PATH. Idempotent.
|
|
8982
9670
|
"if ! command -v pm2 >/dev/null 2>&1; then",
|
|
@@ -8988,11 +9676,11 @@ async function deploy() {
|
|
|
8988
9676
|
// Start codeam pair under PM2. `--merge-logs` writes stdout
|
|
8989
9677
|
// and stderr to the same file so we only need one tail.
|
|
8990
9678
|
// --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
|
-
|
|
9679
|
+
// can't start (e.g. backend unreachable) — three attempts is enough
|
|
9680
|
+
// for transient flakes.
|
|
9681
|
+
// No `--time` (would prefix every line with a timestamp); no
|
|
9682
|
+
// `--no-pmx` either (default off).
|
|
9683
|
+
`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
9684
|
// Give PM2 a moment to spawn the process before we start polling
|
|
8997
9685
|
// status — otherwise the very first jlist can race the spawn.
|
|
8998
9686
|
"sleep 2",
|
|
@@ -9090,83 +9778,6 @@ async function deploy() {
|
|
|
9090
9778
|
function shellQuoteSingle(s) {
|
|
9091
9779
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
9092
9780
|
}
|
|
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
9781
|
function formatLastUsed(iso) {
|
|
9171
9782
|
if (!iso) return "";
|
|
9172
9783
|
const t2 = Date.parse(iso);
|
|
@@ -9352,7 +9963,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
9352
9963
|
// src/commands/version.ts
|
|
9353
9964
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
9354
9965
|
function version() {
|
|
9355
|
-
const v = true ? "2.
|
|
9966
|
+
const v = true ? "2.12.0" : "unknown";
|
|
9356
9967
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
9357
9968
|
}
|
|
9358
9969
|
|
|
@@ -9389,9 +10000,9 @@ function help() {
|
|
|
9389
10000
|
}
|
|
9390
10001
|
|
|
9391
10002
|
// src/lib/updateNotifier.ts
|
|
9392
|
-
var
|
|
9393
|
-
var
|
|
9394
|
-
var
|
|
10003
|
+
var fs16 = __toESM(require("fs"));
|
|
10004
|
+
var os15 = __toESM(require("os"));
|
|
10005
|
+
var path22 = __toESM(require("path"));
|
|
9395
10006
|
var https5 = __toESM(require("https"));
|
|
9396
10007
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
9397
10008
|
var PKG_NAME = "codeam-cli";
|
|
@@ -9399,12 +10010,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
|
9399
10010
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9400
10011
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
9401
10012
|
function cachePath() {
|
|
9402
|
-
const dir =
|
|
9403
|
-
return
|
|
10013
|
+
const dir = path22.join(os15.homedir(), ".codeam");
|
|
10014
|
+
return path22.join(dir, "update-check.json");
|
|
9404
10015
|
}
|
|
9405
10016
|
function readCache() {
|
|
9406
10017
|
try {
|
|
9407
|
-
const raw =
|
|
10018
|
+
const raw = fs16.readFileSync(cachePath(), "utf8");
|
|
9408
10019
|
const parsed = JSON.parse(raw);
|
|
9409
10020
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
9410
10021
|
return parsed;
|
|
@@ -9415,8 +10026,8 @@ function readCache() {
|
|
|
9415
10026
|
function writeCache(cache) {
|
|
9416
10027
|
try {
|
|
9417
10028
|
const file = cachePath();
|
|
9418
|
-
|
|
9419
|
-
|
|
10029
|
+
fs16.mkdirSync(path22.dirname(file), { recursive: true });
|
|
10030
|
+
fs16.writeFileSync(file, JSON.stringify(cache));
|
|
9420
10031
|
} catch {
|
|
9421
10032
|
}
|
|
9422
10033
|
}
|
|
@@ -9487,7 +10098,7 @@ function checkForUpdates() {
|
|
|
9487
10098
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
9488
10099
|
if (process.env.CI) return;
|
|
9489
10100
|
if (!process.stdout.isTTY) return;
|
|
9490
|
-
const current = true ? "2.
|
|
10101
|
+
const current = true ? "2.12.0" : null;
|
|
9491
10102
|
if (!current) return;
|
|
9492
10103
|
const cache = readCache();
|
|
9493
10104
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -9516,7 +10127,7 @@ async function main() {
|
|
|
9516
10127
|
case "help":
|
|
9517
10128
|
return help();
|
|
9518
10129
|
case "pair":
|
|
9519
|
-
return pair();
|
|
10130
|
+
return pair(args);
|
|
9520
10131
|
case "pair-auto":
|
|
9521
10132
|
return pairAuto(args);
|
|
9522
10133
|
case "sessions":
|
|
@@ -9528,7 +10139,7 @@ async function main() {
|
|
|
9528
10139
|
case "deploy":
|
|
9529
10140
|
if (args[0] === "ls" || args[0] === "list") return deployList();
|
|
9530
10141
|
if (args[0] === "stop" || args[0] === "remove") return deployStop();
|
|
9531
|
-
return deploy();
|
|
10142
|
+
return deploy(args);
|
|
9532
10143
|
default:
|
|
9533
10144
|
return start();
|
|
9534
10145
|
}
|