codeam-cli 2.12.17 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/index.js +340 -38
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to `codeam-cli` are documented here.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.13.0] — 2026-05-16
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Forward CODEAM_VERCEL_BYPASS as x-vercel-protection-bypass
|
|
12
|
+
|
|
13
|
+
### CI
|
|
14
|
+
|
|
15
|
+
- **deps:** Cap @types/node major in CLI dependabot config
|
|
16
|
+
|
|
17
|
+
### Chore
|
|
18
|
+
|
|
19
|
+
- **deps:** Bump gradle-wrapper in /apps/jetbrains-plugin (#32)
|
|
20
|
+
- **deps:** Bump org.jetbrains.intellij.platform (#29)
|
|
21
|
+
|
|
22
|
+
## [2.12.17] — 2026-05-16
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **cli:** Submit multi-line composer prompts; document new commands + env vars
|
|
27
|
+
- **vsc-plugin:** Replace placeholder activity bar icon with branded </> mark
|
|
28
|
+
|
|
7
29
|
## [2.12.16] — 2026-05-15
|
|
8
30
|
|
|
9
31
|
### Documentation
|
package/dist/index.js
CHANGED
|
@@ -1018,7 +1018,7 @@ var require_unixTerminal = __commonJS({
|
|
|
1018
1018
|
var terminal_1 = require_terminal();
|
|
1019
1019
|
var utils_1 = require_utils();
|
|
1020
1020
|
var native = utils_1.loadNativeModule("pty");
|
|
1021
|
-
var
|
|
1021
|
+
var pty2 = native.module;
|
|
1022
1022
|
var helperPath = native.dir + "/spawn-helper";
|
|
1023
1023
|
helperPath = path23.resolve(__dirname, helperPath);
|
|
1024
1024
|
helperPath = helperPath.replace("app.asar", "app.asar.unpacked");
|
|
@@ -1076,7 +1076,7 @@ var require_unixTerminal = __commonJS({
|
|
|
1076
1076
|
}
|
|
1077
1077
|
_this.emit("exit", code, signal);
|
|
1078
1078
|
};
|
|
1079
|
-
var term =
|
|
1079
|
+
var term = pty2.fork(file, args2, parsedEnv, cwd, _this._cols, _this._rows, uid, gid, encoding === "utf8", helperPath, onexit);
|
|
1080
1080
|
_this._socket = new tty.ReadStream(term.fd);
|
|
1081
1081
|
if (encoding !== null) {
|
|
1082
1082
|
_this._socket.setEncoding(encoding);
|
|
@@ -1164,7 +1164,7 @@ var require_unixTerminal = __commonJS({
|
|
|
1164
1164
|
var cols = opt.cols || terminal_1.DEFAULT_COLS;
|
|
1165
1165
|
var rows = opt.rows || terminal_1.DEFAULT_ROWS;
|
|
1166
1166
|
var encoding = opt.encoding === void 0 ? "utf8" : opt.encoding;
|
|
1167
|
-
var term =
|
|
1167
|
+
var term = pty2.open(cols, rows);
|
|
1168
1168
|
self._master = new tty.ReadStream(term.master);
|
|
1169
1169
|
if (encoding !== null) {
|
|
1170
1170
|
self._master.setEncoding(encoding);
|
|
@@ -1215,10 +1215,10 @@ var require_unixTerminal = __commonJS({
|
|
|
1215
1215
|
*/
|
|
1216
1216
|
get: function() {
|
|
1217
1217
|
if (process.platform === "darwin") {
|
|
1218
|
-
var title =
|
|
1218
|
+
var title = pty2.process(this._fd);
|
|
1219
1219
|
return title !== "kernel_task" ? title : this._file;
|
|
1220
1220
|
}
|
|
1221
|
-
return
|
|
1221
|
+
return pty2.process(this._fd, this._pty) || this._file;
|
|
1222
1222
|
},
|
|
1223
1223
|
enumerable: false,
|
|
1224
1224
|
configurable: true
|
|
@@ -1227,7 +1227,7 @@ var require_unixTerminal = __commonJS({
|
|
|
1227
1227
|
if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) {
|
|
1228
1228
|
throw new Error("resizing must be done using positive cols and rows");
|
|
1229
1229
|
}
|
|
1230
|
-
|
|
1230
|
+
pty2.resize(this._fd, cols, rows);
|
|
1231
1231
|
this._cols = cols;
|
|
1232
1232
|
this._rows = rows;
|
|
1233
1233
|
};
|
|
@@ -1313,10 +1313,10 @@ var require_lib = __commonJS({
|
|
|
1313
1313
|
} else {
|
|
1314
1314
|
terminalCtor = require_unixTerminal().UnixTerminal;
|
|
1315
1315
|
}
|
|
1316
|
-
function
|
|
1316
|
+
function spawn12(file, args2, opt) {
|
|
1317
1317
|
return new terminalCtor(file, args2, opt);
|
|
1318
1318
|
}
|
|
1319
|
-
exports2.spawn =
|
|
1319
|
+
exports2.spawn = spawn12;
|
|
1320
1320
|
function fork(file, args2, opt) {
|
|
1321
1321
|
return new terminalCtor(file, args2, opt);
|
|
1322
1322
|
}
|
|
@@ -1689,7 +1689,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
1689
1689
|
// package.json
|
|
1690
1690
|
var package_default = {
|
|
1691
1691
|
name: "codeam-cli",
|
|
1692
|
-
version: "2.
|
|
1692
|
+
version: "2.14.0",
|
|
1693
1693
|
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.",
|
|
1694
1694
|
type: "commonjs",
|
|
1695
1695
|
main: "dist/index.js",
|
|
@@ -1817,6 +1817,12 @@ var https = __toESM(require("https"));
|
|
|
1817
1817
|
var http = __toESM(require("http"));
|
|
1818
1818
|
var os2 = __toESM(require("os"));
|
|
1819
1819
|
|
|
1820
|
+
// src/lib/backend-headers.ts
|
|
1821
|
+
function vercelBypassHeader() {
|
|
1822
|
+
const token = process.env.CODEAM_VERCEL_BYPASS;
|
|
1823
|
+
return token ? { "x-vercel-protection-bypass": token } : {};
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1820
1826
|
// src/lib/poll-delay.ts
|
|
1821
1827
|
var MAX_DELAY_MS = 3e4;
|
|
1822
1828
|
function computePollDelay({ baseMs, failures }) {
|
|
@@ -1915,7 +1921,8 @@ async function _postJson(url, body) {
|
|
|
1915
1921
|
method: "POST",
|
|
1916
1922
|
headers: {
|
|
1917
1923
|
"Content-Type": "application/json",
|
|
1918
|
-
"Content-Length": Buffer.byteLength(data)
|
|
1924
|
+
"Content-Length": Buffer.byteLength(data),
|
|
1925
|
+
...vercelBypassHeader()
|
|
1919
1926
|
},
|
|
1920
1927
|
timeout: 1e4
|
|
1921
1928
|
},
|
|
@@ -1957,6 +1964,7 @@ async function _getJson(url) {
|
|
|
1957
1964
|
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
1958
1965
|
path: u2.pathname + u2.search,
|
|
1959
1966
|
method: "GET",
|
|
1967
|
+
headers: { ...vercelBypassHeader() },
|
|
1960
1968
|
timeout: 1e4
|
|
1961
1969
|
},
|
|
1962
1970
|
(res) => {
|
|
@@ -2111,7 +2119,7 @@ var CommandRelayService = class {
|
|
|
2111
2119
|
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
2112
2120
|
path: `${url.pathname}${url.search}`,
|
|
2113
2121
|
method: "GET",
|
|
2114
|
-
headers: { Accept: "text/event-stream", "Cache-Control": "no-cache" },
|
|
2122
|
+
headers: { Accept: "text/event-stream", "Cache-Control": "no-cache", ...vercelBypassHeader() },
|
|
2115
2123
|
timeout: 35e3
|
|
2116
2124
|
},
|
|
2117
2125
|
(res) => {
|
|
@@ -6468,7 +6476,8 @@ var ChunkEmitter = class {
|
|
|
6468
6476
|
// it can route legacy translations / 426 us when we're
|
|
6469
6477
|
// too far behind. Bumped to 2.0.0 with the discriminated-
|
|
6470
6478
|
// chunk + delta-chrome refactor in this release.
|
|
6471
|
-
"X-Codeam-Protocol-Version": "2.0.0"
|
|
6479
|
+
"X-Codeam-Protocol-Version": "2.0.0",
|
|
6480
|
+
...vercelBypassHeader()
|
|
6472
6481
|
};
|
|
6473
6482
|
if (opts.pluginAuthToken) {
|
|
6474
6483
|
this.headers["X-Plugin-Auth-Token"] = opts.pluginAuthToken;
|
|
@@ -6780,6 +6789,31 @@ var OutputService = class _OutputService {
|
|
|
6780
6789
|
this.dispose();
|
|
6781
6790
|
}
|
|
6782
6791
|
}
|
|
6792
|
+
/**
|
|
6793
|
+
* Push a terminal-data chunk for the IDE-integrated terminal
|
|
6794
|
+
* panel. Distinct from chat output: `type: 'terminal_data'`
|
|
6795
|
+
* lets the host filter the SSE stream by terminal session id.
|
|
6796
|
+
* `done: false` because terminal sessions are long-lived — the
|
|
6797
|
+
* `terminal_close` command emits the final chunk separately.
|
|
6798
|
+
*/
|
|
6799
|
+
async sendTerminalChunk(terminalSessionId, data) {
|
|
6800
|
+
await this.emitter.send({
|
|
6801
|
+
type: "terminal_data",
|
|
6802
|
+
terminalSessionId,
|
|
6803
|
+
data,
|
|
6804
|
+
done: false
|
|
6805
|
+
});
|
|
6806
|
+
}
|
|
6807
|
+
/** Final chunk for a terminal session — fires when the PTY
|
|
6808
|
+
* exits, so the host can update UI (badge "exit 0", etc). */
|
|
6809
|
+
async sendTerminalExit(terminalSessionId, exitCode) {
|
|
6810
|
+
await this.emitter.send({
|
|
6811
|
+
type: "terminal_exit",
|
|
6812
|
+
terminalSessionId,
|
|
6813
|
+
exitCode,
|
|
6814
|
+
done: true
|
|
6815
|
+
});
|
|
6816
|
+
}
|
|
6783
6817
|
stopPoll() {
|
|
6784
6818
|
if (this.pollTimer) {
|
|
6785
6819
|
clearInterval(this.pollTimer);
|
|
@@ -6983,7 +7017,8 @@ function post(endpoint, body) {
|
|
|
6983
7017
|
method: "POST",
|
|
6984
7018
|
headers: {
|
|
6985
7019
|
"Content-Type": "application/json",
|
|
6986
|
-
"Content-Length": Buffer.byteLength(payload)
|
|
7020
|
+
"Content-Length": Buffer.byteLength(payload),
|
|
7021
|
+
...vercelBypassHeader()
|
|
6987
7022
|
},
|
|
6988
7023
|
timeout: 15e3
|
|
6989
7024
|
},
|
|
@@ -7277,7 +7312,7 @@ var HistoryService = class _HistoryService {
|
|
|
7277
7312
|
} catch {
|
|
7278
7313
|
return;
|
|
7279
7314
|
}
|
|
7280
|
-
const
|
|
7315
|
+
const sessions3 = [];
|
|
7281
7316
|
for (const entry of entries) {
|
|
7282
7317
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
7283
7318
|
const id = path14.basename(entry.name, ".jsonl");
|
|
@@ -7307,11 +7342,11 @@ var HistoryService = class _HistoryService {
|
|
|
7307
7342
|
}
|
|
7308
7343
|
} catch {
|
|
7309
7344
|
}
|
|
7310
|
-
if (summary)
|
|
7345
|
+
if (summary) sessions3.push({ id, summary, timestamp: mtime });
|
|
7311
7346
|
}
|
|
7312
|
-
if (
|
|
7313
|
-
|
|
7314
|
-
await post("/api/sessions/claude-sessions", { pluginId: this.pluginId, sessions:
|
|
7347
|
+
if (sessions3.length === 0) return;
|
|
7348
|
+
sessions3.sort((a, b) => b.timestamp - a.timestamp);
|
|
7349
|
+
await post("/api/sessions/claude-sessions", { pluginId: this.pluginId, sessions: sessions3 });
|
|
7315
7350
|
}
|
|
7316
7351
|
/**
|
|
7317
7352
|
* Read a specific session's full conversation and POST it to the API in batches.
|
|
@@ -7454,7 +7489,7 @@ function buildKeepAlive(ctx) {
|
|
|
7454
7489
|
var fs14 = __toESM(require("fs"));
|
|
7455
7490
|
var os13 = __toESM(require("os"));
|
|
7456
7491
|
var path17 = __toESM(require("path"));
|
|
7457
|
-
var
|
|
7492
|
+
var import_crypto2 = require("crypto");
|
|
7458
7493
|
var import_child_process8 = require("child_process");
|
|
7459
7494
|
|
|
7460
7495
|
// src/lib/payload.ts
|
|
@@ -7484,7 +7519,26 @@ var startCommandSchema = import_zod2.z.object({
|
|
|
7484
7519
|
message: import_zod2.z.string().max(8e3).optional(),
|
|
7485
7520
|
paths: import_zod2.z.array(import_zod2.z.string().max(4096)).optional(),
|
|
7486
7521
|
side: import_zod2.z.enum(["ours", "theirs"]).optional(),
|
|
7487
|
-
limit: import_zod2.z.number().int().min(1).max(500).optional()
|
|
7522
|
+
limit: import_zod2.z.number().int().min(1).max(500).optional(),
|
|
7523
|
+
// search_files options. `query` is the haystack/needle string,
|
|
7524
|
+
// declared above for list_files. The rest mirror VS Code's
|
|
7525
|
+
// search panel toggles + the @codeam/ide-core SearchOptions
|
|
7526
|
+
// contract.
|
|
7527
|
+
caseSensitive: import_zod2.z.boolean().optional(),
|
|
7528
|
+
wholeWord: import_zod2.z.boolean().optional(),
|
|
7529
|
+
regex: import_zod2.z.boolean().optional(),
|
|
7530
|
+
include: import_zod2.z.array(import_zod2.z.string().max(512)).max(64).optional(),
|
|
7531
|
+
exclude: import_zod2.z.array(import_zod2.z.string().max(512)).max(64).optional(),
|
|
7532
|
+
maxResults: import_zod2.z.number().int().min(1).max(500).optional(),
|
|
7533
|
+
// terminal_open / _write / _resize / _close. `sessionId` is the
|
|
7534
|
+
// opaque uuid returned by `terminal_open` and required by every
|
|
7535
|
+
// subsequent op. `data` carries keystrokes (any UTF-8 string).
|
|
7536
|
+
// `cwd` lets the host pin the spawn directory.
|
|
7537
|
+
sessionId: import_zod2.z.string().min(1).max(128).optional(),
|
|
7538
|
+
data: import_zod2.z.string().max(64 * 1024).optional(),
|
|
7539
|
+
cwd: import_zod2.z.string().max(4096).optional(),
|
|
7540
|
+
cols: import_zod2.z.number().int().min(1).max(500).optional(),
|
|
7541
|
+
rows: import_zod2.z.number().int().min(1).max(200).optional()
|
|
7488
7542
|
});
|
|
7489
7543
|
function parsePayload(schema, raw) {
|
|
7490
7544
|
const result = schema.safeParse(raw);
|
|
@@ -7842,13 +7896,19 @@ async function gitDiffStaged(file, cwd) {
|
|
|
7842
7896
|
return { diff: r.stdout.slice(0, MAX_DIFF_BYTES), truncated };
|
|
7843
7897
|
}
|
|
7844
7898
|
async function gitLog(limit = 30, cwd) {
|
|
7845
|
-
const
|
|
7846
|
-
const fmt =
|
|
7899
|
+
const SEP = "";
|
|
7900
|
+
const fmt = `%H${SEP}%s${SEP}%an${SEP}%ct${SEP}%D`;
|
|
7847
7901
|
const r = await git(["log", `-n${Math.min(limit, 200)}`, `--pretty=format:${fmt}`], cwd);
|
|
7848
7902
|
if (r.code !== 0) return { commits: [], error: r.stderr.trim() };
|
|
7849
7903
|
const commits = r.stdout.split("\n").filter(Boolean).map((line) => {
|
|
7850
|
-
const [
|
|
7851
|
-
return {
|
|
7904
|
+
const [sha, subject, author, ts, refs] = line.split(SEP);
|
|
7905
|
+
return {
|
|
7906
|
+
sha: sha ?? "",
|
|
7907
|
+
subject: subject ?? "",
|
|
7908
|
+
author: author ?? "",
|
|
7909
|
+
timestamp: parseInt(ts ?? "0", 10) * 1e3,
|
|
7910
|
+
refs: (refs ?? "").split(",").map((s) => s.trim().replace(/^HEAD -> /, "")).filter((s) => s.length > 0)
|
|
7911
|
+
};
|
|
7852
7912
|
});
|
|
7853
7913
|
return { commits };
|
|
7854
7914
|
}
|
|
@@ -7887,12 +7947,189 @@ async function gitResolve(file, side, cwd) {
|
|
|
7887
7947
|
if (add.code !== 0) return { error: add.stderr.trim() || "git add (resolve) failed" };
|
|
7888
7948
|
return { ok: true };
|
|
7889
7949
|
}
|
|
7950
|
+
var MAX_SEARCH_HITS = 500;
|
|
7951
|
+
var MAX_SEARCH_BYTES = 256 * 1024;
|
|
7952
|
+
async function searchFiles(opts) {
|
|
7953
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
7954
|
+
const cap = Math.min(opts.maxResults ?? MAX_SEARCH_HITS, MAX_SEARCH_HITS);
|
|
7955
|
+
if (!opts.query.trim()) return { hits: [], total: 0, truncated: false };
|
|
7956
|
+
const args2 = ["grep", "-n", "--column", "-I"];
|
|
7957
|
+
if (!opts.caseSensitive) args2.push("-i");
|
|
7958
|
+
if (opts.wholeWord) args2.push("-w");
|
|
7959
|
+
if (opts.regex) args2.push("-E");
|
|
7960
|
+
else args2.push("-F");
|
|
7961
|
+
args2.push(opts.query);
|
|
7962
|
+
if (opts.include && opts.include.length > 0) {
|
|
7963
|
+
args2.push("--");
|
|
7964
|
+
for (const p2 of opts.include) args2.push(p2);
|
|
7965
|
+
} else if (opts.exclude && opts.exclude.length > 0) {
|
|
7966
|
+
args2.push("--");
|
|
7967
|
+
args2.push(".");
|
|
7968
|
+
}
|
|
7969
|
+
for (const p2 of opts.exclude ?? []) args2.push(`:!${p2}`);
|
|
7970
|
+
const r = await git(args2, cwd);
|
|
7971
|
+
if (r.code !== 0 && r.code !== 1) {
|
|
7972
|
+
return jsSearchFiles(opts, cwd, cap);
|
|
7973
|
+
}
|
|
7974
|
+
const hits = [];
|
|
7975
|
+
const lines = r.stdout.split("\n");
|
|
7976
|
+
let truncated = false;
|
|
7977
|
+
let byteBudget = MAX_SEARCH_BYTES;
|
|
7978
|
+
for (const line of lines) {
|
|
7979
|
+
if (!line) continue;
|
|
7980
|
+
if (hits.length >= cap) {
|
|
7981
|
+
truncated = true;
|
|
7982
|
+
break;
|
|
7983
|
+
}
|
|
7984
|
+
if (byteBudget <= 0) {
|
|
7985
|
+
truncated = true;
|
|
7986
|
+
break;
|
|
7987
|
+
}
|
|
7988
|
+
byteBudget -= line.length;
|
|
7989
|
+
const m = line.match(/^([^]+?):(\d+):(\d+):(.*)$/);
|
|
7990
|
+
if (!m) continue;
|
|
7991
|
+
const filePath = m[1] ?? "";
|
|
7992
|
+
const lineNo = parseInt(m[2] ?? "0", 10);
|
|
7993
|
+
const col = parseInt(m[3] ?? "1", 10);
|
|
7994
|
+
const text = (m[4] ?? "").slice(0, 400);
|
|
7995
|
+
if (!filePath) continue;
|
|
7996
|
+
hits.push({
|
|
7997
|
+
path: filePath,
|
|
7998
|
+
line: lineNo,
|
|
7999
|
+
column: col,
|
|
8000
|
+
text,
|
|
8001
|
+
matchLength: opts.query.length
|
|
8002
|
+
});
|
|
8003
|
+
}
|
|
8004
|
+
return { hits, total: hits.length, truncated };
|
|
8005
|
+
}
|
|
8006
|
+
async function jsSearchFiles(opts, cwd, cap) {
|
|
8007
|
+
const files = await listProjectFiles({ cwd, cap: 2e3 });
|
|
8008
|
+
const hits = [];
|
|
8009
|
+
const needle = opts.caseSensitive ? opts.query : opts.query.toLowerCase();
|
|
8010
|
+
let truncated = files.truncated;
|
|
8011
|
+
for (const f of files.files) {
|
|
8012
|
+
if (hits.length >= cap) {
|
|
8013
|
+
truncated = true;
|
|
8014
|
+
break;
|
|
8015
|
+
}
|
|
8016
|
+
let content = "";
|
|
8017
|
+
try {
|
|
8018
|
+
content = await fs13.readFile(path16.join(cwd, f.path), "utf8");
|
|
8019
|
+
} catch {
|
|
8020
|
+
continue;
|
|
8021
|
+
}
|
|
8022
|
+
const lines = content.split("\n");
|
|
8023
|
+
for (let i = 0; i < lines.length && hits.length < cap; i++) {
|
|
8024
|
+
const line = lines[i] ?? "";
|
|
8025
|
+
const hay = opts.caseSensitive ? line : line.toLowerCase();
|
|
8026
|
+
const idx = hay.indexOf(needle);
|
|
8027
|
+
if (idx === -1) continue;
|
|
8028
|
+
hits.push({
|
|
8029
|
+
path: f.path,
|
|
8030
|
+
line: i + 1,
|
|
8031
|
+
column: idx + 1,
|
|
8032
|
+
text: line.slice(0, 400),
|
|
8033
|
+
matchLength: opts.query.length
|
|
8034
|
+
});
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
return { hits, total: hits.length, truncated };
|
|
8038
|
+
}
|
|
8039
|
+
|
|
8040
|
+
// src/services/terminal-ops.service.ts
|
|
8041
|
+
var import_crypto = require("crypto");
|
|
8042
|
+
var pty = __toESM(require_lib());
|
|
8043
|
+
var MAX_CONCURRENT_SESSIONS = 4;
|
|
8044
|
+
var sessions = /* @__PURE__ */ new Map();
|
|
8045
|
+
var onDataHandler = null;
|
|
8046
|
+
var onExitHandler = null;
|
|
8047
|
+
function registerTerminalHandlers(opts) {
|
|
8048
|
+
onDataHandler = opts.onData;
|
|
8049
|
+
onExitHandler = opts.onExit;
|
|
8050
|
+
}
|
|
8051
|
+
function defaultShell() {
|
|
8052
|
+
if (process.platform === "win32") {
|
|
8053
|
+
return process.env.COMSPEC ?? "powershell.exe";
|
|
8054
|
+
}
|
|
8055
|
+
return process.env.SHELL ?? "/bin/bash";
|
|
8056
|
+
}
|
|
8057
|
+
function openTerminal(opts) {
|
|
8058
|
+
if (sessions.size >= MAX_CONCURRENT_SESSIONS) {
|
|
8059
|
+
return { error: `Too many open terminals (max ${MAX_CONCURRENT_SESSIONS})` };
|
|
8060
|
+
}
|
|
8061
|
+
const shell = opts.shell ?? defaultShell();
|
|
8062
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
8063
|
+
const env = {
|
|
8064
|
+
...process.env,
|
|
8065
|
+
TERM: "xterm-256color",
|
|
8066
|
+
COLORTERM: "truecolor"
|
|
8067
|
+
};
|
|
8068
|
+
env.FORCE_COLOR = "1";
|
|
8069
|
+
try {
|
|
8070
|
+
const term = pty.spawn(shell, [], {
|
|
8071
|
+
name: "xterm-256color",
|
|
8072
|
+
cols: Math.max(1, Math.min(opts.cols ?? 80, 500)),
|
|
8073
|
+
rows: Math.max(1, Math.min(opts.rows ?? 24, 200)),
|
|
8074
|
+
cwd,
|
|
8075
|
+
env,
|
|
8076
|
+
// Windows-specific: ConPTY is the default on Win 10 1809+
|
|
8077
|
+
// and is what we want. node-pty falls back to winpty
|
|
8078
|
+
// automatically on older builds.
|
|
8079
|
+
useConpty: process.platform === "win32" ? true : void 0
|
|
8080
|
+
});
|
|
8081
|
+
const id = (0, import_crypto.randomUUID)();
|
|
8082
|
+
const dataListener = term.onData((data) => {
|
|
8083
|
+
onDataHandler?.({ sessionId: id, data });
|
|
8084
|
+
});
|
|
8085
|
+
const exitListener = term.onExit(({ exitCode }) => {
|
|
8086
|
+
onExitHandler?.({ sessionId: id, exitCode });
|
|
8087
|
+
sessions.delete(id);
|
|
8088
|
+
});
|
|
8089
|
+
sessions.set(id, { id, pty: term, dataListener, exitListener });
|
|
8090
|
+
return { sessionId: id };
|
|
8091
|
+
} catch (e) {
|
|
8092
|
+
return { error: e instanceof Error ? e.message : "spawn failed" };
|
|
8093
|
+
}
|
|
8094
|
+
}
|
|
8095
|
+
function writeTerminal(sessionId, data) {
|
|
8096
|
+
const s = sessions.get(sessionId);
|
|
8097
|
+
if (!s) return { ok: false, error: "No such session" };
|
|
8098
|
+
try {
|
|
8099
|
+
s.pty.write(data);
|
|
8100
|
+
return { ok: true };
|
|
8101
|
+
} catch (e) {
|
|
8102
|
+
return { ok: false, error: e instanceof Error ? e.message : "write failed" };
|
|
8103
|
+
}
|
|
8104
|
+
}
|
|
8105
|
+
function resizeTerminal(sessionId, cols, rows) {
|
|
8106
|
+
const s = sessions.get(sessionId);
|
|
8107
|
+
if (!s) return { ok: false, error: "No such session" };
|
|
8108
|
+
try {
|
|
8109
|
+
s.pty.resize(Math.max(1, Math.min(cols, 500)), Math.max(1, Math.min(rows, 200)));
|
|
8110
|
+
return { ok: true };
|
|
8111
|
+
} catch (e) {
|
|
8112
|
+
return { ok: false, error: e instanceof Error ? e.message : "resize failed" };
|
|
8113
|
+
}
|
|
8114
|
+
}
|
|
8115
|
+
function closeTerminal(sessionId) {
|
|
8116
|
+
const s = sessions.get(sessionId);
|
|
8117
|
+
if (!s) return { ok: true };
|
|
8118
|
+
try {
|
|
8119
|
+
s.dataListener.dispose();
|
|
8120
|
+
s.exitListener.dispose();
|
|
8121
|
+
s.pty.kill();
|
|
8122
|
+
} catch {
|
|
8123
|
+
}
|
|
8124
|
+
sessions.delete(sessionId);
|
|
8125
|
+
return { ok: true };
|
|
8126
|
+
}
|
|
7890
8127
|
|
|
7891
8128
|
// src/commands/start/handlers.ts
|
|
7892
8129
|
function saveFilesTemp(files) {
|
|
7893
8130
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
7894
8131
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
7895
|
-
const tmpPath = path17.join(os13.tmpdir(), `codeam-${(0,
|
|
8132
|
+
const tmpPath = path17.join(os13.tmpdir(), `codeam-${(0, import_crypto2.randomUUID)()}-${safeName}`);
|
|
7896
8133
|
fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
7897
8134
|
return tmpPath;
|
|
7898
8135
|
});
|
|
@@ -8067,7 +8304,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
8067
8304
|
ctx.relay.stop();
|
|
8068
8305
|
process.exit(0);
|
|
8069
8306
|
};
|
|
8070
|
-
var
|
|
8307
|
+
var readFile3 = async (ctx, cmd, parsed) => {
|
|
8071
8308
|
if (!parsed.path) {
|
|
8072
8309
|
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
8073
8310
|
return;
|
|
@@ -8087,6 +8324,58 @@ var listFiles = async (ctx, cmd, parsed) => {
|
|
|
8087
8324
|
const result = await listProjectFiles({ query: parsed.query });
|
|
8088
8325
|
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
8089
8326
|
};
|
|
8327
|
+
var terminalOpenH = async (ctx, cmd, parsed) => {
|
|
8328
|
+
const r = openTerminal({
|
|
8329
|
+
cols: typeof parsed.cols === "number" ? parsed.cols : void 0,
|
|
8330
|
+
rows: typeof parsed.rows === "number" ? parsed.rows : void 0,
|
|
8331
|
+
cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0
|
|
8332
|
+
});
|
|
8333
|
+
if ("error" in r) {
|
|
8334
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: r.error });
|
|
8335
|
+
return;
|
|
8336
|
+
}
|
|
8337
|
+
await ctx.relay.sendResult(cmd.id, "completed", r);
|
|
8338
|
+
};
|
|
8339
|
+
var terminalWriteH = async (ctx, cmd, parsed) => {
|
|
8340
|
+
if (typeof parsed.sessionId !== "string" || typeof parsed.data !== "string") {
|
|
8341
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing sessionId or data" });
|
|
8342
|
+
return;
|
|
8343
|
+
}
|
|
8344
|
+
const r = writeTerminal(parsed.sessionId, parsed.data);
|
|
8345
|
+
await ctx.relay.sendResult(cmd.id, r.ok ? "completed" : "failed", r);
|
|
8346
|
+
};
|
|
8347
|
+
var terminalResizeH = async (ctx, cmd, parsed) => {
|
|
8348
|
+
if (typeof parsed.sessionId !== "string" || typeof parsed.cols !== "number" || typeof parsed.rows !== "number") {
|
|
8349
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing sessionId / cols / rows" });
|
|
8350
|
+
return;
|
|
8351
|
+
}
|
|
8352
|
+
const r = resizeTerminal(parsed.sessionId, parsed.cols, parsed.rows);
|
|
8353
|
+
await ctx.relay.sendResult(cmd.id, r.ok ? "completed" : "failed", r);
|
|
8354
|
+
};
|
|
8355
|
+
var terminalCloseH = async (ctx, cmd, parsed) => {
|
|
8356
|
+
if (typeof parsed.sessionId !== "string") {
|
|
8357
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing sessionId" });
|
|
8358
|
+
return;
|
|
8359
|
+
}
|
|
8360
|
+
const r = closeTerminal(parsed.sessionId);
|
|
8361
|
+
await ctx.relay.sendResult(cmd.id, "completed", r);
|
|
8362
|
+
};
|
|
8363
|
+
var searchFilesH = async (ctx, cmd, parsed) => {
|
|
8364
|
+
if (!parsed.query || typeof parsed.query !== "string") {
|
|
8365
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing query" });
|
|
8366
|
+
return;
|
|
8367
|
+
}
|
|
8368
|
+
const result = await searchFiles({
|
|
8369
|
+
query: parsed.query,
|
|
8370
|
+
caseSensitive: parsed.caseSensitive,
|
|
8371
|
+
wholeWord: parsed.wholeWord,
|
|
8372
|
+
regex: parsed.regex,
|
|
8373
|
+
include: Array.isArray(parsed.include) ? parsed.include : void 0,
|
|
8374
|
+
exclude: Array.isArray(parsed.exclude) ? parsed.exclude : void 0,
|
|
8375
|
+
maxResults: typeof parsed.maxResults === "number" ? parsed.maxResults : void 0
|
|
8376
|
+
});
|
|
8377
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
8378
|
+
};
|
|
8090
8379
|
var gitStatusH = async (ctx, cmd) => {
|
|
8091
8380
|
const result = await gitStatus();
|
|
8092
8381
|
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
@@ -8142,9 +8431,14 @@ var handlers = {
|
|
|
8142
8431
|
set_keep_alive: setKeepAlive,
|
|
8143
8432
|
session_terminated: sessionTerminated,
|
|
8144
8433
|
shutdown_session: shutdownSession,
|
|
8145
|
-
read_file:
|
|
8434
|
+
read_file: readFile3,
|
|
8146
8435
|
write_file: writeFile2,
|
|
8147
8436
|
list_files: listFiles,
|
|
8437
|
+
search_files: searchFilesH,
|
|
8438
|
+
terminal_open: terminalOpenH,
|
|
8439
|
+
terminal_write: terminalWriteH,
|
|
8440
|
+
terminal_resize: terminalResizeH,
|
|
8441
|
+
terminal_close: terminalCloseH,
|
|
8148
8442
|
git_status: gitStatusH,
|
|
8149
8443
|
git_diff: gitDiffH,
|
|
8150
8444
|
git_diff_staged: gitDiffStagedH,
|
|
@@ -8246,6 +8540,14 @@ async function start(requestedAgent) {
|
|
|
8246
8540
|
await dispatchCommand(ctx, cmd);
|
|
8247
8541
|
}, runtime.meta);
|
|
8248
8542
|
ctx.relay = relay;
|
|
8543
|
+
registerTerminalHandlers({
|
|
8544
|
+
onData: ({ sessionId, data }) => {
|
|
8545
|
+
void outputSvc.sendTerminalChunk(sessionId, data);
|
|
8546
|
+
},
|
|
8547
|
+
onExit: ({ sessionId, exitCode }) => {
|
|
8548
|
+
void outputSvc.sendTerminalExit(sessionId, exitCode);
|
|
8549
|
+
}
|
|
8550
|
+
});
|
|
8249
8551
|
function sigintHandler() {
|
|
8250
8552
|
claude.kill();
|
|
8251
8553
|
outputSvc.dispose();
|
|
@@ -8264,7 +8566,7 @@ async function start(requestedAgent) {
|
|
|
8264
8566
|
}
|
|
8265
8567
|
|
|
8266
8568
|
// src/commands/pair.ts
|
|
8267
|
-
var
|
|
8569
|
+
var import_crypto3 = require("crypto");
|
|
8268
8570
|
var import_picocolors3 = __toESM(require("picocolors"));
|
|
8269
8571
|
|
|
8270
8572
|
// src/ui/prompts.ts
|
|
@@ -8273,10 +8575,10 @@ async function confirmAction(message) {
|
|
|
8273
8575
|
if (q(result)) return false;
|
|
8274
8576
|
return result;
|
|
8275
8577
|
}
|
|
8276
|
-
async function selectSession(
|
|
8578
|
+
async function selectSession(sessions3, activeId) {
|
|
8277
8579
|
const result = await _t({
|
|
8278
8580
|
message: "Select active session:",
|
|
8279
|
-
options:
|
|
8581
|
+
options: sessions3.map((s) => ({
|
|
8280
8582
|
value: s.id,
|
|
8281
8583
|
label: `${s.userName} ${s.plan}`,
|
|
8282
8584
|
hint: s.id === activeId ? "active" : `paired ${new Date(s.pairedAt).toLocaleDateString()}`
|
|
@@ -8327,7 +8629,7 @@ async function pair(args2 = []) {
|
|
|
8327
8629
|
const flagAgent = parseAgentFlag(args2);
|
|
8328
8630
|
const agentId = flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
|
|
8329
8631
|
showIntro();
|
|
8330
|
-
const pluginId = (0,
|
|
8632
|
+
const pluginId = (0, import_crypto3.randomUUID)();
|
|
8331
8633
|
const spin = dist_exports.spinner();
|
|
8332
8634
|
spin.start("Requesting pairing code...");
|
|
8333
8635
|
const result = await requestCode(pluginId);
|
|
@@ -8383,7 +8685,7 @@ async function pair(args2 = []) {
|
|
|
8383
8685
|
// src/commands/pair-auto.ts
|
|
8384
8686
|
var fs15 = __toESM(require("fs"));
|
|
8385
8687
|
var os14 = __toESM(require("os"));
|
|
8386
|
-
var
|
|
8688
|
+
var import_crypto4 = require("crypto");
|
|
8387
8689
|
var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
8388
8690
|
function fail(msg) {
|
|
8389
8691
|
console.error(`
|
|
@@ -8427,7 +8729,7 @@ async function claim(token, pluginId) {
|
|
|
8427
8729
|
};
|
|
8428
8730
|
const res = await fetch(url, {
|
|
8429
8731
|
method: "POST",
|
|
8430
|
-
headers: { "Content-Type": "application/json" },
|
|
8732
|
+
headers: { "Content-Type": "application/json", ...vercelBypassHeader() },
|
|
8431
8733
|
body: JSON.stringify(body)
|
|
8432
8734
|
});
|
|
8433
8735
|
const json = await res.json();
|
|
@@ -8445,7 +8747,7 @@ async function claim(token, pluginId) {
|
|
|
8445
8747
|
}
|
|
8446
8748
|
async function pairAuto(args2) {
|
|
8447
8749
|
const token = readTokenFromArgs(args2);
|
|
8448
|
-
const pluginId = (0,
|
|
8750
|
+
const pluginId = (0, import_crypto4.randomUUID)();
|
|
8449
8751
|
console.log(" Claiming pairing token\u2026");
|
|
8450
8752
|
const claimed = await claim(token, pluginId);
|
|
8451
8753
|
if (!isKnownAgentId(claimed.agent)) {
|
|
@@ -8470,7 +8772,7 @@ async function pairAuto(args2) {
|
|
|
8470
8772
|
|
|
8471
8773
|
// src/commands/sessions.ts
|
|
8472
8774
|
var import_picocolors4 = __toESM(require("picocolors"));
|
|
8473
|
-
async function
|
|
8775
|
+
async function sessions2(args2) {
|
|
8474
8776
|
const [sub, id] = args2;
|
|
8475
8777
|
if (sub === "switch") return switchSession();
|
|
8476
8778
|
if (sub === "delete") {
|
|
@@ -10515,7 +10817,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
10515
10817
|
// src/commands/version.ts
|
|
10516
10818
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
10517
10819
|
function version() {
|
|
10518
|
-
const v = true ? "2.
|
|
10820
|
+
const v = true ? "2.14.0" : "unknown";
|
|
10519
10821
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
10520
10822
|
}
|
|
10521
10823
|
|
|
@@ -10654,7 +10956,7 @@ function checkForUpdates() {
|
|
|
10654
10956
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
10655
10957
|
if (process.env.CI) return;
|
|
10656
10958
|
if (!process.stdout.isTTY) return;
|
|
10657
|
-
const current = true ? "2.
|
|
10959
|
+
const current = true ? "2.14.0" : null;
|
|
10658
10960
|
if (!current) return;
|
|
10659
10961
|
const cache = readCache();
|
|
10660
10962
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -10687,7 +10989,7 @@ async function main() {
|
|
|
10687
10989
|
case "pair-auto":
|
|
10688
10990
|
return pairAuto(args);
|
|
10689
10991
|
case "sessions":
|
|
10690
|
-
return
|
|
10992
|
+
return sessions2(args);
|
|
10691
10993
|
case "status":
|
|
10692
10994
|
return status();
|
|
10693
10995
|
case "logout":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"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 — from anywhere.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|