codeam-cli 2.4.33 → 2.4.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +10 -0
- package/dist/index.js +472 -155
- package/package.json +2 -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 fs11 = require("fs");
|
|
523
|
+
var os10 = require("os");
|
|
524
|
+
var path18 = 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 = path18.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 = fs11.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(path18.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(os10.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 fs11 = require("fs");
|
|
1016
|
+
var path18 = 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 = path18.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
|
+
fs11.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() {
|
|
@@ -1390,9 +1390,9 @@ var require_src = __commonJS({
|
|
|
1390
1390
|
});
|
|
1391
1391
|
|
|
1392
1392
|
// src/commands/start.ts
|
|
1393
|
-
var
|
|
1394
|
-
var
|
|
1395
|
-
var
|
|
1393
|
+
var fs8 = __toESM(require("fs"));
|
|
1394
|
+
var os7 = __toESM(require("os"));
|
|
1395
|
+
var path11 = __toESM(require("path"));
|
|
1396
1396
|
var import_crypto = require("crypto");
|
|
1397
1397
|
var import_child_process5 = require("child_process");
|
|
1398
1398
|
var import_picocolors2 = __toESM(require("picocolors"));
|
|
@@ -1482,8 +1482,9 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
1482
1482
|
// package.json
|
|
1483
1483
|
var package_default = {
|
|
1484
1484
|
name: "codeam-cli",
|
|
1485
|
-
version: "2.4.
|
|
1485
|
+
version: "2.4.34",
|
|
1486
1486
|
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
|
+
type: "commonjs",
|
|
1487
1488
|
main: "dist/index.js",
|
|
1488
1489
|
bin: {
|
|
1489
1490
|
codeam: "dist/index.js"
|
|
@@ -1611,6 +1612,60 @@ function computePollDelay({ baseMs, failures }) {
|
|
|
1611
1612
|
return Math.round(jitter);
|
|
1612
1613
|
}
|
|
1613
1614
|
|
|
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
|
+
|
|
1614
1669
|
// src/services/websocket.service.ts
|
|
1615
1670
|
var API_BASE = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
1616
1671
|
var WS_URL = API_BASE.replace("https://", "wss://").replace("http://", "ws://") + "/api/ws";
|
|
@@ -1640,6 +1695,7 @@ var WebSocketService = class {
|
|
|
1640
1695
|
try {
|
|
1641
1696
|
this.client = new import_ws.default(WS_URL);
|
|
1642
1697
|
this.client.on("open", () => {
|
|
1698
|
+
log.trace("ws", `connected to ${WS_URL}`);
|
|
1643
1699
|
this._connected = true;
|
|
1644
1700
|
this.reconnectAttempts = 0;
|
|
1645
1701
|
this.client.send(JSON.stringify({
|
|
@@ -1653,12 +1709,18 @@ var WebSocketService = class {
|
|
|
1653
1709
|
this.client.on("message", (raw) => {
|
|
1654
1710
|
try {
|
|
1655
1711
|
const msg = JSON.parse(raw.toString());
|
|
1656
|
-
if (msg.type === "pong" || msg.type === "auth_success" || msg.type === "auth_error")
|
|
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}`);
|
|
1657
1717
|
this.handlers.forEach((h) => h.onMessage(msg.type, msg.payload ?? {}));
|
|
1658
|
-
} catch {
|
|
1718
|
+
} catch (err) {
|
|
1719
|
+
log.trace("ws", "malformed message", err);
|
|
1659
1720
|
}
|
|
1660
1721
|
});
|
|
1661
|
-
this.client.on("close", () => {
|
|
1722
|
+
this.client.on("close", (code, reason) => {
|
|
1723
|
+
log.trace("ws", `closed code=${code} reason=${reason?.toString() || "(empty)"}`);
|
|
1662
1724
|
this._connected = false;
|
|
1663
1725
|
this.stopHeartbeat();
|
|
1664
1726
|
this.handlers.forEach((h) => h.onDisconnected());
|
|
@@ -1668,9 +1730,11 @@ var WebSocketService = class {
|
|
|
1668
1730
|
this.reconnectTimer = setTimeout(() => this.connect(), delay);
|
|
1669
1731
|
}
|
|
1670
1732
|
});
|
|
1671
|
-
this.client.on("error", () => {
|
|
1733
|
+
this.client.on("error", (err) => {
|
|
1734
|
+
log.trace("ws", "error", err);
|
|
1672
1735
|
});
|
|
1673
|
-
} catch {
|
|
1736
|
+
} catch (err) {
|
|
1737
|
+
log.trace("ws", "sync connect threw", err);
|
|
1674
1738
|
}
|
|
1675
1739
|
}
|
|
1676
1740
|
send(type, payload) {
|
|
@@ -1706,7 +1770,7 @@ var WebSocketService = class {
|
|
|
1706
1770
|
// src/services/pairing.service.ts
|
|
1707
1771
|
var https = __toESM(require("https"));
|
|
1708
1772
|
var http = __toESM(require("http"));
|
|
1709
|
-
var
|
|
1773
|
+
var os3 = __toESM(require("os"));
|
|
1710
1774
|
var API_BASE2 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
1711
1775
|
async function requestCode(pluginId) {
|
|
1712
1776
|
try {
|
|
@@ -1716,7 +1780,7 @@ async function requestCode(pluginId) {
|
|
|
1716
1780
|
pluginId,
|
|
1717
1781
|
ideName: "Terminal (codeam-cli)",
|
|
1718
1782
|
ideVersion: package_default.version,
|
|
1719
|
-
hostname:
|
|
1783
|
+
hostname: os3.hostname(),
|
|
1720
1784
|
runtime,
|
|
1721
1785
|
...codespaceName ? { codespaceName } : {}
|
|
1722
1786
|
});
|
|
@@ -1880,7 +1944,10 @@ var CommandRelayService = class {
|
|
|
1880
1944
|
_running = false;
|
|
1881
1945
|
pollTimer = null;
|
|
1882
1946
|
heartbeatTimer = null;
|
|
1947
|
+
agentsTimer = null;
|
|
1883
1948
|
consecutiveFailures = 0;
|
|
1949
|
+
/** True once `/api/plugin/agents` has accepted at least one report. */
|
|
1950
|
+
agentsRegistered = false;
|
|
1884
1951
|
start() {
|
|
1885
1952
|
if (this.pollTimer) {
|
|
1886
1953
|
clearTimeout(this.pollTimer);
|
|
@@ -1890,11 +1957,19 @@ var CommandRelayService = class {
|
|
|
1890
1957
|
clearInterval(this.heartbeatTimer);
|
|
1891
1958
|
this.heartbeatTimer = null;
|
|
1892
1959
|
}
|
|
1960
|
+
if (this.agentsTimer) {
|
|
1961
|
+
clearInterval(this.agentsTimer);
|
|
1962
|
+
this.agentsTimer = null;
|
|
1963
|
+
}
|
|
1893
1964
|
this._running = true;
|
|
1965
|
+
this.agentsRegistered = false;
|
|
1894
1966
|
this.sendHeartbeat(true);
|
|
1895
1967
|
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(true), 2e4);
|
|
1896
1968
|
void this.pollLoop();
|
|
1897
1969
|
this.reportAgents();
|
|
1970
|
+
this.agentsTimer = setInterval(() => {
|
|
1971
|
+
if (this._running && !this.agentsRegistered) this.reportAgents();
|
|
1972
|
+
}, 5e3);
|
|
1898
1973
|
}
|
|
1899
1974
|
stop() {
|
|
1900
1975
|
if (!this._running) return;
|
|
@@ -1907,6 +1982,10 @@ var CommandRelayService = class {
|
|
|
1907
1982
|
clearInterval(this.heartbeatTimer);
|
|
1908
1983
|
this.heartbeatTimer = null;
|
|
1909
1984
|
}
|
|
1985
|
+
if (this.agentsTimer) {
|
|
1986
|
+
clearInterval(this.agentsTimer);
|
|
1987
|
+
this.agentsTimer = null;
|
|
1988
|
+
}
|
|
1910
1989
|
this.sendHeartbeat(false).catch(() => {
|
|
1911
1990
|
});
|
|
1912
1991
|
}
|
|
@@ -1932,32 +2011,43 @@ var CommandRelayService = class {
|
|
|
1932
2011
|
const commands = data?.data;
|
|
1933
2012
|
this.consecutiveFailures = 0;
|
|
1934
2013
|
if (!Array.isArray(commands)) return;
|
|
2014
|
+
if (commands.length > 0) {
|
|
2015
|
+
log.trace("relay", `poll received ${commands.length} command(s)`);
|
|
2016
|
+
}
|
|
1935
2017
|
for (const obj of commands) {
|
|
1936
2018
|
try {
|
|
2019
|
+
log.trace("relay", `dispatch type=${obj.type} id=${obj.id}`);
|
|
1937
2020
|
await this.onCommand({
|
|
1938
2021
|
id: obj.id,
|
|
1939
2022
|
sessionId: obj.sessionId,
|
|
1940
2023
|
type: obj.type,
|
|
1941
2024
|
payload: obj.payload ?? {}
|
|
1942
2025
|
});
|
|
1943
|
-
} catch {
|
|
2026
|
+
} catch (err) {
|
|
2027
|
+
log.trace("relay", `command handler threw`, err);
|
|
1944
2028
|
}
|
|
1945
2029
|
}
|
|
1946
|
-
} catch {
|
|
2030
|
+
} catch (err) {
|
|
1947
2031
|
this.consecutiveFailures += 1;
|
|
2032
|
+
log.trace(
|
|
2033
|
+
"relay",
|
|
2034
|
+
`poll failed (failures=${this.consecutiveFailures})`,
|
|
2035
|
+
err
|
|
2036
|
+
);
|
|
1948
2037
|
}
|
|
1949
2038
|
}
|
|
1950
2039
|
async sendHeartbeat(online) {
|
|
1951
2040
|
await _postJson(`${API_BASE3}/api/plugin/heartbeat`, {
|
|
1952
2041
|
pluginId: this.pluginId,
|
|
1953
2042
|
online
|
|
1954
|
-
}).catch(() => {
|
|
1955
|
-
});
|
|
2043
|
+
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
1956
2044
|
}
|
|
1957
2045
|
reportAgents() {
|
|
1958
2046
|
_postJson(`${API_BASE3}/api/plugin/agents`, {
|
|
1959
2047
|
pluginId: this.pluginId,
|
|
1960
2048
|
agents: [{ id: "claude-code", name: "Claude Code", icon: "\u{1F916}", installed: true }]
|
|
2049
|
+
}).then(() => {
|
|
2050
|
+
this.agentsRegistered = true;
|
|
1961
2051
|
}).catch(() => {
|
|
1962
2052
|
});
|
|
1963
2053
|
}
|
|
@@ -1965,24 +2055,24 @@ var CommandRelayService = class {
|
|
|
1965
2055
|
|
|
1966
2056
|
// src/services/pty/unix.strategy.ts
|
|
1967
2057
|
var import_child_process = require("child_process");
|
|
1968
|
-
var
|
|
1969
|
-
var
|
|
1970
|
-
var
|
|
2058
|
+
var fs4 = __toESM(require("fs"));
|
|
2059
|
+
var os4 = __toESM(require("os"));
|
|
2060
|
+
var path4 = __toESM(require("path"));
|
|
1971
2061
|
|
|
1972
2062
|
// src/services/pty/types.ts
|
|
1973
|
-
var
|
|
1974
|
-
var
|
|
2063
|
+
var fs3 = __toESM(require("fs"));
|
|
2064
|
+
var path3 = __toESM(require("path"));
|
|
1975
2065
|
function findInPath(name) {
|
|
1976
2066
|
const isWin = process.platform === "win32";
|
|
1977
|
-
const dirs = (process.env.PATH ?? "").split(
|
|
1978
|
-
const hasExt =
|
|
2067
|
+
const dirs = (process.env.PATH ?? "").split(path3.delimiter).filter(Boolean);
|
|
2068
|
+
const hasExt = path3.extname(name).length > 0;
|
|
1979
2069
|
const candidates = isWin && !hasExt ? [`${name}.exe`, `${name}.cmd`, `${name}.bat`, `${name}.ps1`, name] : [name];
|
|
1980
|
-
const accessFlag = isWin ?
|
|
2070
|
+
const accessFlag = isWin ? fs3.constants.F_OK : fs3.constants.X_OK;
|
|
1981
2071
|
for (const dir of dirs) {
|
|
1982
2072
|
for (const candidate of candidates) {
|
|
1983
|
-
const full =
|
|
2073
|
+
const full = path3.join(dir, candidate);
|
|
1984
2074
|
try {
|
|
1985
|
-
|
|
2075
|
+
fs3.accessSync(full, accessFlag);
|
|
1986
2076
|
return full;
|
|
1987
2077
|
} catch {
|
|
1988
2078
|
}
|
|
@@ -2066,8 +2156,8 @@ var UnixPtyStrategy = class {
|
|
|
2066
2156
|
}
|
|
2067
2157
|
const cols = process.stdout.columns || 220;
|
|
2068
2158
|
const rows = process.stdout.rows || 50;
|
|
2069
|
-
this.helperPath =
|
|
2070
|
-
|
|
2159
|
+
this.helperPath = path4.join(os4.tmpdir(), "codeam-pty-helper.py");
|
|
2160
|
+
fs4.writeFileSync(this.helperPath, PYTHON_PTY_HELPER, { mode: 420 });
|
|
2071
2161
|
this.proc = (0, import_child_process.spawn)(python, [this.helperPath, cmd, ...args2], {
|
|
2072
2162
|
stdio: ["pipe", "pipe", "inherit"],
|
|
2073
2163
|
cwd,
|
|
@@ -2196,7 +2286,7 @@ var UnixPtyStrategy = class {
|
|
|
2196
2286
|
removeTempFile() {
|
|
2197
2287
|
if (this.helperPath) {
|
|
2198
2288
|
try {
|
|
2199
|
-
|
|
2289
|
+
fs4.unlinkSync(this.helperPath);
|
|
2200
2290
|
} catch {
|
|
2201
2291
|
}
|
|
2202
2292
|
this.helperPath = null;
|
|
@@ -2268,9 +2358,9 @@ var WindowsPtyStrategy = class {
|
|
|
2268
2358
|
};
|
|
2269
2359
|
|
|
2270
2360
|
// src/services/pty/windows-conpty.strategy.ts
|
|
2271
|
-
var
|
|
2361
|
+
var path5 = __toESM(require("path"));
|
|
2272
2362
|
function loadNodePty() {
|
|
2273
|
-
const vendoredPath =
|
|
2363
|
+
const vendoredPath = path5.join(__dirname, "vendor", "node-pty");
|
|
2274
2364
|
try {
|
|
2275
2365
|
return require(vendoredPath);
|
|
2276
2366
|
} catch (vendorErr) {
|
|
@@ -2320,11 +2410,17 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2320
2410
|
useConpty: true,
|
|
2321
2411
|
conptyInheritCursor: false
|
|
2322
2412
|
});
|
|
2413
|
+
let traceCount = 0;
|
|
2323
2414
|
this.dataSub = this.pty.onData((data) => {
|
|
2324
2415
|
process.stdout.write(data);
|
|
2325
2416
|
this.opts.onData(data);
|
|
2417
|
+
traceCount++;
|
|
2418
|
+
if (traceCount <= 5 || traceCount % 50 === 0) {
|
|
2419
|
+
log.trace("conpty", `onData #${traceCount} ${data.length}B`);
|
|
2420
|
+
}
|
|
2326
2421
|
});
|
|
2327
2422
|
this.exitSub = this.pty.onExit(({ exitCode }) => {
|
|
2423
|
+
log.trace("conpty", `claude exited code=${exitCode ?? 0}`);
|
|
2328
2424
|
this.dispose();
|
|
2329
2425
|
this.opts.onExit(exitCode ?? 0);
|
|
2330
2426
|
});
|
|
@@ -2384,8 +2480,8 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2384
2480
|
|
|
2385
2481
|
// src/services/claude-installer.ts
|
|
2386
2482
|
var import_child_process3 = require("child_process");
|
|
2387
|
-
var
|
|
2388
|
-
var
|
|
2483
|
+
var path6 = __toESM(require("path"));
|
|
2484
|
+
var os5 = __toESM(require("os"));
|
|
2389
2485
|
|
|
2390
2486
|
// ../../node_modules/@clack/prompts/dist/index.mjs
|
|
2391
2487
|
var dist_exports = {};
|
|
@@ -4299,17 +4395,17 @@ ${c2}
|
|
|
4299
4395
|
|
|
4300
4396
|
// src/services/claude-installer.ts
|
|
4301
4397
|
function probeInstallDirs() {
|
|
4302
|
-
const home =
|
|
4398
|
+
const home = os5.homedir();
|
|
4303
4399
|
if (process.platform === "win32") {
|
|
4304
4400
|
return [
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4401
|
+
path6.join(home, ".claude", "local"),
|
|
4402
|
+
path6.join(home, "AppData", "Local", "AnthropicClaude"),
|
|
4403
|
+
path6.join(home, "AppData", "Local", "Programs", "AnthropicClaude")
|
|
4308
4404
|
];
|
|
4309
4405
|
}
|
|
4310
4406
|
return [
|
|
4311
|
-
|
|
4312
|
-
|
|
4407
|
+
path6.join(home, ".local", "bin"),
|
|
4408
|
+
path6.join(home, ".claude", "local"),
|
|
4313
4409
|
"/usr/local/bin"
|
|
4314
4410
|
];
|
|
4315
4411
|
}
|
|
@@ -4318,7 +4414,7 @@ function isAvailable() {
|
|
|
4318
4414
|
}
|
|
4319
4415
|
function augmentPath() {
|
|
4320
4416
|
const dirs = probeInstallDirs();
|
|
4321
|
-
const sep2 =
|
|
4417
|
+
const sep2 = path6.delimiter;
|
|
4322
4418
|
const current = process.env.PATH ?? "";
|
|
4323
4419
|
const existing = new Set(current.split(sep2).filter(Boolean));
|
|
4324
4420
|
const additions = dirs.filter((d3) => !existing.has(d3));
|
|
@@ -4376,12 +4472,12 @@ async function ensureClaudeInstalled() {
|
|
|
4376
4472
|
}
|
|
4377
4473
|
|
|
4378
4474
|
// src/services/claude-resolver.ts
|
|
4379
|
-
var
|
|
4475
|
+
var path7 = __toESM(require("path"));
|
|
4380
4476
|
function buildClaudeLaunch(extraArgs = []) {
|
|
4381
4477
|
const found = findInPath("claude") ?? findInPath("claude-code");
|
|
4382
4478
|
if (!found) return null;
|
|
4383
4479
|
if (process.platform === "win32") {
|
|
4384
|
-
const ext =
|
|
4480
|
+
const ext = path7.extname(found).toLowerCase();
|
|
4385
4481
|
if (ext === ".cmd" || ext === ".bat") {
|
|
4386
4482
|
return { cmd: "cmd.exe", args: ["/c", found, ...extraArgs] };
|
|
4387
4483
|
}
|
|
@@ -4429,11 +4525,13 @@ var ClaudeService = class {
|
|
|
4429
4525
|
}
|
|
4430
4526
|
}
|
|
4431
4527
|
if (process.platform === "win32") {
|
|
4528
|
+
log.trace("claude", `spawn (win32) cmd=${launch.cmd} args=${launch.args.join(" ")}`);
|
|
4432
4529
|
const conpty = WindowsConPtyStrategy.tryCreate(this.strategyOpts);
|
|
4433
4530
|
if (conpty) {
|
|
4434
4531
|
try {
|
|
4435
4532
|
conpty.spawn(launch.cmd, this.opts.cwd, launch.args);
|
|
4436
4533
|
this.strategy = conpty;
|
|
4534
|
+
log.trace("claude", "ConPTY spawn ok");
|
|
4437
4535
|
return;
|
|
4438
4536
|
} catch (err) {
|
|
4439
4537
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -4473,8 +4571,12 @@ var ClaudeService = class {
|
|
|
4473
4571
|
* a fresh event-loop tick, after React has flushed the text into input state.
|
|
4474
4572
|
*/
|
|
4475
4573
|
sendCommand(text) {
|
|
4476
|
-
if (!this.strategy)
|
|
4574
|
+
if (!this.strategy) {
|
|
4575
|
+
log.trace("claude", "sendCommand dropped (no strategy)");
|
|
4576
|
+
return;
|
|
4577
|
+
}
|
|
4477
4578
|
const s = this.strategy;
|
|
4579
|
+
log.trace("claude", `sendCommand text=${text.length}B`);
|
|
4478
4580
|
s.write(text);
|
|
4479
4581
|
setTimeout(() => s.write("\r"), 50);
|
|
4480
4582
|
}
|
|
@@ -4548,12 +4650,12 @@ var https2 = __toESM(require("https"));
|
|
|
4548
4650
|
var http2 = __toESM(require("http"));
|
|
4549
4651
|
|
|
4550
4652
|
// ../../packages/shared/src/protocol/parseChrome.ts
|
|
4551
|
-
var SPINNER_RE = /^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s
|
|
4653
|
+
var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
|
|
4552
4654
|
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;
|
|
4553
4655
|
var TREE_LINE_RE = /^└\s/;
|
|
4554
|
-
var STATUS_LINE_RE =
|
|
4656
|
+
var STATUS_LINE_RE = /^(?:\+|[🔴🟠🟡🟢🔵🟣🟤⚫⚪🌀💭✨])\s/u;
|
|
4555
4657
|
function isChromeLine(line) {
|
|
4556
|
-
const t2 = line.trim();
|
|
4658
|
+
const t2 = line.replace(/️/g, "").trim();
|
|
4557
4659
|
if (!t2) return false;
|
|
4558
4660
|
if (/^[─━—═─\-]{3,}$/.test(t2)) return true;
|
|
4559
4661
|
if (SPINNER_RE.test(t2)) return true;
|
|
@@ -4578,7 +4680,7 @@ function isChromeLine(line) {
|
|
|
4578
4680
|
return false;
|
|
4579
4681
|
}
|
|
4580
4682
|
function parseChromeLine(line) {
|
|
4581
|
-
const t2 = line.trim();
|
|
4683
|
+
const t2 = line.replace(/️/g, "").trim();
|
|
4582
4684
|
if (!t2) return null;
|
|
4583
4685
|
if (/^[─━—═─\-]{3,}$/.test(t2)) return null;
|
|
4584
4686
|
if (/^[❯>]\s*$/.test(t2)) return null;
|
|
@@ -4982,6 +5084,7 @@ var OutputService = class _OutputService {
|
|
|
4982
5084
|
this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
|
|
4983
5085
|
}
|
|
4984
5086
|
newTurn() {
|
|
5087
|
+
log.trace("outputSvc", "newTurn() \u2014 activating output stream");
|
|
4985
5088
|
this.stopPoll();
|
|
4986
5089
|
this.rawBuffer = "";
|
|
4987
5090
|
this.lastSentContent = "";
|
|
@@ -5019,9 +5122,11 @@ var OutputService = class _OutputService {
|
|
|
5019
5122
|
const printable2 = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
|
|
5020
5123
|
if (printable2.trim()) {
|
|
5021
5124
|
this.terminalTurnPending = true;
|
|
5125
|
+
log.trace("outputSvc", `terminal-turn detected (idle, ${raw.length}B)`);
|
|
5022
5126
|
this.onTerminalTurnDetected?.();
|
|
5023
5127
|
}
|
|
5024
5128
|
}
|
|
5129
|
+
log.trace("outputSvc", `push dropped (inactive, ${raw.length}B)`);
|
|
5025
5130
|
return;
|
|
5026
5131
|
}
|
|
5027
5132
|
this.rawBuffer += raw;
|
|
@@ -5031,6 +5136,10 @@ var OutputService = class _OutputService {
|
|
|
5031
5136
|
this.tryExtractSessionId(printable);
|
|
5032
5137
|
this.tryDetectRateLimit(printable);
|
|
5033
5138
|
}
|
|
5139
|
+
log.trace(
|
|
5140
|
+
"outputSvc",
|
|
5141
|
+
`push +${raw.length}B (buf=${this.rawBuffer.length}B printable=${printable.trim().length})`
|
|
5142
|
+
);
|
|
5034
5143
|
}
|
|
5035
5144
|
/** Extract Claude conversation ID from output text (e.g., from /cost command or session resume) */
|
|
5036
5145
|
tryExtractSessionId(text) {
|
|
@@ -5073,6 +5182,10 @@ var OutputService = class _OutputService {
|
|
|
5073
5182
|
const selector = detectSelector(lines) ?? detectListSelector(lines);
|
|
5074
5183
|
if (selector) {
|
|
5075
5184
|
const idleMs2 = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
|
|
5185
|
+
log.trace(
|
|
5186
|
+
"outputSvc",
|
|
5187
|
+
`tick selector found (idleMs=${idleMs2}, options=${selector.options.length})`
|
|
5188
|
+
);
|
|
5076
5189
|
if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
|
|
5077
5190
|
this.stopPoll();
|
|
5078
5191
|
this.active = false;
|
|
@@ -5083,10 +5196,18 @@ var OutputService = class _OutputService {
|
|
|
5083
5196
|
}
|
|
5084
5197
|
const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
5085
5198
|
if (!content) {
|
|
5199
|
+
log.trace(
|
|
5200
|
+
"outputSvc",
|
|
5201
|
+
`tick empty content (raw=${this.rawBuffer.length}B lines=${lines.length} elapsed=${elapsed}ms)`
|
|
5202
|
+
);
|
|
5086
5203
|
if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) this.finalize();
|
|
5087
5204
|
return;
|
|
5088
5205
|
}
|
|
5089
5206
|
const idleMs = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
|
|
5207
|
+
log.trace(
|
|
5208
|
+
"outputSvc",
|
|
5209
|
+
`tick content (raw=${this.rawBuffer.length}B lines=${lines.length} content=${content.length} idleMs=${idleMs})`
|
|
5210
|
+
);
|
|
5090
5211
|
if (idleMs >= _OutputService.IDLE_MS) {
|
|
5091
5212
|
this.finalize();
|
|
5092
5213
|
return;
|
|
@@ -5153,15 +5274,33 @@ var OutputService = class _OutputService {
|
|
|
5153
5274
|
if (this.pluginAuthToken) {
|
|
5154
5275
|
headers["X-Plugin-Auth-Token"] = this.pluginAuthToken;
|
|
5155
5276
|
}
|
|
5277
|
+
log.trace(
|
|
5278
|
+
"outputSvc",
|
|
5279
|
+
`postChunk type=${body.type ?? "(clear)"} done=${body.done === true} bytes=${payload.length}`
|
|
5280
|
+
);
|
|
5156
5281
|
return new Promise((resolve2) => {
|
|
5157
5282
|
const attempt = (attemptsLeft) => {
|
|
5158
5283
|
_transport2.sendOutputChunk(`${API_BASE4}/api/commands/output`, headers, payload).then(({ statusCode, body: resBody }) => {
|
|
5284
|
+
log.trace("outputSvc", `postChunk status=${statusCode}`);
|
|
5285
|
+
if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
|
|
5286
|
+
if (this.active) {
|
|
5287
|
+
process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
|
|
5288
|
+
this.dispose();
|
|
5289
|
+
}
|
|
5290
|
+
resolve2();
|
|
5291
|
+
return;
|
|
5292
|
+
}
|
|
5159
5293
|
if (statusCode >= 400) {
|
|
5160
5294
|
process.stderr.write(`[codeam] output API error ${statusCode}: ${resBody}
|
|
5161
5295
|
`);
|
|
5162
5296
|
}
|
|
5163
5297
|
resolve2();
|
|
5164
|
-
}).catch(() => {
|
|
5298
|
+
}).catch((err) => {
|
|
5299
|
+
log.trace(
|
|
5300
|
+
"outputSvc",
|
|
5301
|
+
`postChunk error (retries left=${attemptsLeft})`,
|
|
5302
|
+
err
|
|
5303
|
+
);
|
|
5165
5304
|
if (attemptsLeft > 0) {
|
|
5166
5305
|
const delay = 200 * (maxRetries - attemptsLeft + 1);
|
|
5167
5306
|
setTimeout(() => attempt(attemptsLeft - 1), delay);
|
|
@@ -5220,33 +5359,12 @@ function _sendOutputChunk(url, headers, payload) {
|
|
|
5220
5359
|
}
|
|
5221
5360
|
|
|
5222
5361
|
// src/services/history.service.ts
|
|
5223
|
-
var
|
|
5224
|
-
var
|
|
5225
|
-
var
|
|
5362
|
+
var fs5 = __toESM(require("fs"));
|
|
5363
|
+
var path8 = __toESM(require("path"));
|
|
5364
|
+
var os6 = __toESM(require("os"));
|
|
5226
5365
|
var https3 = __toESM(require("https"));
|
|
5227
5366
|
var http3 = __toESM(require("http"));
|
|
5228
5367
|
var import_zod = require("zod");
|
|
5229
|
-
|
|
5230
|
-
// src/services/logger.ts
|
|
5231
|
-
var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
5232
|
-
function currentLevel() {
|
|
5233
|
-
const raw = (process.env.CODEAM_LOG ?? "error").toLowerCase();
|
|
5234
|
-
return LEVELS[raw] ?? LEVELS.error;
|
|
5235
|
-
}
|
|
5236
|
-
function emit(level, tag, msg, err) {
|
|
5237
|
-
if (LEVELS[level] > currentLevel()) return;
|
|
5238
|
-
const detail = err instanceof Error ? `: ${err.message}` : err !== void 0 ? `: ${String(err)}` : "";
|
|
5239
|
-
process.stderr.write(`[codeam:${level}] ${tag} \u2014 ${msg}${detail}
|
|
5240
|
-
`);
|
|
5241
|
-
}
|
|
5242
|
-
var log = {
|
|
5243
|
-
error: (tag, msg, err) => emit("error", tag, msg, err),
|
|
5244
|
-
warn: (tag, msg, err) => emit("warn", tag, msg, err),
|
|
5245
|
-
info: (tag, msg, err) => emit("info", tag, msg, err),
|
|
5246
|
-
debug: (tag, msg, err) => emit("debug", tag, msg, err)
|
|
5247
|
-
};
|
|
5248
|
-
|
|
5249
|
-
// src/services/history.service.ts
|
|
5250
5368
|
var historyRecordSchema = import_zod.z.object({
|
|
5251
5369
|
type: import_zod.z.string().optional(),
|
|
5252
5370
|
timestamp: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
|
|
@@ -5259,7 +5377,25 @@ var historyRecordSchema = import_zod.z.object({
|
|
|
5259
5377
|
}).passthrough();
|
|
5260
5378
|
var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
5261
5379
|
function encodeCwd(cwd) {
|
|
5262
|
-
return cwd.replace(
|
|
5380
|
+
return cwd.replace(/[\\/:]/g, "-");
|
|
5381
|
+
}
|
|
5382
|
+
function findProjectDir(cwd) {
|
|
5383
|
+
const projectsRoot = path8.join(os6.homedir(), ".claude", "projects");
|
|
5384
|
+
const primary = path8.join(projectsRoot, encodeCwd(cwd));
|
|
5385
|
+
if (fs5.existsSync(primary)) return primary;
|
|
5386
|
+
try {
|
|
5387
|
+
const entries = fs5.readdirSync(projectsRoot, { withFileTypes: true });
|
|
5388
|
+
const wanted = encodeCwd(cwd);
|
|
5389
|
+
for (const e of entries) {
|
|
5390
|
+
if (!e.isDirectory()) continue;
|
|
5391
|
+
const candidate = e.name.replace(/-+/g, "-");
|
|
5392
|
+
if (candidate === wanted.replace(/-+/g, "-")) {
|
|
5393
|
+
return path8.join(projectsRoot, e.name);
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5396
|
+
} catch {
|
|
5397
|
+
}
|
|
5398
|
+
return null;
|
|
5263
5399
|
}
|
|
5264
5400
|
function extractText(content) {
|
|
5265
5401
|
if (typeof content === "string") return content;
|
|
@@ -5273,7 +5409,7 @@ function parseJsonl(filePath) {
|
|
|
5273
5409
|
const messages = [];
|
|
5274
5410
|
let raw;
|
|
5275
5411
|
try {
|
|
5276
|
-
raw =
|
|
5412
|
+
raw = fs5.readFileSync(filePath, "utf8");
|
|
5277
5413
|
} catch (err) {
|
|
5278
5414
|
if (err.code !== "ENOENT") {
|
|
5279
5415
|
log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
|
|
@@ -5377,7 +5513,7 @@ var HistoryService = class {
|
|
|
5377
5513
|
return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
|
|
5378
5514
|
}
|
|
5379
5515
|
get projectDir() {
|
|
5380
|
-
return
|
|
5516
|
+
return findProjectDir(this.cwd) ?? path8.join(os6.homedir(), ".claude", "projects", encodeCwd(this.cwd));
|
|
5381
5517
|
}
|
|
5382
5518
|
/** Set the current Claude conversation ID (extracted from /cost command or session start) */
|
|
5383
5519
|
setCurrentConversationId(id) {
|
|
@@ -5389,7 +5525,7 @@ var HistoryService = class {
|
|
|
5389
5525
|
/** Return the current message count in the active conversation. */
|
|
5390
5526
|
getCurrentMessageCount() {
|
|
5391
5527
|
if (!this.currentConversationId) return 0;
|
|
5392
|
-
const filePath =
|
|
5528
|
+
const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
5393
5529
|
return parseJsonl(filePath).length;
|
|
5394
5530
|
}
|
|
5395
5531
|
/**
|
|
@@ -5400,7 +5536,7 @@ var HistoryService = class {
|
|
|
5400
5536
|
const deadline = Date.now() + timeoutMs;
|
|
5401
5537
|
while (Date.now() < deadline) {
|
|
5402
5538
|
if (!this.currentConversationId) return null;
|
|
5403
|
-
const filePath =
|
|
5539
|
+
const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
5404
5540
|
const messages = parseJsonl(filePath);
|
|
5405
5541
|
if (messages.length > previousCount) {
|
|
5406
5542
|
for (let i = messages.length - 1; i >= previousCount; i--) {
|
|
@@ -5415,15 +5551,15 @@ var HistoryService = class {
|
|
|
5415
5551
|
detectCurrentConversation() {
|
|
5416
5552
|
const dir = this.projectDir;
|
|
5417
5553
|
try {
|
|
5418
|
-
const files =
|
|
5554
|
+
const files = fs5.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5419
5555
|
try {
|
|
5420
|
-
return { name: e.name, mtime:
|
|
5556
|
+
return { name: e.name, mtime: fs5.statSync(path8.join(dir, e.name)).mtimeMs };
|
|
5421
5557
|
} catch {
|
|
5422
5558
|
return { name: e.name, mtime: 0 };
|
|
5423
5559
|
}
|
|
5424
5560
|
}).sort((a, b) => b.mtime - a.mtime);
|
|
5425
5561
|
if (files.length > 0) {
|
|
5426
|
-
this.currentConversationId =
|
|
5562
|
+
this.currentConversationId = path8.basename(files[0].name, ".jsonl");
|
|
5427
5563
|
}
|
|
5428
5564
|
} catch {
|
|
5429
5565
|
}
|
|
@@ -5455,13 +5591,13 @@ var HistoryService = class {
|
|
|
5455
5591
|
const dir = this.projectDir;
|
|
5456
5592
|
let entries;
|
|
5457
5593
|
try {
|
|
5458
|
-
entries =
|
|
5594
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
5459
5595
|
} catch {
|
|
5460
5596
|
return null;
|
|
5461
5597
|
}
|
|
5462
5598
|
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5463
5599
|
try {
|
|
5464
|
-
return { name: e.name, mtime:
|
|
5600
|
+
return { name: e.name, mtime: fs5.statSync(path8.join(dir, e.name)).mtimeMs };
|
|
5465
5601
|
} catch {
|
|
5466
5602
|
return { name: e.name, mtime: 0 };
|
|
5467
5603
|
}
|
|
@@ -5469,12 +5605,12 @@ var HistoryService = class {
|
|
|
5469
5605
|
if (files.length === 0) return null;
|
|
5470
5606
|
const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
|
|
5471
5607
|
if (!files.some((f) => f.name === targetFile)) return null;
|
|
5472
|
-
return this.extractUsageFromFile(
|
|
5608
|
+
return this.extractUsageFromFile(path8.join(dir, targetFile));
|
|
5473
5609
|
}
|
|
5474
5610
|
extractUsageFromFile(filePath) {
|
|
5475
5611
|
let raw;
|
|
5476
5612
|
try {
|
|
5477
|
-
raw =
|
|
5613
|
+
raw = fs5.readFileSync(filePath, "utf8");
|
|
5478
5614
|
} catch {
|
|
5479
5615
|
return null;
|
|
5480
5616
|
}
|
|
@@ -5519,9 +5655,9 @@ var HistoryService = class {
|
|
|
5519
5655
|
let totalCost = 0;
|
|
5520
5656
|
let files;
|
|
5521
5657
|
try {
|
|
5522
|
-
files =
|
|
5658
|
+
files = fs5.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
|
|
5523
5659
|
try {
|
|
5524
|
-
return
|
|
5660
|
+
return fs5.statSync(path8.join(projectDir, f)).mtimeMs >= monthStartMs;
|
|
5525
5661
|
} catch {
|
|
5526
5662
|
return false;
|
|
5527
5663
|
}
|
|
@@ -5532,7 +5668,7 @@ var HistoryService = class {
|
|
|
5532
5668
|
for (const file of files) {
|
|
5533
5669
|
let raw;
|
|
5534
5670
|
try {
|
|
5535
|
-
raw =
|
|
5671
|
+
raw = fs5.readFileSync(path8.join(projectDir, file), "utf8");
|
|
5536
5672
|
} catch {
|
|
5537
5673
|
continue;
|
|
5538
5674
|
}
|
|
@@ -5567,23 +5703,23 @@ var HistoryService = class {
|
|
|
5567
5703
|
const dir = this.projectDir;
|
|
5568
5704
|
let entries;
|
|
5569
5705
|
try {
|
|
5570
|
-
entries =
|
|
5706
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
5571
5707
|
} catch {
|
|
5572
5708
|
return;
|
|
5573
5709
|
}
|
|
5574
5710
|
const sessions2 = [];
|
|
5575
5711
|
for (const entry of entries) {
|
|
5576
5712
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
5577
|
-
const id =
|
|
5578
|
-
const filePath =
|
|
5713
|
+
const id = path8.basename(entry.name, ".jsonl");
|
|
5714
|
+
const filePath = path8.join(dir, entry.name);
|
|
5579
5715
|
let mtime = Date.now();
|
|
5580
5716
|
try {
|
|
5581
|
-
mtime =
|
|
5717
|
+
mtime = fs5.statSync(filePath).mtimeMs;
|
|
5582
5718
|
} catch {
|
|
5583
5719
|
}
|
|
5584
5720
|
let summary = "";
|
|
5585
5721
|
try {
|
|
5586
|
-
const raw =
|
|
5722
|
+
const raw = fs5.readFileSync(filePath, "utf8");
|
|
5587
5723
|
for (const line of raw.split("\n")) {
|
|
5588
5724
|
if (!line.trim()) continue;
|
|
5589
5725
|
try {
|
|
@@ -5616,7 +5752,7 @@ var HistoryService = class {
|
|
|
5616
5752
|
* showing an empty conversation.
|
|
5617
5753
|
*/
|
|
5618
5754
|
async loadConversation(sessionId) {
|
|
5619
|
-
const filePath =
|
|
5755
|
+
const filePath = path8.join(this.projectDir, `${sessionId}.jsonl`);
|
|
5620
5756
|
const messages = parseJsonl(filePath);
|
|
5621
5757
|
if (messages.length === 0) return;
|
|
5622
5758
|
const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
|
|
@@ -5671,8 +5807,8 @@ function parsePayload(schema, raw) {
|
|
|
5671
5807
|
}
|
|
5672
5808
|
|
|
5673
5809
|
// src/services/file-ops.service.ts
|
|
5674
|
-
var
|
|
5675
|
-
var
|
|
5810
|
+
var fs6 = __toESM(require("fs/promises"));
|
|
5811
|
+
var path9 = __toESM(require("path"));
|
|
5676
5812
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
5677
5813
|
var MAX_WALK_DEPTH = 6;
|
|
5678
5814
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -5707,12 +5843,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
5707
5843
|
"__pycache__"
|
|
5708
5844
|
]);
|
|
5709
5845
|
function isUnder(parent, candidate) {
|
|
5710
|
-
const rel =
|
|
5711
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
5846
|
+
const rel = path9.relative(parent, candidate);
|
|
5847
|
+
return rel === "" || !rel.startsWith("..") && !path9.isAbsolute(rel);
|
|
5712
5848
|
}
|
|
5713
5849
|
async function isExistingFile(absPath) {
|
|
5714
5850
|
try {
|
|
5715
|
-
const stat3 = await
|
|
5851
|
+
const stat3 = await fs6.stat(absPath);
|
|
5716
5852
|
return stat3.isFile();
|
|
5717
5853
|
} catch {
|
|
5718
5854
|
return false;
|
|
@@ -5725,13 +5861,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
5725
5861
|
ctx.visited++;
|
|
5726
5862
|
let entries = [];
|
|
5727
5863
|
try {
|
|
5728
|
-
entries = await
|
|
5864
|
+
entries = await fs6.readdir(dir, { withFileTypes: true });
|
|
5729
5865
|
} catch {
|
|
5730
5866
|
return;
|
|
5731
5867
|
}
|
|
5732
5868
|
for (const e of entries) {
|
|
5733
5869
|
if (!e.isFile()) continue;
|
|
5734
|
-
const full =
|
|
5870
|
+
const full = path9.join(dir, e.name);
|
|
5735
5871
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
5736
5872
|
ctx.matches.push(full);
|
|
5737
5873
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -5741,21 +5877,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
5741
5877
|
if (!e.isDirectory()) continue;
|
|
5742
5878
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
5743
5879
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
5744
|
-
await walkForSuffix(
|
|
5880
|
+
await walkForSuffix(path9.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
5745
5881
|
if (ctx.matches.length >= ctx.cap) return;
|
|
5746
5882
|
}
|
|
5747
5883
|
}
|
|
5748
5884
|
async function findFile(rawPath) {
|
|
5749
5885
|
const cwd = process.cwd();
|
|
5750
|
-
if (
|
|
5751
|
-
const abs =
|
|
5886
|
+
if (path9.isAbsolute(rawPath)) {
|
|
5887
|
+
const abs = path9.normalize(rawPath);
|
|
5752
5888
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
5753
5889
|
}
|
|
5754
|
-
const direct =
|
|
5890
|
+
const direct = path9.resolve(cwd, rawPath);
|
|
5755
5891
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
5756
|
-
const normalized =
|
|
5892
|
+
const normalized = path9.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
5757
5893
|
const needles = [
|
|
5758
|
-
`${
|
|
5894
|
+
`${path9.sep}${normalized}`,
|
|
5759
5895
|
`/${normalized}`
|
|
5760
5896
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
5761
5897
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -5769,7 +5905,7 @@ async function findWriteTarget(rawPath) {
|
|
|
5769
5905
|
const found = await findFile(rawPath);
|
|
5770
5906
|
if (found) return found;
|
|
5771
5907
|
const cwd = process.cwd();
|
|
5772
|
-
const fallback =
|
|
5908
|
+
const fallback = path9.isAbsolute(rawPath) ? path9.normalize(rawPath) : path9.resolve(cwd, rawPath);
|
|
5773
5909
|
if (!isUnder(cwd, fallback)) return null;
|
|
5774
5910
|
return fallback;
|
|
5775
5911
|
}
|
|
@@ -5786,11 +5922,11 @@ async function readProjectFile(rawPath) {
|
|
|
5786
5922
|
if (!abs) {
|
|
5787
5923
|
return { error: `File not found in the project tree: ${rawPath}` };
|
|
5788
5924
|
}
|
|
5789
|
-
const stat3 = await
|
|
5925
|
+
const stat3 = await fs6.stat(abs);
|
|
5790
5926
|
if (stat3.size > MAX_FILE_BYTES) {
|
|
5791
5927
|
return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
5792
5928
|
}
|
|
5793
|
-
const buf = await
|
|
5929
|
+
const buf = await fs6.readFile(abs);
|
|
5794
5930
|
if (looksBinary(buf)) {
|
|
5795
5931
|
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
5796
5932
|
}
|
|
@@ -5809,8 +5945,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
5809
5945
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
5810
5946
|
return { error: "Content too large." };
|
|
5811
5947
|
}
|
|
5812
|
-
await
|
|
5813
|
-
await
|
|
5948
|
+
await fs6.mkdir(path9.dirname(abs), { recursive: true });
|
|
5949
|
+
await fs6.writeFile(abs, content, "utf-8");
|
|
5814
5950
|
return { ok: true };
|
|
5815
5951
|
} catch (e) {
|
|
5816
5952
|
const msg = e instanceof Error ? e.message : "Write failed";
|
|
@@ -5821,8 +5957,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
5821
5957
|
// src/services/project-ops.service.ts
|
|
5822
5958
|
var import_child_process4 = require("child_process");
|
|
5823
5959
|
var import_util = require("util");
|
|
5824
|
-
var
|
|
5825
|
-
var
|
|
5960
|
+
var fs7 = __toESM(require("fs/promises"));
|
|
5961
|
+
var path10 = __toESM(require("path"));
|
|
5826
5962
|
var execFileP = (0, import_util.promisify)(import_child_process4.execFile);
|
|
5827
5963
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
5828
5964
|
"node_modules",
|
|
@@ -5870,7 +6006,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
5870
6006
|
}
|
|
5871
6007
|
let entries = [];
|
|
5872
6008
|
try {
|
|
5873
|
-
entries = await
|
|
6009
|
+
entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
5874
6010
|
} catch {
|
|
5875
6011
|
return;
|
|
5876
6012
|
}
|
|
@@ -5880,18 +6016,18 @@ async function listProjectFiles(opts = {}) {
|
|
|
5880
6016
|
return;
|
|
5881
6017
|
}
|
|
5882
6018
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
5883
|
-
const full =
|
|
6019
|
+
const full = path10.join(dir, e.name);
|
|
5884
6020
|
if (e.isDirectory()) {
|
|
5885
6021
|
if (depth >= 12) continue;
|
|
5886
6022
|
await walk(full, depth + 1);
|
|
5887
6023
|
} else if (e.isFile()) {
|
|
5888
|
-
const rel =
|
|
6024
|
+
const rel = path10.relative(root, full);
|
|
5889
6025
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
5890
6026
|
continue;
|
|
5891
6027
|
}
|
|
5892
6028
|
let size = 0;
|
|
5893
6029
|
try {
|
|
5894
|
-
const st3 = await
|
|
6030
|
+
const st3 = await fs7.stat(full);
|
|
5895
6031
|
size = st3.size;
|
|
5896
6032
|
} catch {
|
|
5897
6033
|
}
|
|
@@ -5993,8 +6129,8 @@ async function gitStatus(cwd) {
|
|
|
5993
6129
|
let hasMergeInProgress = false;
|
|
5994
6130
|
try {
|
|
5995
6131
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
5996
|
-
const mergeHead =
|
|
5997
|
-
await
|
|
6132
|
+
const mergeHead = path10.isAbsolute(gitDir) ? path10.join(gitDir, "MERGE_HEAD") : path10.join(root, gitDir, "MERGE_HEAD");
|
|
6133
|
+
await fs7.access(mergeHead);
|
|
5998
6134
|
hasMergeInProgress = true;
|
|
5999
6135
|
} catch {
|
|
6000
6136
|
}
|
|
@@ -6071,8 +6207,8 @@ async function gitResolve(file, side, cwd) {
|
|
|
6071
6207
|
function saveFilesTemp(files) {
|
|
6072
6208
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
6073
6209
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
6074
|
-
const tmpPath =
|
|
6075
|
-
|
|
6210
|
+
const tmpPath = path11.join(os7.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
|
|
6211
|
+
fs8.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
6076
6212
|
return tmpPath;
|
|
6077
6213
|
});
|
|
6078
6214
|
}
|
|
@@ -6145,8 +6281,8 @@ try:
|
|
|
6145
6281
|
sys.exit((st>>8)&0xFF)
|
|
6146
6282
|
except Exception:sys.exit(0)
|
|
6147
6283
|
`;
|
|
6148
|
-
const helperPath =
|
|
6149
|
-
|
|
6284
|
+
const helperPath = path11.join(os7.tmpdir(), "codeam-quota-helper.py");
|
|
6285
|
+
fs8.writeFileSync(helperPath, helperScript, { mode: 420 });
|
|
6150
6286
|
const python = findInPath("python3") ?? findInPath("python");
|
|
6151
6287
|
if (!python) {
|
|
6152
6288
|
quotaFetchInProgress = false;
|
|
@@ -6178,7 +6314,7 @@ except Exception:sys.exit(0)
|
|
|
6178
6314
|
} catch {
|
|
6179
6315
|
}
|
|
6180
6316
|
try {
|
|
6181
|
-
|
|
6317
|
+
fs8.unlinkSync(helperPath);
|
|
6182
6318
|
} catch {
|
|
6183
6319
|
}
|
|
6184
6320
|
quotaFetchInProgress = false;
|
|
@@ -6228,7 +6364,7 @@ except Exception:sys.exit(0)
|
|
|
6228
6364
|
setTimeout(() => {
|
|
6229
6365
|
for (const p2 of paths) {
|
|
6230
6366
|
try {
|
|
6231
|
-
|
|
6367
|
+
fs8.unlinkSync(p2);
|
|
6232
6368
|
} catch {
|
|
6233
6369
|
}
|
|
6234
6370
|
}
|
|
@@ -6271,6 +6407,25 @@ except Exception:sys.exit(0)
|
|
|
6271
6407
|
}
|
|
6272
6408
|
break;
|
|
6273
6409
|
}
|
|
6410
|
+
case "session_terminated": {
|
|
6411
|
+
showInfo("Session was deleted from the app \u2014 exiting.");
|
|
6412
|
+
try {
|
|
6413
|
+
claude.kill();
|
|
6414
|
+
} catch {
|
|
6415
|
+
}
|
|
6416
|
+
try {
|
|
6417
|
+
const proc = (0, import_child_process5.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6418
|
+
detached: true,
|
|
6419
|
+
stdio: "ignore"
|
|
6420
|
+
});
|
|
6421
|
+
proc.unref();
|
|
6422
|
+
} catch {
|
|
6423
|
+
}
|
|
6424
|
+
outputSvc.dispose();
|
|
6425
|
+
relay.stop();
|
|
6426
|
+
ws.disconnect();
|
|
6427
|
+
process.exit(0);
|
|
6428
|
+
}
|
|
6274
6429
|
case "shutdown_session": {
|
|
6275
6430
|
try {
|
|
6276
6431
|
await relay.sendResult(cmd.id, "success", { ok: true });
|
|
@@ -6451,7 +6606,7 @@ except Exception:sys.exit(0)
|
|
|
6451
6606
|
setTimeout(() => {
|
|
6452
6607
|
for (const p2 of paths) {
|
|
6453
6608
|
try {
|
|
6454
|
-
|
|
6609
|
+
fs8.unlinkSync(p2);
|
|
6455
6610
|
} catch {
|
|
6456
6611
|
}
|
|
6457
6612
|
}
|
|
@@ -6746,9 +6901,9 @@ async function logout() {
|
|
|
6746
6901
|
|
|
6747
6902
|
// src/commands/deploy.ts
|
|
6748
6903
|
var import_child_process10 = require("child_process");
|
|
6749
|
-
var
|
|
6750
|
-
var
|
|
6751
|
-
var
|
|
6904
|
+
var fs9 = __toESM(require("fs"));
|
|
6905
|
+
var os8 = __toESM(require("os"));
|
|
6906
|
+
var path16 = __toESM(require("path"));
|
|
6752
6907
|
var import_util6 = require("util");
|
|
6753
6908
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
6754
6909
|
|
|
@@ -6756,7 +6911,7 @@ var import_picocolors9 = __toESM(require("picocolors"));
|
|
|
6756
6911
|
var import_child_process6 = require("child_process");
|
|
6757
6912
|
var import_util2 = require("util");
|
|
6758
6913
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
6759
|
-
var
|
|
6914
|
+
var path12 = __toESM(require("path"));
|
|
6760
6915
|
var execFileP2 = (0, import_util2.promisify)(import_child_process6.execFile);
|
|
6761
6916
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
6762
6917
|
function resetStdinForChild() {
|
|
@@ -7245,7 +7400,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7245
7400
|
});
|
|
7246
7401
|
}
|
|
7247
7402
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7248
|
-
const remoteDir =
|
|
7403
|
+
const remoteDir = path12.posix.dirname(remotePath);
|
|
7249
7404
|
const parts = [
|
|
7250
7405
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
7251
7406
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -7315,7 +7470,7 @@ function shellQuote(s) {
|
|
|
7315
7470
|
// src/services/providers/gitpod.ts
|
|
7316
7471
|
var import_child_process7 = require("child_process");
|
|
7317
7472
|
var import_util3 = require("util");
|
|
7318
|
-
var
|
|
7473
|
+
var path13 = __toESM(require("path"));
|
|
7319
7474
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
7320
7475
|
var execFileP3 = (0, import_util3.promisify)(import_child_process7.execFile);
|
|
7321
7476
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
@@ -7555,7 +7710,7 @@ var GitpodProvider = class {
|
|
|
7555
7710
|
});
|
|
7556
7711
|
}
|
|
7557
7712
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7558
|
-
const remoteDir =
|
|
7713
|
+
const remoteDir = path13.posix.dirname(remotePath);
|
|
7559
7714
|
const parts = [
|
|
7560
7715
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
7561
7716
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -7591,7 +7746,7 @@ function shellQuote2(s) {
|
|
|
7591
7746
|
// src/services/providers/gitlab-workspaces.ts
|
|
7592
7747
|
var import_child_process8 = require("child_process");
|
|
7593
7748
|
var import_util4 = require("util");
|
|
7594
|
-
var
|
|
7749
|
+
var path14 = __toESM(require("path"));
|
|
7595
7750
|
var execFileP4 = (0, import_util4.promisify)(import_child_process8.execFile);
|
|
7596
7751
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
7597
7752
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
@@ -7851,7 +8006,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
7851
8006
|
}
|
|
7852
8007
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7853
8008
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
7854
|
-
const remoteDir =
|
|
8009
|
+
const remoteDir = path14.posix.dirname(remotePath);
|
|
7855
8010
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
7856
8011
|
if (options.mode != null) {
|
|
7857
8012
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
@@ -7919,7 +8074,7 @@ function shellQuote3(s) {
|
|
|
7919
8074
|
// src/services/providers/railway.ts
|
|
7920
8075
|
var import_child_process9 = require("child_process");
|
|
7921
8076
|
var import_util5 = require("util");
|
|
7922
|
-
var
|
|
8077
|
+
var path15 = __toESM(require("path"));
|
|
7923
8078
|
var execFileP5 = (0, import_util5.promisify)(import_child_process9.execFile);
|
|
7924
8079
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
7925
8080
|
function resetStdinForChild4() {
|
|
@@ -8155,7 +8310,7 @@ var RailwayProvider = class {
|
|
|
8155
8310
|
if (!projectId || !serviceId) {
|
|
8156
8311
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
8157
8312
|
}
|
|
8158
|
-
const remoteDir =
|
|
8313
|
+
const remoteDir = path15.posix.dirname(remotePath);
|
|
8159
8314
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
8160
8315
|
if (options.mode != null) {
|
|
8161
8316
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
@@ -8347,7 +8502,7 @@ async function deploy() {
|
|
|
8347
8502
|
process.exit(1);
|
|
8348
8503
|
}
|
|
8349
8504
|
}
|
|
8350
|
-
const localClaudeDir =
|
|
8505
|
+
const localClaudeDir = path16.join(os8.homedir(), ".claude");
|
|
8351
8506
|
const localCredsKind = await detectLocalClaudeCredentials(localClaudeDir);
|
|
8352
8507
|
let bridged = "none";
|
|
8353
8508
|
if (localCredsKind !== "none") {
|
|
@@ -8391,7 +8546,7 @@ async function deploy() {
|
|
|
8391
8546
|
process.exit(1);
|
|
8392
8547
|
}
|
|
8393
8548
|
claudeStep.stop("\u2713 Claude CLI installed");
|
|
8394
|
-
const haveLocalClaude =
|
|
8549
|
+
const haveLocalClaude = fs9.existsSync(localClaudeDir) && fs9.statSync(localClaudeDir).isDirectory();
|
|
8395
8550
|
if (haveLocalClaude) {
|
|
8396
8551
|
const copyStep = fe();
|
|
8397
8552
|
copyStep.start("Copying local Claude config to workspace\u2026");
|
|
@@ -8445,10 +8600,10 @@ async function deploy() {
|
|
|
8445
8600
|
}
|
|
8446
8601
|
}
|
|
8447
8602
|
if (bridged !== "none") {
|
|
8448
|
-
const localClaudeJson =
|
|
8449
|
-
if (
|
|
8603
|
+
const localClaudeJson = path16.join(os8.homedir(), ".claude.json");
|
|
8604
|
+
if (fs9.existsSync(localClaudeJson)) {
|
|
8450
8605
|
try {
|
|
8451
|
-
const contents =
|
|
8606
|
+
const contents = fs9.readFileSync(localClaudeJson);
|
|
8452
8607
|
await provider.uploadFile(
|
|
8453
8608
|
workspace.id,
|
|
8454
8609
|
"/home/codespace/.claude.json",
|
|
@@ -8638,7 +8793,7 @@ async function runRemoteClaudeLogin(provider, workspaceId) {
|
|
|
8638
8793
|
}
|
|
8639
8794
|
}
|
|
8640
8795
|
async function detectLocalClaudeCredentials(localClaudeDir) {
|
|
8641
|
-
if (
|
|
8796
|
+
if (fs9.existsSync(path16.join(localClaudeDir, ".credentials.json"))) {
|
|
8642
8797
|
return "flat-file";
|
|
8643
8798
|
}
|
|
8644
8799
|
if (process.platform === "darwin") {
|
|
@@ -8671,8 +8826,8 @@ async function verifyClaudeAuth(provider, workspaceId) {
|
|
|
8671
8826
|
}
|
|
8672
8827
|
}
|
|
8673
8828
|
async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
|
|
8674
|
-
const fileBased =
|
|
8675
|
-
if (
|
|
8829
|
+
const fileBased = path16.join(localClaudeDir, ".credentials.json");
|
|
8830
|
+
if (fs9.existsSync(fileBased)) return "flat-file";
|
|
8676
8831
|
if (process.platform === "darwin") {
|
|
8677
8832
|
try {
|
|
8678
8833
|
const { stdout } = await execFileP6(
|
|
@@ -8877,10 +9032,172 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
8877
9032
|
}
|
|
8878
9033
|
}
|
|
8879
9034
|
|
|
9035
|
+
// src/commands/version.ts
|
|
9036
|
+
var import_picocolors11 = __toESM(require("picocolors"));
|
|
9037
|
+
function version() {
|
|
9038
|
+
const v = true ? "2.4.34" : "unknown";
|
|
9039
|
+
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
9040
|
+
}
|
|
9041
|
+
|
|
9042
|
+
// src/commands/help.ts
|
|
9043
|
+
var import_picocolors12 = __toESM(require("picocolors"));
|
|
9044
|
+
function help() {
|
|
9045
|
+
const lines = [
|
|
9046
|
+
"",
|
|
9047
|
+
` ${import_picocolors12.default.bold(import_picocolors12.default.magenta("codeam-cli"))} ${import_picocolors12.default.dim("\u2014 Claude Code remote control")}`,
|
|
9048
|
+
"",
|
|
9049
|
+
` ${import_picocolors12.default.bold("Usage")}`,
|
|
9050
|
+
` ${import_picocolors12.default.cyan("codeam")} ${import_picocolors12.default.dim("[command]")}`,
|
|
9051
|
+
"",
|
|
9052
|
+
` ${import_picocolors12.default.bold("Commands")}`,
|
|
9053
|
+
` ${import_picocolors12.default.white("codeam")} ${import_picocolors12.default.dim("start Claude Code with mobile control")}`,
|
|
9054
|
+
` ${import_picocolors12.default.white("codeam pair")} ${import_picocolors12.default.dim("pair a new mobile device")}`,
|
|
9055
|
+
` ${import_picocolors12.default.white("codeam sessions")} ${import_picocolors12.default.dim("list paired devices")}`,
|
|
9056
|
+
` ${import_picocolors12.default.white("codeam status")} ${import_picocolors12.default.dim("show connection info")}`,
|
|
9057
|
+
` ${import_picocolors12.default.white("codeam logout")} ${import_picocolors12.default.dim("remove all paired sessions")}`,
|
|
9058
|
+
` ${import_picocolors12.default.white("codeam deploy")} ${import_picocolors12.default.dim("provision a cloud workspace (Codespaces) and pair it")}`,
|
|
9059
|
+
` ${import_picocolors12.default.white("codeam deploy ls")} ${import_picocolors12.default.dim("list deployed cloud workspaces")}`,
|
|
9060
|
+
` ${import_picocolors12.default.white("codeam deploy stop")} ${import_picocolors12.default.dim("stop a deployed workspace session")}`,
|
|
9061
|
+
"",
|
|
9062
|
+
` ${import_picocolors12.default.bold("Flags")}`,
|
|
9063
|
+
` ${import_picocolors12.default.white("-v, --version")} ${import_picocolors12.default.dim("print the CLI version")}`,
|
|
9064
|
+
` ${import_picocolors12.default.white("-h, --help")} ${import_picocolors12.default.dim("show this help")}`,
|
|
9065
|
+
"",
|
|
9066
|
+
` ${import_picocolors12.default.bold("Links")}`,
|
|
9067
|
+
` ${import_picocolors12.default.dim("Docs:")} ${import_picocolors12.default.green("https://www.codeagent-mobile.com")}`,
|
|
9068
|
+
` ${import_picocolors12.default.dim("Issues:")} ${import_picocolors12.default.green("https://github.com/edgar-durand/codeagent-mobile-clients/issues")}`,
|
|
9069
|
+
""
|
|
9070
|
+
];
|
|
9071
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
9072
|
+
}
|
|
9073
|
+
|
|
9074
|
+
// src/lib/updateNotifier.ts
|
|
9075
|
+
var fs10 = __toESM(require("fs"));
|
|
9076
|
+
var os9 = __toESM(require("os"));
|
|
9077
|
+
var path17 = __toESM(require("path"));
|
|
9078
|
+
var https4 = __toESM(require("https"));
|
|
9079
|
+
var import_picocolors13 = __toESM(require("picocolors"));
|
|
9080
|
+
var PKG_NAME = "codeam-cli";
|
|
9081
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
9082
|
+
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9083
|
+
var REQUEST_TIMEOUT_MS = 1500;
|
|
9084
|
+
function cachePath() {
|
|
9085
|
+
const dir = path17.join(os9.homedir(), ".codeam");
|
|
9086
|
+
return path17.join(dir, "update-check.json");
|
|
9087
|
+
}
|
|
9088
|
+
function readCache() {
|
|
9089
|
+
try {
|
|
9090
|
+
const raw = fs10.readFileSync(cachePath(), "utf8");
|
|
9091
|
+
const parsed = JSON.parse(raw);
|
|
9092
|
+
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
9093
|
+
return parsed;
|
|
9094
|
+
} catch {
|
|
9095
|
+
return null;
|
|
9096
|
+
}
|
|
9097
|
+
}
|
|
9098
|
+
function writeCache(cache) {
|
|
9099
|
+
try {
|
|
9100
|
+
const file = cachePath();
|
|
9101
|
+
fs10.mkdirSync(path17.dirname(file), { recursive: true });
|
|
9102
|
+
fs10.writeFileSync(file, JSON.stringify(cache));
|
|
9103
|
+
} catch {
|
|
9104
|
+
}
|
|
9105
|
+
}
|
|
9106
|
+
function compareSemver(a, b) {
|
|
9107
|
+
const stripPre = (s) => s.split("-")[0];
|
|
9108
|
+
const aParts = stripPre(a).split(".").map(Number);
|
|
9109
|
+
const bParts = stripPre(b).split(".").map(Number);
|
|
9110
|
+
for (let i = 0; i < 3; i++) {
|
|
9111
|
+
const ai = aParts[i] ?? 0;
|
|
9112
|
+
const bi = bParts[i] ?? 0;
|
|
9113
|
+
if (Number.isNaN(ai) || Number.isNaN(bi)) return 0;
|
|
9114
|
+
if (ai > bi) return 1;
|
|
9115
|
+
if (ai < bi) return -1;
|
|
9116
|
+
}
|
|
9117
|
+
return 0;
|
|
9118
|
+
}
|
|
9119
|
+
function fetchLatest() {
|
|
9120
|
+
return new Promise((resolve2) => {
|
|
9121
|
+
const req = https4.get(
|
|
9122
|
+
REGISTRY_URL,
|
|
9123
|
+
{ headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
|
|
9124
|
+
(res) => {
|
|
9125
|
+
if (res.statusCode !== 200) {
|
|
9126
|
+
res.resume();
|
|
9127
|
+
resolve2(null);
|
|
9128
|
+
return;
|
|
9129
|
+
}
|
|
9130
|
+
let buf = "";
|
|
9131
|
+
res.setEncoding("utf8");
|
|
9132
|
+
res.on("data", (chunk) => {
|
|
9133
|
+
buf += chunk;
|
|
9134
|
+
});
|
|
9135
|
+
res.on("end", () => {
|
|
9136
|
+
try {
|
|
9137
|
+
const json = JSON.parse(buf);
|
|
9138
|
+
if (typeof json.version === "string") {
|
|
9139
|
+
resolve2(json.version);
|
|
9140
|
+
} else {
|
|
9141
|
+
resolve2(null);
|
|
9142
|
+
}
|
|
9143
|
+
} catch {
|
|
9144
|
+
resolve2(null);
|
|
9145
|
+
}
|
|
9146
|
+
});
|
|
9147
|
+
}
|
|
9148
|
+
);
|
|
9149
|
+
req.on("timeout", () => {
|
|
9150
|
+
req.destroy();
|
|
9151
|
+
resolve2(null);
|
|
9152
|
+
});
|
|
9153
|
+
req.on("error", () => resolve2(null));
|
|
9154
|
+
});
|
|
9155
|
+
}
|
|
9156
|
+
function notifyIfStale(currentVersion, latest) {
|
|
9157
|
+
if (compareSemver(latest, currentVersion) <= 0) return;
|
|
9158
|
+
const arrow = import_picocolors13.default.dim("\u2192");
|
|
9159
|
+
const cmd = import_picocolors13.default.cyan("npm install -g codeam-cli");
|
|
9160
|
+
const lines = [
|
|
9161
|
+
"",
|
|
9162
|
+
` ${import_picocolors13.default.yellow("\u25CF")} ${import_picocolors13.default.bold("Update available")} ${import_picocolors13.default.dim(currentVersion)} ${arrow} ${import_picocolors13.default.green(latest)}`,
|
|
9163
|
+
` Run ${cmd} to upgrade.`,
|
|
9164
|
+
""
|
|
9165
|
+
];
|
|
9166
|
+
process.stderr.write(lines.join("\n"));
|
|
9167
|
+
}
|
|
9168
|
+
function checkForUpdates() {
|
|
9169
|
+
if (process.env.NODE_ENV === "test") return;
|
|
9170
|
+
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
9171
|
+
if (process.env.CI) return;
|
|
9172
|
+
if (!process.stdout.isTTY) return;
|
|
9173
|
+
const current = true ? "2.4.34" : null;
|
|
9174
|
+
if (!current) return;
|
|
9175
|
+
const cache = readCache();
|
|
9176
|
+
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
9177
|
+
if (fresh && cache) {
|
|
9178
|
+
notifyIfStale(current, cache.latest);
|
|
9179
|
+
return;
|
|
9180
|
+
}
|
|
9181
|
+
void fetchLatest().then((latest) => {
|
|
9182
|
+
if (!latest) return;
|
|
9183
|
+
writeCache({ fetchedAt: Date.now(), latest });
|
|
9184
|
+
});
|
|
9185
|
+
}
|
|
9186
|
+
|
|
8880
9187
|
// src/index.ts
|
|
8881
9188
|
var [, , command, ...args] = process.argv;
|
|
8882
9189
|
async function main() {
|
|
9190
|
+
const isMetaCommand = command === "--version" || command === "-v" || command === "version" || command === "--help" || command === "-h" || command === "help";
|
|
9191
|
+
if (!isMetaCommand) checkForUpdates();
|
|
8883
9192
|
switch (command) {
|
|
9193
|
+
case "--version":
|
|
9194
|
+
case "-v":
|
|
9195
|
+
case "version":
|
|
9196
|
+
return version();
|
|
9197
|
+
case "--help":
|
|
9198
|
+
case "-h":
|
|
9199
|
+
case "help":
|
|
9200
|
+
return help();
|
|
8884
9201
|
case "pair":
|
|
8885
9202
|
return pair();
|
|
8886
9203
|
case "sessions":
|