codeam-cli 2.4.39 → 2.5.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/dist/index.js +1159 -1067
- 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 fs12 = require("fs");
|
|
523
|
+
var os11 = require("os");
|
|
524
|
+
var path19 = 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 = path19.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 = fs12.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(path19.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(os11.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 fs12 = require("fs");
|
|
1016
|
+
var path19 = 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 = path19.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
|
+
fs12.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 spawn10(file, args2, opt) {
|
|
1317
1317
|
return new terminalCtor(file, args2, opt);
|
|
1318
1318
|
}
|
|
1319
|
-
exports2.spawn =
|
|
1319
|
+
exports2.spawn = spawn10;
|
|
1320
1320
|
function fork(file, args2, opt) {
|
|
1321
1321
|
return new terminalCtor(file, args2, opt);
|
|
1322
1322
|
}
|
|
@@ -1390,11 +1390,6 @@ var require_src = __commonJS({
|
|
|
1390
1390
|
});
|
|
1391
1391
|
|
|
1392
1392
|
// src/commands/start.ts
|
|
1393
|
-
var fs8 = __toESM(require("fs"));
|
|
1394
|
-
var os7 = __toESM(require("os"));
|
|
1395
|
-
var path11 = __toESM(require("path"));
|
|
1396
|
-
var import_crypto = require("crypto");
|
|
1397
|
-
var import_child_process5 = require("child_process");
|
|
1398
1393
|
var import_picocolors2 = __toESM(require("picocolors"));
|
|
1399
1394
|
|
|
1400
1395
|
// src/config.ts
|
|
@@ -1482,7 +1477,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
1482
1477
|
// package.json
|
|
1483
1478
|
var package_default = {
|
|
1484
1479
|
name: "codeam-cli",
|
|
1485
|
-
version: "2.
|
|
1480
|
+
version: "2.5.0",
|
|
1486
1481
|
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.",
|
|
1487
1482
|
type: "commonjs",
|
|
1488
1483
|
main: "dist/index.js",
|
|
@@ -1601,8 +1596,14 @@ function showPairingCode(code, expiresAt) {
|
|
|
1601
1596
|
console.log("");
|
|
1602
1597
|
}
|
|
1603
1598
|
|
|
1604
|
-
// src/services/
|
|
1605
|
-
var
|
|
1599
|
+
// src/services/command-relay.service.ts
|
|
1600
|
+
var https2 = __toESM(require("https"));
|
|
1601
|
+
var http2 = __toESM(require("http"));
|
|
1602
|
+
|
|
1603
|
+
// src/services/pairing.service.ts
|
|
1604
|
+
var https = __toESM(require("https"));
|
|
1605
|
+
var http = __toESM(require("http"));
|
|
1606
|
+
var os2 = __toESM(require("os"));
|
|
1606
1607
|
|
|
1607
1608
|
// src/lib/poll-delay.ts
|
|
1608
1609
|
var MAX_DELAY_MS = 3e4;
|
|
@@ -1612,175 +1613,17 @@ function computePollDelay({ baseMs, failures }) {
|
|
|
1612
1613
|
return Math.round(jitter);
|
|
1613
1614
|
}
|
|
1614
1615
|
|
|
1615
|
-
// src/services/logger.ts
|
|
1616
|
-
var fs2 = __toESM(require("fs"));
|
|
1617
|
-
var os2 = __toESM(require("os"));
|
|
1618
|
-
var path2 = __toESM(require("path"));
|
|
1619
|
-
var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4, trace: 5 };
|
|
1620
|
-
function currentLevel() {
|
|
1621
|
-
if (process.env.CODEAM_DEBUG === "1") return LEVELS.trace;
|
|
1622
|
-
const raw = (process.env.CODEAM_LOG ?? "error").toLowerCase();
|
|
1623
|
-
return LEVELS[raw] ?? LEVELS.error;
|
|
1624
|
-
}
|
|
1625
|
-
var fileEnabled = process.env.CODEAM_DEBUG === "1" || process.env.CODEAM_LOG === "debug" || process.env.CODEAM_LOG === "trace";
|
|
1626
|
-
var debugFilePath = path2.join(os2.homedir(), ".codeam", "debug.log");
|
|
1627
|
-
var fileInitialized = false;
|
|
1628
|
-
function appendToFile(line) {
|
|
1629
|
-
if (!fileEnabled) return;
|
|
1630
|
-
try {
|
|
1631
|
-
if (!fileInitialized) {
|
|
1632
|
-
fs2.mkdirSync(path2.dirname(debugFilePath), { recursive: true });
|
|
1633
|
-
fs2.writeFileSync(
|
|
1634
|
-
debugFilePath,
|
|
1635
|
-
`=== codeam debug log \u2014 pid ${process.pid} \u2014 ${(/* @__PURE__ */ new Date()).toISOString()} ===
|
|
1636
|
-
platform=${process.platform} node=${process.version} cwd=${process.cwd()}
|
|
1637
|
-
|
|
1638
|
-
`
|
|
1639
|
-
);
|
|
1640
|
-
fileInitialized = true;
|
|
1641
|
-
}
|
|
1642
|
-
fs2.appendFileSync(debugFilePath, line);
|
|
1643
|
-
} catch {
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
function emit(level, tag, msg, err) {
|
|
1647
|
-
if (LEVELS[level] > currentLevel()) return;
|
|
1648
|
-
const detail = err instanceof Error ? `: ${err.message}` : err !== void 0 ? `: ${String(err)}` : "";
|
|
1649
|
-
const line = `[codeam:${level}] ${tag} \u2014 ${msg}${detail}
|
|
1650
|
-
`;
|
|
1651
|
-
process.stderr.write(line);
|
|
1652
|
-
if (LEVELS[level] >= LEVELS.debug) {
|
|
1653
|
-
appendToFile(`${(/* @__PURE__ */ new Date()).toISOString()} ${line}`);
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
var log = {
|
|
1657
|
-
error: (tag, msg, err) => emit("error", tag, msg, err),
|
|
1658
|
-
warn: (tag, msg, err) => emit("warn", tag, msg, err),
|
|
1659
|
-
info: (tag, msg, err) => emit("info", tag, msg, err),
|
|
1660
|
-
debug: (tag, msg, err) => emit("debug", tag, msg, err),
|
|
1661
|
-
/**
|
|
1662
|
-
* Verbose pipeline breadcrumb. Only fires when CODEAM_LOG=trace or
|
|
1663
|
-
* CODEAM_DEBUG=1, so call sites can be liberal — they have zero
|
|
1664
|
-
* cost in normal runs.
|
|
1665
|
-
*/
|
|
1666
|
-
trace: (tag, msg, err) => emit("trace", tag, msg, err)
|
|
1667
|
-
};
|
|
1668
|
-
|
|
1669
|
-
// src/services/websocket.service.ts
|
|
1670
|
-
var API_BASE = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
1671
|
-
var WS_URL = API_BASE.replace("https://", "wss://").replace("http://", "ws://") + "/api/ws";
|
|
1672
|
-
var HEARTBEAT_MS = 3e4;
|
|
1673
|
-
var MAX_RECONNECT = 10;
|
|
1674
|
-
var WebSocketService = class {
|
|
1675
|
-
constructor(sessionId, pluginId) {
|
|
1676
|
-
this.sessionId = sessionId;
|
|
1677
|
-
this.pluginId = pluginId;
|
|
1678
|
-
}
|
|
1679
|
-
sessionId;
|
|
1680
|
-
pluginId;
|
|
1681
|
-
client = null;
|
|
1682
|
-
heartbeat = null;
|
|
1683
|
-
reconnectTimer = null;
|
|
1684
|
-
reconnectAttempts = 0;
|
|
1685
|
-
handlers = [];
|
|
1686
|
-
_connected = false;
|
|
1687
|
-
get connected() {
|
|
1688
|
-
return this._connected;
|
|
1689
|
-
}
|
|
1690
|
-
addHandler(h) {
|
|
1691
|
-
this.handlers.push(h);
|
|
1692
|
-
}
|
|
1693
|
-
connect() {
|
|
1694
|
-
this.disconnect();
|
|
1695
|
-
try {
|
|
1696
|
-
this.client = new import_ws.default(WS_URL);
|
|
1697
|
-
this.client.on("open", () => {
|
|
1698
|
-
log.trace("ws", `connected to ${WS_URL}`);
|
|
1699
|
-
this._connected = true;
|
|
1700
|
-
this.reconnectAttempts = 0;
|
|
1701
|
-
this.client.send(JSON.stringify({
|
|
1702
|
-
type: "auth",
|
|
1703
|
-
payload: { sessionId: this.sessionId, pluginId: this.pluginId },
|
|
1704
|
-
timestamp: Date.now()
|
|
1705
|
-
}));
|
|
1706
|
-
this.startHeartbeat();
|
|
1707
|
-
this.handlers.forEach((h) => h.onConnected());
|
|
1708
|
-
});
|
|
1709
|
-
this.client.on("message", (raw) => {
|
|
1710
|
-
try {
|
|
1711
|
-
const msg = JSON.parse(raw.toString());
|
|
1712
|
-
if (msg.type === "pong" || msg.type === "auth_success" || msg.type === "auth_error") {
|
|
1713
|
-
log.trace("ws", `meta msg type=${msg.type}`);
|
|
1714
|
-
return;
|
|
1715
|
-
}
|
|
1716
|
-
log.trace("ws", `dispatch msg type=${msg.type}`);
|
|
1717
|
-
this.handlers.forEach((h) => h.onMessage(msg.type, msg.payload ?? {}));
|
|
1718
|
-
} catch (err) {
|
|
1719
|
-
log.trace("ws", "malformed message", err);
|
|
1720
|
-
}
|
|
1721
|
-
});
|
|
1722
|
-
this.client.on("close", (code, reason) => {
|
|
1723
|
-
log.trace("ws", `closed code=${code} reason=${reason?.toString() || "(empty)"}`);
|
|
1724
|
-
this._connected = false;
|
|
1725
|
-
this.stopHeartbeat();
|
|
1726
|
-
this.handlers.forEach((h) => h.onDisconnected());
|
|
1727
|
-
if (this.reconnectAttempts < MAX_RECONNECT) {
|
|
1728
|
-
this.reconnectAttempts++;
|
|
1729
|
-
const delay = computePollDelay({ baseMs: 1e3, failures: this.reconnectAttempts });
|
|
1730
|
-
this.reconnectTimer = setTimeout(() => this.connect(), delay);
|
|
1731
|
-
}
|
|
1732
|
-
});
|
|
1733
|
-
this.client.on("error", (err) => {
|
|
1734
|
-
log.trace("ws", "error", err);
|
|
1735
|
-
});
|
|
1736
|
-
} catch (err) {
|
|
1737
|
-
log.trace("ws", "sync connect threw", err);
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
send(type, payload) {
|
|
1741
|
-
if (!this._connected || !this.client) return;
|
|
1742
|
-
this.client.send(JSON.stringify({ type, payload, timestamp: Date.now() }));
|
|
1743
|
-
}
|
|
1744
|
-
disconnect() {
|
|
1745
|
-
if (this.reconnectTimer) {
|
|
1746
|
-
clearTimeout(this.reconnectTimer);
|
|
1747
|
-
this.reconnectTimer = null;
|
|
1748
|
-
}
|
|
1749
|
-
this.reconnectAttempts = 0;
|
|
1750
|
-
this.stopHeartbeat();
|
|
1751
|
-
this.client?.removeAllListeners();
|
|
1752
|
-
this.client?.close();
|
|
1753
|
-
this.client = null;
|
|
1754
|
-
this._connected = false;
|
|
1755
|
-
}
|
|
1756
|
-
startHeartbeat() {
|
|
1757
|
-
this.stopHeartbeat();
|
|
1758
|
-
this.heartbeat = setInterval(() => {
|
|
1759
|
-
if (this._connected) this.client?.send(JSON.stringify({ type: "ping", timestamp: Date.now() }));
|
|
1760
|
-
}, HEARTBEAT_MS);
|
|
1761
|
-
}
|
|
1762
|
-
stopHeartbeat() {
|
|
1763
|
-
if (this.heartbeat) {
|
|
1764
|
-
clearInterval(this.heartbeat);
|
|
1765
|
-
this.heartbeat = null;
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
};
|
|
1769
|
-
|
|
1770
1616
|
// src/services/pairing.service.ts
|
|
1771
|
-
var
|
|
1772
|
-
var http = __toESM(require("http"));
|
|
1773
|
-
var os3 = __toESM(require("os"));
|
|
1774
|
-
var API_BASE2 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
1617
|
+
var API_BASE = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
1775
1618
|
async function requestCode(pluginId) {
|
|
1776
1619
|
try {
|
|
1777
1620
|
const runtime = process.env.CODESPACES === "true" ? "github-codespaces" : "local";
|
|
1778
1621
|
const codespaceName = process.env.CODESPACE_NAME;
|
|
1779
|
-
const result = await _transport.postJson(`${
|
|
1622
|
+
const result = await _transport.postJson(`${API_BASE}/api/pairing/code`, {
|
|
1780
1623
|
pluginId,
|
|
1781
1624
|
ideName: "Terminal (codeam-cli)",
|
|
1782
1625
|
ideVersion: package_default.version,
|
|
1783
|
-
hostname:
|
|
1626
|
+
hostname: os2.hostname(),
|
|
1784
1627
|
runtime,
|
|
1785
1628
|
...codespaceName ? { codespaceName } : {}
|
|
1786
1629
|
});
|
|
@@ -1799,7 +1642,7 @@ function pollStatus(pluginId, onPaired, onTimeout) {
|
|
|
1799
1642
|
if (stopped) return;
|
|
1800
1643
|
try {
|
|
1801
1644
|
const result = await _transport.getJson(
|
|
1802
|
-
`${
|
|
1645
|
+
`${API_BASE}/api/pairing/status?pluginId=${pluginId}`
|
|
1803
1646
|
);
|
|
1804
1647
|
consecutiveFailures = 0;
|
|
1805
1648
|
const data = result?.data;
|
|
@@ -1932,8 +1775,62 @@ async function _getJson(url) {
|
|
|
1932
1775
|
});
|
|
1933
1776
|
}
|
|
1934
1777
|
|
|
1778
|
+
// src/services/logger.ts
|
|
1779
|
+
var fs2 = __toESM(require("fs"));
|
|
1780
|
+
var os3 = __toESM(require("os"));
|
|
1781
|
+
var path2 = __toESM(require("path"));
|
|
1782
|
+
var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4, trace: 5 };
|
|
1783
|
+
function currentLevel() {
|
|
1784
|
+
if (process.env.CODEAM_DEBUG === "1") return LEVELS.trace;
|
|
1785
|
+
const raw = (process.env.CODEAM_LOG ?? "error").toLowerCase();
|
|
1786
|
+
return LEVELS[raw] ?? LEVELS.error;
|
|
1787
|
+
}
|
|
1788
|
+
var fileEnabled = process.env.CODEAM_DEBUG === "1" || process.env.CODEAM_LOG === "debug" || process.env.CODEAM_LOG === "trace";
|
|
1789
|
+
var debugFilePath = path2.join(os3.homedir(), ".codeam", "debug.log");
|
|
1790
|
+
var fileInitialized = false;
|
|
1791
|
+
function appendToFile(line) {
|
|
1792
|
+
if (!fileEnabled) return;
|
|
1793
|
+
try {
|
|
1794
|
+
if (!fileInitialized) {
|
|
1795
|
+
fs2.mkdirSync(path2.dirname(debugFilePath), { recursive: true });
|
|
1796
|
+
fs2.writeFileSync(
|
|
1797
|
+
debugFilePath,
|
|
1798
|
+
`=== codeam debug log \u2014 pid ${process.pid} \u2014 ${(/* @__PURE__ */ new Date()).toISOString()} ===
|
|
1799
|
+
platform=${process.platform} node=${process.version} cwd=${process.cwd()}
|
|
1800
|
+
|
|
1801
|
+
`
|
|
1802
|
+
);
|
|
1803
|
+
fileInitialized = true;
|
|
1804
|
+
}
|
|
1805
|
+
fs2.appendFileSync(debugFilePath, line);
|
|
1806
|
+
} catch {
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
function emit(level, tag, msg, err) {
|
|
1810
|
+
if (LEVELS[level] > currentLevel()) return;
|
|
1811
|
+
const detail = err instanceof Error ? `: ${err.message}` : err !== void 0 ? `: ${String(err)}` : "";
|
|
1812
|
+
const line = `[codeam:${level}] ${tag} \u2014 ${msg}${detail}
|
|
1813
|
+
`;
|
|
1814
|
+
process.stderr.write(line);
|
|
1815
|
+
if (LEVELS[level] >= LEVELS.debug) {
|
|
1816
|
+
appendToFile(`${(/* @__PURE__ */ new Date()).toISOString()} ${line}`);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
var log = {
|
|
1820
|
+
error: (tag, msg, err) => emit("error", tag, msg, err),
|
|
1821
|
+
warn: (tag, msg, err) => emit("warn", tag, msg, err),
|
|
1822
|
+
info: (tag, msg, err) => emit("info", tag, msg, err),
|
|
1823
|
+
debug: (tag, msg, err) => emit("debug", tag, msg, err),
|
|
1824
|
+
/**
|
|
1825
|
+
* Verbose pipeline breadcrumb. Only fires when CODEAM_LOG=trace or
|
|
1826
|
+
* CODEAM_DEBUG=1, so call sites can be liberal — they have zero
|
|
1827
|
+
* cost in normal runs.
|
|
1828
|
+
*/
|
|
1829
|
+
trace: (tag, msg, err) => emit("trace", tag, msg, err)
|
|
1830
|
+
};
|
|
1831
|
+
|
|
1935
1832
|
// src/services/command-relay.service.ts
|
|
1936
|
-
var
|
|
1833
|
+
var API_BASE2 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
1937
1834
|
var CommandRelayService = class {
|
|
1938
1835
|
constructor(pluginId, onCommand) {
|
|
1939
1836
|
this.pluginId = pluginId;
|
|
@@ -1942,108 +1839,179 @@ var CommandRelayService = class {
|
|
|
1942
1839
|
pluginId;
|
|
1943
1840
|
onCommand;
|
|
1944
1841
|
_running = false;
|
|
1945
|
-
pollTimer = null;
|
|
1946
1842
|
heartbeatTimer = null;
|
|
1947
1843
|
agentsTimer = null;
|
|
1948
|
-
consecutiveFailures = 0;
|
|
1949
1844
|
/** True once `/api/plugin/agents` has accepted at least one report. */
|
|
1950
1845
|
agentsRegistered = false;
|
|
1846
|
+
/** SSE connection (null when on the polling fallback or stopped). */
|
|
1847
|
+
sseRequest = null;
|
|
1848
|
+
/** Polling backoff state (only used on the fallback). */
|
|
1849
|
+
pollTimer = null;
|
|
1850
|
+
pollFailures = 0;
|
|
1851
|
+
/** Reconnect backoff state for the SSE stream. */
|
|
1852
|
+
sseFailures = 0;
|
|
1853
|
+
sseReconnectTimer = null;
|
|
1951
1854
|
start() {
|
|
1952
|
-
|
|
1953
|
-
clearTimeout(this.pollTimer);
|
|
1954
|
-
this.pollTimer = null;
|
|
1955
|
-
}
|
|
1956
|
-
if (this.heartbeatTimer) {
|
|
1957
|
-
clearInterval(this.heartbeatTimer);
|
|
1958
|
-
this.heartbeatTimer = null;
|
|
1959
|
-
}
|
|
1960
|
-
if (this.agentsTimer) {
|
|
1961
|
-
clearInterval(this.agentsTimer);
|
|
1962
|
-
this.agentsTimer = null;
|
|
1963
|
-
}
|
|
1855
|
+
this.cleanup();
|
|
1964
1856
|
this._running = true;
|
|
1965
1857
|
this.agentsRegistered = false;
|
|
1966
1858
|
this.sendHeartbeat(true);
|
|
1967
1859
|
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(true), 2e4);
|
|
1968
|
-
void this.pollLoop();
|
|
1969
|
-
this.reportAgents();
|
|
1970
1860
|
this.agentsTimer = setInterval(() => {
|
|
1971
1861
|
if (this._running && !this.agentsRegistered) this.reportAgents();
|
|
1972
1862
|
}, 5e3);
|
|
1863
|
+
this.reportAgents();
|
|
1864
|
+
if (process.env.NODE_ENV === "test" || process.env.CODEAM_DISABLE_SSE_PULL === "1") {
|
|
1865
|
+
this.startPollingFallback();
|
|
1866
|
+
} else {
|
|
1867
|
+
this.connectSSE();
|
|
1868
|
+
}
|
|
1973
1869
|
}
|
|
1974
1870
|
stop() {
|
|
1975
1871
|
if (!this._running) return;
|
|
1976
1872
|
this._running = false;
|
|
1977
|
-
|
|
1978
|
-
clearTimeout(this.pollTimer);
|
|
1979
|
-
this.pollTimer = null;
|
|
1980
|
-
}
|
|
1981
|
-
if (this.heartbeatTimer) {
|
|
1982
|
-
clearInterval(this.heartbeatTimer);
|
|
1983
|
-
this.heartbeatTimer = null;
|
|
1984
|
-
}
|
|
1985
|
-
if (this.agentsTimer) {
|
|
1986
|
-
clearInterval(this.agentsTimer);
|
|
1987
|
-
this.agentsTimer = null;
|
|
1988
|
-
}
|
|
1873
|
+
this.cleanup();
|
|
1989
1874
|
this.sendHeartbeat(false).catch(() => {
|
|
1990
1875
|
});
|
|
1991
1876
|
}
|
|
1992
1877
|
async sendResult(commandId, status2, result) {
|
|
1993
|
-
await _postJson(`${
|
|
1878
|
+
await _postJson(`${API_BASE2}/api/commands/result`, { commandId, status: status2, result });
|
|
1879
|
+
}
|
|
1880
|
+
// ─── SSE pull (primary) ──────────────────────────────────────────
|
|
1881
|
+
connectSSE() {
|
|
1882
|
+
if (!this._running) return;
|
|
1883
|
+
const url = new URL(`${API_BASE2}/api/commands/pending/stream`);
|
|
1884
|
+
url.searchParams.set("pluginId", this.pluginId);
|
|
1885
|
+
const transport = url.protocol === "https:" ? https2 : http2;
|
|
1886
|
+
log.trace("relay", `sse connect ${url.pathname}`);
|
|
1887
|
+
const req = transport.request(
|
|
1888
|
+
{
|
|
1889
|
+
hostname: url.hostname,
|
|
1890
|
+
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
1891
|
+
path: `${url.pathname}${url.search}`,
|
|
1892
|
+
method: "GET",
|
|
1893
|
+
headers: { Accept: "text/event-stream", "Cache-Control": "no-cache" },
|
|
1894
|
+
timeout: 35e3
|
|
1895
|
+
},
|
|
1896
|
+
(res) => {
|
|
1897
|
+
if (res.statusCode !== 200) {
|
|
1898
|
+
log.trace("relay", `sse status=${res.statusCode}`);
|
|
1899
|
+
res.resume();
|
|
1900
|
+
this.sseFailures += 1;
|
|
1901
|
+
if (this.sseFailures >= 2) {
|
|
1902
|
+
log.trace("relay", "sse unavailable, falling back to polling");
|
|
1903
|
+
this.startPollingFallback();
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
this.scheduleSseReconnect();
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
this.sseFailures = 0;
|
|
1910
|
+
let buffer = "";
|
|
1911
|
+
res.setEncoding("utf8");
|
|
1912
|
+
res.on("data", (chunk) => {
|
|
1913
|
+
buffer += chunk;
|
|
1914
|
+
let frameEnd;
|
|
1915
|
+
while ((frameEnd = buffer.indexOf("\n\n")) !== -1) {
|
|
1916
|
+
const frame = buffer.slice(0, frameEnd);
|
|
1917
|
+
buffer = buffer.slice(frameEnd + 2);
|
|
1918
|
+
this.handleSseFrame(frame);
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1921
|
+
res.on("end", () => {
|
|
1922
|
+
if (this._running) this.scheduleSseReconnect();
|
|
1923
|
+
});
|
|
1924
|
+
res.on("error", () => {
|
|
1925
|
+
if (this._running) this.scheduleSseReconnect();
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
);
|
|
1929
|
+
req.on("error", (err) => {
|
|
1930
|
+
log.trace("relay", "sse req error", err);
|
|
1931
|
+
this.sseFailures += 1;
|
|
1932
|
+
if (this.sseFailures >= 2) {
|
|
1933
|
+
this.startPollingFallback();
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
this.scheduleSseReconnect();
|
|
1937
|
+
});
|
|
1938
|
+
req.on("timeout", () => {
|
|
1939
|
+
req.destroy();
|
|
1940
|
+
});
|
|
1941
|
+
req.end();
|
|
1942
|
+
this.sseRequest = req;
|
|
1943
|
+
}
|
|
1944
|
+
handleSseFrame(frame) {
|
|
1945
|
+
let event = "message";
|
|
1946
|
+
let data = "";
|
|
1947
|
+
for (const line of frame.split("\n")) {
|
|
1948
|
+
if (line.startsWith("event: ")) event = line.slice(7).trim();
|
|
1949
|
+
else if (line.startsWith("data: ")) data += line.slice(6);
|
|
1950
|
+
}
|
|
1951
|
+
if (event !== "commands" || !data) return;
|
|
1952
|
+
try {
|
|
1953
|
+
const parsed = JSON.parse(data);
|
|
1954
|
+
const commands = parsed.commands ?? [];
|
|
1955
|
+
if (commands.length === 0) return;
|
|
1956
|
+
log.trace("relay", `sse received ${commands.length} command(s)`);
|
|
1957
|
+
void this.dispatchCommands(commands);
|
|
1958
|
+
} catch (err) {
|
|
1959
|
+
log.trace("relay", "sse parse error", err);
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
scheduleSseReconnect() {
|
|
1963
|
+
if (this.sseReconnectTimer) return;
|
|
1964
|
+
const delay = computePollDelay({ baseMs: 1e3, failures: this.sseFailures });
|
|
1965
|
+
this.sseReconnectTimer = setTimeout(() => {
|
|
1966
|
+
this.sseReconnectTimer = null;
|
|
1967
|
+
this.connectSSE();
|
|
1968
|
+
}, delay);
|
|
1969
|
+
}
|
|
1970
|
+
// ─── Polling fallback ────────────────────────────────────────────
|
|
1971
|
+
startPollingFallback() {
|
|
1972
|
+
if (this.pollTimer) return;
|
|
1973
|
+
void this.pollLoop();
|
|
1994
1974
|
}
|
|
1995
1975
|
async pollLoop() {
|
|
1996
1976
|
if (!this._running) return;
|
|
1997
|
-
await this.
|
|
1977
|
+
await this.pollOnce();
|
|
1998
1978
|
if (this._running) {
|
|
1999
|
-
const delay = computePollDelay({
|
|
2000
|
-
baseMs: 2e3,
|
|
2001
|
-
failures: this.consecutiveFailures
|
|
2002
|
-
});
|
|
1979
|
+
const delay = computePollDelay({ baseMs: 2e3, failures: this.pollFailures });
|
|
2003
1980
|
this.pollTimer = setTimeout(() => this.pollLoop(), delay);
|
|
2004
1981
|
}
|
|
2005
1982
|
}
|
|
2006
|
-
async
|
|
1983
|
+
async pollOnce() {
|
|
2007
1984
|
try {
|
|
2008
|
-
const data = await _getJson(
|
|
2009
|
-
`${API_BASE3}/api/commands/pending?pluginId=${this.pluginId}`
|
|
2010
|
-
);
|
|
1985
|
+
const data = await _getJson(`${API_BASE2}/api/commands/pending?pluginId=${this.pluginId}`);
|
|
2011
1986
|
const commands = data?.data;
|
|
2012
|
-
this.
|
|
2013
|
-
if (!Array.isArray(commands)) return;
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
}
|
|
2017
|
-
for (const obj of commands) {
|
|
2018
|
-
try {
|
|
2019
|
-
log.trace("relay", `dispatch type=${obj.type} id=${obj.id}`);
|
|
2020
|
-
await this.onCommand({
|
|
2021
|
-
id: obj.id,
|
|
2022
|
-
sessionId: obj.sessionId,
|
|
2023
|
-
type: obj.type,
|
|
2024
|
-
payload: obj.payload ?? {}
|
|
2025
|
-
});
|
|
2026
|
-
} catch (err) {
|
|
2027
|
-
log.trace("relay", `command handler threw`, err);
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
1987
|
+
this.pollFailures = 0;
|
|
1988
|
+
if (!Array.isArray(commands) || commands.length === 0) return;
|
|
1989
|
+
log.trace("relay", `poll received ${commands.length} command(s)`);
|
|
1990
|
+
await this.dispatchCommands(commands);
|
|
2030
1991
|
} catch (err) {
|
|
2031
|
-
this.
|
|
2032
|
-
log.trace(
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
1992
|
+
this.pollFailures += 1;
|
|
1993
|
+
log.trace("relay", `poll failed (failures=${this.pollFailures})`, err);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
async dispatchCommands(commands) {
|
|
1997
|
+
for (const cmd of commands) {
|
|
1998
|
+
try {
|
|
1999
|
+
log.trace("relay", `dispatch type=${cmd.type} id=${cmd.id}`);
|
|
2000
|
+
await this.onCommand(cmd);
|
|
2001
|
+
} catch (err) {
|
|
2002
|
+
log.trace("relay", "command handler threw", err);
|
|
2003
|
+
}
|
|
2037
2004
|
}
|
|
2038
2005
|
}
|
|
2006
|
+
// ─── Heartbeat + agents ──────────────────────────────────────────
|
|
2039
2007
|
async sendHeartbeat(online) {
|
|
2040
|
-
await _postJson(`${
|
|
2008
|
+
await _postJson(`${API_BASE2}/api/plugin/heartbeat`, {
|
|
2041
2009
|
pluginId: this.pluginId,
|
|
2042
2010
|
online
|
|
2043
2011
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
2044
2012
|
}
|
|
2045
2013
|
reportAgents() {
|
|
2046
|
-
_postJson(`${
|
|
2014
|
+
_postJson(`${API_BASE2}/api/plugin/agents`, {
|
|
2047
2015
|
pluginId: this.pluginId,
|
|
2048
2016
|
agents: [{ id: "claude-code", name: "Claude Code", icon: "\u{1F916}", installed: true }]
|
|
2049
2017
|
}).then(() => {
|
|
@@ -2051,6 +2019,32 @@ var CommandRelayService = class {
|
|
|
2051
2019
|
}).catch(() => {
|
|
2052
2020
|
});
|
|
2053
2021
|
}
|
|
2022
|
+
// ─── Lifecycle ───────────────────────────────────────────────────
|
|
2023
|
+
cleanup() {
|
|
2024
|
+
if (this.pollTimer) {
|
|
2025
|
+
clearTimeout(this.pollTimer);
|
|
2026
|
+
this.pollTimer = null;
|
|
2027
|
+
}
|
|
2028
|
+
if (this.heartbeatTimer) {
|
|
2029
|
+
clearInterval(this.heartbeatTimer);
|
|
2030
|
+
this.heartbeatTimer = null;
|
|
2031
|
+
}
|
|
2032
|
+
if (this.agentsTimer) {
|
|
2033
|
+
clearInterval(this.agentsTimer);
|
|
2034
|
+
this.agentsTimer = null;
|
|
2035
|
+
}
|
|
2036
|
+
if (this.sseReconnectTimer) {
|
|
2037
|
+
clearTimeout(this.sseReconnectTimer);
|
|
2038
|
+
this.sseReconnectTimer = null;
|
|
2039
|
+
}
|
|
2040
|
+
if (this.sseRequest) {
|
|
2041
|
+
try {
|
|
2042
|
+
this.sseRequest.destroy();
|
|
2043
|
+
} catch {
|
|
2044
|
+
}
|
|
2045
|
+
this.sseRequest = null;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2054
2048
|
};
|
|
2055
2049
|
|
|
2056
2050
|
// src/services/pty/unix.strategy.ts
|
|
@@ -4645,10 +4639,6 @@ var ClaudeService = class {
|
|
|
4645
4639
|
}
|
|
4646
4640
|
};
|
|
4647
4641
|
|
|
4648
|
-
// src/services/output.service.ts
|
|
4649
|
-
var https2 = __toESM(require("https"));
|
|
4650
|
-
var http2 = __toESM(require("http"));
|
|
4651
|
-
|
|
4652
4642
|
// ../../packages/shared/src/protocol/parseChrome.ts
|
|
4653
4643
|
var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
|
|
4654
4644
|
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;
|
|
@@ -5019,158 +5009,358 @@ function getContextWindow(model) {
|
|
|
5019
5009
|
return DEFAULT_CONTEXT_WINDOW;
|
|
5020
5010
|
}
|
|
5021
5011
|
|
|
5012
|
+
// src/services/output/chrome-tracker.ts
|
|
5013
|
+
var ChromeStepTracker = class {
|
|
5014
|
+
history = [];
|
|
5015
|
+
sentCount = 0;
|
|
5016
|
+
reset() {
|
|
5017
|
+
this.history = [];
|
|
5018
|
+
this.sentCount = 0;
|
|
5019
|
+
}
|
|
5020
|
+
/** Parse the rendered lines, append unseen steps to the cumulative history. */
|
|
5021
|
+
ingest(lines) {
|
|
5022
|
+
const visible = lines.filter((l) => isChromeLine(l)).map((l) => parseChromeLine(l)).filter((s) => s !== null);
|
|
5023
|
+
if (visible.length === 0) return;
|
|
5024
|
+
for (const step of visible) {
|
|
5025
|
+
const exists = this.history.some(
|
|
5026
|
+
(s) => s.tool === step.tool && s.label === step.label
|
|
5027
|
+
);
|
|
5028
|
+
if (!exists) this.history.push(step);
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
/**
|
|
5032
|
+
* Returns the steps that have NOT yet been shipped on the wire,
|
|
5033
|
+
* marking them as shipped. Empty array means nothing new since
|
|
5034
|
+
* the last call. Caller forwards this as the chunk's
|
|
5035
|
+
* `appendSteps` payload.
|
|
5036
|
+
*/
|
|
5037
|
+
consumeDelta() {
|
|
5038
|
+
if (this.history.length === this.sentCount) return [];
|
|
5039
|
+
const delta = this.history.slice(this.sentCount);
|
|
5040
|
+
this.sentCount = this.history.length;
|
|
5041
|
+
return delta;
|
|
5042
|
+
}
|
|
5043
|
+
/** Snapshot of the cumulative unique-step history (debug + tests). */
|
|
5044
|
+
get cumulativeHistory() {
|
|
5045
|
+
return this.history;
|
|
5046
|
+
}
|
|
5047
|
+
};
|
|
5048
|
+
|
|
5049
|
+
// src/services/output/chunk-emitter.ts
|
|
5050
|
+
var https3 = __toESM(require("https"));
|
|
5051
|
+
var http3 = __toESM(require("http"));
|
|
5052
|
+
var API_BASE3 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
5053
|
+
var ChunkEmitter = class {
|
|
5054
|
+
constructor(opts) {
|
|
5055
|
+
this.opts = opts;
|
|
5056
|
+
this.headers = {
|
|
5057
|
+
"Content-Type": "application/json",
|
|
5058
|
+
// Tell the backend which wire-format version we speak so
|
|
5059
|
+
// it can route legacy translations / 426 us when we're
|
|
5060
|
+
// too far behind. Bumped to 2.0.0 with the discriminated-
|
|
5061
|
+
// chunk + delta-chrome refactor in this release.
|
|
5062
|
+
"X-Codeam-Protocol-Version": "2.0.0"
|
|
5063
|
+
};
|
|
5064
|
+
if (opts.pluginAuthToken) {
|
|
5065
|
+
this.headers["X-Plugin-Auth-Token"] = opts.pluginAuthToken;
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
opts;
|
|
5069
|
+
url = `${API_BASE3}/api/commands/output`;
|
|
5070
|
+
headers;
|
|
5071
|
+
/**
|
|
5072
|
+
* Send a chunk. `body` is the chunk fields minus `sessionId` /
|
|
5073
|
+
* `pluginId` — the emitter splices those in. `critical = true`
|
|
5074
|
+
* triggers up to 3 retries with linear backoff (200/400/600 ms);
|
|
5075
|
+
* non-critical sends are best-effort (a transient miss gets
|
|
5076
|
+
* superseded by the next tick's emission).
|
|
5077
|
+
*/
|
|
5078
|
+
async send(body, opts = {}) {
|
|
5079
|
+
const payload = JSON.stringify({
|
|
5080
|
+
sessionId: this.opts.sessionId,
|
|
5081
|
+
pluginId: this.opts.pluginId,
|
|
5082
|
+
...body
|
|
5083
|
+
});
|
|
5084
|
+
const maxRetries = opts.critical ? 3 : 0;
|
|
5085
|
+
log.trace(
|
|
5086
|
+
"chunkEmitter",
|
|
5087
|
+
`send type=${body.type ?? "(clear)"} bytes=${payload.length}`
|
|
5088
|
+
);
|
|
5089
|
+
return new Promise((resolve2) => {
|
|
5090
|
+
const attempt = (attemptsLeft) => {
|
|
5091
|
+
_transport2.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
|
|
5092
|
+
if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
|
|
5093
|
+
process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
|
|
5094
|
+
resolve2({ dead: true });
|
|
5095
|
+
return;
|
|
5096
|
+
}
|
|
5097
|
+
if (statusCode >= 400) {
|
|
5098
|
+
process.stderr.write(`[codeam] output API error ${statusCode}: ${resBody}
|
|
5099
|
+
`);
|
|
5100
|
+
}
|
|
5101
|
+
log.trace("chunkEmitter", `status=${statusCode}`);
|
|
5102
|
+
resolve2({ dead: false });
|
|
5103
|
+
}).catch((err) => {
|
|
5104
|
+
log.trace(
|
|
5105
|
+
"chunkEmitter",
|
|
5106
|
+
`error retries-left=${attemptsLeft}`,
|
|
5107
|
+
err
|
|
5108
|
+
);
|
|
5109
|
+
if (attemptsLeft > 0) {
|
|
5110
|
+
const delay = 200 * (maxRetries - attemptsLeft + 1);
|
|
5111
|
+
setTimeout(() => attempt(attemptsLeft - 1), delay);
|
|
5112
|
+
} else {
|
|
5113
|
+
resolve2({ dead: false });
|
|
5114
|
+
}
|
|
5115
|
+
});
|
|
5116
|
+
};
|
|
5117
|
+
attempt(maxRetries);
|
|
5118
|
+
});
|
|
5119
|
+
}
|
|
5120
|
+
};
|
|
5121
|
+
var _transport2 = {
|
|
5122
|
+
post: _post
|
|
5123
|
+
};
|
|
5124
|
+
function _post(url, headers, payload) {
|
|
5125
|
+
return new Promise((resolve2, reject) => {
|
|
5126
|
+
let settled = false;
|
|
5127
|
+
const u2 = new URL(url);
|
|
5128
|
+
const transport = u2.protocol === "https:" ? https3 : http3;
|
|
5129
|
+
const req = transport.request(
|
|
5130
|
+
{
|
|
5131
|
+
hostname: u2.hostname,
|
|
5132
|
+
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
5133
|
+
path: u2.pathname,
|
|
5134
|
+
method: "POST",
|
|
5135
|
+
headers: {
|
|
5136
|
+
...headers,
|
|
5137
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
5138
|
+
},
|
|
5139
|
+
timeout: 8e3
|
|
5140
|
+
},
|
|
5141
|
+
(res) => {
|
|
5142
|
+
let resData = "";
|
|
5143
|
+
res.on("data", (c2) => {
|
|
5144
|
+
resData += c2.toString();
|
|
5145
|
+
});
|
|
5146
|
+
res.on("end", () => {
|
|
5147
|
+
if (settled) return;
|
|
5148
|
+
settled = true;
|
|
5149
|
+
resolve2({ statusCode: res.statusCode ?? 0, body: resData });
|
|
5150
|
+
});
|
|
5151
|
+
}
|
|
5152
|
+
);
|
|
5153
|
+
req.on("error", (err) => {
|
|
5154
|
+
if (settled) return;
|
|
5155
|
+
settled = true;
|
|
5156
|
+
reject(err);
|
|
5157
|
+
});
|
|
5158
|
+
req.on("timeout", () => {
|
|
5159
|
+
req.destroy();
|
|
5160
|
+
});
|
|
5161
|
+
req.write(payload);
|
|
5162
|
+
req.end();
|
|
5163
|
+
});
|
|
5164
|
+
}
|
|
5165
|
+
|
|
5166
|
+
// src/services/output/pty-buffer.ts
|
|
5167
|
+
var PtyBuffer = class {
|
|
5168
|
+
raw = "";
|
|
5169
|
+
active = false;
|
|
5170
|
+
lastPushAt = 0;
|
|
5171
|
+
terminalInputPending = false;
|
|
5172
|
+
/** Whether to absorb pushes (`true`) or only watch for terminal input (`false`). */
|
|
5173
|
+
get isActive() {
|
|
5174
|
+
return this.active;
|
|
5175
|
+
}
|
|
5176
|
+
/** Bytes accumulated since the last reset. */
|
|
5177
|
+
get content() {
|
|
5178
|
+
return this.raw;
|
|
5179
|
+
}
|
|
5180
|
+
/** Wall-clock of the most recent printable push (`0` if none yet this turn). */
|
|
5181
|
+
get lastPushTime() {
|
|
5182
|
+
return this.lastPushAt;
|
|
5183
|
+
}
|
|
5184
|
+
/** Length of the accumulated buffer in raw bytes (debug + tests). */
|
|
5185
|
+
get size() {
|
|
5186
|
+
return this.raw.length;
|
|
5187
|
+
}
|
|
5188
|
+
activate() {
|
|
5189
|
+
this.active = true;
|
|
5190
|
+
this.raw = "";
|
|
5191
|
+
this.lastPushAt = 0;
|
|
5192
|
+
this.terminalInputPending = false;
|
|
5193
|
+
}
|
|
5194
|
+
deactivate() {
|
|
5195
|
+
this.active = false;
|
|
5196
|
+
}
|
|
5197
|
+
reset() {
|
|
5198
|
+
this.raw = "";
|
|
5199
|
+
this.lastPushAt = 0;
|
|
5200
|
+
}
|
|
5201
|
+
/**
|
|
5202
|
+
* Ingest a raw PTY frame. Returns whether the buffer was active
|
|
5203
|
+
* at the time (caller cares because rendering only matters for
|
|
5204
|
+
* active frames) and whether this push triggered the
|
|
5205
|
+
* terminal-initiated-turn signal.
|
|
5206
|
+
*/
|
|
5207
|
+
push(raw) {
|
|
5208
|
+
if (!this.active) {
|
|
5209
|
+
let terminalInputDetected = false;
|
|
5210
|
+
if (!this.terminalInputPending && hasPrintable(raw)) {
|
|
5211
|
+
this.terminalInputPending = true;
|
|
5212
|
+
terminalInputDetected = true;
|
|
5213
|
+
}
|
|
5214
|
+
return { active: false, terminalInputDetected };
|
|
5215
|
+
}
|
|
5216
|
+
this.raw += raw;
|
|
5217
|
+
if (hasPrintable(raw)) this.lastPushAt = Date.now();
|
|
5218
|
+
return { active: true, terminalInputDetected: false };
|
|
5219
|
+
}
|
|
5220
|
+
};
|
|
5221
|
+
function hasPrintable(raw) {
|
|
5222
|
+
const stripped = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
|
|
5223
|
+
return stripped.trim().length > 0;
|
|
5224
|
+
}
|
|
5225
|
+
|
|
5226
|
+
// src/services/output/turn-renderer.ts
|
|
5227
|
+
function renderLines(buffer) {
|
|
5228
|
+
return renderToLines(buffer);
|
|
5229
|
+
}
|
|
5230
|
+
function detectAnySelector(lines) {
|
|
5231
|
+
return detectSelector(lines) ?? detectListSelector(lines);
|
|
5232
|
+
}
|
|
5233
|
+
function extractContent(lines) {
|
|
5234
|
+
return filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
5235
|
+
}
|
|
5236
|
+
|
|
5022
5237
|
// src/services/output.service.ts
|
|
5023
|
-
var API_BASE4 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
5024
5238
|
var OutputService = class _OutputService {
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
this.pluginAuthToken = pluginAuthToken;
|
|
5029
|
-
this.onSessionIdDetected = onSessionIdDetected;
|
|
5030
|
-
this.onRateLimitDetected = onRateLimitDetected;
|
|
5031
|
-
this.onTurnComplete = onTurnComplete;
|
|
5032
|
-
this.onTerminalTurnDetected = onTerminalTurnDetected;
|
|
5033
|
-
}
|
|
5034
|
-
sessionId;
|
|
5035
|
-
pluginId;
|
|
5036
|
-
pluginAuthToken;
|
|
5037
|
-
rawBuffer = "";
|
|
5239
|
+
pty = new PtyBuffer();
|
|
5240
|
+
steps = new ChromeStepTracker();
|
|
5241
|
+
emitter;
|
|
5038
5242
|
lastSentContent = "";
|
|
5039
|
-
lastSentChromeStepsJson = "";
|
|
5040
|
-
chromeStepsHistory = [];
|
|
5041
5243
|
pollTimer = null;
|
|
5042
5244
|
startTime = 0;
|
|
5043
|
-
active = false;
|
|
5044
5245
|
terminalTurnPending = false;
|
|
5045
|
-
lastPushTime = 0;
|
|
5046
5246
|
onSessionIdDetected;
|
|
5047
5247
|
onRateLimitDetected;
|
|
5048
5248
|
onTurnComplete;
|
|
5049
5249
|
onTerminalTurnDetected;
|
|
5250
|
+
/** Tick cadence — every 1 s while a turn is active. */
|
|
5050
5251
|
static POLL_MS = 1e3;
|
|
5252
|
+
/** Idle threshold for "the agent's text settled, finalize the turn". */
|
|
5051
5253
|
static IDLE_MS = 3e3;
|
|
5052
|
-
/**
|
|
5254
|
+
/** Same threshold but tighter for selectors (UI is ready to interact immediately). */
|
|
5053
5255
|
static SELECTOR_IDLE_MS = 1500;
|
|
5054
5256
|
/**
|
|
5055
|
-
* Grace period before
|
|
5056
|
-
*
|
|
5057
|
-
*
|
|
5058
|
-
* receiving the input, but we give a 1.5 s margin for loaded machines).
|
|
5257
|
+
* Grace period before tick processes anything — Claude needs ~100-
|
|
5258
|
+
* 200 ms after `\r` to clear the input echo and re-render the TUI.
|
|
5259
|
+
* 1.5 s is a comfortable margin on loaded machines.
|
|
5059
5260
|
*/
|
|
5060
5261
|
static WARMUP_MS = 1500;
|
|
5061
|
-
/** Max idle with
|
|
5262
|
+
/** Max idle with chrome-only output before we stop waiting on the agent. */
|
|
5062
5263
|
static EMPTY_TIMEOUT_MS = 6e4;
|
|
5264
|
+
/** Hard turn cap — pathological no-op turns get cut after 2 minutes. */
|
|
5063
5265
|
static MAX_MS = 12e4;
|
|
5266
|
+
constructor(sessionId, pluginId, onSessionIdDetected, onRateLimitDetected, onTurnComplete, onTerminalTurnDetected, pluginAuthToken) {
|
|
5267
|
+
this.onSessionIdDetected = onSessionIdDetected;
|
|
5268
|
+
this.onRateLimitDetected = onRateLimitDetected;
|
|
5269
|
+
this.onTurnComplete = onTurnComplete;
|
|
5270
|
+
this.onTerminalTurnDetected = onTerminalTurnDetected;
|
|
5271
|
+
this.emitter = new ChunkEmitter({
|
|
5272
|
+
sessionId,
|
|
5273
|
+
pluginId,
|
|
5274
|
+
pluginAuthToken
|
|
5275
|
+
});
|
|
5276
|
+
}
|
|
5277
|
+
// ─── Turn lifecycle ──────────────────────────────────────────────
|
|
5278
|
+
/**
|
|
5279
|
+
* Begin a turn driven by a mobile-side prompt. Resets the buffer
|
|
5280
|
+
* and emits the boundary chunks (clear → new_turn) that tell
|
|
5281
|
+
* clients to wipe the prior agent reply and show "Agent is
|
|
5282
|
+
* typing…".
|
|
5283
|
+
*/
|
|
5284
|
+
newTurn() {
|
|
5285
|
+
log.trace("outputSvc", "newTurn() \u2014 activating output stream");
|
|
5286
|
+
this.beginTurn();
|
|
5287
|
+
this.send({ type: "clear" }, { critical: true }).then(() => this.send({ type: "new_turn", done: false }, { critical: true })).catch(() => {
|
|
5288
|
+
});
|
|
5289
|
+
}
|
|
5064
5290
|
/**
|
|
5065
|
-
*
|
|
5066
|
-
*
|
|
5067
|
-
*
|
|
5068
|
-
*
|
|
5291
|
+
* Begin a turn driven by the user typing locally in their
|
|
5292
|
+
* terminal. Same shape as `newTurn` but additionally sends a
|
|
5293
|
+
* `user_message` so collaborators see the prompt attributed
|
|
5294
|
+
* correctly. `userText` is the prompt text scraped from the
|
|
5295
|
+
* Claude JSONL by `historySvc.waitForNewUserMessage`.
|
|
5069
5296
|
*/
|
|
5070
5297
|
async startTerminalTurn(userText) {
|
|
5071
5298
|
this.terminalTurnPending = false;
|
|
5072
|
-
this.
|
|
5073
|
-
this.
|
|
5074
|
-
this.lastSentContent = "";
|
|
5075
|
-
this.lastSentChromeStepsJson = "";
|
|
5076
|
-
this.chromeStepsHistory = [];
|
|
5077
|
-
this.lastPushTime = 0;
|
|
5078
|
-
this.active = true;
|
|
5079
|
-
this.startTime = Date.now();
|
|
5080
|
-
await this.postChunk({ clear: true });
|
|
5299
|
+
this.beginTurn();
|
|
5300
|
+
await this.send({ type: "clear" }, { critical: true });
|
|
5081
5301
|
if (userText) {
|
|
5082
|
-
await this.
|
|
5302
|
+
await this.send({ type: "user_message", content: userText, done: true }, { critical: true });
|
|
5083
5303
|
}
|
|
5084
|
-
await this.
|
|
5085
|
-
this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
|
|
5086
|
-
}
|
|
5087
|
-
newTurn() {
|
|
5088
|
-
log.trace("outputSvc", "newTurn() \u2014 activating output stream");
|
|
5089
|
-
this.stopPoll();
|
|
5090
|
-
this.rawBuffer = "";
|
|
5091
|
-
this.lastSentContent = "";
|
|
5092
|
-
this.lastSentChromeStepsJson = "";
|
|
5093
|
-
this.chromeStepsHistory = [];
|
|
5094
|
-
this.lastPushTime = 0;
|
|
5095
|
-
this.active = true;
|
|
5096
|
-
this.terminalTurnPending = false;
|
|
5097
|
-
this.startTime = Date.now();
|
|
5098
|
-
this.postChunk({ clear: true }).then(() => this.postChunk({ type: "new_turn", content: "", done: false })).catch(() => {
|
|
5099
|
-
});
|
|
5100
|
-
this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
|
|
5304
|
+
await this.send({ type: "new_turn", done: false }, { critical: true });
|
|
5101
5305
|
}
|
|
5102
5306
|
/**
|
|
5103
|
-
*
|
|
5104
|
-
*
|
|
5105
|
-
*
|
|
5307
|
+
* Begin a turn after a `resume_session` request. Includes the
|
|
5308
|
+
* `resumedSessionId` so the client wipes its history and
|
|
5309
|
+
* re-fetches from the JSONL via `get_conversation`.
|
|
5106
5310
|
*/
|
|
5107
5311
|
async newTurnResume(resumedSessionId) {
|
|
5108
|
-
this.
|
|
5109
|
-
this.
|
|
5110
|
-
this.
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
this.active = true;
|
|
5115
|
-
this.startTime = Date.now();
|
|
5116
|
-
await this.postChunk({ clear: true });
|
|
5117
|
-
await this.postChunk({ type: "new_turn", resumedSessionId, content: "", done: false });
|
|
5118
|
-
this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
|
|
5312
|
+
this.beginTurn();
|
|
5313
|
+
await this.send({ type: "clear" }, { critical: true });
|
|
5314
|
+
await this.send(
|
|
5315
|
+
{ type: "new_turn", done: false, resumedSessionId },
|
|
5316
|
+
{ critical: true }
|
|
5317
|
+
);
|
|
5119
5318
|
}
|
|
5319
|
+
// ─── Pump ────────────────────────────────────────────────────────
|
|
5120
5320
|
push(raw) {
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
log.trace("outputSvc", `terminal-turn detected (idle, ${raw.length}B)`);
|
|
5127
|
-
this.onTerminalTurnDetected?.();
|
|
5128
|
-
}
|
|
5321
|
+
const result = this.pty.push(raw);
|
|
5322
|
+
if (!result.active) {
|
|
5323
|
+
if (result.terminalInputDetected && !this.terminalTurnPending) {
|
|
5324
|
+
this.terminalTurnPending = true;
|
|
5325
|
+
this.onTerminalTurnDetected?.();
|
|
5129
5326
|
}
|
|
5130
5327
|
log.trace("outputSvc", `push dropped (inactive, ${raw.length}B)`);
|
|
5131
5328
|
return;
|
|
5132
5329
|
}
|
|
5133
|
-
this.rawBuffer += raw;
|
|
5134
|
-
const printable = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
|
|
5135
|
-
if (printable.trim()) {
|
|
5136
|
-
this.lastPushTime = Date.now();
|
|
5137
|
-
this.tryExtractSessionId(printable);
|
|
5138
|
-
this.tryDetectRateLimit(printable);
|
|
5139
|
-
}
|
|
5140
5330
|
log.trace(
|
|
5141
5331
|
"outputSvc",
|
|
5142
|
-
`push +${raw.length}B (buf=${this.
|
|
5332
|
+
`push +${raw.length}B (buf=${this.pty.size}B)`
|
|
5143
5333
|
);
|
|
5334
|
+
this.tryExtractSessionId(raw);
|
|
5335
|
+
this.tryDetectRateLimit(raw);
|
|
5144
5336
|
}
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
/Resuming session[:\s]+([a-f0-9-]{36})/i,
|
|
5149
|
-
/Session[:\s]+([a-f0-9-]{36})/i,
|
|
5150
|
-
/Conversation[:\s]+([a-f0-9-]{36})/i,
|
|
5151
|
-
/Session\s+ID[:\s]+([a-f0-9-]{36})/i
|
|
5152
|
-
];
|
|
5153
|
-
for (const pattern of patterns) {
|
|
5154
|
-
const match = text.match(pattern);
|
|
5155
|
-
if (match && this.onSessionIdDetected) {
|
|
5156
|
-
this.onSessionIdDetected(match[1]);
|
|
5157
|
-
return;
|
|
5158
|
-
}
|
|
5159
|
-
}
|
|
5337
|
+
dispose() {
|
|
5338
|
+
this.stopPoll();
|
|
5339
|
+
this.pty.deactivate();
|
|
5160
5340
|
}
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5341
|
+
// ─── Internals ───────────────────────────────────────────────────
|
|
5342
|
+
beginTurn() {
|
|
5343
|
+
this.stopPoll();
|
|
5344
|
+
this.pty.activate();
|
|
5345
|
+
this.steps.reset();
|
|
5346
|
+
this.lastSentContent = "";
|
|
5347
|
+
this.startTime = Date.now();
|
|
5348
|
+
this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
|
|
5349
|
+
}
|
|
5350
|
+
async send(body, opts = {}) {
|
|
5351
|
+
const outcome = await this.emitter.send(body, opts);
|
|
5352
|
+
if (outcome.dead && this.pty.isActive) {
|
|
5353
|
+
this.dispose();
|
|
5166
5354
|
}
|
|
5167
5355
|
}
|
|
5168
|
-
|
|
5169
|
-
this.
|
|
5170
|
-
|
|
5356
|
+
stopPoll() {
|
|
5357
|
+
if (this.pollTimer) {
|
|
5358
|
+
clearInterval(this.pollTimer);
|
|
5359
|
+
this.pollTimer = null;
|
|
5360
|
+
}
|
|
5171
5361
|
}
|
|
5172
5362
|
tick() {
|
|
5173
|
-
if (!this.
|
|
5363
|
+
if (!this.pty.isActive) return;
|
|
5174
5364
|
const now = Date.now();
|
|
5175
5365
|
const elapsed = now - this.startTime;
|
|
5176
5366
|
if (elapsed >= _OutputService.MAX_MS) {
|
|
@@ -5178,36 +5368,51 @@ var OutputService = class _OutputService {
|
|
|
5178
5368
|
return;
|
|
5179
5369
|
}
|
|
5180
5370
|
if (elapsed < _OutputService.WARMUP_MS) return;
|
|
5181
|
-
const lines =
|
|
5182
|
-
this.
|
|
5183
|
-
const
|
|
5371
|
+
const lines = renderLines(this.pty.content);
|
|
5372
|
+
this.steps.ingest(lines);
|
|
5373
|
+
const stepsDelta = this.steps.consumeDelta();
|
|
5374
|
+
if (stepsDelta.length > 0) {
|
|
5375
|
+
this.send({ type: "chrome_steps", appendSteps: stepsDelta }).catch(() => {
|
|
5376
|
+
});
|
|
5377
|
+
}
|
|
5378
|
+
const selector = detectAnySelector(lines);
|
|
5184
5379
|
if (selector) {
|
|
5185
|
-
const idleMs2 = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
|
|
5380
|
+
const idleMs2 = this.pty.lastPushTime > 0 ? now - this.pty.lastPushTime : elapsed;
|
|
5186
5381
|
log.trace(
|
|
5187
5382
|
"outputSvc",
|
|
5188
5383
|
`tick selector found (idleMs=${idleMs2}, options=${selector.options.length})`
|
|
5189
5384
|
);
|
|
5190
5385
|
if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
|
|
5191
5386
|
this.stopPoll();
|
|
5192
|
-
this.
|
|
5193
|
-
this.
|
|
5387
|
+
this.pty.deactivate();
|
|
5388
|
+
this.send(
|
|
5389
|
+
{
|
|
5390
|
+
type: "select_prompt",
|
|
5391
|
+
content: selector.question,
|
|
5392
|
+
options: selector.options,
|
|
5393
|
+
optionDescriptions: selector.optionDescriptions,
|
|
5394
|
+
currentIndex: selector.currentIndex,
|
|
5395
|
+
done: true
|
|
5396
|
+
},
|
|
5397
|
+
{ critical: true }
|
|
5398
|
+
).catch(() => {
|
|
5194
5399
|
});
|
|
5195
5400
|
}
|
|
5196
5401
|
return;
|
|
5197
5402
|
}
|
|
5198
|
-
const content =
|
|
5403
|
+
const content = extractContent(lines);
|
|
5199
5404
|
if (!content) {
|
|
5200
5405
|
log.trace(
|
|
5201
5406
|
"outputSvc",
|
|
5202
|
-
`tick empty content (raw=${this.
|
|
5407
|
+
`tick empty content (raw=${this.pty.size}B lines=${lines.length} elapsed=${elapsed}ms)`
|
|
5203
5408
|
);
|
|
5204
5409
|
if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) this.finalize();
|
|
5205
5410
|
return;
|
|
5206
5411
|
}
|
|
5207
|
-
const idleMs = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
|
|
5412
|
+
const idleMs = this.pty.lastPushTime > 0 ? now - this.pty.lastPushTime : elapsed;
|
|
5208
5413
|
log.trace(
|
|
5209
5414
|
"outputSvc",
|
|
5210
|
-
`tick content (raw=${this.
|
|
5415
|
+
`tick content (raw=${this.pty.size}B lines=${lines.length} content=${content.length} idleMs=${idleMs})`
|
|
5211
5416
|
);
|
|
5212
5417
|
if (idleMs >= _OutputService.IDLE_MS) {
|
|
5213
5418
|
this.finalize();
|
|
@@ -5215,161 +5420,78 @@ var OutputService = class _OutputService {
|
|
|
5215
5420
|
}
|
|
5216
5421
|
if (content !== this.lastSentContent) {
|
|
5217
5422
|
this.lastSentContent = content;
|
|
5218
|
-
this.
|
|
5423
|
+
this.send({ type: "text", content, done: false }).catch(() => {
|
|
5219
5424
|
});
|
|
5220
5425
|
}
|
|
5221
5426
|
}
|
|
5222
5427
|
finalize() {
|
|
5223
|
-
const lines =
|
|
5224
|
-
this.
|
|
5225
|
-
const
|
|
5428
|
+
const lines = renderLines(this.pty.content);
|
|
5429
|
+
this.steps.ingest(lines);
|
|
5430
|
+
const stepsDelta = this.steps.consumeDelta();
|
|
5431
|
+
if (stepsDelta.length > 0) {
|
|
5432
|
+
this.send({ type: "chrome_steps", appendSteps: stepsDelta }).catch(() => {
|
|
5433
|
+
});
|
|
5434
|
+
}
|
|
5435
|
+
const selector = detectAnySelector(lines);
|
|
5226
5436
|
this.stopPoll();
|
|
5227
|
-
this.
|
|
5437
|
+
this.pty.deactivate();
|
|
5228
5438
|
if (selector) {
|
|
5229
|
-
this.
|
|
5439
|
+
this.send(
|
|
5440
|
+
{
|
|
5441
|
+
type: "select_prompt",
|
|
5442
|
+
content: selector.question,
|
|
5443
|
+
options: selector.options,
|
|
5444
|
+
optionDescriptions: selector.optionDescriptions,
|
|
5445
|
+
currentIndex: selector.currentIndex,
|
|
5446
|
+
done: true
|
|
5447
|
+
},
|
|
5448
|
+
{ critical: true }
|
|
5449
|
+
).catch(() => {
|
|
5230
5450
|
});
|
|
5231
5451
|
} else {
|
|
5232
|
-
const content =
|
|
5233
|
-
this.
|
|
5452
|
+
const content = extractContent(lines);
|
|
5453
|
+
this.send(
|
|
5454
|
+
{ type: "text", content, done: true },
|
|
5455
|
+
{ critical: true }
|
|
5456
|
+
).catch(() => {
|
|
5234
5457
|
});
|
|
5235
5458
|
this.onTurnComplete?.();
|
|
5236
5459
|
}
|
|
5237
5460
|
}
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
for (const
|
|
5249
|
-
const
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
this.chromeStepsHistory.push(step);
|
|
5254
|
-
changed = true;
|
|
5461
|
+
// ─── Side-channel observation (session id + rate limit) ──────────
|
|
5462
|
+
tryExtractSessionId(text) {
|
|
5463
|
+
if (!this.onSessionIdDetected) return;
|
|
5464
|
+
const printable = text.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
|
|
5465
|
+
const patterns = [
|
|
5466
|
+
/Resuming session[:\s]+([a-f0-9-]{36})/i,
|
|
5467
|
+
/Session[:\s]+([a-f0-9-]{36})/i,
|
|
5468
|
+
/Conversation[:\s]+([a-f0-9-]{36})/i,
|
|
5469
|
+
/Session\s+ID[:\s]+([a-f0-9-]{36})/i
|
|
5470
|
+
];
|
|
5471
|
+
for (const pattern of patterns) {
|
|
5472
|
+
const match = printable.match(pattern);
|
|
5473
|
+
if (match) {
|
|
5474
|
+
this.onSessionIdDetected(match[1]);
|
|
5475
|
+
return;
|
|
5255
5476
|
}
|
|
5256
5477
|
}
|
|
5257
|
-
if (!changed) return;
|
|
5258
|
-
const json = JSON.stringify(this.chromeStepsHistory);
|
|
5259
|
-
if (json === this.lastSentChromeStepsJson) return;
|
|
5260
|
-
this.lastSentChromeStepsJson = json;
|
|
5261
|
-
this.postChunk({ type: "chrome_steps", content: "", steps: [...this.chromeStepsHistory] }).catch(() => {
|
|
5262
|
-
});
|
|
5263
5478
|
}
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
const
|
|
5267
|
-
const
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
...body
|
|
5271
|
-
});
|
|
5272
|
-
const headers = {
|
|
5273
|
-
"Content-Type": "application/json"
|
|
5274
|
-
};
|
|
5275
|
-
if (this.pluginAuthToken) {
|
|
5276
|
-
headers["X-Plugin-Auth-Token"] = this.pluginAuthToken;
|
|
5277
|
-
}
|
|
5278
|
-
const chunkType = body.type ?? "(clear)";
|
|
5279
|
-
log.trace(
|
|
5280
|
-
"outputSvc",
|
|
5281
|
-
`postChunk type=${chunkType} done=${body.done === true} bytes=${payload.length}`
|
|
5282
|
-
);
|
|
5283
|
-
if (chunkType === "select_prompt" || chunkType === "new_turn" || body.type === "text" && body.done === true) {
|
|
5284
|
-
const preview = payload.length > 2048 ? payload.slice(0, 2048) + "\u2026(truncated)" : payload;
|
|
5285
|
-
log.trace("outputSvc", `payload ${preview}`);
|
|
5479
|
+
tryDetectRateLimit(text) {
|
|
5480
|
+
if (!this.onRateLimitDetected) return;
|
|
5481
|
+
const printable = text.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
|
|
5482
|
+
const match = printable.match(/hit your limit.*resets\s+(.+?)(?:\s*\(|$)/i) ?? printable.match(/rate.?limit.*resets\s+(.+?)(?:\s*\(|$)/i);
|
|
5483
|
+
if (match) {
|
|
5484
|
+
this.onRateLimitDetected(match[1].trim());
|
|
5286
5485
|
}
|
|
5287
|
-
return new Promise((resolve2) => {
|
|
5288
|
-
const attempt = (attemptsLeft) => {
|
|
5289
|
-
_transport2.sendOutputChunk(`${API_BASE4}/api/commands/output`, headers, payload).then(({ statusCode, body: resBody }) => {
|
|
5290
|
-
log.trace("outputSvc", `postChunk status=${statusCode}`);
|
|
5291
|
-
if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
|
|
5292
|
-
if (this.active) {
|
|
5293
|
-
process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
|
|
5294
|
-
this.dispose();
|
|
5295
|
-
}
|
|
5296
|
-
resolve2();
|
|
5297
|
-
return;
|
|
5298
|
-
}
|
|
5299
|
-
if (statusCode >= 400) {
|
|
5300
|
-
process.stderr.write(`[codeam] output API error ${statusCode}: ${resBody}
|
|
5301
|
-
`);
|
|
5302
|
-
}
|
|
5303
|
-
resolve2();
|
|
5304
|
-
}).catch((err) => {
|
|
5305
|
-
log.trace(
|
|
5306
|
-
"outputSvc",
|
|
5307
|
-
`postChunk error (retries left=${attemptsLeft})`,
|
|
5308
|
-
err
|
|
5309
|
-
);
|
|
5310
|
-
if (attemptsLeft > 0) {
|
|
5311
|
-
const delay = 200 * (maxRetries - attemptsLeft + 1);
|
|
5312
|
-
setTimeout(() => attempt(attemptsLeft - 1), delay);
|
|
5313
|
-
} else {
|
|
5314
|
-
resolve2();
|
|
5315
|
-
}
|
|
5316
|
-
});
|
|
5317
|
-
};
|
|
5318
|
-
attempt(maxRetries);
|
|
5319
|
-
});
|
|
5320
5486
|
}
|
|
5321
5487
|
};
|
|
5322
|
-
var _transport2 = {
|
|
5323
|
-
sendOutputChunk: _sendOutputChunk
|
|
5324
|
-
};
|
|
5325
|
-
function _sendOutputChunk(url, headers, payload) {
|
|
5326
|
-
return new Promise((resolve2, reject) => {
|
|
5327
|
-
let settled = false;
|
|
5328
|
-
const u2 = new URL(url);
|
|
5329
|
-
const transport = u2.protocol === "https:" ? https2 : http2;
|
|
5330
|
-
const req = transport.request(
|
|
5331
|
-
{
|
|
5332
|
-
hostname: u2.hostname,
|
|
5333
|
-
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
5334
|
-
path: u2.pathname,
|
|
5335
|
-
method: "POST",
|
|
5336
|
-
headers: {
|
|
5337
|
-
...headers,
|
|
5338
|
-
"Content-Length": Buffer.byteLength(payload)
|
|
5339
|
-
},
|
|
5340
|
-
timeout: 8e3
|
|
5341
|
-
},
|
|
5342
|
-
(res) => {
|
|
5343
|
-
let resData = "";
|
|
5344
|
-
res.on("data", (c2) => {
|
|
5345
|
-
resData += c2.toString();
|
|
5346
|
-
});
|
|
5347
|
-
res.on("end", () => {
|
|
5348
|
-
if (settled) return;
|
|
5349
|
-
settled = true;
|
|
5350
|
-
resolve2({ statusCode: res.statusCode ?? 0, body: resData });
|
|
5351
|
-
});
|
|
5352
|
-
}
|
|
5353
|
-
);
|
|
5354
|
-
req.on("error", (err) => {
|
|
5355
|
-
if (settled) return;
|
|
5356
|
-
settled = true;
|
|
5357
|
-
reject(err);
|
|
5358
|
-
});
|
|
5359
|
-
req.on("timeout", () => {
|
|
5360
|
-
req.destroy();
|
|
5361
|
-
});
|
|
5362
|
-
req.write(payload);
|
|
5363
|
-
req.end();
|
|
5364
|
-
});
|
|
5365
|
-
}
|
|
5366
5488
|
|
|
5367
5489
|
// src/services/history.service.ts
|
|
5368
5490
|
var fs5 = __toESM(require("fs"));
|
|
5369
5491
|
var path8 = __toESM(require("path"));
|
|
5370
5492
|
var os6 = __toESM(require("os"));
|
|
5371
|
-
var
|
|
5372
|
-
var
|
|
5493
|
+
var https4 = __toESM(require("https"));
|
|
5494
|
+
var http4 = __toESM(require("http"));
|
|
5373
5495
|
var import_zod = require("zod");
|
|
5374
5496
|
var historyRecordSchema = import_zod.z.object({
|
|
5375
5497
|
type: import_zod.z.string().optional(),
|
|
@@ -5381,7 +5503,7 @@ var historyRecordSchema = import_zod.z.object({
|
|
|
5381
5503
|
content: import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.unknown())]).optional()
|
|
5382
5504
|
}).passthrough().optional()
|
|
5383
5505
|
}).passthrough();
|
|
5384
|
-
var
|
|
5506
|
+
var API_BASE4 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
5385
5507
|
function encodeCwd(cwd) {
|
|
5386
5508
|
return cwd.replace(/[\\/:]/g, "-");
|
|
5387
5509
|
}
|
|
@@ -5454,8 +5576,8 @@ function parseJsonl(filePath) {
|
|
|
5454
5576
|
function post(endpoint, body) {
|
|
5455
5577
|
return new Promise((resolve2) => {
|
|
5456
5578
|
const payload = JSON.stringify(body);
|
|
5457
|
-
const u2 = new URL(`${
|
|
5458
|
-
const transport = u2.protocol === "https:" ?
|
|
5579
|
+
const u2 = new URL(`${API_BASE4}${endpoint}`);
|
|
5580
|
+
const transport = u2.protocol === "https:" ? https4 : http4;
|
|
5459
5581
|
const req = transport.request(
|
|
5460
5582
|
{
|
|
5461
5583
|
hostname: u2.hostname,
|
|
@@ -5834,6 +5956,161 @@ var HistoryService = class {
|
|
|
5834
5956
|
}
|
|
5835
5957
|
};
|
|
5836
5958
|
|
|
5959
|
+
// src/commands/start/quota-fetcher.ts
|
|
5960
|
+
var fs6 = __toESM(require("fs"));
|
|
5961
|
+
var os7 = __toESM(require("os"));
|
|
5962
|
+
var path9 = __toESM(require("path"));
|
|
5963
|
+
var import_child_process4 = require("child_process");
|
|
5964
|
+
var inProgress = false;
|
|
5965
|
+
var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
|
|
5966
|
+
m,s=pty.openpty()
|
|
5967
|
+
try:
|
|
5968
|
+
fcntl.ioctl(s,termios.TIOCSWINSZ,struct.pack('HHHH',30,120,0,0))
|
|
5969
|
+
except Exception:pass
|
|
5970
|
+
pid=os.fork()
|
|
5971
|
+
if pid==0:
|
|
5972
|
+
os.close(m);os.setsid()
|
|
5973
|
+
try:fcntl.ioctl(s,termios.TIOCSCTTY,0)
|
|
5974
|
+
except Exception:pass
|
|
5975
|
+
for fd in[0,1,2]:os.dup2(s,fd)
|
|
5976
|
+
if s>2:os.close(s)
|
|
5977
|
+
os.execvp(sys.argv[1],sys.argv[1:])
|
|
5978
|
+
sys.exit(127)
|
|
5979
|
+
os.close(s)
|
|
5980
|
+
done=[False]
|
|
5981
|
+
def onchld(n,f):
|
|
5982
|
+
try:os.waitpid(pid,os.WNOHANG)
|
|
5983
|
+
except Exception:pass
|
|
5984
|
+
done[0]=True
|
|
5985
|
+
signal.signal(signal.SIGCHLD,onchld)
|
|
5986
|
+
i=sys.stdin.fileno();o=sys.stdout.fileno()
|
|
5987
|
+
while not done[0]:
|
|
5988
|
+
try:r,_,_=select.select([i,m],[],[],0.1)
|
|
5989
|
+
except OSError as e:
|
|
5990
|
+
if e.errno==errno.EINTR:continue
|
|
5991
|
+
break
|
|
5992
|
+
if i in r:
|
|
5993
|
+
try:
|
|
5994
|
+
d=os.read(i,4096)
|
|
5995
|
+
if d:os.write(m,d)
|
|
5996
|
+
else:break
|
|
5997
|
+
except OSError:break
|
|
5998
|
+
if m in r:
|
|
5999
|
+
try:
|
|
6000
|
+
d=os.read(m,4096)
|
|
6001
|
+
if d:os.write(o,d)
|
|
6002
|
+
except OSError:done[0]=True
|
|
6003
|
+
try:os.kill(pid,signal.SIGTERM)
|
|
6004
|
+
except Exception:pass
|
|
6005
|
+
try:
|
|
6006
|
+
_,st=os.waitpid(pid,0)
|
|
6007
|
+
sys.exit((st>>8)&0xFF)
|
|
6008
|
+
except Exception:sys.exit(0)
|
|
6009
|
+
`;
|
|
6010
|
+
function fetchQuotaUsage(historySvc) {
|
|
6011
|
+
if (inProgress) return;
|
|
6012
|
+
inProgress = true;
|
|
6013
|
+
const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
|
|
6014
|
+
if (!claudeCmd) {
|
|
6015
|
+
inProgress = false;
|
|
6016
|
+
return;
|
|
6017
|
+
}
|
|
6018
|
+
const helperPath = path9.join(os7.tmpdir(), "codeam-quota-helper.py");
|
|
6019
|
+
fs6.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
|
|
6020
|
+
const python = findInPath("python3") ?? findInPath("python");
|
|
6021
|
+
if (!python) {
|
|
6022
|
+
inProgress = false;
|
|
6023
|
+
return;
|
|
6024
|
+
}
|
|
6025
|
+
const proc = (0, import_child_process4.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
|
|
6026
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
6027
|
+
cwd: process.cwd(),
|
|
6028
|
+
env: { ...process.env, TERM: "dumb", COLUMNS: "120", LINES: "30" }
|
|
6029
|
+
});
|
|
6030
|
+
let output = "";
|
|
6031
|
+
proc.stdout?.on("data", (chunk) => {
|
|
6032
|
+
output += chunk.toString("utf8");
|
|
6033
|
+
});
|
|
6034
|
+
setTimeout(() => {
|
|
6035
|
+
proc.stdin?.write("/usage\r");
|
|
6036
|
+
setTimeout(() => {
|
|
6037
|
+
const clean = output.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, " ").replace(/\s+/g, " ");
|
|
6038
|
+
const weekMatch = clean.match(/(\d+)%\s*used/i) || clean.match(/(\d+)\s*%/);
|
|
6039
|
+
if (weekMatch) historySvc.setQuotaPercent(parseInt(weekMatch[1], 10));
|
|
6040
|
+
const resetMatch = clean.match(/resets\s+(.+?)(?:\s*\(|$)/im);
|
|
6041
|
+
if (resetMatch) historySvc.setRateLimitReset(resetMatch[1].trim());
|
|
6042
|
+
try {
|
|
6043
|
+
proc.kill();
|
|
6044
|
+
} catch {
|
|
6045
|
+
}
|
|
6046
|
+
try {
|
|
6047
|
+
fs6.unlinkSync(helperPath);
|
|
6048
|
+
} catch {
|
|
6049
|
+
}
|
|
6050
|
+
inProgress = false;
|
|
6051
|
+
}, 5e3);
|
|
6052
|
+
}, 8e3);
|
|
6053
|
+
proc.on("exit", () => {
|
|
6054
|
+
inProgress = false;
|
|
6055
|
+
});
|
|
6056
|
+
setTimeout(() => {
|
|
6057
|
+
try {
|
|
6058
|
+
proc.kill();
|
|
6059
|
+
} catch {
|
|
6060
|
+
}
|
|
6061
|
+
}, 2e4);
|
|
6062
|
+
}
|
|
6063
|
+
|
|
6064
|
+
// src/commands/start/keep-alive.ts
|
|
6065
|
+
var import_child_process5 = require("child_process");
|
|
6066
|
+
function buildKeepAlive(ctx) {
|
|
6067
|
+
let timer = null;
|
|
6068
|
+
async function setIdleTimeout(minutes) {
|
|
6069
|
+
if (!ctx.inCodespace || !ctx.codespaceName) return;
|
|
6070
|
+
await new Promise((resolve2) => {
|
|
6071
|
+
const proc = (0, import_child_process5.spawn)(
|
|
6072
|
+
"gh",
|
|
6073
|
+
[
|
|
6074
|
+
"api",
|
|
6075
|
+
"-X",
|
|
6076
|
+
"PATCH",
|
|
6077
|
+
`/user/codespaces/${ctx.codespaceName}`,
|
|
6078
|
+
"-F",
|
|
6079
|
+
`idle_timeout_minutes=${minutes}`
|
|
6080
|
+
],
|
|
6081
|
+
{ stdio: "ignore", detached: true }
|
|
6082
|
+
);
|
|
6083
|
+
proc.unref();
|
|
6084
|
+
proc.on("exit", () => resolve2());
|
|
6085
|
+
proc.on("error", () => resolve2());
|
|
6086
|
+
});
|
|
6087
|
+
}
|
|
6088
|
+
return {
|
|
6089
|
+
apply(enabled) {
|
|
6090
|
+
if (timer) {
|
|
6091
|
+
clearInterval(timer);
|
|
6092
|
+
timer = null;
|
|
6093
|
+
}
|
|
6094
|
+
if (!ctx.inCodespace || !ctx.codespaceName) return;
|
|
6095
|
+
if (!enabled) {
|
|
6096
|
+
void setIdleTimeout(30);
|
|
6097
|
+
return;
|
|
6098
|
+
}
|
|
6099
|
+
void setIdleTimeout(240);
|
|
6100
|
+
timer = setInterval(() => {
|
|
6101
|
+
void setIdleTimeout(240);
|
|
6102
|
+
}, 30 * 60 * 1e3);
|
|
6103
|
+
}
|
|
6104
|
+
};
|
|
6105
|
+
}
|
|
6106
|
+
|
|
6107
|
+
// src/commands/start/handlers.ts
|
|
6108
|
+
var fs9 = __toESM(require("fs"));
|
|
6109
|
+
var os8 = __toESM(require("os"));
|
|
6110
|
+
var path12 = __toESM(require("path"));
|
|
6111
|
+
var import_crypto = require("crypto");
|
|
6112
|
+
var import_child_process7 = require("child_process");
|
|
6113
|
+
|
|
5837
6114
|
// src/lib/payload.ts
|
|
5838
6115
|
var import_zod2 = require("zod");
|
|
5839
6116
|
var fileEntrySchema = import_zod2.z.object({
|
|
@@ -5869,8 +6146,8 @@ function parsePayload(schema, raw) {
|
|
|
5869
6146
|
}
|
|
5870
6147
|
|
|
5871
6148
|
// src/services/file-ops.service.ts
|
|
5872
|
-
var
|
|
5873
|
-
var
|
|
6149
|
+
var fs7 = __toESM(require("fs/promises"));
|
|
6150
|
+
var path10 = __toESM(require("path"));
|
|
5874
6151
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
5875
6152
|
var MAX_WALK_DEPTH = 6;
|
|
5876
6153
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -5905,12 +6182,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
5905
6182
|
"__pycache__"
|
|
5906
6183
|
]);
|
|
5907
6184
|
function isUnder(parent, candidate) {
|
|
5908
|
-
const rel =
|
|
5909
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
6185
|
+
const rel = path10.relative(parent, candidate);
|
|
6186
|
+
return rel === "" || !rel.startsWith("..") && !path10.isAbsolute(rel);
|
|
5910
6187
|
}
|
|
5911
6188
|
async function isExistingFile(absPath) {
|
|
5912
6189
|
try {
|
|
5913
|
-
const stat3 = await
|
|
6190
|
+
const stat3 = await fs7.stat(absPath);
|
|
5914
6191
|
return stat3.isFile();
|
|
5915
6192
|
} catch {
|
|
5916
6193
|
return false;
|
|
@@ -5923,13 +6200,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
5923
6200
|
ctx.visited++;
|
|
5924
6201
|
let entries = [];
|
|
5925
6202
|
try {
|
|
5926
|
-
entries = await
|
|
6203
|
+
entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
5927
6204
|
} catch {
|
|
5928
6205
|
return;
|
|
5929
6206
|
}
|
|
5930
6207
|
for (const e of entries) {
|
|
5931
6208
|
if (!e.isFile()) continue;
|
|
5932
|
-
const full =
|
|
6209
|
+
const full = path10.join(dir, e.name);
|
|
5933
6210
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
5934
6211
|
ctx.matches.push(full);
|
|
5935
6212
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -5939,21 +6216,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
5939
6216
|
if (!e.isDirectory()) continue;
|
|
5940
6217
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
5941
6218
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
5942
|
-
await walkForSuffix(
|
|
6219
|
+
await walkForSuffix(path10.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
5943
6220
|
if (ctx.matches.length >= ctx.cap) return;
|
|
5944
6221
|
}
|
|
5945
6222
|
}
|
|
5946
6223
|
async function findFile(rawPath) {
|
|
5947
6224
|
const cwd = process.cwd();
|
|
5948
|
-
if (
|
|
5949
|
-
const abs =
|
|
6225
|
+
if (path10.isAbsolute(rawPath)) {
|
|
6226
|
+
const abs = path10.normalize(rawPath);
|
|
5950
6227
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
5951
6228
|
}
|
|
5952
|
-
const direct =
|
|
6229
|
+
const direct = path10.resolve(cwd, rawPath);
|
|
5953
6230
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
5954
|
-
const normalized =
|
|
6231
|
+
const normalized = path10.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
5955
6232
|
const needles = [
|
|
5956
|
-
`${
|
|
6233
|
+
`${path10.sep}${normalized}`,
|
|
5957
6234
|
`/${normalized}`
|
|
5958
6235
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
5959
6236
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -5967,7 +6244,7 @@ async function findWriteTarget(rawPath) {
|
|
|
5967
6244
|
const found = await findFile(rawPath);
|
|
5968
6245
|
if (found) return found;
|
|
5969
6246
|
const cwd = process.cwd();
|
|
5970
|
-
const fallback =
|
|
6247
|
+
const fallback = path10.isAbsolute(rawPath) ? path10.normalize(rawPath) : path10.resolve(cwd, rawPath);
|
|
5971
6248
|
if (!isUnder(cwd, fallback)) return null;
|
|
5972
6249
|
return fallback;
|
|
5973
6250
|
}
|
|
@@ -5984,11 +6261,11 @@ async function readProjectFile(rawPath) {
|
|
|
5984
6261
|
if (!abs) {
|
|
5985
6262
|
return { error: `File not found in the project tree: ${rawPath}` };
|
|
5986
6263
|
}
|
|
5987
|
-
const stat3 = await
|
|
6264
|
+
const stat3 = await fs7.stat(abs);
|
|
5988
6265
|
if (stat3.size > MAX_FILE_BYTES) {
|
|
5989
6266
|
return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
5990
6267
|
}
|
|
5991
|
-
const buf = await
|
|
6268
|
+
const buf = await fs7.readFile(abs);
|
|
5992
6269
|
if (looksBinary(buf)) {
|
|
5993
6270
|
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
5994
6271
|
}
|
|
@@ -6007,8 +6284,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
6007
6284
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
6008
6285
|
return { error: "Content too large." };
|
|
6009
6286
|
}
|
|
6010
|
-
await
|
|
6011
|
-
await
|
|
6287
|
+
await fs7.mkdir(path10.dirname(abs), { recursive: true });
|
|
6288
|
+
await fs7.writeFile(abs, content, "utf-8");
|
|
6012
6289
|
return { ok: true };
|
|
6013
6290
|
} catch (e) {
|
|
6014
6291
|
const msg = e instanceof Error ? e.message : "Write failed";
|
|
@@ -6017,11 +6294,11 @@ async function writeProjectFile(rawPath, content) {
|
|
|
6017
6294
|
}
|
|
6018
6295
|
|
|
6019
6296
|
// src/services/project-ops.service.ts
|
|
6020
|
-
var
|
|
6297
|
+
var import_child_process6 = require("child_process");
|
|
6021
6298
|
var import_util = require("util");
|
|
6022
|
-
var
|
|
6023
|
-
var
|
|
6024
|
-
var execFileP = (0, import_util.promisify)(
|
|
6299
|
+
var fs8 = __toESM(require("fs/promises"));
|
|
6300
|
+
var path11 = __toESM(require("path"));
|
|
6301
|
+
var execFileP = (0, import_util.promisify)(import_child_process6.execFile);
|
|
6025
6302
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
6026
6303
|
"node_modules",
|
|
6027
6304
|
".git",
|
|
@@ -6068,7 +6345,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
6068
6345
|
}
|
|
6069
6346
|
let entries = [];
|
|
6070
6347
|
try {
|
|
6071
|
-
entries = await
|
|
6348
|
+
entries = await fs8.readdir(dir, { withFileTypes: true });
|
|
6072
6349
|
} catch {
|
|
6073
6350
|
return;
|
|
6074
6351
|
}
|
|
@@ -6078,18 +6355,18 @@ async function listProjectFiles(opts = {}) {
|
|
|
6078
6355
|
return;
|
|
6079
6356
|
}
|
|
6080
6357
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
6081
|
-
const full =
|
|
6358
|
+
const full = path11.join(dir, e.name);
|
|
6082
6359
|
if (e.isDirectory()) {
|
|
6083
6360
|
if (depth >= 12) continue;
|
|
6084
6361
|
await walk(full, depth + 1);
|
|
6085
6362
|
} else if (e.isFile()) {
|
|
6086
|
-
const rel =
|
|
6363
|
+
const rel = path11.relative(root, full);
|
|
6087
6364
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
6088
6365
|
continue;
|
|
6089
6366
|
}
|
|
6090
6367
|
let size = 0;
|
|
6091
6368
|
try {
|
|
6092
|
-
const st3 = await
|
|
6369
|
+
const st3 = await fs8.stat(full);
|
|
6093
6370
|
size = st3.size;
|
|
6094
6371
|
} catch {
|
|
6095
6372
|
}
|
|
@@ -6191,8 +6468,8 @@ async function gitStatus(cwd) {
|
|
|
6191
6468
|
let hasMergeInProgress = false;
|
|
6192
6469
|
try {
|
|
6193
6470
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
6194
|
-
const mergeHead =
|
|
6195
|
-
await
|
|
6471
|
+
const mergeHead = path11.isAbsolute(gitDir) ? path11.join(gitDir, "MERGE_HEAD") : path11.join(root, gitDir, "MERGE_HEAD");
|
|
6472
|
+
await fs8.access(mergeHead);
|
|
6196
6473
|
hasMergeInProgress = true;
|
|
6197
6474
|
} catch {
|
|
6198
6475
|
}
|
|
@@ -6265,15 +6542,261 @@ async function gitResolve(file, side, cwd) {
|
|
|
6265
6542
|
return { ok: true };
|
|
6266
6543
|
}
|
|
6267
6544
|
|
|
6268
|
-
// src/commands/start.ts
|
|
6545
|
+
// src/commands/start/handlers.ts
|
|
6269
6546
|
function saveFilesTemp(files) {
|
|
6270
6547
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
6271
6548
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
6272
|
-
const tmpPath =
|
|
6273
|
-
|
|
6549
|
+
const tmpPath = path12.join(os8.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
|
|
6550
|
+
fs9.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
6274
6551
|
return tmpPath;
|
|
6275
6552
|
});
|
|
6276
6553
|
}
|
|
6554
|
+
function dispatchPrompt(ctx, prompt) {
|
|
6555
|
+
ctx.outputSvc.newTurn();
|
|
6556
|
+
ctx.claude.sendCommand(prompt);
|
|
6557
|
+
}
|
|
6558
|
+
var startTask = (ctx, _cmd, parsed) => {
|
|
6559
|
+
const { prompt, files } = parsed;
|
|
6560
|
+
const effectivePrompt = prompt ?? "";
|
|
6561
|
+
if (files && files.length > 0) {
|
|
6562
|
+
const paths = saveFilesTemp(files);
|
|
6563
|
+
const atRefs = paths.map((p2) => `@${p2}`).join(" ");
|
|
6564
|
+
ctx.outputSvc.newTurn();
|
|
6565
|
+
ctx.claude.sendCommand(`${atRefs} ${effectivePrompt}`.trim());
|
|
6566
|
+
setTimeout(() => {
|
|
6567
|
+
for (const p2 of paths) {
|
|
6568
|
+
try {
|
|
6569
|
+
fs9.unlinkSync(p2);
|
|
6570
|
+
} catch {
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
}, 12e4);
|
|
6574
|
+
} else if (effectivePrompt) {
|
|
6575
|
+
dispatchPrompt(ctx, effectivePrompt);
|
|
6576
|
+
}
|
|
6577
|
+
};
|
|
6578
|
+
var provideInput = (ctx, _cmd, parsed) => {
|
|
6579
|
+
if (parsed.input) dispatchPrompt(ctx, parsed.input);
|
|
6580
|
+
};
|
|
6581
|
+
var selectOption = (ctx, _cmd, parsed) => {
|
|
6582
|
+
const index = parsed.index ?? 0;
|
|
6583
|
+
const from = parsed.from ?? 0;
|
|
6584
|
+
ctx.outputSvc.newTurn();
|
|
6585
|
+
ctx.claude.selectOption(index, from);
|
|
6586
|
+
};
|
|
6587
|
+
var escapeKey = (ctx) => {
|
|
6588
|
+
ctx.outputSvc.newTurn();
|
|
6589
|
+
ctx.claude.sendEscape();
|
|
6590
|
+
};
|
|
6591
|
+
var stopTask = (ctx) => {
|
|
6592
|
+
ctx.claude.interrupt();
|
|
6593
|
+
};
|
|
6594
|
+
var resumeSession = async (ctx, _cmd, parsed) => {
|
|
6595
|
+
const { id, auto } = parsed;
|
|
6596
|
+
if (!id) return;
|
|
6597
|
+
ctx.historySvc.setCurrentConversationId(id);
|
|
6598
|
+
await ctx.historySvc.loadConversation(id);
|
|
6599
|
+
await ctx.outputSvc.newTurnResume(id);
|
|
6600
|
+
ctx.claude.restart(id, auto ?? false);
|
|
6601
|
+
};
|
|
6602
|
+
var getContext = async (ctx, cmd) => {
|
|
6603
|
+
const usage = ctx.historySvc.getCurrentUsage();
|
|
6604
|
+
const monthlyCost = ctx.historySvc.getMonthlyEstimatedCost();
|
|
6605
|
+
const rateLimitReset = ctx.historySvc.getRateLimitReset();
|
|
6606
|
+
const quotaPercent = ctx.historySvc.getQuotaPercent();
|
|
6607
|
+
const base = usage ? { ...usage, monthlyCost } : { used: 0, total: 2e5, percent: 0, model: null, outputTokens: 0, cacheReadTokens: 0, monthlyCost, error: "No usage data found" };
|
|
6608
|
+
const result = {
|
|
6609
|
+
...base,
|
|
6610
|
+
...rateLimitReset ? { rateLimitReset } : {},
|
|
6611
|
+
...quotaPercent !== null ? { quotaPercent } : {}
|
|
6612
|
+
};
|
|
6613
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6614
|
+
};
|
|
6615
|
+
var getConversation = async (ctx, cmd) => {
|
|
6616
|
+
const currentId = ctx.historySvc.getCurrentConversationId();
|
|
6617
|
+
if (!currentId) {
|
|
6618
|
+
await ctx.relay.sendResult(cmd.id, "completed", { conversationId: null });
|
|
6619
|
+
return;
|
|
6620
|
+
}
|
|
6621
|
+
try {
|
|
6622
|
+
await ctx.historySvc.loadConversation(currentId);
|
|
6623
|
+
await ctx.relay.sendResult(cmd.id, "completed", { conversationId: currentId });
|
|
6624
|
+
} catch {
|
|
6625
|
+
await ctx.relay.sendResult(cmd.id, "failed", {});
|
|
6626
|
+
}
|
|
6627
|
+
};
|
|
6628
|
+
var listModels = async (ctx, cmd) => {
|
|
6629
|
+
const models = [
|
|
6630
|
+
{ id: "claude-opus-4-7", label: "Claude Opus 4.7", description: "Most capable", family: "claude", vendor: "anthropic", isDefault: false },
|
|
6631
|
+
{ id: "claude-opus-4-6", label: "Claude Opus 4.6", description: "Top tier", family: "claude", vendor: "anthropic", isDefault: false },
|
|
6632
|
+
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", description: "Balanced", family: "claude", vendor: "anthropic", isDefault: true },
|
|
6633
|
+
{ id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", description: "Fastest", family: "claude", vendor: "anthropic", isDefault: false }
|
|
6634
|
+
];
|
|
6635
|
+
await ctx.relay.sendResult(cmd.id, "completed", { models });
|
|
6636
|
+
};
|
|
6637
|
+
var setKeepAlive = async (ctx, cmd) => {
|
|
6638
|
+
const enabled = !!cmd.payload.enabled;
|
|
6639
|
+
ctx.setKeepAlive(enabled);
|
|
6640
|
+
try {
|
|
6641
|
+
await ctx.relay.sendResult(
|
|
6642
|
+
cmd.id,
|
|
6643
|
+
"success",
|
|
6644
|
+
{
|
|
6645
|
+
enabled,
|
|
6646
|
+
applied: enabled && ctx.keepAliveCtx.inCodespace,
|
|
6647
|
+
runtime: ctx.keepAliveCtx.inCodespace ? "github-codespaces" : "local"
|
|
6648
|
+
}
|
|
6649
|
+
);
|
|
6650
|
+
} catch {
|
|
6651
|
+
}
|
|
6652
|
+
};
|
|
6653
|
+
var sessionTerminated = (ctx) => {
|
|
6654
|
+
showInfo("Session was deleted from the app \u2014 exiting.");
|
|
6655
|
+
try {
|
|
6656
|
+
ctx.claude.kill();
|
|
6657
|
+
} catch {
|
|
6658
|
+
}
|
|
6659
|
+
try {
|
|
6660
|
+
const proc = (0, import_child_process7.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6661
|
+
detached: true,
|
|
6662
|
+
stdio: "ignore"
|
|
6663
|
+
});
|
|
6664
|
+
proc.unref();
|
|
6665
|
+
} catch {
|
|
6666
|
+
}
|
|
6667
|
+
ctx.outputSvc.dispose();
|
|
6668
|
+
ctx.relay.stop();
|
|
6669
|
+
process.exit(0);
|
|
6670
|
+
};
|
|
6671
|
+
var shutdownSession = async (ctx, cmd) => {
|
|
6672
|
+
try {
|
|
6673
|
+
await ctx.relay.sendResult(cmd.id, "success", { ok: true });
|
|
6674
|
+
} catch {
|
|
6675
|
+
}
|
|
6676
|
+
try {
|
|
6677
|
+
ctx.claude.kill();
|
|
6678
|
+
} catch {
|
|
6679
|
+
}
|
|
6680
|
+
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
6681
|
+
try {
|
|
6682
|
+
const stopProc = (0, import_child_process7.spawn)(
|
|
6683
|
+
"bash",
|
|
6684
|
+
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
6685
|
+
{ detached: true, stdio: "ignore" }
|
|
6686
|
+
);
|
|
6687
|
+
stopProc.unref();
|
|
6688
|
+
} catch {
|
|
6689
|
+
}
|
|
6690
|
+
}
|
|
6691
|
+
try {
|
|
6692
|
+
const proc = (0, import_child_process7.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6693
|
+
detached: true,
|
|
6694
|
+
stdio: "ignore"
|
|
6695
|
+
});
|
|
6696
|
+
proc.unref();
|
|
6697
|
+
} catch {
|
|
6698
|
+
}
|
|
6699
|
+
ctx.outputSvc.dispose();
|
|
6700
|
+
ctx.relay.stop();
|
|
6701
|
+
process.exit(0);
|
|
6702
|
+
};
|
|
6703
|
+
var readFile2 = async (ctx, cmd, parsed) => {
|
|
6704
|
+
if (!parsed.path) {
|
|
6705
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
6706
|
+
return;
|
|
6707
|
+
}
|
|
6708
|
+
const result = await readProjectFile(parsed.path);
|
|
6709
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6710
|
+
};
|
|
6711
|
+
var writeFile2 = async (ctx, cmd, parsed) => {
|
|
6712
|
+
if (!parsed.path || typeof parsed.content !== "string") {
|
|
6713
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
|
|
6714
|
+
return;
|
|
6715
|
+
}
|
|
6716
|
+
const result = await writeProjectFile(parsed.path, parsed.content);
|
|
6717
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6718
|
+
};
|
|
6719
|
+
var listFiles = async (ctx, cmd, parsed) => {
|
|
6720
|
+
const result = await listProjectFiles({ query: parsed.query });
|
|
6721
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6722
|
+
};
|
|
6723
|
+
var gitStatusH = async (ctx, cmd) => {
|
|
6724
|
+
const result = await gitStatus();
|
|
6725
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6726
|
+
};
|
|
6727
|
+
var gitDiffH = async (ctx, cmd, parsed) => {
|
|
6728
|
+
const result = await gitDiff(parsed.path ?? null);
|
|
6729
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6730
|
+
};
|
|
6731
|
+
var gitDiffStagedH = async (ctx, cmd, parsed) => {
|
|
6732
|
+
const result = await gitDiffStaged(parsed.path ?? null);
|
|
6733
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6734
|
+
};
|
|
6735
|
+
var gitLogH = async (ctx, cmd, parsed) => {
|
|
6736
|
+
const result = await gitLog(parsed.limit ?? 30);
|
|
6737
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6738
|
+
};
|
|
6739
|
+
var gitCommitH = async (ctx, cmd, parsed) => {
|
|
6740
|
+
if (!parsed.message) {
|
|
6741
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing message" });
|
|
6742
|
+
return;
|
|
6743
|
+
}
|
|
6744
|
+
const result = await gitCommit(parsed.message, parsed.paths);
|
|
6745
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6746
|
+
};
|
|
6747
|
+
var gitPushH = async (ctx, cmd) => {
|
|
6748
|
+
const result = await gitPush();
|
|
6749
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6750
|
+
};
|
|
6751
|
+
var gitPullH = async (ctx, cmd) => {
|
|
6752
|
+
const result = await gitPull();
|
|
6753
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6754
|
+
};
|
|
6755
|
+
var gitResolveH = async (ctx, cmd, parsed) => {
|
|
6756
|
+
if (!parsed.path || !parsed.side) {
|
|
6757
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path or side" });
|
|
6758
|
+
return;
|
|
6759
|
+
}
|
|
6760
|
+
const result = await gitResolve(parsed.path, parsed.side);
|
|
6761
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
6762
|
+
};
|
|
6763
|
+
var handlers = {
|
|
6764
|
+
start_task: startTask,
|
|
6765
|
+
provide_input: provideInput,
|
|
6766
|
+
select_option: selectOption,
|
|
6767
|
+
escape_key: escapeKey,
|
|
6768
|
+
stop_task: stopTask,
|
|
6769
|
+
resume_session: resumeSession,
|
|
6770
|
+
get_context: getContext,
|
|
6771
|
+
get_conversation: getConversation,
|
|
6772
|
+
list_models: listModels,
|
|
6773
|
+
set_keep_alive: setKeepAlive,
|
|
6774
|
+
session_terminated: sessionTerminated,
|
|
6775
|
+
shutdown_session: shutdownSession,
|
|
6776
|
+
read_file: readFile2,
|
|
6777
|
+
write_file: writeFile2,
|
|
6778
|
+
list_files: listFiles,
|
|
6779
|
+
git_status: gitStatusH,
|
|
6780
|
+
git_diff: gitDiffH,
|
|
6781
|
+
git_diff_staged: gitDiffStagedH,
|
|
6782
|
+
git_log: gitLogH,
|
|
6783
|
+
git_commit: gitCommitH,
|
|
6784
|
+
git_push: gitPushH,
|
|
6785
|
+
git_pull: gitPullH,
|
|
6786
|
+
git_resolve: gitResolveH
|
|
6787
|
+
};
|
|
6788
|
+
async function dispatchCommand(ctx, cmd) {
|
|
6789
|
+
const parsed = parsePayload(startCommandSchema, cmd.payload);
|
|
6790
|
+
if (!parsed) {
|
|
6791
|
+
showInfo(`Ignoring malformed ${cmd.type} payload.`);
|
|
6792
|
+
return;
|
|
6793
|
+
}
|
|
6794
|
+
const handler = handlers[cmd.type];
|
|
6795
|
+
if (!handler) return;
|
|
6796
|
+
await handler(ctx, cmd, parsed);
|
|
6797
|
+
}
|
|
6798
|
+
|
|
6799
|
+
// src/commands/start.ts
|
|
6277
6800
|
async function start() {
|
|
6278
6801
|
showIntro();
|
|
6279
6802
|
const session = getActiveSession();
|
|
@@ -6287,434 +6810,32 @@ async function start() {
|
|
|
6287
6810
|
showInfo(`${session.userName} \xB7 ${import_picocolors2.default.cyan(session.plan)}`);
|
|
6288
6811
|
showInfo("Launching Claude Code...\n");
|
|
6289
6812
|
const cwd = process.cwd();
|
|
6290
|
-
const ws = new WebSocketService(session.id, pluginId);
|
|
6291
6813
|
const historySvc = new HistoryService(pluginId, cwd);
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
fcntl.ioctl(s,termios.TIOCSWINSZ,struct.pack('HHHH',30,120,0,0))
|
|
6305
|
-
except Exception:pass
|
|
6306
|
-
pid=os.fork()
|
|
6307
|
-
if pid==0:
|
|
6308
|
-
os.close(m);os.setsid()
|
|
6309
|
-
try:fcntl.ioctl(s,termios.TIOCSCTTY,0)
|
|
6310
|
-
except Exception:pass
|
|
6311
|
-
for fd in[0,1,2]:os.dup2(s,fd)
|
|
6312
|
-
if s>2:os.close(s)
|
|
6313
|
-
os.execvp(sys.argv[1],sys.argv[1:])
|
|
6314
|
-
sys.exit(127)
|
|
6315
|
-
os.close(s)
|
|
6316
|
-
done=[False]
|
|
6317
|
-
def onchld(n,f):
|
|
6318
|
-
try:os.waitpid(pid,os.WNOHANG)
|
|
6319
|
-
except Exception:pass
|
|
6320
|
-
done[0]=True
|
|
6321
|
-
signal.signal(signal.SIGCHLD,onchld)
|
|
6322
|
-
i=sys.stdin.fileno();o=sys.stdout.fileno()
|
|
6323
|
-
while not done[0]:
|
|
6324
|
-
try:r,_,_=select.select([i,m],[],[],0.1)
|
|
6325
|
-
except OSError as e:
|
|
6326
|
-
if e.errno==errno.EINTR:continue
|
|
6327
|
-
break
|
|
6328
|
-
if i in r:
|
|
6329
|
-
try:
|
|
6330
|
-
d=os.read(i,4096)
|
|
6331
|
-
if d:os.write(m,d)
|
|
6332
|
-
else:break
|
|
6333
|
-
except OSError:break
|
|
6334
|
-
if m in r:
|
|
6335
|
-
try:
|
|
6336
|
-
d=os.read(m,4096)
|
|
6337
|
-
if d:os.write(o,d)
|
|
6338
|
-
except OSError:done[0]=True
|
|
6339
|
-
try:os.kill(pid,signal.SIGTERM)
|
|
6340
|
-
except Exception:pass
|
|
6341
|
-
try:
|
|
6342
|
-
_,st=os.waitpid(pid,0)
|
|
6343
|
-
sys.exit((st>>8)&0xFF)
|
|
6344
|
-
except Exception:sys.exit(0)
|
|
6345
|
-
`;
|
|
6346
|
-
const helperPath = path11.join(os7.tmpdir(), "codeam-quota-helper.py");
|
|
6347
|
-
fs8.writeFileSync(helperPath, helperScript, { mode: 420 });
|
|
6348
|
-
const python = findInPath("python3") ?? findInPath("python");
|
|
6349
|
-
if (!python) {
|
|
6350
|
-
quotaFetchInProgress = false;
|
|
6351
|
-
return;
|
|
6352
|
-
}
|
|
6353
|
-
const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
|
|
6354
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
6355
|
-
cwd: process.cwd(),
|
|
6356
|
-
env: { ...process.env, TERM: "dumb", COLUMNS: "120", LINES: "30" }
|
|
6357
|
-
});
|
|
6358
|
-
let output = "";
|
|
6359
|
-
proc.stdout?.on("data", (chunk) => {
|
|
6360
|
-
output += chunk.toString("utf8");
|
|
6361
|
-
});
|
|
6362
|
-
setTimeout(() => {
|
|
6363
|
-
proc.stdin?.write("/usage\r");
|
|
6814
|
+
const keepAliveCtx = {
|
|
6815
|
+
inCodespace: process.env.CODESPACES === "true",
|
|
6816
|
+
codespaceName: process.env.CODESPACE_NAME
|
|
6817
|
+
};
|
|
6818
|
+
const { apply: setKeepAlive2 } = buildKeepAlive(keepAliveCtx);
|
|
6819
|
+
const outputSvc = new OutputService(
|
|
6820
|
+
session.id,
|
|
6821
|
+
pluginId,
|
|
6822
|
+
(conversationId) => historySvc.setCurrentConversationId(conversationId),
|
|
6823
|
+
(reset) => historySvc.setRateLimitReset(reset),
|
|
6824
|
+
() => {
|
|
6825
|
+
if (historySvc.isQuotaStale()) fetchQuotaUsage(historySvc);
|
|
6364
6826
|
setTimeout(() => {
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
historySvc.setQuotaPercent(parseInt(weekMatch[1], 10));
|
|
6369
|
-
}
|
|
6370
|
-
const resetMatch = clean.match(/resets\s+(.+?)(?:\s*\(|$)/im);
|
|
6371
|
-
if (resetMatch) {
|
|
6372
|
-
historySvc.setRateLimitReset(resetMatch[1].trim());
|
|
6373
|
-
}
|
|
6374
|
-
try {
|
|
6375
|
-
proc.kill();
|
|
6376
|
-
} catch {
|
|
6377
|
-
}
|
|
6378
|
-
try {
|
|
6379
|
-
fs8.unlinkSync(helperPath);
|
|
6380
|
-
} catch {
|
|
6381
|
-
}
|
|
6382
|
-
quotaFetchInProgress = false;
|
|
6383
|
-
}, 5e3);
|
|
6384
|
-
}, 8e3);
|
|
6385
|
-
proc.on("exit", () => {
|
|
6386
|
-
quotaFetchInProgress = false;
|
|
6387
|
-
});
|
|
6388
|
-
setTimeout(() => {
|
|
6389
|
-
try {
|
|
6390
|
-
proc.kill();
|
|
6391
|
-
} catch {
|
|
6392
|
-
}
|
|
6393
|
-
}, 2e4);
|
|
6394
|
-
}
|
|
6395
|
-
const outputSvc = new OutputService(session.id, pluginId, (conversationId) => {
|
|
6396
|
-
historySvc.setCurrentConversationId(conversationId);
|
|
6397
|
-
}, (reset) => {
|
|
6398
|
-
historySvc.setRateLimitReset(reset);
|
|
6399
|
-
}, () => {
|
|
6400
|
-
if (historySvc.isQuotaStale()) {
|
|
6401
|
-
fetchQuotaUsage();
|
|
6402
|
-
}
|
|
6403
|
-
setTimeout(() => {
|
|
6404
|
-
historySvc.uploadDelta().catch(() => {
|
|
6405
|
-
});
|
|
6406
|
-
}, 400);
|
|
6407
|
-
}, () => {
|
|
6408
|
-
const prevCount = historySvc.getCurrentMessageCount();
|
|
6409
|
-
historySvc.waitForNewUserMessage(prevCount).then((userText) => outputSvc.startTerminalTurn(userText ?? void 0)).catch(() => outputSvc.startTerminalTurn(void 0));
|
|
6410
|
-
}, session.pluginAuthToken);
|
|
6411
|
-
function sendPrompt(prompt) {
|
|
6412
|
-
outputSvc.newTurn();
|
|
6413
|
-
claude.sendCommand(prompt);
|
|
6414
|
-
}
|
|
6415
|
-
const relay = new CommandRelayService(pluginId, async (cmd) => {
|
|
6416
|
-
const parsed = parsePayload(startCommandSchema, cmd.payload);
|
|
6417
|
-
if (!parsed) {
|
|
6418
|
-
showInfo(`Ignoring malformed ${cmd.type} payload.`);
|
|
6419
|
-
return;
|
|
6420
|
-
}
|
|
6421
|
-
switch (cmd.type) {
|
|
6422
|
-
case "start_task": {
|
|
6423
|
-
const { prompt, files } = parsed;
|
|
6424
|
-
const effectivePrompt = prompt ?? "";
|
|
6425
|
-
if (files && files.length > 0) {
|
|
6426
|
-
const paths = saveFilesTemp(files);
|
|
6427
|
-
const atRefs = paths.map((p2) => `@${p2}`).join(" ");
|
|
6428
|
-
outputSvc.newTurn();
|
|
6429
|
-
claude.sendCommand(`${atRefs} ${effectivePrompt}`.trim());
|
|
6430
|
-
setTimeout(() => {
|
|
6431
|
-
for (const p2 of paths) {
|
|
6432
|
-
try {
|
|
6433
|
-
fs8.unlinkSync(p2);
|
|
6434
|
-
} catch {
|
|
6435
|
-
}
|
|
6436
|
-
}
|
|
6437
|
-
}, 12e4);
|
|
6438
|
-
} else if (effectivePrompt) {
|
|
6439
|
-
sendPrompt(effectivePrompt);
|
|
6440
|
-
}
|
|
6441
|
-
break;
|
|
6442
|
-
}
|
|
6443
|
-
case "provide_input": {
|
|
6444
|
-
const { input } = parsed;
|
|
6445
|
-
if (input) sendPrompt(input);
|
|
6446
|
-
break;
|
|
6447
|
-
}
|
|
6448
|
-
case "select_option": {
|
|
6449
|
-
const index = parsed.index ?? 0;
|
|
6450
|
-
const from = parsed.from ?? 0;
|
|
6451
|
-
outputSvc.newTurn();
|
|
6452
|
-
claude.selectOption(index, from);
|
|
6453
|
-
break;
|
|
6454
|
-
}
|
|
6455
|
-
case "escape_key":
|
|
6456
|
-
outputSvc.newTurn();
|
|
6457
|
-
claude.sendEscape();
|
|
6458
|
-
break;
|
|
6459
|
-
case "stop_task":
|
|
6460
|
-
claude.interrupt();
|
|
6461
|
-
break;
|
|
6462
|
-
case "set_keep_alive": {
|
|
6463
|
-
const enabled = !!cmd.payload.enabled;
|
|
6464
|
-
const inCodespaceEnv = process.env.CODESPACES === "true";
|
|
6465
|
-
setKeepAlive(enabled);
|
|
6466
|
-
try {
|
|
6467
|
-
await relay.sendResult(
|
|
6468
|
-
cmd.id,
|
|
6469
|
-
"success",
|
|
6470
|
-
{ enabled, applied: enabled && inCodespaceEnv, runtime: inCodespaceEnv ? "github-codespaces" : "local" }
|
|
6471
|
-
);
|
|
6472
|
-
} catch {
|
|
6473
|
-
}
|
|
6474
|
-
break;
|
|
6475
|
-
}
|
|
6476
|
-
case "session_terminated": {
|
|
6477
|
-
showInfo("Session was deleted from the app \u2014 exiting.");
|
|
6478
|
-
try {
|
|
6479
|
-
claude.kill();
|
|
6480
|
-
} catch {
|
|
6481
|
-
}
|
|
6482
|
-
try {
|
|
6483
|
-
const proc = (0, import_child_process5.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6484
|
-
detached: true,
|
|
6485
|
-
stdio: "ignore"
|
|
6486
|
-
});
|
|
6487
|
-
proc.unref();
|
|
6488
|
-
} catch {
|
|
6489
|
-
}
|
|
6490
|
-
outputSvc.dispose();
|
|
6491
|
-
relay.stop();
|
|
6492
|
-
ws.disconnect();
|
|
6493
|
-
process.exit(0);
|
|
6494
|
-
}
|
|
6495
|
-
case "shutdown_session": {
|
|
6496
|
-
try {
|
|
6497
|
-
await relay.sendResult(cmd.id, "success", { ok: true });
|
|
6498
|
-
} catch {
|
|
6499
|
-
}
|
|
6500
|
-
try {
|
|
6501
|
-
claude.kill();
|
|
6502
|
-
} catch {
|
|
6503
|
-
}
|
|
6504
|
-
const codespaceName2 = process.env.CODESPACE_NAME;
|
|
6505
|
-
if (codespaceName2 && process.env.CODESPACES === "true") {
|
|
6506
|
-
try {
|
|
6507
|
-
const stopProc = (0, import_child_process5.spawn)(
|
|
6508
|
-
"bash",
|
|
6509
|
-
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(codespaceName2)} >/dev/null 2>&1 || true`],
|
|
6510
|
-
{ detached: true, stdio: "ignore" }
|
|
6511
|
-
);
|
|
6512
|
-
stopProc.unref();
|
|
6513
|
-
} catch {
|
|
6514
|
-
}
|
|
6515
|
-
}
|
|
6516
|
-
try {
|
|
6517
|
-
const proc = (0, import_child_process5.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6518
|
-
detached: true,
|
|
6519
|
-
stdio: "ignore"
|
|
6520
|
-
});
|
|
6521
|
-
proc.unref();
|
|
6522
|
-
} catch {
|
|
6523
|
-
}
|
|
6524
|
-
outputSvc.dispose();
|
|
6525
|
-
relay.stop();
|
|
6526
|
-
ws.disconnect();
|
|
6527
|
-
process.exit(0);
|
|
6528
|
-
}
|
|
6529
|
-
case "get_context": {
|
|
6530
|
-
const usage = historySvc.getCurrentUsage();
|
|
6531
|
-
const monthlyCost = historySvc.getMonthlyEstimatedCost();
|
|
6532
|
-
const rateLimitReset = historySvc.getRateLimitReset();
|
|
6533
|
-
const quotaPercent = historySvc.getQuotaPercent();
|
|
6534
|
-
const base = usage ? { ...usage, monthlyCost } : { used: 0, total: 2e5, percent: 0, model: null, outputTokens: 0, cacheReadTokens: 0, monthlyCost, error: "No usage data found" };
|
|
6535
|
-
const result = { ...base, ...rateLimitReset ? { rateLimitReset } : {}, ...quotaPercent !== null ? { quotaPercent } : {} };
|
|
6536
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6537
|
-
break;
|
|
6538
|
-
}
|
|
6539
|
-
case "resume_session": {
|
|
6540
|
-
const { id, auto } = parsed;
|
|
6541
|
-
if (!id) break;
|
|
6542
|
-
historySvc.setCurrentConversationId(id);
|
|
6543
|
-
await historySvc.loadConversation(id);
|
|
6544
|
-
await outputSvc.newTurnResume(id);
|
|
6545
|
-
claude.restart(id, auto ?? false);
|
|
6546
|
-
break;
|
|
6547
|
-
}
|
|
6548
|
-
case "get_conversation": {
|
|
6549
|
-
const currentId = historySvc.getCurrentConversationId();
|
|
6550
|
-
if (currentId) {
|
|
6551
|
-
try {
|
|
6552
|
-
await historySvc.loadConversation(currentId);
|
|
6553
|
-
await relay.sendResult(cmd.id, "completed", { conversationId: currentId });
|
|
6554
|
-
} catch {
|
|
6555
|
-
await relay.sendResult(cmd.id, "failed", {});
|
|
6556
|
-
}
|
|
6557
|
-
} else {
|
|
6558
|
-
await relay.sendResult(cmd.id, "completed", { conversationId: null });
|
|
6559
|
-
}
|
|
6560
|
-
break;
|
|
6561
|
-
}
|
|
6562
|
-
case "list_models": {
|
|
6563
|
-
const models = [
|
|
6564
|
-
{ id: "claude-opus-4-7", label: "Claude Opus 4.7", description: "Most capable", family: "claude", vendor: "anthropic", isDefault: false },
|
|
6565
|
-
{ id: "claude-opus-4-6", label: "Claude Opus 4.6", description: "Top tier", family: "claude", vendor: "anthropic", isDefault: false },
|
|
6566
|
-
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", description: "Balanced", family: "claude", vendor: "anthropic", isDefault: true },
|
|
6567
|
-
{ id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", description: "Fastest", family: "claude", vendor: "anthropic", isDefault: false }
|
|
6568
|
-
];
|
|
6569
|
-
await relay.sendResult(cmd.id, "completed", { models });
|
|
6570
|
-
break;
|
|
6571
|
-
}
|
|
6572
|
-
case "read_file": {
|
|
6573
|
-
const { path: filePath } = parsed;
|
|
6574
|
-
if (!filePath) {
|
|
6575
|
-
await relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
6576
|
-
break;
|
|
6577
|
-
}
|
|
6578
|
-
const result = await readProjectFile(filePath);
|
|
6579
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6580
|
-
break;
|
|
6581
|
-
}
|
|
6582
|
-
case "write_file": {
|
|
6583
|
-
const { path: filePath, content } = parsed;
|
|
6584
|
-
if (!filePath || typeof content !== "string") {
|
|
6585
|
-
await relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
|
|
6586
|
-
break;
|
|
6587
|
-
}
|
|
6588
|
-
const result = await writeProjectFile(filePath, content);
|
|
6589
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6590
|
-
break;
|
|
6591
|
-
}
|
|
6592
|
-
case "list_files": {
|
|
6593
|
-
const result = await listProjectFiles({ query: parsed.query });
|
|
6594
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6595
|
-
break;
|
|
6596
|
-
}
|
|
6597
|
-
case "git_status": {
|
|
6598
|
-
const result = await gitStatus();
|
|
6599
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6600
|
-
break;
|
|
6601
|
-
}
|
|
6602
|
-
case "git_diff": {
|
|
6603
|
-
const { path: filePath } = parsed;
|
|
6604
|
-
const result = await gitDiff(filePath ?? null);
|
|
6605
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6606
|
-
break;
|
|
6607
|
-
}
|
|
6608
|
-
case "git_diff_staged": {
|
|
6609
|
-
const { path: filePath } = parsed;
|
|
6610
|
-
const result = await gitDiffStaged(filePath ?? null);
|
|
6611
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6612
|
-
break;
|
|
6613
|
-
}
|
|
6614
|
-
case "git_log": {
|
|
6615
|
-
const result = await gitLog(parsed.limit ?? 30);
|
|
6616
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6617
|
-
break;
|
|
6618
|
-
}
|
|
6619
|
-
case "git_commit": {
|
|
6620
|
-
if (!parsed.message) {
|
|
6621
|
-
await relay.sendResult(cmd.id, "failed", { error: "Missing message" });
|
|
6622
|
-
break;
|
|
6623
|
-
}
|
|
6624
|
-
const result = await gitCommit(parsed.message, parsed.paths);
|
|
6625
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6626
|
-
break;
|
|
6627
|
-
}
|
|
6628
|
-
case "git_push": {
|
|
6629
|
-
const result = await gitPush();
|
|
6630
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6631
|
-
break;
|
|
6632
|
-
}
|
|
6633
|
-
case "git_pull": {
|
|
6634
|
-
const result = await gitPull();
|
|
6635
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6636
|
-
break;
|
|
6637
|
-
}
|
|
6638
|
-
case "git_resolve": {
|
|
6639
|
-
const { path: filePath, side } = parsed;
|
|
6640
|
-
if (!filePath || !side) {
|
|
6641
|
-
await relay.sendResult(cmd.id, "failed", { error: "Missing path or side" });
|
|
6642
|
-
break;
|
|
6643
|
-
}
|
|
6644
|
-
const result = await gitResolve(filePath, side);
|
|
6645
|
-
await relay.sendResult(cmd.id, "completed", result);
|
|
6646
|
-
break;
|
|
6647
|
-
}
|
|
6648
|
-
}
|
|
6649
|
-
});
|
|
6650
|
-
ws.addHandler({
|
|
6651
|
-
onConnected() {
|
|
6827
|
+
historySvc.uploadDelta().catch(() => {
|
|
6828
|
+
});
|
|
6829
|
+
}, 400);
|
|
6652
6830
|
},
|
|
6653
|
-
|
|
6831
|
+
() => {
|
|
6832
|
+
const prevCount = historySvc.getCurrentMessageCount();
|
|
6833
|
+
historySvc.waitForNewUserMessage(prevCount).then((userText) => outputSvc.startTerminalTurn(userText ?? void 0)).catch(() => outputSvc.startTerminalTurn(void 0));
|
|
6654
6834
|
},
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
const cmdType = typeof payload.type === "string" ? payload.type : null;
|
|
6658
|
-
if (!cmdType) return;
|
|
6659
|
-
const parsed = parsePayload(startCommandSchema, payload.payload ?? {});
|
|
6660
|
-
if (!parsed) {
|
|
6661
|
-
showInfo(`Ignoring malformed ${cmdType} payload (ws).`);
|
|
6662
|
-
return;
|
|
6663
|
-
}
|
|
6664
|
-
if (cmdType === "start_task") {
|
|
6665
|
-
const { prompt, files } = parsed;
|
|
6666
|
-
const effectivePrompt = prompt ?? "";
|
|
6667
|
-
if (files && files.length > 0) {
|
|
6668
|
-
const paths = saveFilesTemp(files);
|
|
6669
|
-
const atRefs = paths.map((p2) => `@${p2}`).join(" ");
|
|
6670
|
-
outputSvc.newTurn();
|
|
6671
|
-
claude.sendCommand(`${atRefs} ${effectivePrompt}`.trim());
|
|
6672
|
-
setTimeout(() => {
|
|
6673
|
-
for (const p2 of paths) {
|
|
6674
|
-
try {
|
|
6675
|
-
fs8.unlinkSync(p2);
|
|
6676
|
-
} catch {
|
|
6677
|
-
}
|
|
6678
|
-
}
|
|
6679
|
-
}, 12e4);
|
|
6680
|
-
} else if (effectivePrompt) {
|
|
6681
|
-
sendPrompt(effectivePrompt);
|
|
6682
|
-
}
|
|
6683
|
-
} else if (cmdType === "provide_input") {
|
|
6684
|
-
const { input } = parsed;
|
|
6685
|
-
if (input) sendPrompt(input);
|
|
6686
|
-
} else if (cmdType === "select_option") {
|
|
6687
|
-
const index = parsed.index ?? 0;
|
|
6688
|
-
const from = parsed.from ?? 0;
|
|
6689
|
-
outputSvc.newTurn();
|
|
6690
|
-
claude.selectOption(index, from);
|
|
6691
|
-
} else if (cmdType === "escape_key") {
|
|
6692
|
-
outputSvc.newTurn();
|
|
6693
|
-
claude.sendEscape();
|
|
6694
|
-
} else if (cmdType === "stop_task") {
|
|
6695
|
-
claude.interrupt();
|
|
6696
|
-
} else if (cmdType === "get_conversation") {
|
|
6697
|
-
const currentId = historySvc.getCurrentConversationId();
|
|
6698
|
-
if (currentId) {
|
|
6699
|
-
historySvc.loadConversation(currentId).catch(() => {
|
|
6700
|
-
});
|
|
6701
|
-
}
|
|
6702
|
-
} else if (cmdType === "resume_session") {
|
|
6703
|
-
const { id, auto } = parsed;
|
|
6704
|
-
if (id) {
|
|
6705
|
-
const autoFlag = auto ?? false;
|
|
6706
|
-
historySvc.loadConversation(id).then(() => outputSvc.newTurnResume(id)).then(() => {
|
|
6707
|
-
claude.restart(id, autoFlag);
|
|
6708
|
-
}).catch(() => {
|
|
6709
|
-
});
|
|
6710
|
-
}
|
|
6711
|
-
}
|
|
6712
|
-
}
|
|
6713
|
-
});
|
|
6714
|
-
ws.connect();
|
|
6715
|
-
relay.start();
|
|
6835
|
+
session.pluginAuthToken
|
|
6836
|
+
);
|
|
6716
6837
|
const claude = new ClaudeService({
|
|
6717
|
-
cwd
|
|
6838
|
+
cwd,
|
|
6718
6839
|
onData(raw) {
|
|
6719
6840
|
outputSvc.push(raw);
|
|
6720
6841
|
},
|
|
@@ -6722,18 +6843,29 @@ except Exception:sys.exit(0)
|
|
|
6722
6843
|
process.removeListener("SIGINT", sigintHandler);
|
|
6723
6844
|
outputSvc.dispose();
|
|
6724
6845
|
relay.stop();
|
|
6725
|
-
ws.disconnect();
|
|
6726
6846
|
process.exit(code);
|
|
6727
6847
|
}
|
|
6728
6848
|
});
|
|
6849
|
+
const ctx = {
|
|
6850
|
+
outputSvc,
|
|
6851
|
+
claude,
|
|
6852
|
+
historySvc,
|
|
6853
|
+
relay: void 0,
|
|
6854
|
+
setKeepAlive: setKeepAlive2,
|
|
6855
|
+
keepAliveCtx
|
|
6856
|
+
};
|
|
6857
|
+
const relay = new CommandRelayService(pluginId, async (cmd) => {
|
|
6858
|
+
await dispatchCommand(ctx, cmd);
|
|
6859
|
+
});
|
|
6860
|
+
ctx.relay = relay;
|
|
6729
6861
|
function sigintHandler() {
|
|
6730
6862
|
claude.kill();
|
|
6731
6863
|
outputSvc.dispose();
|
|
6732
6864
|
relay.stop();
|
|
6733
|
-
ws.disconnect();
|
|
6734
6865
|
process.exit(0);
|
|
6735
6866
|
}
|
|
6736
6867
|
process.once("SIGINT", sigintHandler);
|
|
6868
|
+
relay.start();
|
|
6737
6869
|
await claude.spawn();
|
|
6738
6870
|
setTimeout(() => {
|
|
6739
6871
|
historySvc.detectCurrentConversation();
|
|
@@ -6745,47 +6877,7 @@ except Exception:sys.exit(0)
|
|
|
6745
6877
|
});
|
|
6746
6878
|
}
|
|
6747
6879
|
}, 2e3);
|
|
6748
|
-
setTimeout(() =>
|
|
6749
|
-
fetchQuotaUsage();
|
|
6750
|
-
}, 5e3);
|
|
6751
|
-
const inCodespace = process.env.CODESPACES === "true";
|
|
6752
|
-
const codespaceName = process.env.CODESPACE_NAME;
|
|
6753
|
-
let keepAliveTimer = null;
|
|
6754
|
-
async function setIdleTimeout(minutes) {
|
|
6755
|
-
if (!inCodespace || !codespaceName) return;
|
|
6756
|
-
await new Promise((resolve2) => {
|
|
6757
|
-
const proc = (0, import_child_process5.spawn)(
|
|
6758
|
-
"gh",
|
|
6759
|
-
[
|
|
6760
|
-
"api",
|
|
6761
|
-
"-X",
|
|
6762
|
-
"PATCH",
|
|
6763
|
-
`/user/codespaces/${codespaceName}`,
|
|
6764
|
-
"-F",
|
|
6765
|
-
`idle_timeout_minutes=${minutes}`
|
|
6766
|
-
],
|
|
6767
|
-
{ stdio: "ignore", detached: true }
|
|
6768
|
-
);
|
|
6769
|
-
proc.unref();
|
|
6770
|
-
proc.on("exit", () => resolve2());
|
|
6771
|
-
proc.on("error", () => resolve2());
|
|
6772
|
-
});
|
|
6773
|
-
}
|
|
6774
|
-
function setKeepAlive(enabled) {
|
|
6775
|
-
if (keepAliveTimer) {
|
|
6776
|
-
clearInterval(keepAliveTimer);
|
|
6777
|
-
keepAliveTimer = null;
|
|
6778
|
-
}
|
|
6779
|
-
if (!inCodespace || !codespaceName) return;
|
|
6780
|
-
if (!enabled) {
|
|
6781
|
-
void setIdleTimeout(30);
|
|
6782
|
-
return;
|
|
6783
|
-
}
|
|
6784
|
-
void setIdleTimeout(240);
|
|
6785
|
-
keepAliveTimer = setInterval(() => {
|
|
6786
|
-
void setIdleTimeout(240);
|
|
6787
|
-
}, 30 * 60 * 1e3);
|
|
6788
|
-
}
|
|
6880
|
+
setTimeout(() => fetchQuotaUsage(historySvc), 5e3);
|
|
6789
6881
|
}
|
|
6790
6882
|
|
|
6791
6883
|
// src/commands/pair.ts
|
|
@@ -6966,19 +7058,19 @@ async function logout() {
|
|
|
6966
7058
|
}
|
|
6967
7059
|
|
|
6968
7060
|
// src/commands/deploy.ts
|
|
6969
|
-
var
|
|
6970
|
-
var
|
|
6971
|
-
var
|
|
6972
|
-
var
|
|
7061
|
+
var import_child_process12 = require("child_process");
|
|
7062
|
+
var fs10 = __toESM(require("fs"));
|
|
7063
|
+
var os9 = __toESM(require("os"));
|
|
7064
|
+
var path17 = __toESM(require("path"));
|
|
6973
7065
|
var import_util6 = require("util");
|
|
6974
7066
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
6975
7067
|
|
|
6976
7068
|
// src/services/providers/github-codespaces.ts
|
|
6977
|
-
var
|
|
7069
|
+
var import_child_process8 = require("child_process");
|
|
6978
7070
|
var import_util2 = require("util");
|
|
6979
7071
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
6980
|
-
var
|
|
6981
|
-
var execFileP2 = (0, import_util2.promisify)(
|
|
7072
|
+
var path13 = __toESM(require("path"));
|
|
7073
|
+
var execFileP2 = (0, import_util2.promisify)(import_child_process8.execFile);
|
|
6982
7074
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
6983
7075
|
function resetStdinForChild() {
|
|
6984
7076
|
if (process.stdin.isTTY) {
|
|
@@ -7022,7 +7114,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7022
7114
|
if (!isAuthed) {
|
|
7023
7115
|
resetStdinForChild();
|
|
7024
7116
|
await new Promise((resolve2, reject) => {
|
|
7025
|
-
const proc = (0,
|
|
7117
|
+
const proc = (0, import_child_process8.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
7026
7118
|
stdio: "inherit"
|
|
7027
7119
|
});
|
|
7028
7120
|
proc.on("exit", (code) => {
|
|
@@ -7056,7 +7148,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7056
7148
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
7057
7149
|
resetStdinForChild();
|
|
7058
7150
|
const refreshCode = await new Promise((resolve2, reject) => {
|
|
7059
|
-
const proc = (0,
|
|
7151
|
+
const proc = (0, import_child_process8.spawn)(
|
|
7060
7152
|
"gh",
|
|
7061
7153
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
7062
7154
|
{ stdio: "inherit" }
|
|
@@ -7206,7 +7298,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7206
7298
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
7207
7299
|
resetStdinForChild();
|
|
7208
7300
|
const ok = await new Promise((resolve2) => {
|
|
7209
|
-
const proc = (0,
|
|
7301
|
+
const proc = (0, import_child_process8.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
7210
7302
|
proc.on("exit", (code) => resolve2(code === 0));
|
|
7211
7303
|
proc.on("error", () => resolve2(false));
|
|
7212
7304
|
});
|
|
@@ -7233,7 +7325,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7233
7325
|
);
|
|
7234
7326
|
resetStdinForChild();
|
|
7235
7327
|
await new Promise((resolve2, reject) => {
|
|
7236
|
-
const proc = (0,
|
|
7328
|
+
const proc = (0, import_child_process8.spawn)(
|
|
7237
7329
|
"gh",
|
|
7238
7330
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
7239
7331
|
{ stdio: "inherit" }
|
|
@@ -7411,7 +7503,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7411
7503
|
async streamCommand(workspaceId, command2) {
|
|
7412
7504
|
resetStdinForChild();
|
|
7413
7505
|
return new Promise((resolve2, reject) => {
|
|
7414
|
-
const proc = (0,
|
|
7506
|
+
const proc = (0, import_child_process8.spawn)(
|
|
7415
7507
|
"gh",
|
|
7416
7508
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
7417
7509
|
{ stdio: "inherit" }
|
|
@@ -7438,11 +7530,11 @@ var GitHubCodespacesProvider = class {
|
|
|
7438
7530
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
7439
7531
|
];
|
|
7440
7532
|
await new Promise((resolve2, reject) => {
|
|
7441
|
-
const tar = (0,
|
|
7533
|
+
const tar = (0, import_child_process8.spawn)("tar", tarArgs, {
|
|
7442
7534
|
stdio: ["ignore", "pipe", "pipe"],
|
|
7443
7535
|
env: tarEnv
|
|
7444
7536
|
});
|
|
7445
|
-
const ssh = (0,
|
|
7537
|
+
const ssh = (0, import_child_process8.spawn)("gh", sshArgs, {
|
|
7446
7538
|
stdio: [tar.stdout, "pipe", "pipe"]
|
|
7447
7539
|
});
|
|
7448
7540
|
let tarErr = "";
|
|
@@ -7466,7 +7558,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7466
7558
|
});
|
|
7467
7559
|
}
|
|
7468
7560
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7469
|
-
const remoteDir =
|
|
7561
|
+
const remoteDir = path13.posix.dirname(remotePath);
|
|
7470
7562
|
const parts = [
|
|
7471
7563
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
7472
7564
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -7476,7 +7568,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7476
7568
|
}
|
|
7477
7569
|
const cmd = parts.join(" && ");
|
|
7478
7570
|
await new Promise((resolve2, reject) => {
|
|
7479
|
-
const proc = (0,
|
|
7571
|
+
const proc = (0, import_child_process8.spawn)(
|
|
7480
7572
|
"gh",
|
|
7481
7573
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
7482
7574
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -7534,11 +7626,11 @@ function shellQuote(s) {
|
|
|
7534
7626
|
}
|
|
7535
7627
|
|
|
7536
7628
|
// src/services/providers/gitpod.ts
|
|
7537
|
-
var
|
|
7629
|
+
var import_child_process9 = require("child_process");
|
|
7538
7630
|
var import_util3 = require("util");
|
|
7539
|
-
var
|
|
7631
|
+
var path14 = __toESM(require("path"));
|
|
7540
7632
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
7541
|
-
var execFileP3 = (0, import_util3.promisify)(
|
|
7633
|
+
var execFileP3 = (0, import_util3.promisify)(import_child_process9.execFile);
|
|
7542
7634
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
7543
7635
|
function resetStdinForChild2() {
|
|
7544
7636
|
if (process.stdin.isTTY) {
|
|
@@ -7578,7 +7670,7 @@ var GitpodProvider = class {
|
|
|
7578
7670
|
);
|
|
7579
7671
|
resetStdinForChild2();
|
|
7580
7672
|
await new Promise((resolve2, reject) => {
|
|
7581
|
-
const proc = (0,
|
|
7673
|
+
const proc = (0, import_child_process9.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
7582
7674
|
proc.on("exit", (code) => {
|
|
7583
7675
|
if (code === 0) resolve2();
|
|
7584
7676
|
else reject(new Error("gitpod login failed."));
|
|
@@ -7730,7 +7822,7 @@ var GitpodProvider = class {
|
|
|
7730
7822
|
async streamCommand(workspaceId, command2) {
|
|
7731
7823
|
resetStdinForChild2();
|
|
7732
7824
|
return new Promise((resolve2, reject) => {
|
|
7733
|
-
const proc = (0,
|
|
7825
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7734
7826
|
"gitpod",
|
|
7735
7827
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
7736
7828
|
{ stdio: "inherit" }
|
|
@@ -7750,11 +7842,11 @@ var GitpodProvider = class {
|
|
|
7750
7842
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
7751
7843
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
7752
7844
|
await new Promise((resolve2, reject) => {
|
|
7753
|
-
const tar = (0,
|
|
7845
|
+
const tar = (0, import_child_process9.spawn)("tar", tarArgs, {
|
|
7754
7846
|
stdio: ["ignore", "pipe", "pipe"],
|
|
7755
7847
|
env: tarEnv
|
|
7756
7848
|
});
|
|
7757
|
-
const ssh = (0,
|
|
7849
|
+
const ssh = (0, import_child_process9.spawn)(
|
|
7758
7850
|
"gitpod",
|
|
7759
7851
|
["workspace", "ssh", workspaceId, "--", remoteCmd],
|
|
7760
7852
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -7776,7 +7868,7 @@ var GitpodProvider = class {
|
|
|
7776
7868
|
});
|
|
7777
7869
|
}
|
|
7778
7870
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7779
|
-
const remoteDir =
|
|
7871
|
+
const remoteDir = path14.posix.dirname(remotePath);
|
|
7780
7872
|
const parts = [
|
|
7781
7873
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
7782
7874
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -7786,7 +7878,7 @@ var GitpodProvider = class {
|
|
|
7786
7878
|
}
|
|
7787
7879
|
const cmd = parts.join(" && ");
|
|
7788
7880
|
await new Promise((resolve2, reject) => {
|
|
7789
|
-
const proc = (0,
|
|
7881
|
+
const proc = (0, import_child_process9.spawn)(
|
|
7790
7882
|
"gitpod",
|
|
7791
7883
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
7792
7884
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -7810,10 +7902,10 @@ function shellQuote2(s) {
|
|
|
7810
7902
|
}
|
|
7811
7903
|
|
|
7812
7904
|
// src/services/providers/gitlab-workspaces.ts
|
|
7813
|
-
var
|
|
7905
|
+
var import_child_process10 = require("child_process");
|
|
7814
7906
|
var import_util4 = require("util");
|
|
7815
|
-
var
|
|
7816
|
-
var execFileP4 = (0, import_util4.promisify)(
|
|
7907
|
+
var path15 = __toESM(require("path"));
|
|
7908
|
+
var execFileP4 = (0, import_util4.promisify)(import_child_process10.execFile);
|
|
7817
7909
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
7818
7910
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
7819
7911
|
function resetStdinForChild3() {
|
|
@@ -7855,7 +7947,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
7855
7947
|
);
|
|
7856
7948
|
resetStdinForChild3();
|
|
7857
7949
|
await new Promise((resolve2, reject) => {
|
|
7858
|
-
const proc = (0,
|
|
7950
|
+
const proc = (0, import_child_process10.spawn)(
|
|
7859
7951
|
"glab",
|
|
7860
7952
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
7861
7953
|
{ stdio: "inherit" }
|
|
@@ -8027,7 +8119,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8027
8119
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8028
8120
|
resetStdinForChild3();
|
|
8029
8121
|
return new Promise((resolve2, reject) => {
|
|
8030
|
-
const proc = (0,
|
|
8122
|
+
const proc = (0, import_child_process10.spawn)(
|
|
8031
8123
|
"ssh",
|
|
8032
8124
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
8033
8125
|
{ stdio: "inherit" }
|
|
@@ -8048,8 +8140,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8048
8140
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8049
8141
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
8050
8142
|
await new Promise((resolve2, reject) => {
|
|
8051
|
-
const tar = (0,
|
|
8052
|
-
const ssh = (0,
|
|
8143
|
+
const tar = (0, import_child_process10.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
8144
|
+
const ssh = (0, import_child_process10.spawn)(
|
|
8053
8145
|
"ssh",
|
|
8054
8146
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
|
|
8055
8147
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8072,14 +8164,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8072
8164
|
}
|
|
8073
8165
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8074
8166
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8075
|
-
const remoteDir =
|
|
8167
|
+
const remoteDir = path15.posix.dirname(remotePath);
|
|
8076
8168
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
8077
8169
|
if (options.mode != null) {
|
|
8078
8170
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
8079
8171
|
}
|
|
8080
8172
|
const cmd = parts.join(" && ");
|
|
8081
8173
|
await new Promise((resolve2, reject) => {
|
|
8082
|
-
const proc = (0,
|
|
8174
|
+
const proc = (0, import_child_process10.spawn)(
|
|
8083
8175
|
"ssh",
|
|
8084
8176
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
8085
8177
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8138,10 +8230,10 @@ function shellQuote3(s) {
|
|
|
8138
8230
|
}
|
|
8139
8231
|
|
|
8140
8232
|
// src/services/providers/railway.ts
|
|
8141
|
-
var
|
|
8233
|
+
var import_child_process11 = require("child_process");
|
|
8142
8234
|
var import_util5 = require("util");
|
|
8143
|
-
var
|
|
8144
|
-
var execFileP5 = (0, import_util5.promisify)(
|
|
8235
|
+
var path16 = __toESM(require("path"));
|
|
8236
|
+
var execFileP5 = (0, import_util5.promisify)(import_child_process11.execFile);
|
|
8145
8237
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
8146
8238
|
function resetStdinForChild4() {
|
|
8147
8239
|
if (process.stdin.isTTY) {
|
|
@@ -8182,7 +8274,7 @@ var RailwayProvider = class {
|
|
|
8182
8274
|
);
|
|
8183
8275
|
resetStdinForChild4();
|
|
8184
8276
|
await new Promise((resolve2, reject) => {
|
|
8185
|
-
const proc = (0,
|
|
8277
|
+
const proc = (0, import_child_process11.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
8186
8278
|
proc.on("exit", (code) => {
|
|
8187
8279
|
if (code === 0) resolve2();
|
|
8188
8280
|
else reject(new Error("railway login failed."));
|
|
@@ -8325,7 +8417,7 @@ var RailwayProvider = class {
|
|
|
8325
8417
|
}
|
|
8326
8418
|
resetStdinForChild4();
|
|
8327
8419
|
return new Promise((resolve2, reject) => {
|
|
8328
|
-
const proc = (0,
|
|
8420
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8329
8421
|
"railway",
|
|
8330
8422
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
8331
8423
|
{ stdio: "inherit" }
|
|
@@ -8349,8 +8441,8 @@ var RailwayProvider = class {
|
|
|
8349
8441
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8350
8442
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
8351
8443
|
await new Promise((resolve2, reject) => {
|
|
8352
|
-
const tar = (0,
|
|
8353
|
-
const sh = (0,
|
|
8444
|
+
const tar = (0, import_child_process11.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
8445
|
+
const sh = (0, import_child_process11.spawn)(
|
|
8354
8446
|
"railway",
|
|
8355
8447
|
["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
|
|
8356
8448
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8376,14 +8468,14 @@ var RailwayProvider = class {
|
|
|
8376
8468
|
if (!projectId || !serviceId) {
|
|
8377
8469
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
8378
8470
|
}
|
|
8379
|
-
const remoteDir =
|
|
8471
|
+
const remoteDir = path16.posix.dirname(remotePath);
|
|
8380
8472
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
8381
8473
|
if (options.mode != null) {
|
|
8382
8474
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
8383
8475
|
}
|
|
8384
8476
|
const cmd = parts.join(" && ");
|
|
8385
8477
|
await new Promise((resolve2, reject) => {
|
|
8386
|
-
const proc = (0,
|
|
8478
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8387
8479
|
"railway",
|
|
8388
8480
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
8389
8481
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8415,7 +8507,7 @@ var PROVIDERS = [
|
|
|
8415
8507
|
];
|
|
8416
8508
|
|
|
8417
8509
|
// src/commands/deploy.ts
|
|
8418
|
-
var execFileP6 = (0, import_util6.promisify)(
|
|
8510
|
+
var execFileP6 = (0, import_util6.promisify)(import_child_process12.execFile);
|
|
8419
8511
|
async function deploy() {
|
|
8420
8512
|
console.log();
|
|
8421
8513
|
mt(import_picocolors9.default.bgMagenta(import_picocolors9.default.white(" codeam deploy ")));
|
|
@@ -8568,7 +8660,7 @@ async function deploy() {
|
|
|
8568
8660
|
process.exit(1);
|
|
8569
8661
|
}
|
|
8570
8662
|
}
|
|
8571
|
-
const localClaudeDir =
|
|
8663
|
+
const localClaudeDir = path17.join(os9.homedir(), ".claude");
|
|
8572
8664
|
const localCredsKind = await detectLocalClaudeCredentials(localClaudeDir);
|
|
8573
8665
|
let bridged = "none";
|
|
8574
8666
|
if (localCredsKind !== "none") {
|
|
@@ -8612,7 +8704,7 @@ async function deploy() {
|
|
|
8612
8704
|
process.exit(1);
|
|
8613
8705
|
}
|
|
8614
8706
|
claudeStep.stop("\u2713 Claude CLI installed");
|
|
8615
|
-
const haveLocalClaude =
|
|
8707
|
+
const haveLocalClaude = fs10.existsSync(localClaudeDir) && fs10.statSync(localClaudeDir).isDirectory();
|
|
8616
8708
|
if (haveLocalClaude) {
|
|
8617
8709
|
const copyStep = fe();
|
|
8618
8710
|
copyStep.start("Copying local Claude config to workspace\u2026");
|
|
@@ -8666,10 +8758,10 @@ async function deploy() {
|
|
|
8666
8758
|
}
|
|
8667
8759
|
}
|
|
8668
8760
|
if (bridged !== "none") {
|
|
8669
|
-
const localClaudeJson =
|
|
8670
|
-
if (
|
|
8761
|
+
const localClaudeJson = path17.join(os9.homedir(), ".claude.json");
|
|
8762
|
+
if (fs10.existsSync(localClaudeJson)) {
|
|
8671
8763
|
try {
|
|
8672
|
-
const contents =
|
|
8764
|
+
const contents = fs10.readFileSync(localClaudeJson);
|
|
8673
8765
|
await provider.uploadFile(
|
|
8674
8766
|
workspace.id,
|
|
8675
8767
|
"/home/codespace/.claude.json",
|
|
@@ -8859,7 +8951,7 @@ async function runRemoteClaudeLogin(provider, workspaceId) {
|
|
|
8859
8951
|
}
|
|
8860
8952
|
}
|
|
8861
8953
|
async function detectLocalClaudeCredentials(localClaudeDir) {
|
|
8862
|
-
if (
|
|
8954
|
+
if (fs10.existsSync(path17.join(localClaudeDir, ".credentials.json"))) {
|
|
8863
8955
|
return "flat-file";
|
|
8864
8956
|
}
|
|
8865
8957
|
if (process.platform === "darwin") {
|
|
@@ -8892,8 +8984,8 @@ async function verifyClaudeAuth(provider, workspaceId) {
|
|
|
8892
8984
|
}
|
|
8893
8985
|
}
|
|
8894
8986
|
async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
|
|
8895
|
-
const fileBased =
|
|
8896
|
-
if (
|
|
8987
|
+
const fileBased = path17.join(localClaudeDir, ".credentials.json");
|
|
8988
|
+
if (fs10.existsSync(fileBased)) return "flat-file";
|
|
8897
8989
|
if (process.platform === "darwin") {
|
|
8898
8990
|
try {
|
|
8899
8991
|
const { stdout } = await execFileP6(
|
|
@@ -9101,7 +9193,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
9101
9193
|
// src/commands/version.ts
|
|
9102
9194
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
9103
9195
|
function version() {
|
|
9104
|
-
const v = true ? "2.
|
|
9196
|
+
const v = true ? "2.5.0" : "unknown";
|
|
9105
9197
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
9106
9198
|
}
|
|
9107
9199
|
|
|
@@ -9138,22 +9230,22 @@ function help() {
|
|
|
9138
9230
|
}
|
|
9139
9231
|
|
|
9140
9232
|
// src/lib/updateNotifier.ts
|
|
9141
|
-
var
|
|
9142
|
-
var
|
|
9143
|
-
var
|
|
9144
|
-
var
|
|
9233
|
+
var fs11 = __toESM(require("fs"));
|
|
9234
|
+
var os10 = __toESM(require("os"));
|
|
9235
|
+
var path18 = __toESM(require("path"));
|
|
9236
|
+
var https5 = __toESM(require("https"));
|
|
9145
9237
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
9146
9238
|
var PKG_NAME = "codeam-cli";
|
|
9147
9239
|
var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
9148
9240
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9149
9241
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
9150
9242
|
function cachePath() {
|
|
9151
|
-
const dir =
|
|
9152
|
-
return
|
|
9243
|
+
const dir = path18.join(os10.homedir(), ".codeam");
|
|
9244
|
+
return path18.join(dir, "update-check.json");
|
|
9153
9245
|
}
|
|
9154
9246
|
function readCache() {
|
|
9155
9247
|
try {
|
|
9156
|
-
const raw =
|
|
9248
|
+
const raw = fs11.readFileSync(cachePath(), "utf8");
|
|
9157
9249
|
const parsed = JSON.parse(raw);
|
|
9158
9250
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
9159
9251
|
return parsed;
|
|
@@ -9164,8 +9256,8 @@ function readCache() {
|
|
|
9164
9256
|
function writeCache(cache) {
|
|
9165
9257
|
try {
|
|
9166
9258
|
const file = cachePath();
|
|
9167
|
-
|
|
9168
|
-
|
|
9259
|
+
fs11.mkdirSync(path18.dirname(file), { recursive: true });
|
|
9260
|
+
fs11.writeFileSync(file, JSON.stringify(cache));
|
|
9169
9261
|
} catch {
|
|
9170
9262
|
}
|
|
9171
9263
|
}
|
|
@@ -9184,7 +9276,7 @@ function compareSemver(a, b) {
|
|
|
9184
9276
|
}
|
|
9185
9277
|
function fetchLatest() {
|
|
9186
9278
|
return new Promise((resolve2) => {
|
|
9187
|
-
const req =
|
|
9279
|
+
const req = https5.get(
|
|
9188
9280
|
REGISTRY_URL,
|
|
9189
9281
|
{ headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
|
|
9190
9282
|
(res) => {
|
|
@@ -9236,7 +9328,7 @@ function checkForUpdates() {
|
|
|
9236
9328
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
9237
9329
|
if (process.env.CI) return;
|
|
9238
9330
|
if (!process.stdout.isTTY) return;
|
|
9239
|
-
const current = true ? "2.
|
|
9331
|
+
const current = true ? "2.5.0" : null;
|
|
9240
9332
|
if (!current) return;
|
|
9241
9333
|
const cache = readCache();
|
|
9242
9334
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|