codeam-cli 2.4.32 → 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 +490 -157
- 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) {
|
|
@@ -2293,6 +2383,7 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2293
2383
|
dataSub = null;
|
|
2294
2384
|
exitSub = null;
|
|
2295
2385
|
rawModeSet = false;
|
|
2386
|
+
resizeHandler = null;
|
|
2296
2387
|
/**
|
|
2297
2388
|
* Factory that returns a working ConPTY strategy or `null` if
|
|
2298
2389
|
* node-pty can't load. The caller (claude.service.ts) decides
|
|
@@ -2304,10 +2395,12 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2304
2395
|
return new _WindowsConPtyStrategy(opts, lib);
|
|
2305
2396
|
}
|
|
2306
2397
|
spawn(cmd, cwd, args2 = []) {
|
|
2398
|
+
const cols = process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 120;
|
|
2399
|
+
const rows = process.stdout.rows && process.stdout.rows > 0 ? process.stdout.rows : 30;
|
|
2307
2400
|
this.pty = this.lib.spawn(cmd, args2, {
|
|
2308
2401
|
name: "xterm-256color",
|
|
2309
|
-
cols
|
|
2310
|
-
rows
|
|
2402
|
+
cols,
|
|
2403
|
+
rows,
|
|
2311
2404
|
cwd,
|
|
2312
2405
|
env: {
|
|
2313
2406
|
...process.env,
|
|
@@ -2317,11 +2410,17 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2317
2410
|
useConpty: true,
|
|
2318
2411
|
conptyInheritCursor: false
|
|
2319
2412
|
});
|
|
2413
|
+
let traceCount = 0;
|
|
2320
2414
|
this.dataSub = this.pty.onData((data) => {
|
|
2321
2415
|
process.stdout.write(data);
|
|
2322
2416
|
this.opts.onData(data);
|
|
2417
|
+
traceCount++;
|
|
2418
|
+
if (traceCount <= 5 || traceCount % 50 === 0) {
|
|
2419
|
+
log.trace("conpty", `onData #${traceCount} ${data.length}B`);
|
|
2420
|
+
}
|
|
2323
2421
|
});
|
|
2324
2422
|
this.exitSub = this.pty.onExit(({ exitCode }) => {
|
|
2423
|
+
log.trace("conpty", `claude exited code=${exitCode ?? 0}`);
|
|
2325
2424
|
this.dispose();
|
|
2326
2425
|
this.opts.onExit(exitCode ?? 0);
|
|
2327
2426
|
});
|
|
@@ -2334,6 +2433,15 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2334
2433
|
}
|
|
2335
2434
|
process.stdin.resume();
|
|
2336
2435
|
process.stdin.on("data", this.stdinHandler);
|
|
2436
|
+
this.resizeHandler = () => {
|
|
2437
|
+
const c2 = process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : cols;
|
|
2438
|
+
const r = process.stdout.rows && process.stdout.rows > 0 ? process.stdout.rows : rows;
|
|
2439
|
+
try {
|
|
2440
|
+
this.pty?.resize(c2, r);
|
|
2441
|
+
} catch {
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
process.stdout.on("resize", this.resizeHandler);
|
|
2337
2445
|
}
|
|
2338
2446
|
write(data) {
|
|
2339
2447
|
if (!this.pty) return;
|
|
@@ -2352,6 +2460,10 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2352
2460
|
this.dataSub = null;
|
|
2353
2461
|
this.exitSub = null;
|
|
2354
2462
|
process.stdin.removeListener("data", this.stdinHandler);
|
|
2463
|
+
if (this.resizeHandler) {
|
|
2464
|
+
process.stdout.removeListener("resize", this.resizeHandler);
|
|
2465
|
+
this.resizeHandler = null;
|
|
2466
|
+
}
|
|
2355
2467
|
if (this.rawModeSet && process.stdin.isTTY) {
|
|
2356
2468
|
try {
|
|
2357
2469
|
process.stdin.setRawMode(false);
|
|
@@ -2368,8 +2480,8 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
2368
2480
|
|
|
2369
2481
|
// src/services/claude-installer.ts
|
|
2370
2482
|
var import_child_process3 = require("child_process");
|
|
2371
|
-
var
|
|
2372
|
-
var
|
|
2483
|
+
var path6 = __toESM(require("path"));
|
|
2484
|
+
var os5 = __toESM(require("os"));
|
|
2373
2485
|
|
|
2374
2486
|
// ../../node_modules/@clack/prompts/dist/index.mjs
|
|
2375
2487
|
var dist_exports = {};
|
|
@@ -4283,17 +4395,17 @@ ${c2}
|
|
|
4283
4395
|
|
|
4284
4396
|
// src/services/claude-installer.ts
|
|
4285
4397
|
function probeInstallDirs() {
|
|
4286
|
-
const home =
|
|
4398
|
+
const home = os5.homedir();
|
|
4287
4399
|
if (process.platform === "win32") {
|
|
4288
4400
|
return [
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4401
|
+
path6.join(home, ".claude", "local"),
|
|
4402
|
+
path6.join(home, "AppData", "Local", "AnthropicClaude"),
|
|
4403
|
+
path6.join(home, "AppData", "Local", "Programs", "AnthropicClaude")
|
|
4292
4404
|
];
|
|
4293
4405
|
}
|
|
4294
4406
|
return [
|
|
4295
|
-
|
|
4296
|
-
|
|
4407
|
+
path6.join(home, ".local", "bin"),
|
|
4408
|
+
path6.join(home, ".claude", "local"),
|
|
4297
4409
|
"/usr/local/bin"
|
|
4298
4410
|
];
|
|
4299
4411
|
}
|
|
@@ -4302,7 +4414,7 @@ function isAvailable() {
|
|
|
4302
4414
|
}
|
|
4303
4415
|
function augmentPath() {
|
|
4304
4416
|
const dirs = probeInstallDirs();
|
|
4305
|
-
const sep2 =
|
|
4417
|
+
const sep2 = path6.delimiter;
|
|
4306
4418
|
const current = process.env.PATH ?? "";
|
|
4307
4419
|
const existing = new Set(current.split(sep2).filter(Boolean));
|
|
4308
4420
|
const additions = dirs.filter((d3) => !existing.has(d3));
|
|
@@ -4360,12 +4472,12 @@ async function ensureClaudeInstalled() {
|
|
|
4360
4472
|
}
|
|
4361
4473
|
|
|
4362
4474
|
// src/services/claude-resolver.ts
|
|
4363
|
-
var
|
|
4475
|
+
var path7 = __toESM(require("path"));
|
|
4364
4476
|
function buildClaudeLaunch(extraArgs = []) {
|
|
4365
4477
|
const found = findInPath("claude") ?? findInPath("claude-code");
|
|
4366
4478
|
if (!found) return null;
|
|
4367
4479
|
if (process.platform === "win32") {
|
|
4368
|
-
const ext =
|
|
4480
|
+
const ext = path7.extname(found).toLowerCase();
|
|
4369
4481
|
if (ext === ".cmd" || ext === ".bat") {
|
|
4370
4482
|
return { cmd: "cmd.exe", args: ["/c", found, ...extraArgs] };
|
|
4371
4483
|
}
|
|
@@ -4413,11 +4525,13 @@ var ClaudeService = class {
|
|
|
4413
4525
|
}
|
|
4414
4526
|
}
|
|
4415
4527
|
if (process.platform === "win32") {
|
|
4528
|
+
log.trace("claude", `spawn (win32) cmd=${launch.cmd} args=${launch.args.join(" ")}`);
|
|
4416
4529
|
const conpty = WindowsConPtyStrategy.tryCreate(this.strategyOpts);
|
|
4417
4530
|
if (conpty) {
|
|
4418
4531
|
try {
|
|
4419
4532
|
conpty.spawn(launch.cmd, this.opts.cwd, launch.args);
|
|
4420
4533
|
this.strategy = conpty;
|
|
4534
|
+
log.trace("claude", "ConPTY spawn ok");
|
|
4421
4535
|
return;
|
|
4422
4536
|
} catch (err) {
|
|
4423
4537
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -4457,8 +4571,12 @@ var ClaudeService = class {
|
|
|
4457
4571
|
* a fresh event-loop tick, after React has flushed the text into input state.
|
|
4458
4572
|
*/
|
|
4459
4573
|
sendCommand(text) {
|
|
4460
|
-
if (!this.strategy)
|
|
4574
|
+
if (!this.strategy) {
|
|
4575
|
+
log.trace("claude", "sendCommand dropped (no strategy)");
|
|
4576
|
+
return;
|
|
4577
|
+
}
|
|
4461
4578
|
const s = this.strategy;
|
|
4579
|
+
log.trace("claude", `sendCommand text=${text.length}B`);
|
|
4462
4580
|
s.write(text);
|
|
4463
4581
|
setTimeout(() => s.write("\r"), 50);
|
|
4464
4582
|
}
|
|
@@ -4532,12 +4650,12 @@ var https2 = __toESM(require("https"));
|
|
|
4532
4650
|
var http2 = __toESM(require("http"));
|
|
4533
4651
|
|
|
4534
4652
|
// ../../packages/shared/src/protocol/parseChrome.ts
|
|
4535
|
-
var SPINNER_RE = /^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s
|
|
4653
|
+
var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
|
|
4536
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;
|
|
4537
4655
|
var TREE_LINE_RE = /^└\s/;
|
|
4538
|
-
var STATUS_LINE_RE =
|
|
4656
|
+
var STATUS_LINE_RE = /^(?:\+|[🔴🟠🟡🟢🔵🟣🟤⚫⚪🌀💭✨])\s/u;
|
|
4539
4657
|
function isChromeLine(line) {
|
|
4540
|
-
const t2 = line.trim();
|
|
4658
|
+
const t2 = line.replace(/️/g, "").trim();
|
|
4541
4659
|
if (!t2) return false;
|
|
4542
4660
|
if (/^[─━—═─\-]{3,}$/.test(t2)) return true;
|
|
4543
4661
|
if (SPINNER_RE.test(t2)) return true;
|
|
@@ -4562,7 +4680,7 @@ function isChromeLine(line) {
|
|
|
4562
4680
|
return false;
|
|
4563
4681
|
}
|
|
4564
4682
|
function parseChromeLine(line) {
|
|
4565
|
-
const t2 = line.trim();
|
|
4683
|
+
const t2 = line.replace(/️/g, "").trim();
|
|
4566
4684
|
if (!t2) return null;
|
|
4567
4685
|
if (/^[─━—═─\-]{3,}$/.test(t2)) return null;
|
|
4568
4686
|
if (/^[❯>]\s*$/.test(t2)) return null;
|
|
@@ -4966,6 +5084,7 @@ var OutputService = class _OutputService {
|
|
|
4966
5084
|
this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
|
|
4967
5085
|
}
|
|
4968
5086
|
newTurn() {
|
|
5087
|
+
log.trace("outputSvc", "newTurn() \u2014 activating output stream");
|
|
4969
5088
|
this.stopPoll();
|
|
4970
5089
|
this.rawBuffer = "";
|
|
4971
5090
|
this.lastSentContent = "";
|
|
@@ -5003,9 +5122,11 @@ var OutputService = class _OutputService {
|
|
|
5003
5122
|
const printable2 = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
|
|
5004
5123
|
if (printable2.trim()) {
|
|
5005
5124
|
this.terminalTurnPending = true;
|
|
5125
|
+
log.trace("outputSvc", `terminal-turn detected (idle, ${raw.length}B)`);
|
|
5006
5126
|
this.onTerminalTurnDetected?.();
|
|
5007
5127
|
}
|
|
5008
5128
|
}
|
|
5129
|
+
log.trace("outputSvc", `push dropped (inactive, ${raw.length}B)`);
|
|
5009
5130
|
return;
|
|
5010
5131
|
}
|
|
5011
5132
|
this.rawBuffer += raw;
|
|
@@ -5015,6 +5136,10 @@ var OutputService = class _OutputService {
|
|
|
5015
5136
|
this.tryExtractSessionId(printable);
|
|
5016
5137
|
this.tryDetectRateLimit(printable);
|
|
5017
5138
|
}
|
|
5139
|
+
log.trace(
|
|
5140
|
+
"outputSvc",
|
|
5141
|
+
`push +${raw.length}B (buf=${this.rawBuffer.length}B printable=${printable.trim().length})`
|
|
5142
|
+
);
|
|
5018
5143
|
}
|
|
5019
5144
|
/** Extract Claude conversation ID from output text (e.g., from /cost command or session resume) */
|
|
5020
5145
|
tryExtractSessionId(text) {
|
|
@@ -5057,6 +5182,10 @@ var OutputService = class _OutputService {
|
|
|
5057
5182
|
const selector = detectSelector(lines) ?? detectListSelector(lines);
|
|
5058
5183
|
if (selector) {
|
|
5059
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
|
+
);
|
|
5060
5189
|
if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
|
|
5061
5190
|
this.stopPoll();
|
|
5062
5191
|
this.active = false;
|
|
@@ -5067,10 +5196,18 @@ var OutputService = class _OutputService {
|
|
|
5067
5196
|
}
|
|
5068
5197
|
const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
5069
5198
|
if (!content) {
|
|
5199
|
+
log.trace(
|
|
5200
|
+
"outputSvc",
|
|
5201
|
+
`tick empty content (raw=${this.rawBuffer.length}B lines=${lines.length} elapsed=${elapsed}ms)`
|
|
5202
|
+
);
|
|
5070
5203
|
if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) this.finalize();
|
|
5071
5204
|
return;
|
|
5072
5205
|
}
|
|
5073
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
|
+
);
|
|
5074
5211
|
if (idleMs >= _OutputService.IDLE_MS) {
|
|
5075
5212
|
this.finalize();
|
|
5076
5213
|
return;
|
|
@@ -5137,15 +5274,33 @@ var OutputService = class _OutputService {
|
|
|
5137
5274
|
if (this.pluginAuthToken) {
|
|
5138
5275
|
headers["X-Plugin-Auth-Token"] = this.pluginAuthToken;
|
|
5139
5276
|
}
|
|
5277
|
+
log.trace(
|
|
5278
|
+
"outputSvc",
|
|
5279
|
+
`postChunk type=${body.type ?? "(clear)"} done=${body.done === true} bytes=${payload.length}`
|
|
5280
|
+
);
|
|
5140
5281
|
return new Promise((resolve2) => {
|
|
5141
5282
|
const attempt = (attemptsLeft) => {
|
|
5142
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
|
+
}
|
|
5143
5293
|
if (statusCode >= 400) {
|
|
5144
5294
|
process.stderr.write(`[codeam] output API error ${statusCode}: ${resBody}
|
|
5145
5295
|
`);
|
|
5146
5296
|
}
|
|
5147
5297
|
resolve2();
|
|
5148
|
-
}).catch(() => {
|
|
5298
|
+
}).catch((err) => {
|
|
5299
|
+
log.trace(
|
|
5300
|
+
"outputSvc",
|
|
5301
|
+
`postChunk error (retries left=${attemptsLeft})`,
|
|
5302
|
+
err
|
|
5303
|
+
);
|
|
5149
5304
|
if (attemptsLeft > 0) {
|
|
5150
5305
|
const delay = 200 * (maxRetries - attemptsLeft + 1);
|
|
5151
5306
|
setTimeout(() => attempt(attemptsLeft - 1), delay);
|
|
@@ -5204,33 +5359,12 @@ function _sendOutputChunk(url, headers, payload) {
|
|
|
5204
5359
|
}
|
|
5205
5360
|
|
|
5206
5361
|
// src/services/history.service.ts
|
|
5207
|
-
var
|
|
5208
|
-
var
|
|
5209
|
-
var
|
|
5362
|
+
var fs5 = __toESM(require("fs"));
|
|
5363
|
+
var path8 = __toESM(require("path"));
|
|
5364
|
+
var os6 = __toESM(require("os"));
|
|
5210
5365
|
var https3 = __toESM(require("https"));
|
|
5211
5366
|
var http3 = __toESM(require("http"));
|
|
5212
5367
|
var import_zod = require("zod");
|
|
5213
|
-
|
|
5214
|
-
// src/services/logger.ts
|
|
5215
|
-
var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
5216
|
-
function currentLevel() {
|
|
5217
|
-
const raw = (process.env.CODEAM_LOG ?? "error").toLowerCase();
|
|
5218
|
-
return LEVELS[raw] ?? LEVELS.error;
|
|
5219
|
-
}
|
|
5220
|
-
function emit(level, tag, msg, err) {
|
|
5221
|
-
if (LEVELS[level] > currentLevel()) return;
|
|
5222
|
-
const detail = err instanceof Error ? `: ${err.message}` : err !== void 0 ? `: ${String(err)}` : "";
|
|
5223
|
-
process.stderr.write(`[codeam:${level}] ${tag} \u2014 ${msg}${detail}
|
|
5224
|
-
`);
|
|
5225
|
-
}
|
|
5226
|
-
var log = {
|
|
5227
|
-
error: (tag, msg, err) => emit("error", tag, msg, err),
|
|
5228
|
-
warn: (tag, msg, err) => emit("warn", tag, msg, err),
|
|
5229
|
-
info: (tag, msg, err) => emit("info", tag, msg, err),
|
|
5230
|
-
debug: (tag, msg, err) => emit("debug", tag, msg, err)
|
|
5231
|
-
};
|
|
5232
|
-
|
|
5233
|
-
// src/services/history.service.ts
|
|
5234
5368
|
var historyRecordSchema = import_zod.z.object({
|
|
5235
5369
|
type: import_zod.z.string().optional(),
|
|
5236
5370
|
timestamp: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
|
|
@@ -5243,7 +5377,25 @@ var historyRecordSchema = import_zod.z.object({
|
|
|
5243
5377
|
}).passthrough();
|
|
5244
5378
|
var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
5245
5379
|
function encodeCwd(cwd) {
|
|
5246
|
-
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;
|
|
5247
5399
|
}
|
|
5248
5400
|
function extractText(content) {
|
|
5249
5401
|
if (typeof content === "string") return content;
|
|
@@ -5257,7 +5409,7 @@ function parseJsonl(filePath) {
|
|
|
5257
5409
|
const messages = [];
|
|
5258
5410
|
let raw;
|
|
5259
5411
|
try {
|
|
5260
|
-
raw =
|
|
5412
|
+
raw = fs5.readFileSync(filePath, "utf8");
|
|
5261
5413
|
} catch (err) {
|
|
5262
5414
|
if (err.code !== "ENOENT") {
|
|
5263
5415
|
log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
|
|
@@ -5361,7 +5513,7 @@ var HistoryService = class {
|
|
|
5361
5513
|
return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
|
|
5362
5514
|
}
|
|
5363
5515
|
get projectDir() {
|
|
5364
|
-
return
|
|
5516
|
+
return findProjectDir(this.cwd) ?? path8.join(os6.homedir(), ".claude", "projects", encodeCwd(this.cwd));
|
|
5365
5517
|
}
|
|
5366
5518
|
/** Set the current Claude conversation ID (extracted from /cost command or session start) */
|
|
5367
5519
|
setCurrentConversationId(id) {
|
|
@@ -5373,7 +5525,7 @@ var HistoryService = class {
|
|
|
5373
5525
|
/** Return the current message count in the active conversation. */
|
|
5374
5526
|
getCurrentMessageCount() {
|
|
5375
5527
|
if (!this.currentConversationId) return 0;
|
|
5376
|
-
const filePath =
|
|
5528
|
+
const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
5377
5529
|
return parseJsonl(filePath).length;
|
|
5378
5530
|
}
|
|
5379
5531
|
/**
|
|
@@ -5384,7 +5536,7 @@ var HistoryService = class {
|
|
|
5384
5536
|
const deadline = Date.now() + timeoutMs;
|
|
5385
5537
|
while (Date.now() < deadline) {
|
|
5386
5538
|
if (!this.currentConversationId) return null;
|
|
5387
|
-
const filePath =
|
|
5539
|
+
const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
5388
5540
|
const messages = parseJsonl(filePath);
|
|
5389
5541
|
if (messages.length > previousCount) {
|
|
5390
5542
|
for (let i = messages.length - 1; i >= previousCount; i--) {
|
|
@@ -5399,15 +5551,15 @@ var HistoryService = class {
|
|
|
5399
5551
|
detectCurrentConversation() {
|
|
5400
5552
|
const dir = this.projectDir;
|
|
5401
5553
|
try {
|
|
5402
|
-
const files =
|
|
5554
|
+
const files = fs5.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5403
5555
|
try {
|
|
5404
|
-
return { name: e.name, mtime:
|
|
5556
|
+
return { name: e.name, mtime: fs5.statSync(path8.join(dir, e.name)).mtimeMs };
|
|
5405
5557
|
} catch {
|
|
5406
5558
|
return { name: e.name, mtime: 0 };
|
|
5407
5559
|
}
|
|
5408
5560
|
}).sort((a, b) => b.mtime - a.mtime);
|
|
5409
5561
|
if (files.length > 0) {
|
|
5410
|
-
this.currentConversationId =
|
|
5562
|
+
this.currentConversationId = path8.basename(files[0].name, ".jsonl");
|
|
5411
5563
|
}
|
|
5412
5564
|
} catch {
|
|
5413
5565
|
}
|
|
@@ -5439,13 +5591,13 @@ var HistoryService = class {
|
|
|
5439
5591
|
const dir = this.projectDir;
|
|
5440
5592
|
let entries;
|
|
5441
5593
|
try {
|
|
5442
|
-
entries =
|
|
5594
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
5443
5595
|
} catch {
|
|
5444
5596
|
return null;
|
|
5445
5597
|
}
|
|
5446
5598
|
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
5447
5599
|
try {
|
|
5448
|
-
return { name: e.name, mtime:
|
|
5600
|
+
return { name: e.name, mtime: fs5.statSync(path8.join(dir, e.name)).mtimeMs };
|
|
5449
5601
|
} catch {
|
|
5450
5602
|
return { name: e.name, mtime: 0 };
|
|
5451
5603
|
}
|
|
@@ -5453,12 +5605,12 @@ var HistoryService = class {
|
|
|
5453
5605
|
if (files.length === 0) return null;
|
|
5454
5606
|
const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
|
|
5455
5607
|
if (!files.some((f) => f.name === targetFile)) return null;
|
|
5456
|
-
return this.extractUsageFromFile(
|
|
5608
|
+
return this.extractUsageFromFile(path8.join(dir, targetFile));
|
|
5457
5609
|
}
|
|
5458
5610
|
extractUsageFromFile(filePath) {
|
|
5459
5611
|
let raw;
|
|
5460
5612
|
try {
|
|
5461
|
-
raw =
|
|
5613
|
+
raw = fs5.readFileSync(filePath, "utf8");
|
|
5462
5614
|
} catch {
|
|
5463
5615
|
return null;
|
|
5464
5616
|
}
|
|
@@ -5503,9 +5655,9 @@ var HistoryService = class {
|
|
|
5503
5655
|
let totalCost = 0;
|
|
5504
5656
|
let files;
|
|
5505
5657
|
try {
|
|
5506
|
-
files =
|
|
5658
|
+
files = fs5.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
|
|
5507
5659
|
try {
|
|
5508
|
-
return
|
|
5660
|
+
return fs5.statSync(path8.join(projectDir, f)).mtimeMs >= monthStartMs;
|
|
5509
5661
|
} catch {
|
|
5510
5662
|
return false;
|
|
5511
5663
|
}
|
|
@@ -5516,7 +5668,7 @@ var HistoryService = class {
|
|
|
5516
5668
|
for (const file of files) {
|
|
5517
5669
|
let raw;
|
|
5518
5670
|
try {
|
|
5519
|
-
raw =
|
|
5671
|
+
raw = fs5.readFileSync(path8.join(projectDir, file), "utf8");
|
|
5520
5672
|
} catch {
|
|
5521
5673
|
continue;
|
|
5522
5674
|
}
|
|
@@ -5551,23 +5703,23 @@ var HistoryService = class {
|
|
|
5551
5703
|
const dir = this.projectDir;
|
|
5552
5704
|
let entries;
|
|
5553
5705
|
try {
|
|
5554
|
-
entries =
|
|
5706
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
5555
5707
|
} catch {
|
|
5556
5708
|
return;
|
|
5557
5709
|
}
|
|
5558
5710
|
const sessions2 = [];
|
|
5559
5711
|
for (const entry of entries) {
|
|
5560
5712
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
5561
|
-
const id =
|
|
5562
|
-
const filePath =
|
|
5713
|
+
const id = path8.basename(entry.name, ".jsonl");
|
|
5714
|
+
const filePath = path8.join(dir, entry.name);
|
|
5563
5715
|
let mtime = Date.now();
|
|
5564
5716
|
try {
|
|
5565
|
-
mtime =
|
|
5717
|
+
mtime = fs5.statSync(filePath).mtimeMs;
|
|
5566
5718
|
} catch {
|
|
5567
5719
|
}
|
|
5568
5720
|
let summary = "";
|
|
5569
5721
|
try {
|
|
5570
|
-
const raw =
|
|
5722
|
+
const raw = fs5.readFileSync(filePath, "utf8");
|
|
5571
5723
|
for (const line of raw.split("\n")) {
|
|
5572
5724
|
if (!line.trim()) continue;
|
|
5573
5725
|
try {
|
|
@@ -5600,7 +5752,7 @@ var HistoryService = class {
|
|
|
5600
5752
|
* showing an empty conversation.
|
|
5601
5753
|
*/
|
|
5602
5754
|
async loadConversation(sessionId) {
|
|
5603
|
-
const filePath =
|
|
5755
|
+
const filePath = path8.join(this.projectDir, `${sessionId}.jsonl`);
|
|
5604
5756
|
const messages = parseJsonl(filePath);
|
|
5605
5757
|
if (messages.length === 0) return;
|
|
5606
5758
|
const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
|
|
@@ -5655,8 +5807,8 @@ function parsePayload(schema, raw) {
|
|
|
5655
5807
|
}
|
|
5656
5808
|
|
|
5657
5809
|
// src/services/file-ops.service.ts
|
|
5658
|
-
var
|
|
5659
|
-
var
|
|
5810
|
+
var fs6 = __toESM(require("fs/promises"));
|
|
5811
|
+
var path9 = __toESM(require("path"));
|
|
5660
5812
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
5661
5813
|
var MAX_WALK_DEPTH = 6;
|
|
5662
5814
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -5691,12 +5843,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
5691
5843
|
"__pycache__"
|
|
5692
5844
|
]);
|
|
5693
5845
|
function isUnder(parent, candidate) {
|
|
5694
|
-
const rel =
|
|
5695
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
5846
|
+
const rel = path9.relative(parent, candidate);
|
|
5847
|
+
return rel === "" || !rel.startsWith("..") && !path9.isAbsolute(rel);
|
|
5696
5848
|
}
|
|
5697
5849
|
async function isExistingFile(absPath) {
|
|
5698
5850
|
try {
|
|
5699
|
-
const stat3 = await
|
|
5851
|
+
const stat3 = await fs6.stat(absPath);
|
|
5700
5852
|
return stat3.isFile();
|
|
5701
5853
|
} catch {
|
|
5702
5854
|
return false;
|
|
@@ -5709,13 +5861,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
5709
5861
|
ctx.visited++;
|
|
5710
5862
|
let entries = [];
|
|
5711
5863
|
try {
|
|
5712
|
-
entries = await
|
|
5864
|
+
entries = await fs6.readdir(dir, { withFileTypes: true });
|
|
5713
5865
|
} catch {
|
|
5714
5866
|
return;
|
|
5715
5867
|
}
|
|
5716
5868
|
for (const e of entries) {
|
|
5717
5869
|
if (!e.isFile()) continue;
|
|
5718
|
-
const full =
|
|
5870
|
+
const full = path9.join(dir, e.name);
|
|
5719
5871
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
5720
5872
|
ctx.matches.push(full);
|
|
5721
5873
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -5725,21 +5877,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
5725
5877
|
if (!e.isDirectory()) continue;
|
|
5726
5878
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
5727
5879
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
5728
|
-
await walkForSuffix(
|
|
5880
|
+
await walkForSuffix(path9.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
5729
5881
|
if (ctx.matches.length >= ctx.cap) return;
|
|
5730
5882
|
}
|
|
5731
5883
|
}
|
|
5732
5884
|
async function findFile(rawPath) {
|
|
5733
5885
|
const cwd = process.cwd();
|
|
5734
|
-
if (
|
|
5735
|
-
const abs =
|
|
5886
|
+
if (path9.isAbsolute(rawPath)) {
|
|
5887
|
+
const abs = path9.normalize(rawPath);
|
|
5736
5888
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
5737
5889
|
}
|
|
5738
|
-
const direct =
|
|
5890
|
+
const direct = path9.resolve(cwd, rawPath);
|
|
5739
5891
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
5740
|
-
const normalized =
|
|
5892
|
+
const normalized = path9.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
5741
5893
|
const needles = [
|
|
5742
|
-
`${
|
|
5894
|
+
`${path9.sep}${normalized}`,
|
|
5743
5895
|
`/${normalized}`
|
|
5744
5896
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
5745
5897
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -5753,7 +5905,7 @@ async function findWriteTarget(rawPath) {
|
|
|
5753
5905
|
const found = await findFile(rawPath);
|
|
5754
5906
|
if (found) return found;
|
|
5755
5907
|
const cwd = process.cwd();
|
|
5756
|
-
const fallback =
|
|
5908
|
+
const fallback = path9.isAbsolute(rawPath) ? path9.normalize(rawPath) : path9.resolve(cwd, rawPath);
|
|
5757
5909
|
if (!isUnder(cwd, fallback)) return null;
|
|
5758
5910
|
return fallback;
|
|
5759
5911
|
}
|
|
@@ -5770,11 +5922,11 @@ async function readProjectFile(rawPath) {
|
|
|
5770
5922
|
if (!abs) {
|
|
5771
5923
|
return { error: `File not found in the project tree: ${rawPath}` };
|
|
5772
5924
|
}
|
|
5773
|
-
const stat3 = await
|
|
5925
|
+
const stat3 = await fs6.stat(abs);
|
|
5774
5926
|
if (stat3.size > MAX_FILE_BYTES) {
|
|
5775
5927
|
return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
5776
5928
|
}
|
|
5777
|
-
const buf = await
|
|
5929
|
+
const buf = await fs6.readFile(abs);
|
|
5778
5930
|
if (looksBinary(buf)) {
|
|
5779
5931
|
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
5780
5932
|
}
|
|
@@ -5793,8 +5945,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
5793
5945
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
5794
5946
|
return { error: "Content too large." };
|
|
5795
5947
|
}
|
|
5796
|
-
await
|
|
5797
|
-
await
|
|
5948
|
+
await fs6.mkdir(path9.dirname(abs), { recursive: true });
|
|
5949
|
+
await fs6.writeFile(abs, content, "utf-8");
|
|
5798
5950
|
return { ok: true };
|
|
5799
5951
|
} catch (e) {
|
|
5800
5952
|
const msg = e instanceof Error ? e.message : "Write failed";
|
|
@@ -5805,8 +5957,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
5805
5957
|
// src/services/project-ops.service.ts
|
|
5806
5958
|
var import_child_process4 = require("child_process");
|
|
5807
5959
|
var import_util = require("util");
|
|
5808
|
-
var
|
|
5809
|
-
var
|
|
5960
|
+
var fs7 = __toESM(require("fs/promises"));
|
|
5961
|
+
var path10 = __toESM(require("path"));
|
|
5810
5962
|
var execFileP = (0, import_util.promisify)(import_child_process4.execFile);
|
|
5811
5963
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
5812
5964
|
"node_modules",
|
|
@@ -5854,7 +6006,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
5854
6006
|
}
|
|
5855
6007
|
let entries = [];
|
|
5856
6008
|
try {
|
|
5857
|
-
entries = await
|
|
6009
|
+
entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
5858
6010
|
} catch {
|
|
5859
6011
|
return;
|
|
5860
6012
|
}
|
|
@@ -5864,18 +6016,18 @@ async function listProjectFiles(opts = {}) {
|
|
|
5864
6016
|
return;
|
|
5865
6017
|
}
|
|
5866
6018
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
5867
|
-
const full =
|
|
6019
|
+
const full = path10.join(dir, e.name);
|
|
5868
6020
|
if (e.isDirectory()) {
|
|
5869
6021
|
if (depth >= 12) continue;
|
|
5870
6022
|
await walk(full, depth + 1);
|
|
5871
6023
|
} else if (e.isFile()) {
|
|
5872
|
-
const rel =
|
|
6024
|
+
const rel = path10.relative(root, full);
|
|
5873
6025
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
5874
6026
|
continue;
|
|
5875
6027
|
}
|
|
5876
6028
|
let size = 0;
|
|
5877
6029
|
try {
|
|
5878
|
-
const st3 = await
|
|
6030
|
+
const st3 = await fs7.stat(full);
|
|
5879
6031
|
size = st3.size;
|
|
5880
6032
|
} catch {
|
|
5881
6033
|
}
|
|
@@ -5977,8 +6129,8 @@ async function gitStatus(cwd) {
|
|
|
5977
6129
|
let hasMergeInProgress = false;
|
|
5978
6130
|
try {
|
|
5979
6131
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
5980
|
-
const mergeHead =
|
|
5981
|
-
await
|
|
6132
|
+
const mergeHead = path10.isAbsolute(gitDir) ? path10.join(gitDir, "MERGE_HEAD") : path10.join(root, gitDir, "MERGE_HEAD");
|
|
6133
|
+
await fs7.access(mergeHead);
|
|
5982
6134
|
hasMergeInProgress = true;
|
|
5983
6135
|
} catch {
|
|
5984
6136
|
}
|
|
@@ -6055,8 +6207,8 @@ async function gitResolve(file, side, cwd) {
|
|
|
6055
6207
|
function saveFilesTemp(files) {
|
|
6056
6208
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
6057
6209
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
6058
|
-
const tmpPath =
|
|
6059
|
-
|
|
6210
|
+
const tmpPath = path11.join(os7.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
|
|
6211
|
+
fs8.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
6060
6212
|
return tmpPath;
|
|
6061
6213
|
});
|
|
6062
6214
|
}
|
|
@@ -6129,8 +6281,8 @@ try:
|
|
|
6129
6281
|
sys.exit((st>>8)&0xFF)
|
|
6130
6282
|
except Exception:sys.exit(0)
|
|
6131
6283
|
`;
|
|
6132
|
-
const helperPath =
|
|
6133
|
-
|
|
6284
|
+
const helperPath = path11.join(os7.tmpdir(), "codeam-quota-helper.py");
|
|
6285
|
+
fs8.writeFileSync(helperPath, helperScript, { mode: 420 });
|
|
6134
6286
|
const python = findInPath("python3") ?? findInPath("python");
|
|
6135
6287
|
if (!python) {
|
|
6136
6288
|
quotaFetchInProgress = false;
|
|
@@ -6162,7 +6314,7 @@ except Exception:sys.exit(0)
|
|
|
6162
6314
|
} catch {
|
|
6163
6315
|
}
|
|
6164
6316
|
try {
|
|
6165
|
-
|
|
6317
|
+
fs8.unlinkSync(helperPath);
|
|
6166
6318
|
} catch {
|
|
6167
6319
|
}
|
|
6168
6320
|
quotaFetchInProgress = false;
|
|
@@ -6212,7 +6364,7 @@ except Exception:sys.exit(0)
|
|
|
6212
6364
|
setTimeout(() => {
|
|
6213
6365
|
for (const p2 of paths) {
|
|
6214
6366
|
try {
|
|
6215
|
-
|
|
6367
|
+
fs8.unlinkSync(p2);
|
|
6216
6368
|
} catch {
|
|
6217
6369
|
}
|
|
6218
6370
|
}
|
|
@@ -6255,6 +6407,25 @@ except Exception:sys.exit(0)
|
|
|
6255
6407
|
}
|
|
6256
6408
|
break;
|
|
6257
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
|
+
}
|
|
6258
6429
|
case "shutdown_session": {
|
|
6259
6430
|
try {
|
|
6260
6431
|
await relay.sendResult(cmd.id, "success", { ok: true });
|
|
@@ -6435,7 +6606,7 @@ except Exception:sys.exit(0)
|
|
|
6435
6606
|
setTimeout(() => {
|
|
6436
6607
|
for (const p2 of paths) {
|
|
6437
6608
|
try {
|
|
6438
|
-
|
|
6609
|
+
fs8.unlinkSync(p2);
|
|
6439
6610
|
} catch {
|
|
6440
6611
|
}
|
|
6441
6612
|
}
|
|
@@ -6730,9 +6901,9 @@ async function logout() {
|
|
|
6730
6901
|
|
|
6731
6902
|
// src/commands/deploy.ts
|
|
6732
6903
|
var import_child_process10 = require("child_process");
|
|
6733
|
-
var
|
|
6734
|
-
var
|
|
6735
|
-
var
|
|
6904
|
+
var fs9 = __toESM(require("fs"));
|
|
6905
|
+
var os8 = __toESM(require("os"));
|
|
6906
|
+
var path16 = __toESM(require("path"));
|
|
6736
6907
|
var import_util6 = require("util");
|
|
6737
6908
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
6738
6909
|
|
|
@@ -6740,7 +6911,7 @@ var import_picocolors9 = __toESM(require("picocolors"));
|
|
|
6740
6911
|
var import_child_process6 = require("child_process");
|
|
6741
6912
|
var import_util2 = require("util");
|
|
6742
6913
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
6743
|
-
var
|
|
6914
|
+
var path12 = __toESM(require("path"));
|
|
6744
6915
|
var execFileP2 = (0, import_util2.promisify)(import_child_process6.execFile);
|
|
6745
6916
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
6746
6917
|
function resetStdinForChild() {
|
|
@@ -7229,7 +7400,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7229
7400
|
});
|
|
7230
7401
|
}
|
|
7231
7402
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7232
|
-
const remoteDir =
|
|
7403
|
+
const remoteDir = path12.posix.dirname(remotePath);
|
|
7233
7404
|
const parts = [
|
|
7234
7405
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
7235
7406
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -7299,7 +7470,7 @@ function shellQuote(s) {
|
|
|
7299
7470
|
// src/services/providers/gitpod.ts
|
|
7300
7471
|
var import_child_process7 = require("child_process");
|
|
7301
7472
|
var import_util3 = require("util");
|
|
7302
|
-
var
|
|
7473
|
+
var path13 = __toESM(require("path"));
|
|
7303
7474
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
7304
7475
|
var execFileP3 = (0, import_util3.promisify)(import_child_process7.execFile);
|
|
7305
7476
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
@@ -7539,7 +7710,7 @@ var GitpodProvider = class {
|
|
|
7539
7710
|
});
|
|
7540
7711
|
}
|
|
7541
7712
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7542
|
-
const remoteDir =
|
|
7713
|
+
const remoteDir = path13.posix.dirname(remotePath);
|
|
7543
7714
|
const parts = [
|
|
7544
7715
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
7545
7716
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -7575,7 +7746,7 @@ function shellQuote2(s) {
|
|
|
7575
7746
|
// src/services/providers/gitlab-workspaces.ts
|
|
7576
7747
|
var import_child_process8 = require("child_process");
|
|
7577
7748
|
var import_util4 = require("util");
|
|
7578
|
-
var
|
|
7749
|
+
var path14 = __toESM(require("path"));
|
|
7579
7750
|
var execFileP4 = (0, import_util4.promisify)(import_child_process8.execFile);
|
|
7580
7751
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
7581
7752
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
@@ -7835,7 +8006,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
7835
8006
|
}
|
|
7836
8007
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
7837
8008
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
7838
|
-
const remoteDir =
|
|
8009
|
+
const remoteDir = path14.posix.dirname(remotePath);
|
|
7839
8010
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
7840
8011
|
if (options.mode != null) {
|
|
7841
8012
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
@@ -7903,7 +8074,7 @@ function shellQuote3(s) {
|
|
|
7903
8074
|
// src/services/providers/railway.ts
|
|
7904
8075
|
var import_child_process9 = require("child_process");
|
|
7905
8076
|
var import_util5 = require("util");
|
|
7906
|
-
var
|
|
8077
|
+
var path15 = __toESM(require("path"));
|
|
7907
8078
|
var execFileP5 = (0, import_util5.promisify)(import_child_process9.execFile);
|
|
7908
8079
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
7909
8080
|
function resetStdinForChild4() {
|
|
@@ -8139,7 +8310,7 @@ var RailwayProvider = class {
|
|
|
8139
8310
|
if (!projectId || !serviceId) {
|
|
8140
8311
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
8141
8312
|
}
|
|
8142
|
-
const remoteDir =
|
|
8313
|
+
const remoteDir = path15.posix.dirname(remotePath);
|
|
8143
8314
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
8144
8315
|
if (options.mode != null) {
|
|
8145
8316
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
@@ -8331,7 +8502,7 @@ async function deploy() {
|
|
|
8331
8502
|
process.exit(1);
|
|
8332
8503
|
}
|
|
8333
8504
|
}
|
|
8334
|
-
const localClaudeDir =
|
|
8505
|
+
const localClaudeDir = path16.join(os8.homedir(), ".claude");
|
|
8335
8506
|
const localCredsKind = await detectLocalClaudeCredentials(localClaudeDir);
|
|
8336
8507
|
let bridged = "none";
|
|
8337
8508
|
if (localCredsKind !== "none") {
|
|
@@ -8375,7 +8546,7 @@ async function deploy() {
|
|
|
8375
8546
|
process.exit(1);
|
|
8376
8547
|
}
|
|
8377
8548
|
claudeStep.stop("\u2713 Claude CLI installed");
|
|
8378
|
-
const haveLocalClaude =
|
|
8549
|
+
const haveLocalClaude = fs9.existsSync(localClaudeDir) && fs9.statSync(localClaudeDir).isDirectory();
|
|
8379
8550
|
if (haveLocalClaude) {
|
|
8380
8551
|
const copyStep = fe();
|
|
8381
8552
|
copyStep.start("Copying local Claude config to workspace\u2026");
|
|
@@ -8429,10 +8600,10 @@ async function deploy() {
|
|
|
8429
8600
|
}
|
|
8430
8601
|
}
|
|
8431
8602
|
if (bridged !== "none") {
|
|
8432
|
-
const localClaudeJson =
|
|
8433
|
-
if (
|
|
8603
|
+
const localClaudeJson = path16.join(os8.homedir(), ".claude.json");
|
|
8604
|
+
if (fs9.existsSync(localClaudeJson)) {
|
|
8434
8605
|
try {
|
|
8435
|
-
const contents =
|
|
8606
|
+
const contents = fs9.readFileSync(localClaudeJson);
|
|
8436
8607
|
await provider.uploadFile(
|
|
8437
8608
|
workspace.id,
|
|
8438
8609
|
"/home/codespace/.claude.json",
|
|
@@ -8622,7 +8793,7 @@ async function runRemoteClaudeLogin(provider, workspaceId) {
|
|
|
8622
8793
|
}
|
|
8623
8794
|
}
|
|
8624
8795
|
async function detectLocalClaudeCredentials(localClaudeDir) {
|
|
8625
|
-
if (
|
|
8796
|
+
if (fs9.existsSync(path16.join(localClaudeDir, ".credentials.json"))) {
|
|
8626
8797
|
return "flat-file";
|
|
8627
8798
|
}
|
|
8628
8799
|
if (process.platform === "darwin") {
|
|
@@ -8655,8 +8826,8 @@ async function verifyClaudeAuth(provider, workspaceId) {
|
|
|
8655
8826
|
}
|
|
8656
8827
|
}
|
|
8657
8828
|
async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
|
|
8658
|
-
const fileBased =
|
|
8659
|
-
if (
|
|
8829
|
+
const fileBased = path16.join(localClaudeDir, ".credentials.json");
|
|
8830
|
+
if (fs9.existsSync(fileBased)) return "flat-file";
|
|
8660
8831
|
if (process.platform === "darwin") {
|
|
8661
8832
|
try {
|
|
8662
8833
|
const { stdout } = await execFileP6(
|
|
@@ -8861,10 +9032,172 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
8861
9032
|
}
|
|
8862
9033
|
}
|
|
8863
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
|
+
|
|
8864
9187
|
// src/index.ts
|
|
8865
9188
|
var [, , command, ...args] = process.argv;
|
|
8866
9189
|
async function main() {
|
|
9190
|
+
const isMetaCommand = command === "--version" || command === "-v" || command === "version" || command === "--help" || command === "-h" || command === "help";
|
|
9191
|
+
if (!isMetaCommand) checkForUpdates();
|
|
8867
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();
|
|
8868
9201
|
case "pair":
|
|
8869
9202
|
return pair();
|
|
8870
9203
|
case "sessions":
|