codeam-cli 2.15.1 → 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/CHANGELOG.md +7 -0
- package/README.md +7 -5
- package/dist/index.js +736 -126
- package/package.json +14 -13
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,6 +7190,7 @@ async function jsSearchFiles(opts, cwd, cap) {
|
|
|
6735
7190
|
}
|
|
6736
7191
|
|
|
6737
7192
|
// src/services/terminal-ops.service.ts
|
|
7193
|
+
var import_child_process9 = require("child_process");
|
|
6738
7194
|
var import_crypto = require("crypto");
|
|
6739
7195
|
var import_path = __toESM(require("path"));
|
|
6740
7196
|
var MAX_CONCURRENT_SESSIONS = 4;
|
|
@@ -6768,6 +7224,135 @@ function defaultShell() {
|
|
|
6768
7224
|
}
|
|
6769
7225
|
return process.env.SHELL ?? "/bin/bash";
|
|
6770
7226
|
}
|
|
7227
|
+
var PYTHON_TERMINAL_HELPER = `import os,pty,sys,select,signal,struct,fcntl,termios,errno,re
|
|
7228
|
+
m,s=pty.openpty()
|
|
7229
|
+
try:
|
|
7230
|
+
cols=int(os.environ.get('COLUMNS','80'))
|
|
7231
|
+
rows=int(os.environ.get('LINES','24'))
|
|
7232
|
+
fcntl.ioctl(s,termios.TIOCSWINSZ,struct.pack('HHHH',rows,cols,0,0))
|
|
7233
|
+
except Exception:pass
|
|
7234
|
+
pid=os.fork()
|
|
7235
|
+
if pid==0:
|
|
7236
|
+
os.close(m)
|
|
7237
|
+
os.setsid()
|
|
7238
|
+
try:fcntl.ioctl(s,termios.TIOCSCTTY,0)
|
|
7239
|
+
except Exception:pass
|
|
7240
|
+
for fd in[0,1,2]:os.dup2(s,fd)
|
|
7241
|
+
if s>2:os.close(s)
|
|
7242
|
+
os.execvp(sys.argv[1],sys.argv[1:])
|
|
7243
|
+
sys.exit(127)
|
|
7244
|
+
os.close(s)
|
|
7245
|
+
done=[False]
|
|
7246
|
+
def onchld(n,f):
|
|
7247
|
+
try:os.waitpid(pid,os.WNOHANG)
|
|
7248
|
+
except Exception:pass
|
|
7249
|
+
done[0]=True
|
|
7250
|
+
signal.signal(signal.SIGCHLD,onchld)
|
|
7251
|
+
signal.signal(signal.SIGHUP,signal.SIG_IGN)
|
|
7252
|
+
i=sys.stdin.fileno()
|
|
7253
|
+
o=sys.stdout.fileno()
|
|
7254
|
+
in_buf=b''
|
|
7255
|
+
resize_re=re.compile(rb'\\x00CW (\\d+) (\\d+)\\n')
|
|
7256
|
+
while not done[0]:
|
|
7257
|
+
try:r,_,_=select.select([i,m],[],[],0.1)
|
|
7258
|
+
except OSError as e:
|
|
7259
|
+
if e.errno==errno.EINTR:continue
|
|
7260
|
+
break
|
|
7261
|
+
if i in r:
|
|
7262
|
+
try:
|
|
7263
|
+
d=os.read(i,4096)
|
|
7264
|
+
if not d:break
|
|
7265
|
+
in_buf+=d
|
|
7266
|
+
while True:
|
|
7267
|
+
mo=resize_re.search(in_buf)
|
|
7268
|
+
if not mo:break
|
|
7269
|
+
try:
|
|
7270
|
+
rows=int(mo.group(1));cols=int(mo.group(2))
|
|
7271
|
+
fcntl.ioctl(m,termios.TIOCSWINSZ,struct.pack('HHHH',rows,cols,0,0))
|
|
7272
|
+
except Exception:pass
|
|
7273
|
+
in_buf=in_buf[:mo.start()]+in_buf[mo.end():]
|
|
7274
|
+
if in_buf:
|
|
7275
|
+
# Don't forward a dangling NUL that might be the
|
|
7276
|
+
# start of an incomplete resize marker \u2014 hold it
|
|
7277
|
+
# until the next read so the regex matches.
|
|
7278
|
+
nul=in_buf.rfind(b'\\x00')
|
|
7279
|
+
if nul>=0 and len(in_buf)-nul<32:
|
|
7280
|
+
tail=in_buf[nul:];body=in_buf[:nul]
|
|
7281
|
+
if body:os.write(m,body)
|
|
7282
|
+
in_buf=tail
|
|
7283
|
+
else:
|
|
7284
|
+
os.write(m,in_buf);in_buf=b''
|
|
7285
|
+
except OSError:break
|
|
7286
|
+
if m in r:
|
|
7287
|
+
try:
|
|
7288
|
+
d=os.read(m,4096)
|
|
7289
|
+
if d:os.write(o,d)
|
|
7290
|
+
except OSError:done[0]=True
|
|
7291
|
+
try:os.kill(pid,signal.SIGTERM)
|
|
7292
|
+
except Exception:pass
|
|
7293
|
+
try:
|
|
7294
|
+
_,st=os.waitpid(pid,0)
|
|
7295
|
+
sys.exit((st>>8)&0xFF)
|
|
7296
|
+
except Exception:sys.exit(0)
|
|
7297
|
+
`;
|
|
7298
|
+
function findPython3() {
|
|
7299
|
+
for (const name of ["python3", "python"]) {
|
|
7300
|
+
try {
|
|
7301
|
+
const out = require("child_process").spawnSync("which", [name], { encoding: "utf8" });
|
|
7302
|
+
if (out.status === 0 && out.stdout?.trim()) return out.stdout.trim();
|
|
7303
|
+
} catch {
|
|
7304
|
+
}
|
|
7305
|
+
}
|
|
7306
|
+
return null;
|
|
7307
|
+
}
|
|
7308
|
+
function createPythonSession(id, shell, cwd, env, cols, rows) {
|
|
7309
|
+
const python = findPython3();
|
|
7310
|
+
if (!python) {
|
|
7311
|
+
return { error: "python3 not found on PATH \u2014 required for terminal sessions on Linux/macOS without node-pty." };
|
|
7312
|
+
}
|
|
7313
|
+
let child;
|
|
7314
|
+
try {
|
|
7315
|
+
child = (0, import_child_process9.spawn)(python, ["-c", PYTHON_TERMINAL_HELPER, shell], {
|
|
7316
|
+
cwd,
|
|
7317
|
+
env: { ...env, COLUMNS: String(cols), LINES: String(rows) },
|
|
7318
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
7319
|
+
});
|
|
7320
|
+
} catch (e) {
|
|
7321
|
+
return { error: e instanceof Error ? e.message : "python spawn failed" };
|
|
7322
|
+
}
|
|
7323
|
+
child.stdout.on("data", (buf) => {
|
|
7324
|
+
onDataHandler?.({ sessionId: id, data: buf.toString("utf8") });
|
|
7325
|
+
});
|
|
7326
|
+
child.stderr.on("data", (buf) => {
|
|
7327
|
+
onDataHandler?.({ sessionId: id, data: buf.toString("utf8") });
|
|
7328
|
+
});
|
|
7329
|
+
child.on("exit", (code) => {
|
|
7330
|
+
onExitHandler?.({ sessionId: id, exitCode: code ?? 0 });
|
|
7331
|
+
sessions.delete(id);
|
|
7332
|
+
});
|
|
7333
|
+
return {
|
|
7334
|
+
id,
|
|
7335
|
+
write(data) {
|
|
7336
|
+
try {
|
|
7337
|
+
child.stdin.write(data);
|
|
7338
|
+
} catch {
|
|
7339
|
+
}
|
|
7340
|
+
},
|
|
7341
|
+
resize(cs, rs) {
|
|
7342
|
+
try {
|
|
7343
|
+
child.stdin.write(`\0CW ${rs} ${cs}
|
|
7344
|
+
`);
|
|
7345
|
+
} catch {
|
|
7346
|
+
}
|
|
7347
|
+
},
|
|
7348
|
+
kill() {
|
|
7349
|
+
try {
|
|
7350
|
+
child.kill("SIGTERM");
|
|
7351
|
+
} catch {
|
|
7352
|
+
}
|
|
7353
|
+
}
|
|
7354
|
+
};
|
|
7355
|
+
}
|
|
6771
7356
|
function openTerminal(opts) {
|
|
6772
7357
|
if (sessions.size >= MAX_CONCURRENT_SESSIONS) {
|
|
6773
7358
|
return { error: `Too many open terminals (max ${MAX_CONCURRENT_SESSIONS})` };
|
|
@@ -6777,46 +7362,61 @@ function openTerminal(opts) {
|
|
|
6777
7362
|
const env = {
|
|
6778
7363
|
...process.env,
|
|
6779
7364
|
TERM: "xterm-256color",
|
|
6780
|
-
COLORTERM: "truecolor"
|
|
7365
|
+
COLORTERM: "truecolor",
|
|
7366
|
+
FORCE_COLOR: "1"
|
|
6781
7367
|
};
|
|
6782
|
-
|
|
7368
|
+
const cols = Math.max(1, Math.min(opts.cols ?? 80, 500));
|
|
7369
|
+
const rows = Math.max(1, Math.min(opts.rows ?? 24, 200));
|
|
7370
|
+
const id = (0, import_crypto.randomUUID)();
|
|
6783
7371
|
const ptyMod = loadNodePty2();
|
|
6784
|
-
if (
|
|
7372
|
+
if (ptyMod) {
|
|
7373
|
+
try {
|
|
7374
|
+
const term = ptyMod.spawn(shell, [], {
|
|
7375
|
+
name: "xterm-256color",
|
|
7376
|
+
cols,
|
|
7377
|
+
rows,
|
|
7378
|
+
cwd,
|
|
7379
|
+
env,
|
|
7380
|
+
useConpty: process.platform === "win32" ? true : void 0
|
|
7381
|
+
});
|
|
7382
|
+
const dataListener = term.onData((data) => {
|
|
7383
|
+
onDataHandler?.({ sessionId: id, data });
|
|
7384
|
+
});
|
|
7385
|
+
const exitListener = term.onExit(({ exitCode }) => {
|
|
7386
|
+
onExitHandler?.({ sessionId: id, exitCode });
|
|
7387
|
+
sessions.delete(id);
|
|
7388
|
+
});
|
|
7389
|
+
sessions.set(id, {
|
|
7390
|
+
id,
|
|
7391
|
+
write: (d3) => term.write(d3),
|
|
7392
|
+
resize: (cs, rs) => term.resize(cs, rs),
|
|
7393
|
+
kill: () => {
|
|
7394
|
+
dataListener.dispose();
|
|
7395
|
+
exitListener.dispose();
|
|
7396
|
+
term.kill();
|
|
7397
|
+
}
|
|
7398
|
+
});
|
|
7399
|
+
return { sessionId: id };
|
|
7400
|
+
} catch (e) {
|
|
7401
|
+
if (process.platform === "win32") {
|
|
7402
|
+
return { error: e instanceof Error ? e.message : "spawn failed" };
|
|
7403
|
+
}
|
|
7404
|
+
}
|
|
7405
|
+
} else if (process.platform === "win32") {
|
|
6785
7406
|
return {
|
|
6786
7407
|
error: `node-pty native module unavailable on ${process.platform}-${process.arch}; terminal feature disabled for this platform`
|
|
6787
7408
|
};
|
|
6788
7409
|
}
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
rows: Math.max(1, Math.min(opts.rows ?? 24, 200)),
|
|
6794
|
-
cwd,
|
|
6795
|
-
env,
|
|
6796
|
-
// Windows-specific: ConPTY is the default on Win 10 1809+
|
|
6797
|
-
// and is what we want. node-pty falls back to winpty
|
|
6798
|
-
// automatically on older builds.
|
|
6799
|
-
useConpty: process.platform === "win32" ? true : void 0
|
|
6800
|
-
});
|
|
6801
|
-
const id = (0, import_crypto.randomUUID)();
|
|
6802
|
-
const dataListener = term.onData((data) => {
|
|
6803
|
-
onDataHandler?.({ sessionId: id, data });
|
|
6804
|
-
});
|
|
6805
|
-
const exitListener = term.onExit(({ exitCode }) => {
|
|
6806
|
-
onExitHandler?.({ sessionId: id, exitCode });
|
|
6807
|
-
sessions.delete(id);
|
|
6808
|
-
});
|
|
6809
|
-
sessions.set(id, { id, pty: term, dataListener, exitListener });
|
|
6810
|
-
return { sessionId: id };
|
|
6811
|
-
} catch (e) {
|
|
6812
|
-
return { error: e instanceof Error ? e.message : "spawn failed" };
|
|
6813
|
-
}
|
|
7410
|
+
const sess = createPythonSession(id, shell, cwd, env, cols, rows);
|
|
7411
|
+
if ("error" in sess) return { error: sess.error };
|
|
7412
|
+
sessions.set(id, sess);
|
|
7413
|
+
return { sessionId: id };
|
|
6814
7414
|
}
|
|
6815
7415
|
function writeTerminal(sessionId, data) {
|
|
6816
7416
|
const s = sessions.get(sessionId);
|
|
6817
7417
|
if (!s) return { ok: false, error: "No such session" };
|
|
6818
7418
|
try {
|
|
6819
|
-
s.
|
|
7419
|
+
s.write(data);
|
|
6820
7420
|
return { ok: true };
|
|
6821
7421
|
} catch (e) {
|
|
6822
7422
|
return { ok: false, error: e instanceof Error ? e.message : "write failed" };
|
|
@@ -6826,7 +7426,7 @@ function resizeTerminal(sessionId, cols, rows) {
|
|
|
6826
7426
|
const s = sessions.get(sessionId);
|
|
6827
7427
|
if (!s) return { ok: false, error: "No such session" };
|
|
6828
7428
|
try {
|
|
6829
|
-
s.
|
|
7429
|
+
s.resize?.(Math.max(1, Math.min(cols, 500)), Math.max(1, Math.min(rows, 200)));
|
|
6830
7430
|
return { ok: true };
|
|
6831
7431
|
} catch (e) {
|
|
6832
7432
|
return { ok: false, error: e instanceof Error ? e.message : "resize failed" };
|
|
@@ -6836,9 +7436,7 @@ function closeTerminal(sessionId) {
|
|
|
6836
7436
|
const s = sessions.get(sessionId);
|
|
6837
7437
|
if (!s) return { ok: true };
|
|
6838
7438
|
try {
|
|
6839
|
-
s.
|
|
6840
|
-
s.exitListener.dispose();
|
|
6841
|
-
s.pty.kill();
|
|
7439
|
+
s.kill();
|
|
6842
7440
|
} catch {
|
|
6843
7441
|
}
|
|
6844
7442
|
sessions.delete(sessionId);
|
|
@@ -6849,7 +7447,7 @@ function closeTerminal(sessionId) {
|
|
|
6849
7447
|
function saveFilesTemp(files) {
|
|
6850
7448
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
6851
7449
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
6852
|
-
const tmpPath =
|
|
7450
|
+
const tmpPath = path19.join(os13.tmpdir(), `codeam-${(0, import_crypto2.randomUUID)()}-${safeName}`);
|
|
6853
7451
|
fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
6854
7452
|
return tmpPath;
|
|
6855
7453
|
});
|
|
@@ -6981,7 +7579,7 @@ var sessionTerminated = (ctx) => {
|
|
|
6981
7579
|
} catch {
|
|
6982
7580
|
}
|
|
6983
7581
|
try {
|
|
6984
|
-
const proc = (0,
|
|
7582
|
+
const proc = (0, import_child_process10.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
6985
7583
|
detached: true,
|
|
6986
7584
|
stdio: "ignore"
|
|
6987
7585
|
});
|
|
@@ -7003,7 +7601,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
7003
7601
|
}
|
|
7004
7602
|
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
7005
7603
|
try {
|
|
7006
|
-
const stopProc = (0,
|
|
7604
|
+
const stopProc = (0, import_child_process10.spawn)(
|
|
7007
7605
|
"bash",
|
|
7008
7606
|
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
7009
7607
|
{ detached: true, stdio: "ignore" }
|
|
@@ -7013,7 +7611,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
7013
7611
|
}
|
|
7014
7612
|
}
|
|
7015
7613
|
try {
|
|
7016
|
-
const proc = (0,
|
|
7614
|
+
const proc = (0, import_child_process10.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
7017
7615
|
detached: true,
|
|
7018
7616
|
stdio: "ignore"
|
|
7019
7617
|
});
|
|
@@ -7232,6 +7830,12 @@ async function start(requestedAgent) {
|
|
|
7232
7830
|
session.pluginAuthToken,
|
|
7233
7831
|
runtime
|
|
7234
7832
|
);
|
|
7833
|
+
const fileWatcher = session.pluginAuthToken ? new FileWatcherService({
|
|
7834
|
+
workingDir: cwd,
|
|
7835
|
+
sessionId: session.id,
|
|
7836
|
+
pluginId,
|
|
7837
|
+
pluginAuthToken: session.pluginAuthToken
|
|
7838
|
+
}) : null;
|
|
7235
7839
|
const claude = new AgentService(
|
|
7236
7840
|
runtime,
|
|
7237
7841
|
{
|
|
@@ -7243,6 +7847,7 @@ async function start(requestedAgent) {
|
|
|
7243
7847
|
process.removeListener("SIGINT", sigintHandler);
|
|
7244
7848
|
outputSvc.dispose();
|
|
7245
7849
|
relay.stop();
|
|
7850
|
+
void fileWatcher?.stop();
|
|
7246
7851
|
process.exit(code);
|
|
7247
7852
|
}
|
|
7248
7853
|
}
|
|
@@ -7272,12 +7877,17 @@ async function start(requestedAgent) {
|
|
|
7272
7877
|
claude.kill();
|
|
7273
7878
|
outputSvc.dispose();
|
|
7274
7879
|
relay.stop();
|
|
7880
|
+
void fileWatcher?.stop();
|
|
7275
7881
|
process.exit(0);
|
|
7276
7882
|
}
|
|
7277
7883
|
process.once("SIGINT", sigintHandler);
|
|
7278
7884
|
await claude.spawn();
|
|
7279
7885
|
await outputSvc.startTerminalTurn();
|
|
7280
7886
|
relay.start();
|
|
7887
|
+
if (fileWatcher) {
|
|
7888
|
+
fileWatcher.start().catch(() => {
|
|
7889
|
+
});
|
|
7890
|
+
}
|
|
7281
7891
|
setTimeout(() => {
|
|
7282
7892
|
historySvc.load().catch(() => {
|
|
7283
7893
|
});
|
|
@@ -7406,7 +8016,7 @@ async function pair(args2 = []) {
|
|
|
7406
8016
|
var fs15 = __toESM(require("fs"));
|
|
7407
8017
|
var os14 = __toESM(require("os"));
|
|
7408
8018
|
var import_crypto4 = require("crypto");
|
|
7409
|
-
var
|
|
8019
|
+
var API_BASE6 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
7410
8020
|
function fail(msg) {
|
|
7411
8021
|
console.error(`
|
|
7412
8022
|
${msg}
|
|
@@ -7421,12 +8031,12 @@ function readTokenFromArgs(args2) {
|
|
|
7421
8031
|
}
|
|
7422
8032
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
7423
8033
|
if (fileFlag) {
|
|
7424
|
-
const
|
|
8034
|
+
const path25 = fileFlag.slice("--token-file=".length);
|
|
7425
8035
|
try {
|
|
7426
|
-
const content = fs15.readFileSync(
|
|
7427
|
-
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`);
|
|
7428
8038
|
try {
|
|
7429
|
-
fs15.unlinkSync(
|
|
8039
|
+
fs15.unlinkSync(path25);
|
|
7430
8040
|
} catch {
|
|
7431
8041
|
}
|
|
7432
8042
|
return content;
|
|
@@ -7438,7 +8048,7 @@ function readTokenFromArgs(args2) {
|
|
|
7438
8048
|
fail("codeam pair-auto requires --token-file=<path>, --token=<value>, or CODEAM_AUTO_TOKEN env");
|
|
7439
8049
|
}
|
|
7440
8050
|
async function claim(token, pluginId) {
|
|
7441
|
-
const url = `${
|
|
8051
|
+
const url = `${API_BASE6}/api/pairing/claim-auto-token`;
|
|
7442
8052
|
const body = {
|
|
7443
8053
|
token,
|
|
7444
8054
|
pluginId,
|
|
@@ -7593,11 +8203,11 @@ async function logout() {
|
|
|
7593
8203
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
7594
8204
|
|
|
7595
8205
|
// src/services/providers/github-codespaces.ts
|
|
7596
|
-
var
|
|
8206
|
+
var import_child_process11 = require("child_process");
|
|
7597
8207
|
var import_util3 = require("util");
|
|
7598
8208
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
7599
|
-
var
|
|
7600
|
-
var execFileP3 = (0, import_util3.promisify)(
|
|
8209
|
+
var path20 = __toESM(require("path"));
|
|
8210
|
+
var execFileP3 = (0, import_util3.promisify)(import_child_process11.execFile);
|
|
7601
8211
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
7602
8212
|
function resetStdinForChild() {
|
|
7603
8213
|
if (process.stdin.isTTY) {
|
|
@@ -7641,7 +8251,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7641
8251
|
if (!isAuthed) {
|
|
7642
8252
|
resetStdinForChild();
|
|
7643
8253
|
await new Promise((resolve2, reject) => {
|
|
7644
|
-
const proc = (0,
|
|
8254
|
+
const proc = (0, import_child_process11.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
7645
8255
|
stdio: "inherit"
|
|
7646
8256
|
});
|
|
7647
8257
|
proc.on("exit", (code) => {
|
|
@@ -7675,7 +8285,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7675
8285
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
7676
8286
|
resetStdinForChild();
|
|
7677
8287
|
const refreshCode = await new Promise((resolve2, reject) => {
|
|
7678
|
-
const proc = (0,
|
|
8288
|
+
const proc = (0, import_child_process11.spawn)(
|
|
7679
8289
|
"gh",
|
|
7680
8290
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
7681
8291
|
{ stdio: "inherit" }
|
|
@@ -7825,7 +8435,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7825
8435
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
7826
8436
|
resetStdinForChild();
|
|
7827
8437
|
const ok = await new Promise((resolve2) => {
|
|
7828
|
-
const proc = (0,
|
|
8438
|
+
const proc = (0, import_child_process11.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
7829
8439
|
proc.on("exit", (code) => resolve2(code === 0));
|
|
7830
8440
|
proc.on("error", () => resolve2(false));
|
|
7831
8441
|
});
|
|
@@ -7852,7 +8462,7 @@ var GitHubCodespacesProvider = class {
|
|
|
7852
8462
|
);
|
|
7853
8463
|
resetStdinForChild();
|
|
7854
8464
|
await new Promise((resolve2, reject) => {
|
|
7855
|
-
const proc = (0,
|
|
8465
|
+
const proc = (0, import_child_process11.spawn)(
|
|
7856
8466
|
"gh",
|
|
7857
8467
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
7858
8468
|
{ stdio: "inherit" }
|
|
@@ -8030,7 +8640,7 @@ var GitHubCodespacesProvider = class {
|
|
|
8030
8640
|
async streamCommand(workspaceId, command2) {
|
|
8031
8641
|
resetStdinForChild();
|
|
8032
8642
|
return new Promise((resolve2, reject) => {
|
|
8033
|
-
const proc = (0,
|
|
8643
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8034
8644
|
"gh",
|
|
8035
8645
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
8036
8646
|
{ stdio: "inherit" }
|
|
@@ -8057,11 +8667,11 @@ var GitHubCodespacesProvider = class {
|
|
|
8057
8667
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
8058
8668
|
];
|
|
8059
8669
|
await new Promise((resolve2, reject) => {
|
|
8060
|
-
const tar = (0,
|
|
8670
|
+
const tar = (0, import_child_process11.spawn)("tar", tarArgs, {
|
|
8061
8671
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8062
8672
|
env: tarEnv
|
|
8063
8673
|
});
|
|
8064
|
-
const ssh = (0,
|
|
8674
|
+
const ssh = (0, import_child_process11.spawn)("gh", sshArgs, {
|
|
8065
8675
|
stdio: [tar.stdout, "pipe", "pipe"]
|
|
8066
8676
|
});
|
|
8067
8677
|
let tarErr = "";
|
|
@@ -8085,7 +8695,7 @@ var GitHubCodespacesProvider = class {
|
|
|
8085
8695
|
});
|
|
8086
8696
|
}
|
|
8087
8697
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8088
|
-
const remoteDir =
|
|
8698
|
+
const remoteDir = path20.posix.dirname(remotePath);
|
|
8089
8699
|
const parts = [
|
|
8090
8700
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
8091
8701
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -8095,7 +8705,7 @@ var GitHubCodespacesProvider = class {
|
|
|
8095
8705
|
}
|
|
8096
8706
|
const cmd = parts.join(" && ");
|
|
8097
8707
|
await new Promise((resolve2, reject) => {
|
|
8098
|
-
const proc = (0,
|
|
8708
|
+
const proc = (0, import_child_process11.spawn)(
|
|
8099
8709
|
"gh",
|
|
8100
8710
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
8101
8711
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8153,11 +8763,11 @@ function shellQuote(s) {
|
|
|
8153
8763
|
}
|
|
8154
8764
|
|
|
8155
8765
|
// src/services/providers/gitpod.ts
|
|
8156
|
-
var
|
|
8766
|
+
var import_child_process12 = require("child_process");
|
|
8157
8767
|
var import_util4 = require("util");
|
|
8158
|
-
var
|
|
8768
|
+
var path21 = __toESM(require("path"));
|
|
8159
8769
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
8160
|
-
var execFileP4 = (0, import_util4.promisify)(
|
|
8770
|
+
var execFileP4 = (0, import_util4.promisify)(import_child_process12.execFile);
|
|
8161
8771
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
8162
8772
|
function resetStdinForChild2() {
|
|
8163
8773
|
if (process.stdin.isTTY) {
|
|
@@ -8197,7 +8807,7 @@ var GitpodProvider = class {
|
|
|
8197
8807
|
);
|
|
8198
8808
|
resetStdinForChild2();
|
|
8199
8809
|
await new Promise((resolve2, reject) => {
|
|
8200
|
-
const proc = (0,
|
|
8810
|
+
const proc = (0, import_child_process12.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
8201
8811
|
proc.on("exit", (code) => {
|
|
8202
8812
|
if (code === 0) resolve2();
|
|
8203
8813
|
else reject(new Error("gitpod login failed."));
|
|
@@ -8349,7 +8959,7 @@ var GitpodProvider = class {
|
|
|
8349
8959
|
async streamCommand(workspaceId, command2) {
|
|
8350
8960
|
resetStdinForChild2();
|
|
8351
8961
|
return new Promise((resolve2, reject) => {
|
|
8352
|
-
const proc = (0,
|
|
8962
|
+
const proc = (0, import_child_process12.spawn)(
|
|
8353
8963
|
"gitpod",
|
|
8354
8964
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
8355
8965
|
{ stdio: "inherit" }
|
|
@@ -8369,11 +8979,11 @@ var GitpodProvider = class {
|
|
|
8369
8979
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8370
8980
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
8371
8981
|
await new Promise((resolve2, reject) => {
|
|
8372
|
-
const tar = (0,
|
|
8982
|
+
const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
|
|
8373
8983
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8374
8984
|
env: tarEnv
|
|
8375
8985
|
});
|
|
8376
|
-
const ssh = (0,
|
|
8986
|
+
const ssh = (0, import_child_process12.spawn)(
|
|
8377
8987
|
"gitpod",
|
|
8378
8988
|
["workspace", "ssh", workspaceId, "--", remoteCmd],
|
|
8379
8989
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8395,7 +9005,7 @@ var GitpodProvider = class {
|
|
|
8395
9005
|
});
|
|
8396
9006
|
}
|
|
8397
9007
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8398
|
-
const remoteDir =
|
|
9008
|
+
const remoteDir = path21.posix.dirname(remotePath);
|
|
8399
9009
|
const parts = [
|
|
8400
9010
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
8401
9011
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -8405,7 +9015,7 @@ var GitpodProvider = class {
|
|
|
8405
9015
|
}
|
|
8406
9016
|
const cmd = parts.join(" && ");
|
|
8407
9017
|
await new Promise((resolve2, reject) => {
|
|
8408
|
-
const proc = (0,
|
|
9018
|
+
const proc = (0, import_child_process12.spawn)(
|
|
8409
9019
|
"gitpod",
|
|
8410
9020
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
8411
9021
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8429,10 +9039,10 @@ function shellQuote2(s) {
|
|
|
8429
9039
|
}
|
|
8430
9040
|
|
|
8431
9041
|
// src/services/providers/gitlab-workspaces.ts
|
|
8432
|
-
var
|
|
9042
|
+
var import_child_process13 = require("child_process");
|
|
8433
9043
|
var import_util5 = require("util");
|
|
8434
|
-
var
|
|
8435
|
-
var execFileP5 = (0, import_util5.promisify)(
|
|
9044
|
+
var path22 = __toESM(require("path"));
|
|
9045
|
+
var execFileP5 = (0, import_util5.promisify)(import_child_process13.execFile);
|
|
8436
9046
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
8437
9047
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
8438
9048
|
function resetStdinForChild3() {
|
|
@@ -8474,7 +9084,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
8474
9084
|
);
|
|
8475
9085
|
resetStdinForChild3();
|
|
8476
9086
|
await new Promise((resolve2, reject) => {
|
|
8477
|
-
const proc = (0,
|
|
9087
|
+
const proc = (0, import_child_process13.spawn)(
|
|
8478
9088
|
"glab",
|
|
8479
9089
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
8480
9090
|
{ stdio: "inherit" }
|
|
@@ -8646,7 +9256,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8646
9256
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8647
9257
|
resetStdinForChild3();
|
|
8648
9258
|
return new Promise((resolve2, reject) => {
|
|
8649
|
-
const proc = (0,
|
|
9259
|
+
const proc = (0, import_child_process13.spawn)(
|
|
8650
9260
|
"ssh",
|
|
8651
9261
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
8652
9262
|
{ stdio: "inherit" }
|
|
@@ -8667,8 +9277,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8667
9277
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8668
9278
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
8669
9279
|
await new Promise((resolve2, reject) => {
|
|
8670
|
-
const tar = (0,
|
|
8671
|
-
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)(
|
|
8672
9282
|
"ssh",
|
|
8673
9283
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
|
|
8674
9284
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8691,14 +9301,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
8691
9301
|
}
|
|
8692
9302
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
8693
9303
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
8694
|
-
const remoteDir =
|
|
9304
|
+
const remoteDir = path22.posix.dirname(remotePath);
|
|
8695
9305
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
8696
9306
|
if (options.mode != null) {
|
|
8697
9307
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
8698
9308
|
}
|
|
8699
9309
|
const cmd = parts.join(" && ");
|
|
8700
9310
|
await new Promise((resolve2, reject) => {
|
|
8701
|
-
const proc = (0,
|
|
9311
|
+
const proc = (0, import_child_process13.spawn)(
|
|
8702
9312
|
"ssh",
|
|
8703
9313
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
8704
9314
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -8757,10 +9367,10 @@ function shellQuote3(s) {
|
|
|
8757
9367
|
}
|
|
8758
9368
|
|
|
8759
9369
|
// src/services/providers/railway.ts
|
|
8760
|
-
var
|
|
9370
|
+
var import_child_process14 = require("child_process");
|
|
8761
9371
|
var import_util6 = require("util");
|
|
8762
|
-
var
|
|
8763
|
-
var execFileP6 = (0, import_util6.promisify)(
|
|
9372
|
+
var path23 = __toESM(require("path"));
|
|
9373
|
+
var execFileP6 = (0, import_util6.promisify)(import_child_process14.execFile);
|
|
8764
9374
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
8765
9375
|
function resetStdinForChild4() {
|
|
8766
9376
|
if (process.stdin.isTTY) {
|
|
@@ -8801,7 +9411,7 @@ var RailwayProvider = class {
|
|
|
8801
9411
|
);
|
|
8802
9412
|
resetStdinForChild4();
|
|
8803
9413
|
await new Promise((resolve2, reject) => {
|
|
8804
|
-
const proc = (0,
|
|
9414
|
+
const proc = (0, import_child_process14.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
8805
9415
|
proc.on("exit", (code) => {
|
|
8806
9416
|
if (code === 0) resolve2();
|
|
8807
9417
|
else reject(new Error("railway login failed."));
|
|
@@ -8944,7 +9554,7 @@ var RailwayProvider = class {
|
|
|
8944
9554
|
}
|
|
8945
9555
|
resetStdinForChild4();
|
|
8946
9556
|
return new Promise((resolve2, reject) => {
|
|
8947
|
-
const proc = (0,
|
|
9557
|
+
const proc = (0, import_child_process14.spawn)(
|
|
8948
9558
|
"railway",
|
|
8949
9559
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
8950
9560
|
{ stdio: "inherit" }
|
|
@@ -8968,8 +9578,8 @@ var RailwayProvider = class {
|
|
|
8968
9578
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
8969
9579
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
8970
9580
|
await new Promise((resolve2, reject) => {
|
|
8971
|
-
const tar = (0,
|
|
8972
|
-
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)(
|
|
8973
9583
|
"railway",
|
|
8974
9584
|
["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
|
|
8975
9585
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -8995,14 +9605,14 @@ var RailwayProvider = class {
|
|
|
8995
9605
|
if (!projectId || !serviceId) {
|
|
8996
9606
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
8997
9607
|
}
|
|
8998
|
-
const remoteDir =
|
|
9608
|
+
const remoteDir = path23.posix.dirname(remotePath);
|
|
8999
9609
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
9000
9610
|
if (options.mode != null) {
|
|
9001
9611
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
9002
9612
|
}
|
|
9003
9613
|
const cmd = parts.join(" && ");
|
|
9004
9614
|
await new Promise((resolve2, reject) => {
|
|
9005
|
-
const proc = (0,
|
|
9615
|
+
const proc = (0, import_child_process14.spawn)(
|
|
9006
9616
|
"railway",
|
|
9007
9617
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
9008
9618
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -9537,7 +10147,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
9537
10147
|
// src/commands/version.ts
|
|
9538
10148
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
9539
10149
|
function version() {
|
|
9540
|
-
const v = true ? "2.15.
|
|
10150
|
+
const v = true ? "2.15.5" : "unknown";
|
|
9541
10151
|
console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
|
|
9542
10152
|
}
|
|
9543
10153
|
|
|
@@ -9580,16 +10190,16 @@ function help() {
|
|
|
9580
10190
|
// src/lib/updateNotifier.ts
|
|
9581
10191
|
var fs16 = __toESM(require("fs"));
|
|
9582
10192
|
var os15 = __toESM(require("os"));
|
|
9583
|
-
var
|
|
9584
|
-
var
|
|
10193
|
+
var path24 = __toESM(require("path"));
|
|
10194
|
+
var https6 = __toESM(require("https"));
|
|
9585
10195
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
9586
10196
|
var PKG_NAME = "codeam-cli";
|
|
9587
10197
|
var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
9588
10198
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9589
10199
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
9590
10200
|
function cachePath() {
|
|
9591
|
-
const dir =
|
|
9592
|
-
return
|
|
10201
|
+
const dir = path24.join(os15.homedir(), ".codeam");
|
|
10202
|
+
return path24.join(dir, "update-check.json");
|
|
9593
10203
|
}
|
|
9594
10204
|
function readCache() {
|
|
9595
10205
|
try {
|
|
@@ -9604,7 +10214,7 @@ function readCache() {
|
|
|
9604
10214
|
function writeCache(cache) {
|
|
9605
10215
|
try {
|
|
9606
10216
|
const file = cachePath();
|
|
9607
|
-
fs16.mkdirSync(
|
|
10217
|
+
fs16.mkdirSync(path24.dirname(file), { recursive: true });
|
|
9608
10218
|
fs16.writeFileSync(file, JSON.stringify(cache));
|
|
9609
10219
|
} catch {
|
|
9610
10220
|
}
|
|
@@ -9624,7 +10234,7 @@ function compareSemver(a, b) {
|
|
|
9624
10234
|
}
|
|
9625
10235
|
function fetchLatest() {
|
|
9626
10236
|
return new Promise((resolve2) => {
|
|
9627
|
-
const req =
|
|
10237
|
+
const req = https6.get(
|
|
9628
10238
|
REGISTRY_URL,
|
|
9629
10239
|
{ headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
|
|
9630
10240
|
(res) => {
|
|
@@ -9676,7 +10286,7 @@ function checkForUpdates() {
|
|
|
9676
10286
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
9677
10287
|
if (process.env.CI) return;
|
|
9678
10288
|
if (!process.stdout.isTTY) return;
|
|
9679
|
-
const current = true ? "2.15.
|
|
10289
|
+
const current = true ? "2.15.5" : null;
|
|
9680
10290
|
if (!current) return;
|
|
9681
10291
|
const cache = readCache();
|
|
9682
10292
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|