codeam-cli 2.15.2 → 2.15.5
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/README.md +7 -5
- package/dist/index.js +562 -95
- package/package.json +14 -13
package/README.md
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
[](https://github.com/edgar-durand/codeagent-mobile-clients/blob/main/LICENSE)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
|
|
8
|
-
> **
|
|
9
|
-
>
|
|
8
|
+
> **The workflow-continuity bridge for AI coding agents.**
|
|
9
|
+
> Wrap Claude Code or Codex once, then supervise, approve, and redirect from any device — async.
|
|
10
10
|
|
|
11
|
-
`codeam-cli` is the
|
|
11
|
+
`codeam-cli` is the terminal bridge for [**CodeAgent Mobile**](https://codeagent-mobile.com). It wraps AI coding agents inside a pseudo-terminal and streams the entire session — output, diffs, interactive selectors — to your phone or web dashboard so you can stay in the loop while the agent runs for hours instead of seconds.
|
|
12
12
|
|
|
13
13
|
Currently supports **[Claude Code](https://claude.ai/code)** (Anthropic) and **[OpenAI Codex](https://github.com/openai/codex)** — start either via `codeam` (Claude Code) or `codeam codex` (OpenAI Codex).
|
|
14
14
|
|
|
@@ -16,9 +16,11 @@ Currently supports **[Claude Code](https://claude.ai/code)** (Anthropic) and **[
|
|
|
16
16
|
|
|
17
17
|
## Why does this exist?
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
AI agents went async. They write, refactor, test, and ship code on their own — for hours, not seconds. Most CLI workflows still pin you to one screen while that happens.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
`codeam-cli` is the supervision layer on top: run the agent locally exactly like you would today, and a paired phone / browser becomes a remote checkpoint. Approve diffs while you're away from the desk. Redirect a long-running refactor over coffee. Step into a meeting without losing the session.
|
|
22
|
+
|
|
23
|
+
Same terminal, same project, same files — just no longer chained to the desk.
|
|
22
24
|
|
|
23
25
|
---
|
|
24
26
|
|
package/dist/index.js
CHANGED
|
@@ -386,8 +386,8 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
386
386
|
// package.json
|
|
387
387
|
var package_default = {
|
|
388
388
|
name: "codeam-cli",
|
|
389
|
-
version: "2.15.
|
|
390
|
-
description: "
|
|
389
|
+
version: "2.15.5",
|
|
390
|
+
description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
|
|
391
391
|
type: "commonjs",
|
|
392
392
|
main: "dist/index.js",
|
|
393
393
|
bin: {
|
|
@@ -410,29 +410,29 @@ var package_default = {
|
|
|
410
410
|
keywords: [
|
|
411
411
|
"claude",
|
|
412
412
|
"claude-code",
|
|
413
|
-
"claude-code-mobile",
|
|
414
413
|
"ai",
|
|
415
414
|
"ai-agent",
|
|
416
415
|
"ai-coding",
|
|
417
|
-
"ai-
|
|
416
|
+
"ai-workflow",
|
|
417
|
+
"ai-workflow-continuity",
|
|
418
|
+
"async-ai-productivity",
|
|
419
|
+
"remote-ai-supervision",
|
|
418
420
|
"cli",
|
|
419
|
-
"
|
|
420
|
-
"
|
|
421
|
-
"mobile-ide",
|
|
422
|
-
"mobile-coding",
|
|
423
|
-
"pair-programming",
|
|
421
|
+
"developer-tools",
|
|
422
|
+
"devtools",
|
|
424
423
|
"codeagent",
|
|
425
424
|
"codeam",
|
|
426
425
|
"anthropic",
|
|
426
|
+
"openai-codex",
|
|
427
427
|
"cursor",
|
|
428
428
|
"copilot",
|
|
429
429
|
"jetbrains",
|
|
430
430
|
"vscode",
|
|
431
|
+
"windsurf",
|
|
431
432
|
"mcp",
|
|
432
|
-
"
|
|
433
|
-
"
|
|
434
|
-
"
|
|
435
|
-
"agent-control"
|
|
433
|
+
"remote-development",
|
|
434
|
+
"agent-control",
|
|
435
|
+
"agent-operations"
|
|
436
436
|
],
|
|
437
437
|
homepage: "https://codeagent-mobile.com",
|
|
438
438
|
bugs: {
|
|
@@ -456,6 +456,7 @@ var package_default = {
|
|
|
456
456
|
},
|
|
457
457
|
dependencies: {
|
|
458
458
|
"@clack/prompts": "^1.2.0",
|
|
459
|
+
chokidar: "^3.6.0",
|
|
459
460
|
picocolors: "^1.1.0",
|
|
460
461
|
"qrcode-terminal": "^0.12.0",
|
|
461
462
|
ws: "^8.18.0",
|
|
@@ -6125,6 +6126,460 @@ var HistoryService = class _HistoryService {
|
|
|
6125
6126
|
}
|
|
6126
6127
|
};
|
|
6127
6128
|
|
|
6129
|
+
// src/services/file-watcher.service.ts
|
|
6130
|
+
var import_child_process6 = require("child_process");
|
|
6131
|
+
var path15 = __toESM(require("path"));
|
|
6132
|
+
|
|
6133
|
+
// src/services/file-watcher/diff-parser.ts
|
|
6134
|
+
var HUNK_HEADER_RE = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
6135
|
+
function parseUnifiedDiff(diff) {
|
|
6136
|
+
if (!diff || diff.trim().length === 0) {
|
|
6137
|
+
return {
|
|
6138
|
+
fileStatus: "modified",
|
|
6139
|
+
hunks: [],
|
|
6140
|
+
totalLinesAdded: 0,
|
|
6141
|
+
totalLinesRemoved: 0
|
|
6142
|
+
};
|
|
6143
|
+
}
|
|
6144
|
+
const rawLines = diff.split(/\r?\n/);
|
|
6145
|
+
const fileStatus = detectFileStatus(rawLines);
|
|
6146
|
+
const hunks = [];
|
|
6147
|
+
let current = null;
|
|
6148
|
+
let oldLine = 0;
|
|
6149
|
+
let newLine = 0;
|
|
6150
|
+
let totalAdded = 0;
|
|
6151
|
+
let totalRemoved = 0;
|
|
6152
|
+
for (const raw of rawLines) {
|
|
6153
|
+
if (raw.startsWith("@@")) {
|
|
6154
|
+
const match = raw.match(HUNK_HEADER_RE);
|
|
6155
|
+
if (!match) continue;
|
|
6156
|
+
if (current) hunks.push(current);
|
|
6157
|
+
oldLine = parseInt(match[1], 10);
|
|
6158
|
+
newLine = parseInt(match[2], 10);
|
|
6159
|
+
current = {
|
|
6160
|
+
header: raw,
|
|
6161
|
+
lines: [],
|
|
6162
|
+
linesAdded: 0,
|
|
6163
|
+
linesRemoved: 0
|
|
6164
|
+
};
|
|
6165
|
+
continue;
|
|
6166
|
+
}
|
|
6167
|
+
if (current === null) continue;
|
|
6168
|
+
if (raw.startsWith("\\ No newline")) continue;
|
|
6169
|
+
if (raw.startsWith("+")) {
|
|
6170
|
+
current.lines.push({ type: "add", lineNumber: newLine, text: raw.slice(1) });
|
|
6171
|
+
current.linesAdded += 1;
|
|
6172
|
+
totalAdded += 1;
|
|
6173
|
+
newLine += 1;
|
|
6174
|
+
continue;
|
|
6175
|
+
}
|
|
6176
|
+
if (raw.startsWith("-")) {
|
|
6177
|
+
current.lines.push({ type: "remove", lineNumber: oldLine, text: raw.slice(1) });
|
|
6178
|
+
current.linesRemoved += 1;
|
|
6179
|
+
totalRemoved += 1;
|
|
6180
|
+
oldLine += 1;
|
|
6181
|
+
continue;
|
|
6182
|
+
}
|
|
6183
|
+
if (raw.startsWith(" ")) {
|
|
6184
|
+
current.lines.push({ type: "context", lineNumber: newLine, text: raw.slice(1) });
|
|
6185
|
+
newLine += 1;
|
|
6186
|
+
oldLine += 1;
|
|
6187
|
+
continue;
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
if (current) hunks.push(current);
|
|
6191
|
+
return {
|
|
6192
|
+
fileStatus,
|
|
6193
|
+
hunks,
|
|
6194
|
+
totalLinesAdded: totalAdded,
|
|
6195
|
+
totalLinesRemoved: totalRemoved
|
|
6196
|
+
};
|
|
6197
|
+
}
|
|
6198
|
+
function detectFileStatus(rawLines) {
|
|
6199
|
+
for (const line of rawLines) {
|
|
6200
|
+
if (line.startsWith("@@")) break;
|
|
6201
|
+
if (line.startsWith("new file mode")) return "added";
|
|
6202
|
+
if (line.startsWith("deleted file mode")) return "deleted";
|
|
6203
|
+
if (line.startsWith("rename from ") || line.startsWith("rename to ")) {
|
|
6204
|
+
return "renamed";
|
|
6205
|
+
}
|
|
6206
|
+
if (line.startsWith("--- /dev/null")) return "added";
|
|
6207
|
+
if (line.startsWith("+++ /dev/null")) return "deleted";
|
|
6208
|
+
}
|
|
6209
|
+
return "modified";
|
|
6210
|
+
}
|
|
6211
|
+
|
|
6212
|
+
// src/services/file-watcher/transport.ts
|
|
6213
|
+
var http5 = __toESM(require("http"));
|
|
6214
|
+
var https5 = __toESM(require("https"));
|
|
6215
|
+
var _transport3 = {
|
|
6216
|
+
post: _post2
|
|
6217
|
+
};
|
|
6218
|
+
function _post2(url, headers, payload) {
|
|
6219
|
+
return new Promise((resolve2, reject) => {
|
|
6220
|
+
let settled = false;
|
|
6221
|
+
const u2 = new URL(url);
|
|
6222
|
+
const lib = u2.protocol === "https:" ? https5 : http5;
|
|
6223
|
+
const req = lib.request(
|
|
6224
|
+
{
|
|
6225
|
+
hostname: u2.hostname,
|
|
6226
|
+
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
6227
|
+
path: u2.pathname + u2.search,
|
|
6228
|
+
method: "POST",
|
|
6229
|
+
headers: {
|
|
6230
|
+
...headers,
|
|
6231
|
+
...vercelBypassHeader(),
|
|
6232
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
6233
|
+
},
|
|
6234
|
+
timeout: 8e3
|
|
6235
|
+
},
|
|
6236
|
+
(res) => {
|
|
6237
|
+
let body = "";
|
|
6238
|
+
res.on("data", (c2) => {
|
|
6239
|
+
body += c2.toString();
|
|
6240
|
+
});
|
|
6241
|
+
res.on("end", () => {
|
|
6242
|
+
if (settled) return;
|
|
6243
|
+
settled = true;
|
|
6244
|
+
resolve2({ statusCode: res.statusCode ?? 0, body });
|
|
6245
|
+
});
|
|
6246
|
+
}
|
|
6247
|
+
);
|
|
6248
|
+
req.on("error", (err) => {
|
|
6249
|
+
if (settled) return;
|
|
6250
|
+
settled = true;
|
|
6251
|
+
reject(err);
|
|
6252
|
+
});
|
|
6253
|
+
req.on("timeout", () => {
|
|
6254
|
+
req.destroy();
|
|
6255
|
+
});
|
|
6256
|
+
req.write(payload);
|
|
6257
|
+
req.end();
|
|
6258
|
+
});
|
|
6259
|
+
}
|
|
6260
|
+
|
|
6261
|
+
// src/services/file-watcher.service.ts
|
|
6262
|
+
var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
6263
|
+
var DEBOUNCE_MS = 250;
|
|
6264
|
+
var MAX_RETRIES = 2;
|
|
6265
|
+
var RETRY_BACKOFF_MS = 300;
|
|
6266
|
+
var FileWatcherService = class {
|
|
6267
|
+
constructor(opts) {
|
|
6268
|
+
this.opts = opts;
|
|
6269
|
+
this.apiBase = opts.apiBaseUrl ?? API_BASE5;
|
|
6270
|
+
}
|
|
6271
|
+
opts;
|
|
6272
|
+
watcher = null;
|
|
6273
|
+
pending = /* @__PURE__ */ new Map();
|
|
6274
|
+
apiBase;
|
|
6275
|
+
stopped = false;
|
|
6276
|
+
/**
|
|
6277
|
+
* Start watching `opts.workingDir`. Idempotent (second call is a
|
|
6278
|
+
* no-op). Resolves once chokidar's initial scan completes; that
|
|
6279
|
+
* way the `start.ts` orchestrator can sequence "agent up → watcher
|
|
6280
|
+
* ready" deterministically if it wants to, though today it
|
|
6281
|
+
* doesn't await this.
|
|
6282
|
+
*/
|
|
6283
|
+
async start() {
|
|
6284
|
+
if (this.watcher) return;
|
|
6285
|
+
if (this.stopped) {
|
|
6286
|
+
throw new Error("FileWatcherService has already been stopped \u2014 re-instantiate to restart.");
|
|
6287
|
+
}
|
|
6288
|
+
let chokidar;
|
|
6289
|
+
try {
|
|
6290
|
+
chokidar = require("chokidar");
|
|
6291
|
+
} catch (err) {
|
|
6292
|
+
log.warn(
|
|
6293
|
+
"fileWatcher",
|
|
6294
|
+
`chokidar unavailable \u2014 file change emission disabled`,
|
|
6295
|
+
err
|
|
6296
|
+
);
|
|
6297
|
+
return;
|
|
6298
|
+
}
|
|
6299
|
+
const watcher = chokidar.watch(this.opts.workingDir, {
|
|
6300
|
+
ignored: [
|
|
6301
|
+
/(^|[\\/])\../,
|
|
6302
|
+
// dot-files & dot-dirs (.git, .next, .expo, .DS_Store, …)
|
|
6303
|
+
/node_modules/,
|
|
6304
|
+
/dist/,
|
|
6305
|
+
/build/,
|
|
6306
|
+
/out/,
|
|
6307
|
+
/coverage/,
|
|
6308
|
+
/\.turbo/,
|
|
6309
|
+
/\.cache/,
|
|
6310
|
+
/\.parcel-cache/,
|
|
6311
|
+
// Build outputs that aren't a typical "dist" target
|
|
6312
|
+
/target\//,
|
|
6313
|
+
/__pycache__/
|
|
6314
|
+
],
|
|
6315
|
+
ignoreInitial: true,
|
|
6316
|
+
// we only care about post-start changes
|
|
6317
|
+
persistent: true,
|
|
6318
|
+
awaitWriteFinish: {
|
|
6319
|
+
// Coalesces rapid sequential writes (npm install spam, build
|
|
6320
|
+
// tools emitting bursts). Lower than chokidar's default so
|
|
6321
|
+
// the user sees their Files screen update within 0.5 s of
|
|
6322
|
+
// saving.
|
|
6323
|
+
stabilityThreshold: 150,
|
|
6324
|
+
pollInterval: 50
|
|
6325
|
+
}
|
|
6326
|
+
});
|
|
6327
|
+
watcher.on("add", (filePath) => this.schedule(filePath, "add"));
|
|
6328
|
+
watcher.on("change", (filePath) => this.schedule(filePath, "change"));
|
|
6329
|
+
watcher.on("unlink", (filePath) => this.schedule(filePath, "unlink"));
|
|
6330
|
+
this.watcher = watcher;
|
|
6331
|
+
log.info(
|
|
6332
|
+
"fileWatcher",
|
|
6333
|
+
`watching ${this.opts.workingDir} for session=${this.opts.sessionId.slice(0, 8)}`
|
|
6334
|
+
);
|
|
6335
|
+
}
|
|
6336
|
+
/**
|
|
6337
|
+
* Stop watching. Idempotent — safe to call multiple times. After
|
|
6338
|
+
* stop, the instance is dead; create a new one to resume. (This
|
|
6339
|
+
* matches the `OutputService` / `CommandRelayService` style.)
|
|
6340
|
+
*/
|
|
6341
|
+
async stop() {
|
|
6342
|
+
if (this.stopped) return;
|
|
6343
|
+
this.stopped = true;
|
|
6344
|
+
for (const entry of this.pending.values()) {
|
|
6345
|
+
clearTimeout(entry.timer);
|
|
6346
|
+
}
|
|
6347
|
+
this.pending.clear();
|
|
6348
|
+
if (this.watcher) {
|
|
6349
|
+
try {
|
|
6350
|
+
await this.watcher.close();
|
|
6351
|
+
} catch (err) {
|
|
6352
|
+
log.warn("fileWatcher", "error closing chokidar", err);
|
|
6353
|
+
}
|
|
6354
|
+
this.watcher = null;
|
|
6355
|
+
}
|
|
6356
|
+
log.info("fileWatcher", `stopped (session=${this.opts.sessionId.slice(0, 8)})`);
|
|
6357
|
+
}
|
|
6358
|
+
/**
|
|
6359
|
+
* Coalesce rapid writes per-file. Each fresh event resets the
|
|
6360
|
+
* 250 ms debounce timer. When the timer fires, we compute the
|
|
6361
|
+
* diff once and emit.
|
|
6362
|
+
*
|
|
6363
|
+
* `unlink` events bypass the diff path — we emit a synthetic
|
|
6364
|
+
* deletion directly because `git diff <path>` for a removed file
|
|
6365
|
+
* produces a diff that's already encoded as a deletion in
|
|
6366
|
+
* `parseUnifiedDiff`, but in practice the file is gone and the
|
|
6367
|
+
* synthetic path is simpler and avoids a race with git's index.
|
|
6368
|
+
*/
|
|
6369
|
+
schedule(absPath, changeType) {
|
|
6370
|
+
if (this.stopped) return;
|
|
6371
|
+
const existing = this.pending.get(absPath);
|
|
6372
|
+
if (existing) clearTimeout(existing.timer);
|
|
6373
|
+
const timer = setTimeout(() => {
|
|
6374
|
+
this.pending.delete(absPath);
|
|
6375
|
+
void this.emitForFile(absPath, changeType);
|
|
6376
|
+
}, DEBOUNCE_MS);
|
|
6377
|
+
this.pending.set(absPath, {
|
|
6378
|
+
lastEventAt: Date.now(),
|
|
6379
|
+
timer,
|
|
6380
|
+
changeType
|
|
6381
|
+
});
|
|
6382
|
+
}
|
|
6383
|
+
/**
|
|
6384
|
+
* Visible for tests — lets vitest pump a synthetic file event
|
|
6385
|
+
* through the debounce + diff + emit pipeline without spinning up
|
|
6386
|
+
* a real chokidar watcher.
|
|
6387
|
+
*/
|
|
6388
|
+
/* @internal */
|
|
6389
|
+
_scheduleForTest(absPath, changeType) {
|
|
6390
|
+
this.schedule(absPath, changeType);
|
|
6391
|
+
}
|
|
6392
|
+
async emitForFile(absPath, changeType) {
|
|
6393
|
+
if (this.stopped) return;
|
|
6394
|
+
const relPath = path15.relative(this.opts.workingDir, absPath);
|
|
6395
|
+
if (!relPath || relPath.startsWith("..")) {
|
|
6396
|
+
return;
|
|
6397
|
+
}
|
|
6398
|
+
let diffText = "";
|
|
6399
|
+
let fileStatus = "modified";
|
|
6400
|
+
if (changeType === "unlink") {
|
|
6401
|
+
const diff = await this.gitDiff(relPath);
|
|
6402
|
+
if (diff !== null && diff.trim().length > 0) {
|
|
6403
|
+
diffText = diff;
|
|
6404
|
+
} else {
|
|
6405
|
+
await this.postFileChanged({
|
|
6406
|
+
sessionId: this.opts.sessionId,
|
|
6407
|
+
pluginId: this.opts.pluginId,
|
|
6408
|
+
filePath: relPath,
|
|
6409
|
+
fileStatus: "deleted",
|
|
6410
|
+
linesAdded: 0,
|
|
6411
|
+
linesRemoved: 0,
|
|
6412
|
+
hunkCount: 0
|
|
6413
|
+
});
|
|
6414
|
+
return;
|
|
6415
|
+
}
|
|
6416
|
+
fileStatus = "deleted";
|
|
6417
|
+
} else {
|
|
6418
|
+
const diff = await this.gitDiff(relPath);
|
|
6419
|
+
if (diff === null) {
|
|
6420
|
+
log.warn(
|
|
6421
|
+
"fileWatcher",
|
|
6422
|
+
`git diff failed for ${relPath} \u2014 emitting file-changed only`
|
|
6423
|
+
);
|
|
6424
|
+
await this.postFileChanged({
|
|
6425
|
+
sessionId: this.opts.sessionId,
|
|
6426
|
+
pluginId: this.opts.pluginId,
|
|
6427
|
+
filePath: relPath,
|
|
6428
|
+
fileStatus: changeType === "add" ? "added" : "modified",
|
|
6429
|
+
linesAdded: 0,
|
|
6430
|
+
linesRemoved: 0,
|
|
6431
|
+
hunkCount: 0
|
|
6432
|
+
});
|
|
6433
|
+
return;
|
|
6434
|
+
}
|
|
6435
|
+
diffText = diff;
|
|
6436
|
+
}
|
|
6437
|
+
const parsed = parseUnifiedDiff(diffText);
|
|
6438
|
+
const finalStatus = parsed.fileStatus !== "modified" ? parsed.fileStatus : changeType === "add" ? "added" : changeType === "unlink" ? "deleted" : fileStatus;
|
|
6439
|
+
const reviewStatus = parsed.hunks.length > 0 ? "awaiting_review" : void 0;
|
|
6440
|
+
await this.postFileChanged({
|
|
6441
|
+
sessionId: this.opts.sessionId,
|
|
6442
|
+
pluginId: this.opts.pluginId,
|
|
6443
|
+
filePath: relPath,
|
|
6444
|
+
fileStatus: finalStatus,
|
|
6445
|
+
linesAdded: parsed.totalLinesAdded,
|
|
6446
|
+
linesRemoved: parsed.totalLinesRemoved,
|
|
6447
|
+
hunkCount: parsed.hunks.length,
|
|
6448
|
+
reviewStatus
|
|
6449
|
+
});
|
|
6450
|
+
for (const hunk of parsed.hunks) {
|
|
6451
|
+
await this.postReviewHunk({
|
|
6452
|
+
sessionId: this.opts.sessionId,
|
|
6453
|
+
pluginId: this.opts.pluginId,
|
|
6454
|
+
filePath: relPath,
|
|
6455
|
+
fileStatus: finalStatus,
|
|
6456
|
+
hunkHeader: hunk.header,
|
|
6457
|
+
lines: hunk.lines,
|
|
6458
|
+
linesAdded: hunk.linesAdded,
|
|
6459
|
+
linesRemoved: hunk.linesRemoved
|
|
6460
|
+
});
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
/**
|
|
6464
|
+
* Compute the unified diff for a single path relative to the
|
|
6465
|
+
* working dir. Returns `null` when git is unavailable or the cwd
|
|
6466
|
+
* is not a repo. Returns `''` when there's no diff (a touch that
|
|
6467
|
+
* didn't change content).
|
|
6468
|
+
*
|
|
6469
|
+
* For tracked files we use `git diff --no-color -- <path>` which
|
|
6470
|
+
* compares the worktree against HEAD's blob.
|
|
6471
|
+
* For untracked files (`git ls-files --error-unmatch` exits non-
|
|
6472
|
+
* zero) we use `git diff --no-color --no-index /dev/null <path>`,
|
|
6473
|
+
* which produces an "added"-shaped diff against an empty source.
|
|
6474
|
+
*/
|
|
6475
|
+
async gitDiff(relPath) {
|
|
6476
|
+
const tracked = await runGit(
|
|
6477
|
+
this.opts.workingDir,
|
|
6478
|
+
["diff", "--no-color", "--", relPath]
|
|
6479
|
+
);
|
|
6480
|
+
if (tracked === null) return null;
|
|
6481
|
+
if (tracked.trim().length > 0) return tracked;
|
|
6482
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
6483
|
+
const untracked = await runGit(
|
|
6484
|
+
this.opts.workingDir,
|
|
6485
|
+
["diff", "--no-color", "--no-index", "--", devNull, relPath],
|
|
6486
|
+
{ allowNonZeroExit: true }
|
|
6487
|
+
);
|
|
6488
|
+
return untracked ?? "";
|
|
6489
|
+
}
|
|
6490
|
+
async postFileChanged(body) {
|
|
6491
|
+
await this.postWithRetries(`${this.apiBase}/api/files/changed`, body);
|
|
6492
|
+
}
|
|
6493
|
+
async postReviewHunk(body) {
|
|
6494
|
+
await this.postWithRetries(`${this.apiBase}/api/review/hunks`, body);
|
|
6495
|
+
}
|
|
6496
|
+
async postWithRetries(url, body) {
|
|
6497
|
+
const payload = JSON.stringify(body);
|
|
6498
|
+
const headers = {
|
|
6499
|
+
"Content-Type": "application/json",
|
|
6500
|
+
"X-Codeam-Protocol-Version": "2.0.0",
|
|
6501
|
+
"X-Plugin-Auth-Token": this.opts.pluginAuthToken
|
|
6502
|
+
};
|
|
6503
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {
|
|
6504
|
+
try {
|
|
6505
|
+
const { statusCode, body: resBody } = await _transport3.post(url, headers, payload);
|
|
6506
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
6507
|
+
log.trace(
|
|
6508
|
+
"fileWatcher",
|
|
6509
|
+
`post ok url=${url} status=${statusCode} path=${body.filePath}`
|
|
6510
|
+
);
|
|
6511
|
+
return;
|
|
6512
|
+
}
|
|
6513
|
+
if (statusCode === 410 || statusCode === 404) {
|
|
6514
|
+
log.warn(
|
|
6515
|
+
"fileWatcher",
|
|
6516
|
+
`session dead (status=${statusCode}) \u2014 dropping ${body.filePath}`
|
|
6517
|
+
);
|
|
6518
|
+
this.stopped = true;
|
|
6519
|
+
return;
|
|
6520
|
+
}
|
|
6521
|
+
log.warn(
|
|
6522
|
+
"fileWatcher",
|
|
6523
|
+
`post failed url=${url} status=${statusCode} attempt=${attempt + 1} body=${resBody.slice(0, 200)}`
|
|
6524
|
+
);
|
|
6525
|
+
} catch (err) {
|
|
6526
|
+
log.warn(
|
|
6527
|
+
"fileWatcher",
|
|
6528
|
+
`post error url=${url} attempt=${attempt + 1}`,
|
|
6529
|
+
err
|
|
6530
|
+
);
|
|
6531
|
+
}
|
|
6532
|
+
if (attempt < MAX_RETRIES) {
|
|
6533
|
+
await sleep(RETRY_BACKOFF_MS * (attempt + 1));
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
log.warn(
|
|
6537
|
+
"fileWatcher",
|
|
6538
|
+
`giving up after ${MAX_RETRIES + 1} attempts \u2014 path=${body.filePath}`
|
|
6539
|
+
);
|
|
6540
|
+
}
|
|
6541
|
+
};
|
|
6542
|
+
function sleep(ms) {
|
|
6543
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
6544
|
+
}
|
|
6545
|
+
async function runGit(cwd, args2, opts = {}) {
|
|
6546
|
+
return _runGit(cwd, args2, opts);
|
|
6547
|
+
}
|
|
6548
|
+
var _gitSeam = {
|
|
6549
|
+
run: _runGitImpl
|
|
6550
|
+
};
|
|
6551
|
+
async function _runGitImpl(cwd, args2, opts = {}) {
|
|
6552
|
+
return new Promise((resolve2) => {
|
|
6553
|
+
let proc;
|
|
6554
|
+
try {
|
|
6555
|
+
proc = (0, import_child_process6.spawn)("git", args2, { cwd, env: process.env });
|
|
6556
|
+
} catch {
|
|
6557
|
+
resolve2(null);
|
|
6558
|
+
return;
|
|
6559
|
+
}
|
|
6560
|
+
let stdout = "";
|
|
6561
|
+
let stderr = "";
|
|
6562
|
+
proc.stdout?.on("data", (c2) => {
|
|
6563
|
+
stdout += c2.toString();
|
|
6564
|
+
});
|
|
6565
|
+
proc.stderr?.on("data", (c2) => {
|
|
6566
|
+
stderr += c2.toString();
|
|
6567
|
+
});
|
|
6568
|
+
proc.on("error", () => resolve2(null));
|
|
6569
|
+
proc.on("close", (code) => {
|
|
6570
|
+
if (code === 0 || opts.allowNonZeroExit) {
|
|
6571
|
+
resolve2(stdout);
|
|
6572
|
+
} else {
|
|
6573
|
+
log.trace("fileWatcher", `git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`);
|
|
6574
|
+
resolve2(null);
|
|
6575
|
+
}
|
|
6576
|
+
});
|
|
6577
|
+
});
|
|
6578
|
+
}
|
|
6579
|
+
function _runGit(cwd, args2, opts = {}) {
|
|
6580
|
+
return _gitSeam.run(cwd, args2, opts);
|
|
6581
|
+
}
|
|
6582
|
+
|
|
6128
6583
|
// src/commands/start/quota-fetcher.ts
|
|
6129
6584
|
var inProgress = false;
|
|
6130
6585
|
function fetchQuotaUsage(runtime, historySvc) {
|
|
@@ -6140,13 +6595,13 @@ function fetchQuotaUsage(runtime, historySvc) {
|
|
|
6140
6595
|
}
|
|
6141
6596
|
|
|
6142
6597
|
// src/commands/start/keep-alive.ts
|
|
6143
|
-
var
|
|
6598
|
+
var import_child_process7 = require("child_process");
|
|
6144
6599
|
function buildKeepAlive(ctx) {
|
|
6145
6600
|
let timer = null;
|
|
6146
6601
|
async function setIdleTimeout(minutes) {
|
|
6147
6602
|
if (!ctx.inCodespace || !ctx.codespaceName) return;
|
|
6148
6603
|
await new Promise((resolve2) => {
|
|
6149
|
-
const proc = (0,
|
|
6604
|
+
const proc = (0, import_child_process7.spawn)(
|
|
6150
6605
|
"gh",
|
|
6151
6606
|
[
|
|
6152
6607
|
"api",
|
|
@@ -6185,9 +6640,9 @@ function buildKeepAlive(ctx) {
|
|
|
6185
6640
|
// src/commands/start/handlers.ts
|
|
6186
6641
|
var fs14 = __toESM(require("fs"));
|
|
6187
6642
|
var os13 = __toESM(require("os"));
|
|
6188
|
-
var
|
|
6643
|
+
var path19 = __toESM(require("path"));
|
|
6189
6644
|
var import_crypto2 = require("crypto");
|
|
6190
|
-
var
|
|
6645
|
+
var import_child_process10 = require("child_process");
|
|
6191
6646
|
|
|
6192
6647
|
// src/lib/payload.ts
|
|
6193
6648
|
var import_zod2 = require("zod");
|
|
@@ -6244,7 +6699,7 @@ function parsePayload(schema, raw) {
|
|
|
6244
6699
|
|
|
6245
6700
|
// src/services/file-ops.service.ts
|
|
6246
6701
|
var fs12 = __toESM(require("fs/promises"));
|
|
6247
|
-
var
|
|
6702
|
+
var path16 = __toESM(require("path"));
|
|
6248
6703
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
6249
6704
|
var MAX_WALK_DEPTH = 6;
|
|
6250
6705
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -6279,8 +6734,8 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
6279
6734
|
"__pycache__"
|
|
6280
6735
|
]);
|
|
6281
6736
|
function isUnder(parent, candidate) {
|
|
6282
|
-
const rel =
|
|
6283
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
6737
|
+
const rel = path16.relative(parent, candidate);
|
|
6738
|
+
return rel === "" || !rel.startsWith("..") && !path16.isAbsolute(rel);
|
|
6284
6739
|
}
|
|
6285
6740
|
async function isExistingFile(absPath) {
|
|
6286
6741
|
try {
|
|
@@ -6303,7 +6758,7 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
6303
6758
|
}
|
|
6304
6759
|
for (const e of entries) {
|
|
6305
6760
|
if (!e.isFile()) continue;
|
|
6306
|
-
const full =
|
|
6761
|
+
const full = path16.join(dir, e.name);
|
|
6307
6762
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
6308
6763
|
ctx.matches.push(full);
|
|
6309
6764
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -6313,21 +6768,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
6313
6768
|
if (!e.isDirectory()) continue;
|
|
6314
6769
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
6315
6770
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
6316
|
-
await walkForSuffix(
|
|
6771
|
+
await walkForSuffix(path16.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
6317
6772
|
if (ctx.matches.length >= ctx.cap) return;
|
|
6318
6773
|
}
|
|
6319
6774
|
}
|
|
6320
6775
|
async function findFile(rawPath) {
|
|
6321
6776
|
const cwd = process.cwd();
|
|
6322
|
-
if (
|
|
6323
|
-
const abs =
|
|
6777
|
+
if (path16.isAbsolute(rawPath)) {
|
|
6778
|
+
const abs = path16.normalize(rawPath);
|
|
6324
6779
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
6325
6780
|
}
|
|
6326
|
-
const direct =
|
|
6781
|
+
const direct = path16.resolve(cwd, rawPath);
|
|
6327
6782
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
6328
|
-
const normalized =
|
|
6783
|
+
const normalized = path16.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
6329
6784
|
const needles = [
|
|
6330
|
-
`${
|
|
6785
|
+
`${path16.sep}${normalized}`,
|
|
6331
6786
|
`/${normalized}`
|
|
6332
6787
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
6333
6788
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -6341,7 +6796,7 @@ async function findWriteTarget(rawPath) {
|
|
|
6341
6796
|
const found = await findFile(rawPath);
|
|
6342
6797
|
if (found) return found;
|
|
6343
6798
|
const cwd = process.cwd();
|
|
6344
|
-
const fallback =
|
|
6799
|
+
const fallback = path16.isAbsolute(rawPath) ? path16.normalize(rawPath) : path16.resolve(cwd, rawPath);
|
|
6345
6800
|
if (!isUnder(cwd, fallback)) return null;
|
|
6346
6801
|
return fallback;
|
|
6347
6802
|
}
|
|
@@ -6381,7 +6836,7 @@ async function writeProjectFile(rawPath, content) {
|
|
|
6381
6836
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
6382
6837
|
return { error: "Content too large." };
|
|
6383
6838
|
}
|
|
6384
|
-
await fs12.mkdir(
|
|
6839
|
+
await fs12.mkdir(path16.dirname(abs), { recursive: true });
|
|
6385
6840
|
await fs12.writeFile(abs, content, "utf-8");
|
|
6386
6841
|
return { ok: true };
|
|
6387
6842
|
} catch (e) {
|
|
@@ -6391,11 +6846,11 @@ async function writeProjectFile(rawPath, content) {
|
|
|
6391
6846
|
}
|
|
6392
6847
|
|
|
6393
6848
|
// src/services/project-ops.service.ts
|
|
6394
|
-
var
|
|
6849
|
+
var import_child_process8 = require("child_process");
|
|
6395
6850
|
var import_util2 = require("util");
|
|
6396
6851
|
var fs13 = __toESM(require("fs/promises"));
|
|
6397
|
-
var
|
|
6398
|
-
var execFileP2 = (0, import_util2.promisify)(
|
|
6852
|
+
var path17 = __toESM(require("path"));
|
|
6853
|
+
var execFileP2 = (0, import_util2.promisify)(import_child_process8.execFile);
|
|
6399
6854
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
6400
6855
|
"node_modules",
|
|
6401
6856
|
".git",
|
|
@@ -6452,12 +6907,12 @@ async function listProjectFiles(opts = {}) {
|
|
|
6452
6907
|
return;
|
|
6453
6908
|
}
|
|
6454
6909
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
6455
|
-
const full =
|
|
6910
|
+
const full = path17.join(dir, e.name);
|
|
6456
6911
|
if (e.isDirectory()) {
|
|
6457
6912
|
if (depth >= 12) continue;
|
|
6458
6913
|
await walk(full, depth + 1);
|
|
6459
6914
|
} else if (e.isFile()) {
|
|
6460
|
-
const rel =
|
|
6915
|
+
const rel = path17.relative(root, full);
|
|
6461
6916
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
6462
6917
|
continue;
|
|
6463
6918
|
}
|
|
@@ -6565,7 +7020,7 @@ async function gitStatus(cwd) {
|
|
|
6565
7020
|
let hasMergeInProgress = false;
|
|
6566
7021
|
try {
|
|
6567
7022
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
6568
|
-
const mergeHead =
|
|
7023
|
+
const mergeHead = path17.isAbsolute(gitDir) ? path17.join(gitDir, "MERGE_HEAD") : path17.join(root, gitDir, "MERGE_HEAD");
|
|
6569
7024
|
await fs13.access(mergeHead);
|
|
6570
7025
|
hasMergeInProgress = true;
|
|
6571
7026
|
} catch {
|
|
@@ -6712,7 +7167,7 @@ async function jsSearchFiles(opts, cwd, cap) {
|
|
|
6712
7167
|
}
|
|
6713
7168
|
let content = "";
|
|
6714
7169
|
try {
|
|
6715
|
-
content = await fs13.readFile(
|
|
7170
|
+
content = await fs13.readFile(path17.join(cwd, f.path), "utf8");
|
|
6716
7171
|
} catch {
|
|
6717
7172
|
continue;
|
|
6718
7173
|
}
|
|
@@ -6735,7 +7190,7 @@ async function jsSearchFiles(opts, cwd, cap) {
|
|
|
6735
7190
|
}
|
|
6736
7191
|
|
|
6737
7192
|
// src/services/terminal-ops.service.ts
|
|
6738
|
-
var
|
|
7193
|
+
var import_child_process9 = require("child_process");
|
|
6739
7194
|
var import_crypto = require("crypto");
|
|
6740
7195
|
var import_path = __toESM(require("path"));
|
|
6741
7196
|
var MAX_CONCURRENT_SESSIONS = 4;
|
|
@@ -6857,7 +7312,7 @@ function createPythonSession(id, shell, cwd, env, cols, rows) {
|
|
|
6857
7312
|
}
|
|
6858
7313
|
let child;
|
|
6859
7314
|
try {
|
|
6860
|
-
child = (0,
|
|
7315
|
+
child = (0, import_child_process9.spawn)(python, ["-c", PYTHON_TERMINAL_HELPER, shell], {
|
|
6861
7316
|
cwd,
|
|
6862
7317
|
env: { ...env, COLUMNS: String(cols), LINES: String(rows) },
|
|
6863
7318
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6992,7 +7447,7 @@ function closeTerminal(sessionId) {
|
|
|
6992
7447
|
function saveFilesTemp(files) {
|
|
6993
7448
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
6994
7449
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
6995
|
-
const tmpPath =
|
|
7450
|
+
const tmpPath = path19.join(os13.tmpdir(), `codeam-${(0, import_crypto2.randomUUID)()}-${safeName}`);
|
|
6996
7451
|
fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
6997
7452
|
return tmpPath;
|
|
6998
7453
|
});
|
|
@@ -7124,7 +7579,7 @@ var sessionTerminated = (ctx) => {
|
|
|
7124
7579
|
} catch {
|
|
7125
7580
|
}
|
|
7126
7581
|
try {
|
|
7127
|
-
const proc = (0,
|
|
7582
|
+
const proc = (0, import_child_process10.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
7128
7583
|
detached: true,
|
|
7129
7584
|
stdio: "ignore"
|
|
7130
7585
|
});
|
|
@@ -7146,7 +7601,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
7146
7601
|
}
|
|
7147
7602
|
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
7148
7603
|
try {
|
|
7149
|
-
const stopProc = (0,
|
|
7604
|
+
const stopProc = (0, import_child_process10.spawn)(
|
|
7150
7605
|
"bash",
|
|
7151
7606
|
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
7152
7607
|
{ detached: true, stdio: "ignore" }
|
|
@@ -7156,7 +7611,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
7156
7611
|
}
|
|
7157
7612
|
}
|
|
7158
7613
|
try {
|
|
7159
|
-
const proc = (0,
|
|
7614
|
+
const proc = (0, import_child_process10.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
7160
7615
|
detached: true,
|
|
7161
7616
|
stdio: "ignore"
|
|
7162
7617
|
});
|
|
@@ -7375,6 +7830,12 @@ async function start(requestedAgent) {
|
|
|
7375
7830
|
session.pluginAuthToken,
|
|
7376
7831
|
runtime
|
|
7377
7832
|
);
|
|
7833
|
+
const fileWatcher = session.pluginAuthToken ? new FileWatcherService({
|
|
7834
|
+
workingDir: cwd,
|
|
7835
|
+
sessionId: session.id,
|
|
7836
|
+
pluginId,
|
|
7837
|
+
pluginAuthToken: session.pluginAuthToken
|
|
7838
|
+
}) : null;
|
|
7378
7839
|
const claude = new AgentService(
|
|
7379
7840
|
runtime,
|
|
7380
7841
|
{
|
|
@@ -7386,6 +7847,7 @@ async function start(requestedAgent) {
|
|
|
7386
7847
|
process.removeListener("SIGINT", sigintHandler);
|
|
7387
7848
|
outputSvc.dispose();
|
|
7388
7849
|
relay.stop();
|
|
7850
|
+
void fileWatcher?.stop();
|
|
7389
7851
|
process.exit(code);
|
|
7390
7852
|
}
|
|
7391
7853
|
}
|
|
@@ -7415,12 +7877,17 @@ async function start(requestedAgent) {
|
|
|
7415
7877
|
claude.kill();
|
|
7416
7878
|
outputSvc.dispose();
|
|
7417
7879
|
relay.stop();
|
|
7880
|
+
void fileWatcher?.stop();
|
|
7418
7881
|
process.exit(0);
|
|
7419
7882
|
}
|
|
7420
7883
|
process.once("SIGINT", sigintHandler);
|
|
7421
7884
|
await claude.spawn();
|
|
7422
7885
|
await outputSvc.startTerminalTurn();
|
|
7423
7886
|
relay.start();
|
|
7887
|
+
if (fileWatcher) {
|
|
7888
|
+
fileWatcher.start().catch(() => {
|
|
7889
|
+
});
|
|
7890
|
+
}
|
|
7424
7891
|
setTimeout(() => {
|
|
7425
7892
|
historySvc.load().catch(() => {
|
|
7426
7893
|
});
|
|
@@ -7549,7 +8016,7 @@ async function pair(args2 = []) {
|
|
|
7549
8016
|
var fs15 = __toESM(require("fs"));
|
|
7550
8017
|
var os14 = __toESM(require("os"));
|
|
7551
8018
|
var import_crypto4 = require("crypto");
|
|
7552
|
-
var
|
|
8019
|
+
var API_BASE6 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
7553
8020
|
function fail(msg) {
|
|
7554
8021
|
console.error(`
|
|
7555
8022
|
${msg}
|
|
@@ -7564,12 +8031,12 @@ function readTokenFromArgs(args2) {
|
|
|
7564
8031
|
}
|
|
7565
8032
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
7566
8033
|
if (fileFlag) {
|
|
7567
|
-
const
|
|
8034
|
+
const path25 = fileFlag.slice("--token-file=".length);
|
|
7568
8035
|
try {
|
|
7569
|
-
const content = fs15.readFileSync(
|
|
7570
|
-
if (content.length === 0) fail(`--token-file ${
|
|
8036
|
+
const content = fs15.readFileSync(path25, "utf8").trim();
|
|
8037
|
+
if (content.length === 0) fail(`--token-file ${path25} is empty`);
|
|
7571
8038
|
try {
|
|
7572
|
-
fs15.unlinkSync(
|
|
8039
|
+
fs15.unlinkSync(path25);
|
|
7573
8040
|
} catch {
|
|
7574
8041
|
}
|
|
7575
8042
|
return content;
|
|
@@ -7581,7 +8048,7 @@ function readTokenFromArgs(args2) {
|
|
|
7581
8048
|
fail("codeam pair-auto requires --token-file=<path>, --token=<value>, or CODEAM_AUTO_TOKEN env");
|
|
7582
8049
|
}
|
|
7583
8050
|
async function claim(token, pluginId) {
|
|
7584
|
-
const url = `${
|
|
8051
|
+
const url = `${API_BASE6}/api/pairing/claim-auto-token`;
|
|
7585
8052
|
const body = {
|
|
7586
8053
|
token,
|
|
7587
8054
|
pluginId,
|
|
@@ -7736,11 +8203,11 @@ async function logout() {
|
|
|
7736
8203
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
7737
8204
|
|
|
7738
8205
|
// src/services/providers/github-codespaces.ts
|
|
7739
|
-
var
|
|
8206
|
+
var import_child_process11 = require("child_process");
|
|
7740
8207
|
var import_util3 = require("util");
|
|
7741
8208
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
7742
|
-
var
|
|
7743
|
-
var execFileP3 = (0, import_util3.promisify)(
|
|
8209
|
+
var path20 = __toESM(require("path"));
|
|
8210
|
+
var execFileP3 = (0, import_util3.promisify)(import_child_process11.execFile);
|
|
7744
8211
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
7745
8212
|
function resetStdinForChild() {
|
|
7746
8213
|
if (process.stdin.isTTY) {
|
|
@@ -7784,7 +8251,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7784
8251
|
if (!isAuthed) {
|
|
7785
8252
|
resetStdinForChild();
|
|
7786
8253
|
await new Promise((resolve2, reject) => {
|
|
7787
|
-
const proc = (0,
|
|
8254
|
+
const proc = (0, import_child_process11.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
7788
8255
|
stdio: "inherit"
|
|
7789
8256
|
});
|
|
7790
8257
|
proc.on("exit", (code) => {
|
|
@@ -7818,7 +8285,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7818
8285
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
7819
8286
|
resetStdinForChild();
|
|
7820
8287
|
const refreshCode = await new Promise((resolve2, reject) => {
|
|
7821
|
-
const proc = (0,
|
|
8288
|
+
const proc = (0, import_child_process11.spawn)(
|
|
7822
8289
|
"gh",
|
|
7823
8290
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
7824
8291
|
{ stdio: "inherit" }
|
|
@@ -7968,7 +8435,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7968
8435
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
7969
8436
|
resetStdinForChild();
|
|
7970
8437
|
const ok = await new Promise((resolve2) => {
|
|
7971
|
-
const proc = (0,
|
|
8438
|
+
const proc = (0, import_child_process11.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
7972
8439
|
proc.on("exit", (code) => resolve2(code === 0));
|
|
7973
8440
|
proc.on("error", () => resolve2(false));
|
|
7974
8441
|
});
|
|
@@ -7995,7 +8462,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7995
8462
|
);
|
|
7996
8463
|
resetStdinForChild();
|
|
7997
8464
|
await new Promise((resolve2, reject) => {
|
|
7998
|
-
const proc = (0,
|
|
8465
|
+
const proc = (0, import_child_process11.spawn)(
|
|
7999
8466
|
"gh",
|
|
8000
8467
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
8001
8468
|
{ stdio: "inherit" }
|
|
@@ -8173,7 +8640,7 @@ var GitHubCodespacesProvider = class {
|
|
|
8173
8640
|
async streamCommand(workspaceId, command2) {
|
|
8174
8641
|
resetStdinForChild();
|
|
8175
8642
|
return new Promise((resolve2, reject) => {
|
|
8176
|
-
const proc = (0,
|
|
8643
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8177
8644
|
"gh",
|
|
8178
8645
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
8179
8646
|
{ stdio: "inherit" }
|
|
@@ -8200,11 +8667,11 @@ var GitHubCodespacesProvider = class {
|
|
|
8200
8667
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
8201
8668
|
];
|
|
8202
8669
|
await new Promise((resolve2, reject) => {
|
|
8203
|
-
const tar = (0,
|
|
8670
|
+
const tar = (0, import_child_process11.spawn)("tar", tarArgs, {
|
|
8204
8671
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8205
8672
|
env: tarEnv
|
|
8206
8673
|
});
|
|
8207
|
-
const ssh = (0,
|
|
8674
|
+
const ssh = (0, import_child_process11.spawn)("gh", sshArgs, {
|
|
8208
8675
|
stdio: [tar.stdout, "pipe", "pipe"]
|
|
8209
8676
|
});
|
|
8210
8677
|
let tarErr = "";
|
|
@@ -8228,7 +8695,7 @@ var GitHubCodespacesProvider = class {
|
|
|
8228
8695
|
});
|
|
8229
8696
|
}
|
|
8230
8697
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8231
|
-
const remoteDir =
|
|
8698
|
+
const remoteDir = path20.posix.dirname(remotePath);
|
|
8232
8699
|
const parts = [
|
|
8233
8700
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
8234
8701
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -8238,7 +8705,7 @@ var GitHubCodespacesProvider = class {
|
|
|
8238
8705
|
}
|
|
8239
8706
|
const cmd = parts.join(" && ");
|
|
8240
8707
|
await new Promise((resolve2, reject) => {
|
|
8241
|
-
const proc = (0,
|
|
8708
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8242
8709
|
"gh",
|
|
8243
8710
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
8244
8711
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8296,11 +8763,11 @@ function shellQuote(s) {
|
|
|
8296
8763
|
}
|
|
8297
8764
|
|
|
8298
8765
|
// src/services/providers/gitpod.ts
|
|
8299
|
-
var
|
|
8766
|
+
var import_child_process12 = require("child_process");
|
|
8300
8767
|
var import_util4 = require("util");
|
|
8301
|
-
var
|
|
8768
|
+
var path21 = __toESM(require("path"));
|
|
8302
8769
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
8303
|
-
var execFileP4 = (0, import_util4.promisify)(
|
|
8770
|
+
var execFileP4 = (0, import_util4.promisify)(import_child_process12.execFile);
|
|
8304
8771
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
8305
8772
|
function resetStdinForChild2() {
|
|
8306
8773
|
if (process.stdin.isTTY) {
|
|
@@ -8340,7 +8807,7 @@ var GitpodProvider = class {
|
|
|
8340
8807
|
);
|
|
8341
8808
|
resetStdinForChild2();
|
|
8342
8809
|
await new Promise((resolve2, reject) => {
|
|
8343
|
-
const proc = (0,
|
|
8810
|
+
const proc = (0, import_child_process12.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
8344
8811
|
proc.on("exit", (code) => {
|
|
8345
8812
|
if (code === 0) resolve2();
|
|
8346
8813
|
else reject(new Error("gitpod login failed."));
|
|
@@ -8492,7 +8959,7 @@ var GitpodProvider = class {
|
|
|
8492
8959
|
async streamCommand(workspaceId, command2) {
|
|
8493
8960
|
resetStdinForChild2();
|
|
8494
8961
|
return new Promise((resolve2, reject) => {
|
|
8495
|
-
const proc = (0,
|
|
8962
|
+
const proc = (0, import_child_process12.spawn)(
|
|
8496
8963
|
"gitpod",
|
|
8497
8964
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
8498
8965
|
{ stdio: "inherit" }
|
|
@@ -8512,11 +8979,11 @@ var GitpodProvider = class {
|
|
|
8512
8979
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8513
8980
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
8514
8981
|
await new Promise((resolve2, reject) => {
|
|
8515
|
-
const tar = (0,
|
|
8982
|
+
const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
|
|
8516
8983
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8517
8984
|
env: tarEnv
|
|
8518
8985
|
});
|
|
8519
|
-
const ssh = (0,
|
|
8986
|
+
const ssh = (0, import_child_process12.spawn)(
|
|
8520
8987
|
"gitpod",
|
|
8521
8988
|
["workspace", "ssh", workspaceId, "--", remoteCmd],
|
|
8522
8989
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8538,7 +9005,7 @@ var GitpodProvider = class {
|
|
|
8538
9005
|
});
|
|
8539
9006
|
}
|
|
8540
9007
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8541
|
-
const remoteDir =
|
|
9008
|
+
const remoteDir = path21.posix.dirname(remotePath);
|
|
8542
9009
|
const parts = [
|
|
8543
9010
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
8544
9011
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -8548,7 +9015,7 @@ var GitpodProvider = class {
|
|
|
8548
9015
|
}
|
|
8549
9016
|
const cmd = parts.join(" && ");
|
|
8550
9017
|
await new Promise((resolve2, reject) => {
|
|
8551
|
-
const proc = (0,
|
|
9018
|
+
const proc = (0, import_child_process12.spawn)(
|
|
8552
9019
|
"gitpod",
|
|
8553
9020
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
8554
9021
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8572,10 +9039,10 @@ function shellQuote2(s) {
|
|
|
8572
9039
|
}
|
|
8573
9040
|
|
|
8574
9041
|
// src/services/providers/gitlab-workspaces.ts
|
|
8575
|
-
var
|
|
9042
|
+
var import_child_process13 = require("child_process");
|
|
8576
9043
|
var import_util5 = require("util");
|
|
8577
|
-
var
|
|
8578
|
-
var execFileP5 = (0, import_util5.promisify)(
|
|
9044
|
+
var path22 = __toESM(require("path"));
|
|
9045
|
+
var execFileP5 = (0, import_util5.promisify)(import_child_process13.execFile);
|
|
8579
9046
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
8580
9047
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
8581
9048
|
function resetStdinForChild3() {
|
|
@@ -8617,7 +9084,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8617
9084
|
);
|
|
8618
9085
|
resetStdinForChild3();
|
|
8619
9086
|
await new Promise((resolve2, reject) => {
|
|
8620
|
-
const proc = (0,
|
|
9087
|
+
const proc = (0, import_child_process13.spawn)(
|
|
8621
9088
|
"glab",
|
|
8622
9089
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
8623
9090
|
{ stdio: "inherit" }
|
|
@@ -8789,7 +9256,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8789
9256
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8790
9257
|
resetStdinForChild3();
|
|
8791
9258
|
return new Promise((resolve2, reject) => {
|
|
8792
|
-
const proc = (0,
|
|
9259
|
+
const proc = (0, import_child_process13.spawn)(
|
|
8793
9260
|
"ssh",
|
|
8794
9261
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
8795
9262
|
{ stdio: "inherit" }
|
|
@@ -8810,8 +9277,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8810
9277
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8811
9278
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
8812
9279
|
await new Promise((resolve2, reject) => {
|
|
8813
|
-
const tar = (0,
|
|
8814
|
-
const ssh = (0,
|
|
9280
|
+
const tar = (0, import_child_process13.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
9281
|
+
const ssh = (0, import_child_process13.spawn)(
|
|
8815
9282
|
"ssh",
|
|
8816
9283
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
|
|
8817
9284
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8834,14 +9301,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8834
9301
|
}
|
|
8835
9302
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8836
9303
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8837
|
-
const remoteDir =
|
|
9304
|
+
const remoteDir = path22.posix.dirname(remotePath);
|
|
8838
9305
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
8839
9306
|
if (options.mode != null) {
|
|
8840
9307
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
8841
9308
|
}
|
|
8842
9309
|
const cmd = parts.join(" && ");
|
|
8843
9310
|
await new Promise((resolve2, reject) => {
|
|
8844
|
-
const proc = (0,
|
|
9311
|
+
const proc = (0, import_child_process13.spawn)(
|
|
8845
9312
|
"ssh",
|
|
8846
9313
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
8847
9314
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8900,10 +9367,10 @@ function shellQuote3(s) {
|
|
|
8900
9367
|
}
|
|
8901
9368
|
|
|
8902
9369
|
// src/services/providers/railway.ts
|
|
8903
|
-
var
|
|
9370
|
+
var import_child_process14 = require("child_process");
|
|
8904
9371
|
var import_util6 = require("util");
|
|
8905
|
-
var
|
|
8906
|
-
var execFileP6 = (0, import_util6.promisify)(
|
|
9372
|
+
var path23 = __toESM(require("path"));
|
|
9373
|
+
var execFileP6 = (0, import_util6.promisify)(import_child_process14.execFile);
|
|
8907
9374
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
8908
9375
|
function resetStdinForChild4() {
|
|
8909
9376
|
if (process.stdin.isTTY) {
|
|
@@ -8944,7 +9411,7 @@ var RailwayProvider = class {
|
|
|
8944
9411
|
);
|
|
8945
9412
|
resetStdinForChild4();
|
|
8946
9413
|
await new Promise((resolve2, reject) => {
|
|
8947
|
-
const proc = (0,
|
|
9414
|
+
const proc = (0, import_child_process14.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
8948
9415
|
proc.on("exit", (code) => {
|
|
8949
9416
|
if (code === 0) resolve2();
|
|
8950
9417
|
else reject(new Error("railway login failed."));
|
|
@@ -9087,7 +9554,7 @@ var RailwayProvider = class {
|
|
|
9087
9554
|
}
|
|
9088
9555
|
resetStdinForChild4();
|
|
9089
9556
|
return new Promise((resolve2, reject) => {
|
|
9090
|
-
const proc = (0,
|
|
9557
|
+
const proc = (0, import_child_process14.spawn)(
|
|
9091
9558
|
"railway",
|
|
9092
9559
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
9093
9560
|
{ stdio: "inherit" }
|
|
@@ -9111,8 +9578,8 @@ var RailwayProvider = class {
|
|
|
9111
9578
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
9112
9579
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
9113
9580
|
await new Promise((resolve2, reject) => {
|
|
9114
|
-
const tar = (0,
|
|
9115
|
-
const sh = (0,
|
|
9581
|
+
const tar = (0, import_child_process14.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
9582
|
+
const sh = (0, import_child_process14.spawn)(
|
|
9116
9583
|
"railway",
|
|
9117
9584
|
["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
|
|
9118
9585
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -9138,14 +9605,14 @@ var RailwayProvider = class {
|
|
|
9138
9605
|
if (!projectId || !serviceId) {
|
|
9139
9606
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
9140
9607
|
}
|
|
9141
|
-
const remoteDir =
|
|
9608
|
+
const remoteDir = path23.posix.dirname(remotePath);
|
|
9142
9609
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
9143
9610
|
if (options.mode != null) {
|
|
9144
9611
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
9145
9612
|
}
|
|
9146
9613
|
const cmd = parts.join(" && ");
|
|
9147
9614
|
await new Promise((resolve2, reject) => {
|
|
9148
|
-
const proc = (0,
|
|
9615
|
+
const proc = (0, import_child_process14.spawn)(
|
|
9149
9616
|
"railway",
|
|
9150
9617
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
9151
9618
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -9680,7 +10147,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
9680
10147
|
// src/commands/version.ts
|
|
9681
10148
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
9682
10149
|
function version() {
|
|
9683
|
-
const v = true ? "2.15.
|
|
10150
|
+
const v = true ? "2.15.5" : "unknown";
|
|
9684
10151
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
9685
10152
|
}
|
|
9686
10153
|
|
|
@@ -9723,16 +10190,16 @@ function help() {
|
|
|
9723
10190
|
// src/lib/updateNotifier.ts
|
|
9724
10191
|
var fs16 = __toESM(require("fs"));
|
|
9725
10192
|
var os15 = __toESM(require("os"));
|
|
9726
|
-
var
|
|
9727
|
-
var
|
|
10193
|
+
var path24 = __toESM(require("path"));
|
|
10194
|
+
var https6 = __toESM(require("https"));
|
|
9728
10195
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
9729
10196
|
var PKG_NAME = "codeam-cli";
|
|
9730
10197
|
var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
9731
10198
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9732
10199
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
9733
10200
|
function cachePath() {
|
|
9734
|
-
const dir =
|
|
9735
|
-
return
|
|
10201
|
+
const dir = path24.join(os15.homedir(), ".codeam");
|
|
10202
|
+
return path24.join(dir, "update-check.json");
|
|
9736
10203
|
}
|
|
9737
10204
|
function readCache() {
|
|
9738
10205
|
try {
|
|
@@ -9747,7 +10214,7 @@ function readCache() {
|
|
|
9747
10214
|
function writeCache(cache) {
|
|
9748
10215
|
try {
|
|
9749
10216
|
const file = cachePath();
|
|
9750
|
-
fs16.mkdirSync(
|
|
10217
|
+
fs16.mkdirSync(path24.dirname(file), { recursive: true });
|
|
9751
10218
|
fs16.writeFileSync(file, JSON.stringify(cache));
|
|
9752
10219
|
} catch {
|
|
9753
10220
|
}
|
|
@@ -9767,7 +10234,7 @@ function compareSemver(a, b) {
|
|
|
9767
10234
|
}
|
|
9768
10235
|
function fetchLatest() {
|
|
9769
10236
|
return new Promise((resolve2) => {
|
|
9770
|
-
const req =
|
|
10237
|
+
const req = https6.get(
|
|
9771
10238
|
REGISTRY_URL,
|
|
9772
10239
|
{ headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
|
|
9773
10240
|
(res) => {
|
|
@@ -9819,7 +10286,7 @@ function checkForUpdates() {
|
|
|
9819
10286
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
9820
10287
|
if (process.env.CI) return;
|
|
9821
10288
|
if (!process.stdout.isTTY) return;
|
|
9822
|
-
const current = true ? "2.15.
|
|
10289
|
+
const current = true ? "2.15.5" : null;
|
|
9823
10290
|
if (!current) return;
|
|
9824
10291
|
const cache = readCache();
|
|
9825
10292
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.15.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.15.5",
|
|
4
|
+
"description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -24,29 +24,29 @@
|
|
|
24
24
|
"keywords": [
|
|
25
25
|
"claude",
|
|
26
26
|
"claude-code",
|
|
27
|
-
"claude-code-mobile",
|
|
28
27
|
"ai",
|
|
29
28
|
"ai-agent",
|
|
30
29
|
"ai-coding",
|
|
31
|
-
"ai-
|
|
30
|
+
"ai-workflow",
|
|
31
|
+
"ai-workflow-continuity",
|
|
32
|
+
"async-ai-productivity",
|
|
33
|
+
"remote-ai-supervision",
|
|
32
34
|
"cli",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"mobile-ide",
|
|
36
|
-
"mobile-coding",
|
|
37
|
-
"pair-programming",
|
|
35
|
+
"developer-tools",
|
|
36
|
+
"devtools",
|
|
38
37
|
"codeagent",
|
|
39
38
|
"codeam",
|
|
40
39
|
"anthropic",
|
|
40
|
+
"openai-codex",
|
|
41
41
|
"cursor",
|
|
42
42
|
"copilot",
|
|
43
43
|
"jetbrains",
|
|
44
44
|
"vscode",
|
|
45
|
+
"windsurf",
|
|
45
46
|
"mcp",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"agent-control"
|
|
47
|
+
"remote-development",
|
|
48
|
+
"agent-control",
|
|
49
|
+
"agent-operations"
|
|
50
50
|
],
|
|
51
51
|
"homepage": "https://codeagent-mobile.com",
|
|
52
52
|
"bugs": {
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@clack/prompts": "^1.2.0",
|
|
73
|
+
"chokidar": "^3.6.0",
|
|
73
74
|
"picocolors": "^1.1.0",
|
|
74
75
|
"qrcode-terminal": "^0.12.0",
|
|
75
76
|
"ws": "^8.18.0",
|