codeam-cli 2.16.0 → 2.16.2

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/index.js +411 -232
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __commonJS = (cb, mod) => function __require() {
10
13
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
14
  };
@@ -30,6 +33,38 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
33
  mod
31
34
  ));
32
35
 
36
+ // src/services/pty/types.ts
37
+ var types_exports = {};
38
+ __export(types_exports, {
39
+ findInPath: () => findInPath
40
+ });
41
+ function findInPath(name) {
42
+ const isWin = process.platform === "win32";
43
+ const dirs = (process.env.PATH ?? "").split(path3.delimiter).filter(Boolean);
44
+ const hasExt = path3.extname(name).length > 0;
45
+ const candidates = isWin && !hasExt ? [`${name}.exe`, `${name}.cmd`, `${name}.bat`, `${name}.ps1`, name] : [name];
46
+ const accessFlag = isWin ? fs3.constants.F_OK : fs3.constants.X_OK;
47
+ for (const dir of dirs) {
48
+ for (const candidate of candidates) {
49
+ const full = path3.join(dir, candidate);
50
+ try {
51
+ fs3.accessSync(full, accessFlag);
52
+ return full;
53
+ } catch {
54
+ }
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+ var fs3, path3;
60
+ var init_types = __esm({
61
+ "src/services/pty/types.ts"() {
62
+ "use strict";
63
+ fs3 = __toESM(require("fs"));
64
+ path3 = __toESM(require("path"));
65
+ }
66
+ });
67
+
33
68
  // ../../node_modules/sisteransi/src/index.js
34
69
  var require_src = __commonJS({
35
70
  "../../node_modules/sisteransi/src/index.js"(exports2, module2) {
@@ -389,7 +424,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
389
424
  // package.json
390
425
  var package_default = {
391
426
  name: "codeam-cli",
392
- version: "2.16.0",
427
+ version: "2.16.2",
393
428
  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.",
394
429
  type: "commonjs",
395
430
  main: "dist/index.js",
@@ -676,7 +711,7 @@ async function postLinkCredential(input) {
676
711
  }
677
712
  }
678
713
  async function _postJsonAuthed(url, body, pluginAuthToken) {
679
- return new Promise((resolve2, reject) => {
714
+ return new Promise((resolve3, reject) => {
680
715
  const data = JSON.stringify(body);
681
716
  const u2 = new URL(url);
682
717
  const transport = u2.protocol === "https:" ? https : http;
@@ -708,9 +743,9 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
708
743
  return;
709
744
  }
710
745
  try {
711
- resolve2(JSON.parse(responseBody));
746
+ resolve3(JSON.parse(responseBody));
712
747
  } catch {
713
- resolve2(null);
748
+ resolve3(null);
714
749
  }
715
750
  });
716
751
  }
@@ -725,7 +760,7 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
725
760
  });
726
761
  }
727
762
  async function _postJson(url, body) {
728
- return new Promise((resolve2, reject) => {
763
+ return new Promise((resolve3, reject) => {
729
764
  const data = JSON.stringify(body);
730
765
  const u2 = new URL(url);
731
766
  const transport = u2.protocol === "https:" ? https : http;
@@ -754,9 +789,9 @@ async function _postJson(url, body) {
754
789
  return;
755
790
  }
756
791
  try {
757
- resolve2(JSON.parse(body2));
792
+ resolve3(JSON.parse(body2));
758
793
  } catch {
759
- resolve2(null);
794
+ resolve3(null);
760
795
  }
761
796
  });
762
797
  }
@@ -771,7 +806,7 @@ async function _postJson(url, body) {
771
806
  });
772
807
  }
773
808
  async function _getJson(url) {
774
- return new Promise((resolve2, reject) => {
809
+ return new Promise((resolve3, reject) => {
775
810
  const u2 = new URL(url);
776
811
  const transport = u2.protocol === "https:" ? https : http;
777
812
  const req = transport.request(
@@ -795,9 +830,9 @@ async function _getJson(url) {
795
830
  return;
796
831
  }
797
832
  try {
798
- resolve2(JSON.parse(body));
833
+ resolve3(JSON.parse(body));
799
834
  } catch {
800
- resolve2(null);
835
+ resolve3(null);
801
836
  }
802
837
  });
803
838
  }
@@ -1105,30 +1140,7 @@ var import_child_process2 = require("child_process");
1105
1140
  var fs4 = __toESM(require("fs"));
1106
1141
  var os4 = __toESM(require("os"));
1107
1142
  var path4 = __toESM(require("path"));
1108
-
1109
- // src/services/pty/types.ts
1110
- var fs3 = __toESM(require("fs"));
1111
- var path3 = __toESM(require("path"));
1112
- function findInPath(name) {
1113
- const isWin = process.platform === "win32";
1114
- const dirs = (process.env.PATH ?? "").split(path3.delimiter).filter(Boolean);
1115
- const hasExt = path3.extname(name).length > 0;
1116
- const candidates = isWin && !hasExt ? [`${name}.exe`, `${name}.cmd`, `${name}.bat`, `${name}.ps1`, name] : [name];
1117
- const accessFlag = isWin ? fs3.constants.F_OK : fs3.constants.X_OK;
1118
- for (const dir of dirs) {
1119
- for (const candidate of candidates) {
1120
- const full = path3.join(dir, candidate);
1121
- try {
1122
- fs3.accessSync(full, accessFlag);
1123
- return full;
1124
- } catch {
1125
- }
1126
- }
1127
- }
1128
- return null;
1129
- }
1130
-
1131
- // src/services/pty/unix.strategy.ts
1143
+ init_types();
1132
1144
  var PYTHON_PTY_HELPER = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
1133
1145
  m,s=pty.openpty()
1134
1146
  try:
@@ -3441,6 +3453,7 @@ ${c2}
3441
3453
  } }).prompt();
3442
3454
 
3443
3455
  // src/services/claude-installer.ts
3456
+ init_types();
3444
3457
  function probeInstallDirs() {
3445
3458
  const home = os5.homedir();
3446
3459
  if (process.platform === "win32") {
@@ -3478,15 +3491,15 @@ function runInstaller() {
3478
3491
  "-Command",
3479
3492
  "irm https://claude.ai/install.ps1 | iex"
3480
3493
  ] : ["-c", "curl -fsSL https://claude.ai/install.sh | bash"];
3481
- return new Promise((resolve2) => {
3494
+ return new Promise((resolve3) => {
3482
3495
  const proc = (0, import_child_process4.spawn)(cmd, args2, { stdio: "inherit" });
3483
3496
  proc.on("error", (err) => {
3484
3497
  console.error(`
3485
3498
  \u2717 Installer failed to launch: ${err.message}`);
3486
- resolve2(false);
3499
+ resolve3(false);
3487
3500
  });
3488
3501
  proc.on("exit", (code) => {
3489
- resolve2(code === 0);
3502
+ resolve3(code === 0);
3490
3503
  });
3491
3504
  });
3492
3505
  }
@@ -3520,6 +3533,7 @@ async function ensureClaudeInstalled() {
3520
3533
 
3521
3534
  // src/services/claude-resolver.ts
3522
3535
  var path7 = __toESM(require("path"));
3536
+ init_types();
3523
3537
  function buildClaudeLaunch(extraArgs = []) {
3524
3538
  const found = findInPath("claude") ?? findInPath("claude-code");
3525
3539
  if (!found) return null;
@@ -3779,6 +3793,7 @@ var fs5 = __toESM(require("fs"));
3779
3793
  var os6 = __toESM(require("os"));
3780
3794
  var path8 = __toESM(require("path"));
3781
3795
  var import_child_process5 = require("child_process");
3796
+ init_types();
3782
3797
  var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
3783
3798
  m,s=pty.openpty()
3784
3799
  try:
@@ -3834,17 +3849,17 @@ function parseUsageOutput(raw) {
3834
3849
  return { percent, resetAt };
3835
3850
  }
3836
3851
  async function fetchClaudeQuota() {
3837
- return new Promise((resolve2) => {
3852
+ return new Promise((resolve3) => {
3838
3853
  const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
3839
3854
  if (!claudeCmd) {
3840
- resolve2(null);
3855
+ resolve3(null);
3841
3856
  return;
3842
3857
  }
3843
3858
  const helperPath = path8.join(os6.tmpdir(), "codeam-quota-helper.py");
3844
3859
  fs5.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
3845
3860
  const python = findInPath("python3") ?? findInPath("python");
3846
3861
  if (!python) {
3847
- resolve2(null);
3862
+ resolve3(null);
3848
3863
  return;
3849
3864
  }
3850
3865
  const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
@@ -3871,13 +3886,13 @@ async function fetchClaudeQuota() {
3871
3886
  fs5.unlinkSync(helperPath);
3872
3887
  } catch {
3873
3888
  }
3874
- resolve2(result);
3889
+ resolve3(result);
3875
3890
  }, 5e3);
3876
3891
  }, 8e3);
3877
3892
  setTimeout(() => {
3878
3893
  if (!resolved) {
3879
3894
  resolved = true;
3880
- resolve2(null);
3895
+ resolve3(null);
3881
3896
  }
3882
3897
  try {
3883
3898
  proc.kill();
@@ -4522,6 +4537,7 @@ var ClaudeDeployStrategy = class {
4522
4537
 
4523
4538
  // src/agents/codex/runtime.ts
4524
4539
  var import_node_child_process = require("child_process");
4540
+ init_types();
4525
4541
 
4526
4542
  // src/agents/codex/history.ts
4527
4543
  var import_node_fs2 = __toESM(require("fs"));
@@ -5116,13 +5132,13 @@ var CodexRuntimeStrategy = class {
5116
5132
  }
5117
5133
  };
5118
5134
  async function installCodexViaNpm() {
5119
- return new Promise((resolve2, reject) => {
5135
+ return new Promise((resolve3, reject) => {
5120
5136
  const npm = process.platform === "win32" ? "npm.cmd" : "npm";
5121
5137
  const proc = (0, import_node_child_process.spawn)(npm, ["install", "-g", "@openai/codex"], {
5122
5138
  stdio: "inherit"
5123
5139
  });
5124
5140
  proc.on("close", (code) => {
5125
- if (code === 0) resolve2();
5141
+ if (code === 0) resolve3();
5126
5142
  else reject(new Error(`npm install -g @openai/codex exited ${code}`));
5127
5143
  });
5128
5144
  proc.on("error", reject);
@@ -5321,14 +5337,14 @@ var ChunkEmitter = class {
5321
5337
  "chunkEmitter",
5322
5338
  `send type=${body.type ?? "(clear)"} bytes=${payload.length} done=${body.done === true}`
5323
5339
  );
5324
- return new Promise((resolve2) => {
5340
+ return new Promise((resolve3) => {
5325
5341
  const attempt = (attemptsLeft) => {
5326
5342
  _transport2.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
5327
5343
  const tookMs = Date.now() - t0;
5328
5344
  if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
5329
5345
  process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
5330
5346
  log.info("chunkEmitter", `dead status=${statusCode} took=${tookMs}ms`);
5331
- resolve2({ dead: true });
5347
+ resolve3({ dead: true });
5332
5348
  return;
5333
5349
  }
5334
5350
  if (statusCode >= 400) {
@@ -5338,7 +5354,7 @@ var ChunkEmitter = class {
5338
5354
  } else {
5339
5355
  log.info("chunkEmitter", `ok status=${statusCode} took=${tookMs}ms`);
5340
5356
  }
5341
- resolve2({ dead: false });
5357
+ resolve3({ dead: false });
5342
5358
  }).catch((err) => {
5343
5359
  log.warn(
5344
5360
  "chunkEmitter",
@@ -5349,7 +5365,7 @@ var ChunkEmitter = class {
5349
5365
  const delay = 200 * (maxRetries - attemptsLeft + 1);
5350
5366
  setTimeout(() => attempt(attemptsLeft - 1), delay);
5351
5367
  } else {
5352
- resolve2({ dead: false });
5368
+ resolve3({ dead: false });
5353
5369
  }
5354
5370
  });
5355
5371
  };
@@ -5361,7 +5377,7 @@ var _transport2 = {
5361
5377
  post: _post
5362
5378
  };
5363
5379
  function _post(url, headers, payload) {
5364
- return new Promise((resolve2, reject) => {
5380
+ return new Promise((resolve3, reject) => {
5365
5381
  let settled = false;
5366
5382
  const u2 = new URL(url);
5367
5383
  const transport = u2.protocol === "https:" ? https3 : http3;
@@ -5385,7 +5401,7 @@ function _post(url, headers, payload) {
5385
5401
  res.on("end", () => {
5386
5402
  if (settled) return;
5387
5403
  settled = true;
5388
- resolve2({ statusCode: res.statusCode ?? 0, body: resData });
5404
+ resolve3({ statusCode: res.statusCode ?? 0, body: resData });
5389
5405
  });
5390
5406
  }
5391
5407
  );
@@ -5821,7 +5837,7 @@ function parseJsonl(filePath) {
5821
5837
  return messages;
5822
5838
  }
5823
5839
  function post(endpoint, body) {
5824
- return new Promise((resolve2) => {
5840
+ return new Promise((resolve3) => {
5825
5841
  const payload = JSON.stringify(body);
5826
5842
  const u2 = new URL(`${API_BASE4}${endpoint}`);
5827
5843
  const transport = u2.protocol === "https:" ? https4 : http4;
@@ -5842,17 +5858,17 @@ function post(endpoint, body) {
5842
5858
  res.resume();
5843
5859
  const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
5844
5860
  if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${res.statusCode}`);
5845
- resolve2(ok);
5861
+ resolve3(ok);
5846
5862
  }
5847
5863
  );
5848
5864
  req.on("error", (err) => {
5849
5865
  log.warn("history:post", `${endpoint} network error`, err);
5850
- resolve2(false);
5866
+ resolve3(false);
5851
5867
  });
5852
5868
  req.on("timeout", () => {
5853
5869
  log.warn("history:post", `${endpoint} timeout after 15s`);
5854
5870
  req.destroy();
5855
- resolve2(false);
5871
+ resolve3(false);
5856
5872
  });
5857
5873
  req.write(payload);
5858
5874
  req.end();
@@ -6246,6 +6262,7 @@ var HistoryService = class _HistoryService {
6246
6262
 
6247
6263
  // src/services/file-watcher.service.ts
6248
6264
  var import_child_process7 = require("child_process");
6265
+ var os13 = __toESM(require("os"));
6249
6266
  var path15 = __toESM(require("path"));
6250
6267
 
6251
6268
  // src/services/file-watcher/diff-parser.ts
@@ -6334,7 +6351,7 @@ var _transport3 = {
6334
6351
  post: _post2
6335
6352
  };
6336
6353
  function _post2(url, headers, payload) {
6337
- return new Promise((resolve2, reject) => {
6354
+ return new Promise((resolve3, reject) => {
6338
6355
  let settled = false;
6339
6356
  const u2 = new URL(url);
6340
6357
  const lib = u2.protocol === "https:" ? https5 : http5;
@@ -6359,7 +6376,7 @@ function _post2(url, headers, payload) {
6359
6376
  res.on("end", () => {
6360
6377
  if (settled) return;
6361
6378
  settled = true;
6362
- resolve2({ statusCode: res.statusCode ?? 0, body });
6379
+ resolve3({ statusCode: res.statusCode ?? 0, body });
6363
6380
  });
6364
6381
  }
6365
6382
  );
@@ -6381,6 +6398,44 @@ var API_BASE5 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
6381
6398
  var DEBOUNCE_MS = 250;
6382
6399
  var MAX_RETRIES = 2;
6383
6400
  var RETRY_BACKOFF_MS = 300;
6401
+ var WINDOWS_LEGACY_JUNCTIONS = [
6402
+ /[\\/]Application Data([\\/]|$)/i,
6403
+ /[\\/]Cookies([\\/]|$)/i,
6404
+ /[\\/]Local Settings([\\/]|$)/i,
6405
+ /[\\/]My Documents([\\/]|$)/i,
6406
+ /[\\/]NetHood([\\/]|$)/i,
6407
+ /[\\/]PrintHood([\\/]|$)/i,
6408
+ /[\\/]Recent([\\/]|$)/i,
6409
+ /[\\/]SendTo([\\/]|$)/i,
6410
+ /[\\/]Start Menu([\\/]|$)/i,
6411
+ /[\\/]Templates([\\/]|$)/i
6412
+ ];
6413
+ function isUnsafeWindowsWatchRoot(dir, homedir12) {
6414
+ const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
6415
+ const cwd = norm(dir);
6416
+ const home = norm(homedir12);
6417
+ if (cwd === home) return true;
6418
+ if (/^[a-z]:$/.test(cwd)) return true;
6419
+ const sysRoots = [
6420
+ "c:\\windows",
6421
+ "c:\\program files",
6422
+ "c:\\program files (x86)",
6423
+ "c:\\programdata"
6424
+ ];
6425
+ for (const root of sysRoots) {
6426
+ if (cwd === root || cwd.startsWith(root + "\\")) return true;
6427
+ }
6428
+ return false;
6429
+ }
6430
+ var _chokidarSeam = {
6431
+ load: () => {
6432
+ try {
6433
+ return require("chokidar");
6434
+ } catch {
6435
+ return null;
6436
+ }
6437
+ }
6438
+ };
6384
6439
  var FileWatcherService = class {
6385
6440
  constructor(opts) {
6386
6441
  this.opts = opts;
@@ -6403,18 +6458,23 @@ var FileWatcherService = class {
6403
6458
  if (this.stopped) {
6404
6459
  throw new Error("FileWatcherService has already been stopped \u2014 re-instantiate to restart.");
6405
6460
  }
6406
- let chokidar;
6407
- try {
6408
- chokidar = require("chokidar");
6409
- } catch (err) {
6461
+ const isWin = process.platform === "win32";
6462
+ if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os13.homedir())) {
6463
+ log.warn(
6464
+ "fileWatcher",
6465
+ `refusing to watch ${this.opts.workingDir} \u2014 looks like a Windows user-profile or system path. Run codeam from your project folder to enable file change emission.`
6466
+ );
6467
+ return;
6468
+ }
6469
+ const chokidar2 = _chokidarSeam.load();
6470
+ if (!chokidar2) {
6410
6471
  log.warn(
6411
6472
  "fileWatcher",
6412
- `chokidar unavailable \u2014 file change emission disabled`,
6413
- err
6473
+ `chokidar unavailable \u2014 file change emission disabled`
6414
6474
  );
6415
6475
  return;
6416
6476
  }
6417
- const watcher = chokidar.watch(this.opts.workingDir, {
6477
+ const watcher = chokidar2.watch(this.opts.workingDir, {
6418
6478
  ignored: [
6419
6479
  /(^|[\\/])\../,
6420
6480
  // dot-files & dot-dirs (.git, .next, .expo, .DS_Store, …)
@@ -6428,11 +6488,22 @@ var FileWatcherService = class {
6428
6488
  /\.parcel-cache/,
6429
6489
  // Build outputs that aren't a typical "dist" target
6430
6490
  /target\//,
6431
- /__pycache__/
6491
+ /__pycache__/,
6492
+ // Windows-only: skip legacy user-profile junctions whose ACLs
6493
+ // throw EPERM during chokidar's recursive traversal.
6494
+ ...isWin ? WINDOWS_LEGACY_JUNCTIONS : []
6432
6495
  ],
6433
6496
  ignoreInitial: true,
6434
6497
  // we only care about post-start changes
6435
6498
  persistent: true,
6499
+ // Windows-only safety net: don't follow reparse points, and let
6500
+ // chokidar swallow EPERM/EACCES on unreadable paths instead of
6501
+ // bubbling them up as fatal errors. Both are no-ops on macOS
6502
+ // (fsevents traversal doesn't fail on permission errors).
6503
+ ...isWin ? {
6504
+ followSymlinks: false,
6505
+ ignorePermissionErrors: true
6506
+ } : {},
6436
6507
  awaitWriteFinish: {
6437
6508
  // Coalesces rapid sequential writes (npm install spam, build
6438
6509
  // tools emitting bursts). Lower than chokidar's default so
@@ -6445,6 +6516,13 @@ var FileWatcherService = class {
6445
6516
  watcher.on("add", (filePath) => this.schedule(filePath, "add"));
6446
6517
  watcher.on("change", (filePath) => this.schedule(filePath, "change"));
6447
6518
  watcher.on("unlink", (filePath) => this.schedule(filePath, "unlink"));
6519
+ watcher.on("error", (err) => {
6520
+ const code = err?.code ?? "unknown";
6521
+ log.warn(
6522
+ "fileWatcher",
6523
+ `chokidar error (code=${code}) \u2014 watcher continues: ${err}`
6524
+ );
6525
+ });
6448
6526
  this.watcher = watcher;
6449
6527
  log.info(
6450
6528
  "fileWatcher",
@@ -6667,12 +6745,12 @@ var _gitSeam = {
6667
6745
  run: _runGitImpl
6668
6746
  };
6669
6747
  async function _runGitImpl(cwd, args2, opts = {}) {
6670
- return new Promise((resolve2) => {
6748
+ return new Promise((resolve3) => {
6671
6749
  let proc;
6672
6750
  try {
6673
6751
  proc = (0, import_child_process7.spawn)("git", args2, { cwd, env: process.env });
6674
6752
  } catch {
6675
- resolve2(null);
6753
+ resolve3(null);
6676
6754
  return;
6677
6755
  }
6678
6756
  let stdout = "";
@@ -6683,13 +6761,13 @@ async function _runGitImpl(cwd, args2, opts = {}) {
6683
6761
  proc.stderr?.on("data", (c2) => {
6684
6762
  stderr += c2.toString();
6685
6763
  });
6686
- proc.on("error", () => resolve2(null));
6764
+ proc.on("error", () => resolve3(null));
6687
6765
  proc.on("close", (code) => {
6688
6766
  if (code === 0 || opts.allowNonZeroExit) {
6689
- resolve2(stdout);
6767
+ resolve3(stdout);
6690
6768
  } else {
6691
6769
  log.trace("fileWatcher", `git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`);
6692
- resolve2(null);
6770
+ resolve3(null);
6693
6771
  }
6694
6772
  });
6695
6773
  });
@@ -6709,7 +6787,7 @@ var _transport4 = {
6709
6787
  get: _get
6710
6788
  };
6711
6789
  function _post3(url, headers, payload) {
6712
- return new Promise((resolve2, reject) => {
6790
+ return new Promise((resolve3, reject) => {
6713
6791
  let settled = false;
6714
6792
  const u2 = new URL(url);
6715
6793
  const lib = u2.protocol === "https:" ? https6 : http6;
@@ -6734,7 +6812,7 @@ function _post3(url, headers, payload) {
6734
6812
  res.on("end", () => {
6735
6813
  if (settled) return;
6736
6814
  settled = true;
6737
- resolve2({ statusCode: res.statusCode ?? 0, body });
6815
+ resolve3({ statusCode: res.statusCode ?? 0, body });
6738
6816
  });
6739
6817
  }
6740
6818
  );
@@ -6751,7 +6829,7 @@ function _post3(url, headers, payload) {
6751
6829
  });
6752
6830
  }
6753
6831
  function _get(url, headers) {
6754
- return new Promise((resolve2, reject) => {
6832
+ return new Promise((resolve3, reject) => {
6755
6833
  let settled = false;
6756
6834
  const u2 = new URL(url);
6757
6835
  const lib = u2.protocol === "https:" ? https6 : http6;
@@ -6775,7 +6853,7 @@ function _get(url, headers) {
6775
6853
  res.on("end", () => {
6776
6854
  if (settled) return;
6777
6855
  settled = true;
6778
- resolve2({ statusCode: res.statusCode ?? 0, body });
6856
+ resolve3({ statusCode: res.statusCode ?? 0, body });
6779
6857
  });
6780
6858
  }
6781
6859
  );
@@ -7144,7 +7222,7 @@ function buildKeepAlive(ctx) {
7144
7222
  let timer = null;
7145
7223
  async function setIdleTimeout(minutes) {
7146
7224
  if (!ctx.inCodespace || !ctx.codespaceName) return;
7147
- await new Promise((resolve2) => {
7225
+ await new Promise((resolve3) => {
7148
7226
  const proc = (0, import_child_process8.spawn)(
7149
7227
  "gh",
7150
7228
  [
@@ -7158,8 +7236,8 @@ function buildKeepAlive(ctx) {
7158
7236
  { stdio: "ignore", detached: true }
7159
7237
  );
7160
7238
  proc.unref();
7161
- proc.on("exit", () => resolve2());
7162
- proc.on("error", () => resolve2());
7239
+ proc.on("exit", () => resolve3());
7240
+ proc.on("error", () => resolve3());
7163
7241
  });
7164
7242
  }
7165
7243
  return {
@@ -7183,7 +7261,7 @@ function buildKeepAlive(ctx) {
7183
7261
 
7184
7262
  // src/commands/start/handlers.ts
7185
7263
  var fs14 = __toESM(require("fs"));
7186
- var os13 = __toESM(require("os"));
7264
+ var os14 = __toESM(require("os"));
7187
7265
  var path19 = __toESM(require("path"));
7188
7266
  var import_crypto3 = require("crypto");
7189
7267
  var import_child_process11 = require("child_process");
@@ -7991,7 +8069,7 @@ function closeTerminal(sessionId) {
7991
8069
  function saveFilesTemp(files) {
7992
8070
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
7993
8071
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
7994
- const tmpPath = path19.join(os13.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
8072
+ const tmpPath = path19.join(os14.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
7995
8073
  fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
7996
8074
  return tmpPath;
7997
8075
  });
@@ -8537,7 +8615,7 @@ async function pair(args2 = []) {
8537
8615
  waitSpin.message(waitMessage());
8538
8616
  }, 1e3);
8539
8617
  countdownInterval.unref?.();
8540
- await new Promise((resolve2) => {
8618
+ await new Promise((resolve3) => {
8541
8619
  let stopPolling = null;
8542
8620
  function sigintHandler() {
8543
8621
  clearInterval(countdownInterval);
@@ -8564,7 +8642,7 @@ async function pair(args2 = []) {
8564
8642
  saveCliConfig({ ...loadCliConfig(), preferredAgent: agentId });
8565
8643
  showSuccess(`Paired with ${info.userName} (${info.plan})`);
8566
8644
  console.log("");
8567
- resolve2();
8645
+ resolve3();
8568
8646
  },
8569
8647
  () => {
8570
8648
  clearInterval(countdownInterval);
@@ -8580,7 +8658,7 @@ async function pair(args2 = []) {
8580
8658
 
8581
8659
  // src/commands/pair-auto.ts
8582
8660
  var fs15 = __toESM(require("fs"));
8583
- var os14 = __toESM(require("os"));
8661
+ var os15 = __toESM(require("os"));
8584
8662
  var import_crypto5 = require("crypto");
8585
8663
  var API_BASE7 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
8586
8664
  function fail(msg) {
@@ -8597,12 +8675,12 @@ function readTokenFromArgs(args2) {
8597
8675
  }
8598
8676
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
8599
8677
  if (fileFlag) {
8600
- const path27 = fileFlag.slice("--token-file=".length);
8678
+ const path28 = fileFlag.slice("--token-file=".length);
8601
8679
  try {
8602
- const content = fs15.readFileSync(path27, "utf8").trim();
8603
- if (content.length === 0) fail(`--token-file ${path27} is empty`);
8680
+ const content = fs15.readFileSync(path28, "utf8").trim();
8681
+ if (content.length === 0) fail(`--token-file ${path28} is empty`);
8604
8682
  try {
8605
- fs15.unlinkSync(path27);
8683
+ fs15.unlinkSync(path28);
8606
8684
  } catch {
8607
8685
  }
8608
8686
  return content;
@@ -8620,7 +8698,7 @@ async function claim(token, pluginId) {
8620
8698
  pluginId,
8621
8699
  ideName: "codeam-cli (codespace)",
8622
8700
  ideVersion: process.env.npm_package_version ?? "unknown",
8623
- hostname: os14.hostname(),
8701
+ hostname: os15.hostname(),
8624
8702
  codespaceName: process.env.CODESPACE_NAME ?? "",
8625
8703
  // Current git branch of the codespace's working directory, so the
8626
8704
  // backend can populate `PairedSession.branch` for the codespace pair.
@@ -8820,12 +8898,12 @@ var GitHubCodespacesProvider = class {
8820
8898
  }
8821
8899
  if (!isAuthed) {
8822
8900
  resetStdinForChild();
8823
- await new Promise((resolve2, reject) => {
8901
+ await new Promise((resolve3, reject) => {
8824
8902
  const proc = (0, import_child_process12.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
8825
8903
  stdio: "inherit"
8826
8904
  });
8827
8905
  proc.on("exit", (code) => {
8828
- if (code === 0) resolve2();
8906
+ if (code === 0) resolve3();
8829
8907
  else reject(new Error("gh auth login failed."));
8830
8908
  });
8831
8909
  proc.on("error", reject);
@@ -8854,13 +8932,13 @@ var GitHubCodespacesProvider = class {
8854
8932
  }
8855
8933
  wt(noteLines.join("\n"), "One more permission needed");
8856
8934
  resetStdinForChild();
8857
- const refreshCode = await new Promise((resolve2, reject) => {
8935
+ const refreshCode = await new Promise((resolve3, reject) => {
8858
8936
  const proc = (0, import_child_process12.spawn)(
8859
8937
  "gh",
8860
8938
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
8861
8939
  { stdio: "inherit" }
8862
8940
  );
8863
- proc.on("exit", (code) => resolve2(code ?? 1));
8941
+ proc.on("exit", (code) => resolve3(code ?? 1));
8864
8942
  proc.on("error", reject);
8865
8943
  });
8866
8944
  if (refreshCode !== 0) {
@@ -9004,10 +9082,10 @@ var GitHubCodespacesProvider = class {
9004
9082
  if (q(proceed) || !proceed) return;
9005
9083
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
9006
9084
  resetStdinForChild();
9007
- const ok = await new Promise((resolve2) => {
9085
+ const ok = await new Promise((resolve3) => {
9008
9086
  const proc = (0, import_child_process12.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
9009
- proc.on("exit", (code) => resolve2(code === 0));
9010
- proc.on("error", () => resolve2(false));
9087
+ proc.on("exit", (code) => resolve3(code === 0));
9088
+ proc.on("error", () => resolve3(false));
9011
9089
  });
9012
9090
  if (ok) O2.success("gh installed");
9013
9091
  else O2.error("gh install failed");
@@ -9031,14 +9109,14 @@ var GitHubCodespacesProvider = class {
9031
9109
  "Expanding GitHub scopes"
9032
9110
  );
9033
9111
  resetStdinForChild();
9034
- await new Promise((resolve2, reject) => {
9112
+ await new Promise((resolve3, reject) => {
9035
9113
  const proc = (0, import_child_process12.spawn)(
9036
9114
  "gh",
9037
9115
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
9038
9116
  { stdio: "inherit" }
9039
9117
  );
9040
9118
  proc.on("exit", (code) => {
9041
- if (code === 0) resolve2();
9119
+ if (code === 0) resolve3();
9042
9120
  else reject(new Error(
9043
9121
  "gh auth refresh failed. Re-run `gh auth refresh -h github.com -s repo,read:org` manually."
9044
9122
  ));
@@ -9209,13 +9287,13 @@ var GitHubCodespacesProvider = class {
9209
9287
  }
9210
9288
  async streamCommand(workspaceId, command2) {
9211
9289
  resetStdinForChild();
9212
- return new Promise((resolve2, reject) => {
9290
+ return new Promise((resolve3, reject) => {
9213
9291
  const proc = (0, import_child_process12.spawn)(
9214
9292
  "gh",
9215
9293
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
9216
9294
  { stdio: "inherit" }
9217
9295
  );
9218
- proc.on("exit", (code) => resolve2({ code: code ?? 0 }));
9296
+ proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
9219
9297
  proc.on("error", reject);
9220
9298
  });
9221
9299
  }
@@ -9236,7 +9314,7 @@ var GitHubCodespacesProvider = class {
9236
9314
  "--",
9237
9315
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
9238
9316
  ];
9239
- await new Promise((resolve2, reject) => {
9317
+ await new Promise((resolve3, reject) => {
9240
9318
  const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
9241
9319
  stdio: ["ignore", "pipe", "pipe"],
9242
9320
  env: tarEnv
@@ -9256,7 +9334,7 @@ var GitHubCodespacesProvider = class {
9256
9334
  ssh.on("error", reject);
9257
9335
  ssh.on("exit", (code) => {
9258
9336
  if (code === 0) {
9259
- resolve2();
9337
+ resolve3();
9260
9338
  } else {
9261
9339
  const reason = (sshErr || tarErr || `exit ${code}`).trim().slice(0, 500);
9262
9340
  reject(new Error(`Remote tar failed: ${reason}`));
@@ -9274,7 +9352,7 @@ var GitHubCodespacesProvider = class {
9274
9352
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote(remotePath)}`);
9275
9353
  }
9276
9354
  const cmd = parts.join(" && ");
9277
- await new Promise((resolve2, reject) => {
9355
+ await new Promise((resolve3, reject) => {
9278
9356
  const proc = (0, import_child_process12.spawn)(
9279
9357
  "gh",
9280
9358
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
@@ -9286,7 +9364,7 @@ var GitHubCodespacesProvider = class {
9286
9364
  });
9287
9365
  proc.on("error", reject);
9288
9366
  proc.on("exit", (code) => {
9289
- if (code === 0) resolve2();
9367
+ if (code === 0) resolve3();
9290
9368
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
9291
9369
  });
9292
9370
  proc.stdin?.write(contents);
@@ -9376,10 +9454,10 @@ var GitpodProvider = class {
9376
9454
  "Authenticating Gitpod"
9377
9455
  );
9378
9456
  resetStdinForChild2();
9379
- await new Promise((resolve2, reject) => {
9457
+ await new Promise((resolve3, reject) => {
9380
9458
  const proc = (0, import_child_process13.spawn)("gitpod", ["login"], { stdio: "inherit" });
9381
9459
  proc.on("exit", (code) => {
9382
- if (code === 0) resolve2();
9460
+ if (code === 0) resolve3();
9383
9461
  else reject(new Error("gitpod login failed."));
9384
9462
  });
9385
9463
  proc.on("error", reject);
@@ -9528,13 +9606,13 @@ var GitpodProvider = class {
9528
9606
  }
9529
9607
  async streamCommand(workspaceId, command2) {
9530
9608
  resetStdinForChild2();
9531
- return new Promise((resolve2, reject) => {
9609
+ return new Promise((resolve3, reject) => {
9532
9610
  const proc = (0, import_child_process13.spawn)(
9533
9611
  "gitpod",
9534
9612
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
9535
9613
  { stdio: "inherit" }
9536
9614
  );
9537
- proc.on("exit", (code) => resolve2({ code: code ?? 0 }));
9615
+ proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
9538
9616
  proc.on("error", reject);
9539
9617
  });
9540
9618
  }
@@ -9548,7 +9626,7 @@ var GitpodProvider = class {
9548
9626
  tarArgs.push(".");
9549
9627
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
9550
9628
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
9551
- await new Promise((resolve2, reject) => {
9629
+ await new Promise((resolve3, reject) => {
9552
9630
  const tar = (0, import_child_process13.spawn)("tar", tarArgs, {
9553
9631
  stdio: ["ignore", "pipe", "pipe"],
9554
9632
  env: tarEnv
@@ -9569,7 +9647,7 @@ var GitpodProvider = class {
9569
9647
  tar.on("error", reject);
9570
9648
  ssh.on("error", reject);
9571
9649
  ssh.on("exit", (code) => {
9572
- if (code === 0) resolve2();
9650
+ if (code === 0) resolve3();
9573
9651
  else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
9574
9652
  });
9575
9653
  });
@@ -9584,7 +9662,7 @@ var GitpodProvider = class {
9584
9662
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote2(remotePath)}`);
9585
9663
  }
9586
9664
  const cmd = parts.join(" && ");
9587
- await new Promise((resolve2, reject) => {
9665
+ await new Promise((resolve3, reject) => {
9588
9666
  const proc = (0, import_child_process13.spawn)(
9589
9667
  "gitpod",
9590
9668
  ["workspace", "ssh", workspaceId, "--", cmd],
@@ -9596,7 +9674,7 @@ var GitpodProvider = class {
9596
9674
  });
9597
9675
  proc.on("error", reject);
9598
9676
  proc.on("exit", (code) => {
9599
- if (code === 0) resolve2();
9677
+ if (code === 0) resolve3();
9600
9678
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
9601
9679
  });
9602
9680
  proc.stdin?.write(contents);
@@ -9653,14 +9731,14 @@ var GitLabWorkspacesProvider = class {
9653
9731
  "Authenticating GitLab"
9654
9732
  );
9655
9733
  resetStdinForChild3();
9656
- await new Promise((resolve2, reject) => {
9734
+ await new Promise((resolve3, reject) => {
9657
9735
  const proc = (0, import_child_process14.spawn)(
9658
9736
  "glab",
9659
9737
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
9660
9738
  { stdio: "inherit" }
9661
9739
  );
9662
9740
  proc.on("exit", (code) => {
9663
- if (code === 0) resolve2();
9741
+ if (code === 0) resolve3();
9664
9742
  else reject(new Error("glab auth login failed."));
9665
9743
  });
9666
9744
  proc.on("error", reject);
@@ -9825,13 +9903,13 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9825
9903
  async streamCommand(workspaceId, command2) {
9826
9904
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
9827
9905
  resetStdinForChild3();
9828
- return new Promise((resolve2, reject) => {
9906
+ return new Promise((resolve3, reject) => {
9829
9907
  const proc = (0, import_child_process14.spawn)(
9830
9908
  "ssh",
9831
9909
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
9832
9910
  { stdio: "inherit" }
9833
9911
  );
9834
- proc.on("exit", (code) => resolve2({ code: code ?? 0 }));
9912
+ proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
9835
9913
  proc.on("error", reject);
9836
9914
  });
9837
9915
  }
@@ -9846,7 +9924,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9846
9924
  tarArgs.push(".");
9847
9925
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
9848
9926
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
9849
- await new Promise((resolve2, reject) => {
9927
+ await new Promise((resolve3, reject) => {
9850
9928
  const tar = (0, import_child_process14.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
9851
9929
  const ssh = (0, import_child_process14.spawn)(
9852
9930
  "ssh",
@@ -9864,7 +9942,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9864
9942
  tar.on("error", reject);
9865
9943
  ssh.on("error", reject);
9866
9944
  ssh.on("exit", (code) => {
9867
- if (code === 0) resolve2();
9945
+ if (code === 0) resolve3();
9868
9946
  else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
9869
9947
  });
9870
9948
  });
@@ -9877,7 +9955,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9877
9955
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
9878
9956
  }
9879
9957
  const cmd = parts.join(" && ");
9880
- await new Promise((resolve2, reject) => {
9958
+ await new Promise((resolve3, reject) => {
9881
9959
  const proc = (0, import_child_process14.spawn)(
9882
9960
  "ssh",
9883
9961
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
@@ -9889,7 +9967,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9889
9967
  });
9890
9968
  proc.on("error", reject);
9891
9969
  proc.on("exit", (code) => {
9892
- if (code === 0) resolve2();
9970
+ if (code === 0) resolve3();
9893
9971
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
9894
9972
  });
9895
9973
  proc.stdin?.write(contents);
@@ -9980,10 +10058,10 @@ var RailwayProvider = class {
9980
10058
  "Authenticating Railway"
9981
10059
  );
9982
10060
  resetStdinForChild4();
9983
- await new Promise((resolve2, reject) => {
10061
+ await new Promise((resolve3, reject) => {
9984
10062
  const proc = (0, import_child_process15.spawn)("railway", ["login"], { stdio: "inherit" });
9985
10063
  proc.on("exit", (code) => {
9986
- if (code === 0) resolve2();
10064
+ if (code === 0) resolve3();
9987
10065
  else reject(new Error("railway login failed."));
9988
10066
  });
9989
10067
  proc.on("error", reject);
@@ -10123,13 +10201,13 @@ var RailwayProvider = class {
10123
10201
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
10124
10202
  }
10125
10203
  resetStdinForChild4();
10126
- return new Promise((resolve2, reject) => {
10204
+ return new Promise((resolve3, reject) => {
10127
10205
  const proc = (0, import_child_process15.spawn)(
10128
10206
  "railway",
10129
10207
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
10130
10208
  { stdio: "inherit" }
10131
10209
  );
10132
- proc.on("exit", (code) => resolve2({ code: code ?? 0 }));
10210
+ proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
10133
10211
  proc.on("error", reject);
10134
10212
  });
10135
10213
  }
@@ -10147,7 +10225,7 @@ var RailwayProvider = class {
10147
10225
  tarArgs.push(".");
10148
10226
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
10149
10227
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
10150
- await new Promise((resolve2, reject) => {
10228
+ await new Promise((resolve3, reject) => {
10151
10229
  const tar = (0, import_child_process15.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
10152
10230
  const sh = (0, import_child_process15.spawn)(
10153
10231
  "railway",
@@ -10165,7 +10243,7 @@ var RailwayProvider = class {
10165
10243
  tar.on("error", reject);
10166
10244
  sh.on("error", reject);
10167
10245
  sh.on("exit", (code) => {
10168
- if (code === 0) resolve2();
10246
+ if (code === 0) resolve3();
10169
10247
  else reject(new Error(`Remote tar failed: ${(shErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
10170
10248
  });
10171
10249
  });
@@ -10181,7 +10259,7 @@ var RailwayProvider = class {
10181
10259
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
10182
10260
  }
10183
10261
  const cmd = parts.join(" && ");
10184
- await new Promise((resolve2, reject) => {
10262
+ await new Promise((resolve3, reject) => {
10185
10263
  const proc = (0, import_child_process15.spawn)(
10186
10264
  "railway",
10187
10265
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
@@ -10193,7 +10271,7 @@ var RailwayProvider = class {
10193
10271
  });
10194
10272
  proc.on("error", reject);
10195
10273
  proc.on("exit", (code) => {
10196
- if (code === 0) resolve2();
10274
+ if (code === 0) resolve3();
10197
10275
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
10198
10276
  });
10199
10277
  proc.stdin?.write(contents);
@@ -10717,57 +10795,64 @@ async function stopWorkspaceFromLocal(target) {
10717
10795
  // src/commands/link.ts
10718
10796
  var import_node_child_process3 = require("child_process");
10719
10797
  var import_node_crypto = require("crypto");
10798
+ var fs18 = __toESM(require("fs"));
10799
+ var path26 = __toESM(require("path"));
10800
+ var import_chokidar = __toESM(require("chokidar"));
10720
10801
  var import_picocolors11 = __toESM(require("picocolors"));
10721
10802
 
10722
10803
  // src/agents/claude/local-token.ts
10723
10804
  var import_node_child_process2 = require("child_process");
10724
10805
  var fs16 = __toESM(require("fs"));
10725
- var os15 = __toESM(require("os"));
10806
+ var os16 = __toESM(require("os"));
10726
10807
  var path24 = __toESM(require("path"));
10727
10808
  var import_node_util3 = require("util");
10728
10809
  var execFileP7 = (0, import_node_util3.promisify)(import_node_child_process2.execFile);
10729
- function claudeCredentialsPath() {
10730
- return path24.join(os15.homedir(), ".claude", ".credentials.json");
10810
+ var KEYCHAIN_SERVICE_NAMES = [
10811
+ "Claude Code-credentials",
10812
+ "claude-code-credentials",
10813
+ "Claude",
10814
+ "Anthropic Claude"
10815
+ ];
10816
+ function claudeCredentialsPaths() {
10817
+ const home = os16.homedir();
10818
+ return [
10819
+ path24.join(home, ".claude", ".credentials.json"),
10820
+ path24.join(home, ".config", "claude", ".credentials.json")
10821
+ ];
10731
10822
  }
10732
10823
  async function extractLocalClaudeToken() {
10733
- const flat = claudeCredentialsPath();
10734
- if (fs16.existsSync(flat)) {
10824
+ for (const flat of claudeCredentialsPaths()) {
10825
+ if (!fs16.existsSync(flat)) continue;
10735
10826
  const credential = fs16.readFileSync(flat, "utf8").trim();
10736
10827
  if (credential.length > 0) {
10737
10828
  return { method: "oauth", credential, source: "flat-file" };
10738
10829
  }
10739
10830
  }
10740
10831
  if (process.platform === "darwin") {
10741
- try {
10742
- const { stdout } = await execFileP7(
10743
- "security",
10744
- ["find-generic-password", "-s", "Claude Code-credentials", "-w"],
10745
- { maxBuffer: 1024 * 1024 }
10746
- );
10747
- const credential = stdout.trim();
10748
- if (credential.length > 0) {
10749
- return { method: "oauth", credential, source: "macos-keychain" };
10832
+ for (const service of KEYCHAIN_SERVICE_NAMES) {
10833
+ try {
10834
+ const { stdout } = await execFileP7(
10835
+ "security",
10836
+ ["find-generic-password", "-s", service, "-w"],
10837
+ { maxBuffer: 1024 * 1024 }
10838
+ );
10839
+ const credential = stdout.trim();
10840
+ if (credential.length > 0) {
10841
+ return { method: "oauth", credential, source: "macos-keychain" };
10842
+ }
10843
+ } catch {
10750
10844
  }
10751
- } catch {
10752
10845
  }
10753
10846
  }
10754
10847
  return null;
10755
10848
  }
10756
- function claudeCredentialsMtime() {
10757
- const flat = claudeCredentialsPath();
10758
- try {
10759
- return fs16.statSync(flat).mtimeMs;
10760
- } catch {
10761
- return null;
10762
- }
10763
- }
10764
10849
 
10765
10850
  // src/agents/codex/local-token.ts
10766
10851
  var fs17 = __toESM(require("fs"));
10767
- var os16 = __toESM(require("os"));
10852
+ var os17 = __toESM(require("os"));
10768
10853
  var path25 = __toESM(require("path"));
10769
10854
  function codexCredentialsPath() {
10770
- return path25.join(os16.homedir(), ".codex", "auth.json");
10855
+ return path25.join(os17.homedir(), ".codex", "auth.json");
10771
10856
  }
10772
10857
  async function extractLocalCodexToken() {
10773
10858
  const file = codexCredentialsPath();
@@ -10776,13 +10861,8 @@ async function extractLocalCodexToken() {
10776
10861
  if (credential.length === 0) return null;
10777
10862
  return { method: "oauth", credential, source: "flat-file" };
10778
10863
  }
10779
- function codexCredentialsMtime() {
10780
- const file = codexCredentialsPath();
10781
- try {
10782
- return fs17.statSync(file).mtimeMs;
10783
- } catch {
10784
- return null;
10785
- }
10864
+ function codexCredentialsPaths() {
10865
+ return [codexCredentialsPath()];
10786
10866
  }
10787
10867
 
10788
10868
  // src/commands/link.ts
@@ -10791,23 +10871,38 @@ var AGENT_META = {
10791
10871
  internalId: "claude",
10792
10872
  publicId: "claude_code",
10793
10873
  binary: "claude",
10794
- loginArgs: ["login"],
10795
10874
  displayName: "Claude Code",
10796
10875
  vendor: "Anthropic",
10797
- credentialsHint: "~/.claude/.credentials.json (or macOS Keychain)",
10876
+ credentialsHint: "~/.claude/.credentials.json or the macOS Keychain",
10877
+ watchPaths: claudeCredentialsPaths,
10798
10878
  extract: extractLocalClaudeToken,
10799
- mtime: claudeCredentialsMtime
10879
+ ensureInstalled: ensureClaudeInstalled,
10880
+ launchLogin: () => {
10881
+ const child = (0, import_node_child_process3.spawn)("claude", [], { stdio: ["pipe", "inherit", "inherit"] });
10882
+ child.stdin?.write("/login\n");
10883
+ return child;
10884
+ }
10800
10885
  },
10801
10886
  codex: {
10802
10887
  internalId: "codex",
10803
10888
  publicId: "codex",
10804
10889
  binary: "codex",
10805
- loginArgs: ["login"],
10806
10890
  displayName: "Codex",
10807
10891
  vendor: "OpenAI",
10808
10892
  credentialsHint: "~/.codex/auth.json",
10893
+ watchPaths: codexCredentialsPaths,
10809
10894
  extract: extractLocalCodexToken,
10810
- mtime: codexCredentialsMtime
10895
+ ensureInstalled: async () => {
10896
+ const { findInPath: findInPath2 } = await Promise.resolve().then(() => (init_types(), types_exports));
10897
+ if (findInPath2("codex")) return true;
10898
+ showError(
10899
+ "codex binary not found on PATH. Install it first (https://github.com/openai/codex-cli) then re-run `codeam link codex`."
10900
+ );
10901
+ return false;
10902
+ },
10903
+ launchLogin: () => {
10904
+ return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
10905
+ }
10811
10906
  }
10812
10907
  };
10813
10908
  function parseLinkArgs(args2) {
@@ -10825,11 +10920,15 @@ function parseLinkArgs(args2) {
10825
10920
  );
10826
10921
  }
10827
10922
  const reuseExisting = args2.includes("--reuse-existing");
10828
- return { agent: normalised, reuseExisting };
10923
+ const apiKeyArg = args2.find((a) => a.startsWith("--api-key="));
10924
+ const apiKey = apiKeyArg ? apiKeyArg.slice("--api-key=".length) : null;
10925
+ const tokenFileArg = args2.find((a) => a.startsWith("--token-file="));
10926
+ const tokenFile = tokenFileArg ? tokenFileArg.slice("--token-file=".length) : null;
10927
+ return { agent: normalised, reuseExisting, apiKey, tokenFile };
10829
10928
  }
10830
10929
  async function link(args2 = []) {
10831
- const { agent, reuseExisting } = parseLinkArgs(args2);
10832
- const meta = AGENT_META[agent];
10930
+ const parsed = parseLinkArgs(args2);
10931
+ const meta = AGENT_META[parsed.agent];
10833
10932
  showIntro();
10834
10933
  console.log(
10835
10934
  import_picocolors11.default.bold(` Link ${meta.displayName}`) + import_picocolors11.default.dim(` \xB7 ${meta.vendor}`)
@@ -10853,7 +10952,7 @@ async function link(args2 = []) {
10853
10952
  waitSpin.start(waitMsg());
10854
10953
  const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
10855
10954
  countdown.unref?.();
10856
- const paired = await new Promise((resolve2, reject) => {
10955
+ const paired = await new Promise((resolve3, reject) => {
10857
10956
  let stopPoll = null;
10858
10957
  const sigint = () => {
10859
10958
  clearInterval(countdown);
@@ -10866,7 +10965,7 @@ async function link(args2 = []) {
10866
10965
  process.removeListener("SIGINT", sigint);
10867
10966
  clearInterval(countdown);
10868
10967
  waitSpin.stop("Paired");
10869
- resolve2(info);
10968
+ resolve3(info);
10870
10969
  },
10871
10970
  () => {
10872
10971
  clearInterval(countdown);
@@ -10893,36 +10992,135 @@ async function link(args2 = []) {
10893
10992
  agent: meta.internalId
10894
10993
  });
10895
10994
  saveCliConfig({ ...loadCliConfig(), preferredAgent: meta.internalId });
10896
- let token = await meta.extract();
10897
- if (token && reuseExisting) {
10898
- showInfo(`Reusing existing ${meta.displayName} token at ${import_picocolors11.default.bold(meta.credentialsHint)}.`);
10899
- } else {
10900
- const beforeMtime = meta.mtime();
10901
- showInfo(`Launching ${import_picocolors11.default.bold(`${meta.binary} ${meta.loginArgs.join(" ")}`)} \u2014 complete the sign-in in your browser, then return here.`);
10902
- console.log("");
10903
- const code = await runAgentLogin(meta);
10904
- console.log("");
10905
- if (code !== 0) {
10906
- showError(
10907
- `${meta.binary} ${meta.loginArgs.join(" ")} exited with code ${code}. Re-run when ready.`
10908
- );
10909
- process.exit(1);
10910
- }
10911
- const refreshed = await meta.extract();
10912
- const afterMtime = meta.mtime();
10913
- if (!refreshed) {
10914
- showError(
10915
- `${meta.displayName} login finished but no credential was found at ${meta.credentialsHint}. Re-run when ready.`
10916
- );
10995
+ if (parsed.apiKey) {
10996
+ await uploadAndSucceed(meta, paired, pluginId, {
10997
+ method: "api_key",
10998
+ credential: parsed.apiKey.trim(),
10999
+ source: "manual"
11000
+ });
11001
+ return;
11002
+ }
11003
+ if (parsed.tokenFile) {
11004
+ const credential = fs18.readFileSync(path26.resolve(parsed.tokenFile), "utf8").trim();
11005
+ if (!credential) {
11006
+ showError(`--token-file ${parsed.tokenFile} is empty.`);
10917
11007
  process.exit(1);
10918
11008
  }
10919
- if (token && refreshed.credential === token.credential && beforeMtime !== null && afterMtime !== null && afterMtime <= beforeMtime) {
10920
- showError(
10921
- `${meta.displayName} login didn't produce a fresh token. Re-run when ready, or pass --reuse-existing to keep the current one.`
10922
- );
10923
- process.exit(1);
11009
+ await uploadAndSucceed(meta, paired, pluginId, {
11010
+ method: "oauth",
11011
+ credential,
11012
+ source: "manual"
11013
+ });
11014
+ return;
11015
+ }
11016
+ const installSpin = dist_exports.spinner();
11017
+ installSpin.start(`Checking that ${meta.binary} is installed...`);
11018
+ const installed = await meta.ensureInstalled();
11019
+ if (!installed) {
11020
+ installSpin.stop("Failed");
11021
+ showError(`Could not install ${meta.displayName}. Install it manually then re-run.`);
11022
+ process.exit(1);
11023
+ }
11024
+ installSpin.stop(`${meta.displayName} is installed`);
11025
+ const existing = await meta.extract();
11026
+ if (existing) {
11027
+ showInfo(`Found existing ${meta.displayName} credentials at ${import_picocolors11.default.bold(existing.source)}.`);
11028
+ await uploadAndSucceed(meta, paired, pluginId, existing);
11029
+ return;
11030
+ }
11031
+ if (parsed.reuseExisting) {
11032
+ showError(
11033
+ `--reuse-existing set, but no local ${meta.displayName} credentials were found at ${meta.credentialsHint}.`
11034
+ );
11035
+ process.exit(1);
11036
+ }
11037
+ showInfo(
11038
+ `No local ${meta.displayName} credentials found. Launching the sign-in \u2014 complete it in your browser, the CLI will detect the new token and finish automatically.`
11039
+ );
11040
+ console.log("");
11041
+ const captured = await captureFreshCredentials(meta);
11042
+ console.log("");
11043
+ await uploadAndSucceed(meta, paired, pluginId, captured);
11044
+ }
11045
+ async function captureFreshCredentials(meta) {
11046
+ const isWin = process.platform === "win32";
11047
+ const watcher = import_chokidar.default.watch(meta.watchPaths(), {
11048
+ persistent: true,
11049
+ ignoreInitial: false,
11050
+ awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
11051
+ // Windows-only: when a credential file's ancestor is missing,
11052
+ // chokidar walks up to the closest existing parent and starts
11053
+ // traversing it. On a default Windows shell that ancestor is
11054
+ // `C:\Users\<u>`, which contains legacy junctions whose ACL makes
11055
+ // `fs.watch` throw EPERM (#43). These flags are no-ops on macOS,
11056
+ // where the user has read access to their entire home.
11057
+ ...isWin ? { followSymlinks: false, ignorePermissionErrors: true } : {}
11058
+ });
11059
+ watcher.on("error", () => {
11060
+ });
11061
+ let child = null;
11062
+ let keychainPoll = null;
11063
+ const cleanup = () => {
11064
+ void watcher.close();
11065
+ if (keychainPoll) clearInterval(keychainPoll);
11066
+ if (child && !child.killed) {
11067
+ try {
11068
+ child.kill("SIGTERM");
11069
+ } catch {
11070
+ }
10924
11071
  }
10925
- token = refreshed;
11072
+ };
11073
+ try {
11074
+ const token = await new Promise((resolve3, reject) => {
11075
+ let settled = false;
11076
+ const tryExtract = async () => {
11077
+ if (settled) return;
11078
+ const t2 = await meta.extract();
11079
+ if (t2 && !settled) {
11080
+ settled = true;
11081
+ resolve3(t2);
11082
+ }
11083
+ };
11084
+ watcher.on("add", () => void tryExtract());
11085
+ watcher.on("change", () => void tryExtract());
11086
+ keychainPoll = setInterval(() => void tryExtract(), 2e3);
11087
+ keychainPoll.unref?.();
11088
+ const sigint = () => {
11089
+ if (settled) return;
11090
+ settled = true;
11091
+ reject(new Error("cancelled"));
11092
+ };
11093
+ process.once("SIGINT", sigint);
11094
+ setTimeout(() => {
11095
+ if (settled) return;
11096
+ settled = true;
11097
+ reject(new Error(`Timed out waiting for ${meta.displayName} sign-in (5 minutes).`));
11098
+ }, 5 * 6e4);
11099
+ child = meta.launchLogin();
11100
+ child.on("exit", () => {
11101
+ void tryExtract().then(() => {
11102
+ if (!settled) {
11103
+ settled = true;
11104
+ reject(
11105
+ new Error(
11106
+ `${meta.binary} exited but no credentials were written at ${meta.credentialsHint}.`
11107
+ )
11108
+ );
11109
+ }
11110
+ });
11111
+ });
11112
+ });
11113
+ cleanup();
11114
+ return token;
11115
+ } catch (err) {
11116
+ cleanup();
11117
+ throw err;
11118
+ }
11119
+ }
11120
+ async function uploadAndSucceed(meta, paired, pluginId, token) {
11121
+ if (!paired.pluginAuthToken) {
11122
+ showError("Missing pluginAuthToken; re-run codeam link.");
11123
+ process.exit(1);
10926
11124
  }
10927
11125
  const uploadSpin = dist_exports.spinner();
10928
11126
  uploadSpin.start("Sealing credential in your vault...");
@@ -10937,12 +11135,10 @@ async function link(args2 = []) {
10937
11135
  if (!result.ok) {
10938
11136
  uploadSpin.stop("Failed");
10939
11137
  if (result.status === 401) {
10940
- showError(
10941
- "Pair token rejected by the backend (401). Re-run `codeam link` to start fresh."
10942
- );
11138
+ showError("Pair token rejected by the backend (401). Re-run `codeam link`.");
10943
11139
  } else if (result.status === 404) {
10944
11140
  showError(
10945
- `${meta.displayName} link endpoint not available on this backend (404). The api-v2 deployment may not yet include the /api/plugin/agents/:agentId/link route.`
11141
+ `${meta.displayName} link endpoint not available on this backend (404). The api-v2 deployment may not yet include the route.`
10946
11142
  );
10947
11143
  } else {
10948
11144
  showError(`Upload failed: ${result.message}`);
@@ -10957,28 +11153,11 @@ async function link(args2 = []) {
10957
11153
  );
10958
11154
  console.log("");
10959
11155
  }
10960
- function runAgentLogin(meta) {
10961
- return new Promise((resolve2) => {
10962
- const child = (0, import_node_child_process3.spawn)(meta.binary, meta.loginArgs, { stdio: "inherit" });
10963
- child.on("error", (err) => {
10964
- if (err.code === "ENOENT") {
10965
- showError(
10966
- `${meta.binary} binary not found on PATH. Install ${meta.displayName} first, then re-run \`codeam link ${meta.internalId}\`.`
10967
- );
10968
- resolve2(127);
10969
- return;
10970
- }
10971
- showError(`Failed to launch ${meta.binary}: ${err.message}`);
10972
- resolve2(1);
10973
- });
10974
- child.on("exit", (code) => resolve2(code ?? 1));
10975
- });
10976
- }
10977
11156
 
10978
11157
  // src/commands/version.ts
10979
11158
  var import_picocolors12 = __toESM(require("picocolors"));
10980
11159
  function version() {
10981
- const v = true ? "2.16.0" : "unknown";
11160
+ const v = true ? "2.16.2" : "unknown";
10982
11161
  console.log(`${import_picocolors12.default.bold("codeam-cli")} ${import_picocolors12.default.cyan(v)}`);
10983
11162
  }
10984
11163
 
@@ -11020,9 +11199,9 @@ function help() {
11020
11199
  }
11021
11200
 
11022
11201
  // src/lib/updateNotifier.ts
11023
- var fs18 = __toESM(require("fs"));
11024
- var os17 = __toESM(require("os"));
11025
- var path26 = __toESM(require("path"));
11202
+ var fs19 = __toESM(require("fs"));
11203
+ var os18 = __toESM(require("os"));
11204
+ var path27 = __toESM(require("path"));
11026
11205
  var https7 = __toESM(require("https"));
11027
11206
  var import_picocolors14 = __toESM(require("picocolors"));
11028
11207
  var PKG_NAME = "codeam-cli";
@@ -11030,12 +11209,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
11030
11209
  var TTL_MS = 24 * 60 * 60 * 1e3;
11031
11210
  var REQUEST_TIMEOUT_MS = 1500;
11032
11211
  function cachePath() {
11033
- const dir = path26.join(os17.homedir(), ".codeam");
11034
- return path26.join(dir, "update-check.json");
11212
+ const dir = path27.join(os18.homedir(), ".codeam");
11213
+ return path27.join(dir, "update-check.json");
11035
11214
  }
11036
11215
  function readCache() {
11037
11216
  try {
11038
- const raw = fs18.readFileSync(cachePath(), "utf8");
11217
+ const raw = fs19.readFileSync(cachePath(), "utf8");
11039
11218
  const parsed = JSON.parse(raw);
11040
11219
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
11041
11220
  return parsed;
@@ -11046,8 +11225,8 @@ function readCache() {
11046
11225
  function writeCache(cache) {
11047
11226
  try {
11048
11227
  const file = cachePath();
11049
- fs18.mkdirSync(path26.dirname(file), { recursive: true });
11050
- fs18.writeFileSync(file, JSON.stringify(cache));
11228
+ fs19.mkdirSync(path27.dirname(file), { recursive: true });
11229
+ fs19.writeFileSync(file, JSON.stringify(cache));
11051
11230
  } catch {
11052
11231
  }
11053
11232
  }
@@ -11065,14 +11244,14 @@ function compareSemver(a, b) {
11065
11244
  return 0;
11066
11245
  }
11067
11246
  function fetchLatest() {
11068
- return new Promise((resolve2) => {
11247
+ return new Promise((resolve3) => {
11069
11248
  const req = https7.get(
11070
11249
  REGISTRY_URL,
11071
11250
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
11072
11251
  (res) => {
11073
11252
  if (res.statusCode !== 200) {
11074
11253
  res.resume();
11075
- resolve2(null);
11254
+ resolve3(null);
11076
11255
  return;
11077
11256
  }
11078
11257
  let buf = "";
@@ -11084,21 +11263,21 @@ function fetchLatest() {
11084
11263
  try {
11085
11264
  const json = JSON.parse(buf);
11086
11265
  if (typeof json.version === "string") {
11087
- resolve2(json.version);
11266
+ resolve3(json.version);
11088
11267
  } else {
11089
- resolve2(null);
11268
+ resolve3(null);
11090
11269
  }
11091
11270
  } catch {
11092
- resolve2(null);
11271
+ resolve3(null);
11093
11272
  }
11094
11273
  });
11095
11274
  }
11096
11275
  );
11097
11276
  req.on("timeout", () => {
11098
11277
  req.destroy();
11099
- resolve2(null);
11278
+ resolve3(null);
11100
11279
  });
11101
- req.on("error", () => resolve2(null));
11280
+ req.on("error", () => resolve3(null));
11102
11281
  });
11103
11282
  }
11104
11283
  function notifyIfStale(currentVersion, latest) {
@@ -11118,7 +11297,7 @@ function checkForUpdates() {
11118
11297
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
11119
11298
  if (process.env.CI) return;
11120
11299
  if (!process.stdout.isTTY) return;
11121
- const current = true ? "2.16.0" : null;
11300
+ const current = true ? "2.16.2" : null;
11122
11301
  if (!current) return;
11123
11302
  const cache = readCache();
11124
11303
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;