codeam-cli 2.15.8 → 2.16.1

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 +18 -0
  2. package/dist/index.js +611 -164
  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.15.8",
427
+ version: "2.16.1",
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",
@@ -646,10 +681,86 @@ function pollStatus(pluginId, onPaired, onTimeout) {
646
681
  }
647
682
  var _transport = {
648
683
  postJson: _postJson,
649
- getJson: _getJson
684
+ getJson: _getJson,
685
+ postJsonAuthed: _postJsonAuthed
650
686
  };
687
+ async function postLinkCredential(input) {
688
+ const body = {
689
+ sessionId: input.sessionId,
690
+ pluginId: input.pluginId,
691
+ method: input.method,
692
+ credential: input.credential
693
+ };
694
+ if (input.modelPreference) {
695
+ body.modelPreference = input.modelPreference;
696
+ }
697
+ try {
698
+ await _transport.postJsonAuthed(
699
+ `${API_BASE}/api/plugin/agents/${input.agentId}/link`,
700
+ body,
701
+ input.pluginAuthToken
702
+ );
703
+ return { ok: true };
704
+ } catch (err) {
705
+ const e = err;
706
+ return {
707
+ ok: false,
708
+ status: typeof e.statusCode === "number" ? e.statusCode : 0,
709
+ message: e.message || "unknown"
710
+ };
711
+ }
712
+ }
713
+ async function _postJsonAuthed(url, body, pluginAuthToken) {
714
+ return new Promise((resolve3, reject) => {
715
+ const data = JSON.stringify(body);
716
+ const u2 = new URL(url);
717
+ const transport = u2.protocol === "https:" ? https : http;
718
+ const req = transport.request(
719
+ {
720
+ hostname: u2.hostname,
721
+ port: u2.port || (u2.protocol === "https:" ? 443 : 80),
722
+ path: u2.pathname + u2.search,
723
+ method: "POST",
724
+ headers: {
725
+ "Content-Type": "application/json",
726
+ "Content-Length": Buffer.byteLength(data),
727
+ "X-Plugin-Auth-Token": pluginAuthToken,
728
+ ...vercelBypassHeader()
729
+ },
730
+ timeout: 15e3
731
+ },
732
+ (res) => {
733
+ res.on("error", reject);
734
+ let responseBody = "";
735
+ res.on("data", (chunk) => {
736
+ responseBody += chunk.toString();
737
+ });
738
+ res.on("end", () => {
739
+ if (res.statusCode && res.statusCode >= 400) {
740
+ const err = new Error(`HTTP ${res.statusCode}: ${responseBody.slice(0, 200)}`);
741
+ err.statusCode = res.statusCode;
742
+ reject(err);
743
+ return;
744
+ }
745
+ try {
746
+ resolve3(JSON.parse(responseBody));
747
+ } catch {
748
+ resolve3(null);
749
+ }
750
+ });
751
+ }
752
+ );
753
+ req.on("error", reject);
754
+ req.on("timeout", () => {
755
+ req.destroy();
756
+ reject(new Error("timeout"));
757
+ });
758
+ req.write(data);
759
+ req.end();
760
+ });
761
+ }
651
762
  async function _postJson(url, body) {
652
- return new Promise((resolve2, reject) => {
763
+ return new Promise((resolve3, reject) => {
653
764
  const data = JSON.stringify(body);
654
765
  const u2 = new URL(url);
655
766
  const transport = u2.protocol === "https:" ? https : http;
@@ -678,9 +789,9 @@ async function _postJson(url, body) {
678
789
  return;
679
790
  }
680
791
  try {
681
- resolve2(JSON.parse(body2));
792
+ resolve3(JSON.parse(body2));
682
793
  } catch {
683
- resolve2(null);
794
+ resolve3(null);
684
795
  }
685
796
  });
686
797
  }
@@ -695,7 +806,7 @@ async function _postJson(url, body) {
695
806
  });
696
807
  }
697
808
  async function _getJson(url) {
698
- return new Promise((resolve2, reject) => {
809
+ return new Promise((resolve3, reject) => {
699
810
  const u2 = new URL(url);
700
811
  const transport = u2.protocol === "https:" ? https : http;
701
812
  const req = transport.request(
@@ -719,9 +830,9 @@ async function _getJson(url) {
719
830
  return;
720
831
  }
721
832
  try {
722
- resolve2(JSON.parse(body));
833
+ resolve3(JSON.parse(body));
723
834
  } catch {
724
- resolve2(null);
835
+ resolve3(null);
725
836
  }
726
837
  });
727
838
  }
@@ -1029,30 +1140,7 @@ var import_child_process2 = require("child_process");
1029
1140
  var fs4 = __toESM(require("fs"));
1030
1141
  var os4 = __toESM(require("os"));
1031
1142
  var path4 = __toESM(require("path"));
1032
-
1033
- // src/services/pty/types.ts
1034
- var fs3 = __toESM(require("fs"));
1035
- var path3 = __toESM(require("path"));
1036
- function findInPath(name) {
1037
- const isWin = process.platform === "win32";
1038
- const dirs = (process.env.PATH ?? "").split(path3.delimiter).filter(Boolean);
1039
- const hasExt = path3.extname(name).length > 0;
1040
- const candidates = isWin && !hasExt ? [`${name}.exe`, `${name}.cmd`, `${name}.bat`, `${name}.ps1`, name] : [name];
1041
- const accessFlag = isWin ? fs3.constants.F_OK : fs3.constants.X_OK;
1042
- for (const dir of dirs) {
1043
- for (const candidate of candidates) {
1044
- const full = path3.join(dir, candidate);
1045
- try {
1046
- fs3.accessSync(full, accessFlag);
1047
- return full;
1048
- } catch {
1049
- }
1050
- }
1051
- }
1052
- return null;
1053
- }
1054
-
1055
- // src/services/pty/unix.strategy.ts
1143
+ init_types();
1056
1144
  var PYTHON_PTY_HELPER = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
1057
1145
  m,s=pty.openpty()
1058
1146
  try:
@@ -3365,6 +3453,7 @@ ${c2}
3365
3453
  } }).prompt();
3366
3454
 
3367
3455
  // src/services/claude-installer.ts
3456
+ init_types();
3368
3457
  function probeInstallDirs() {
3369
3458
  const home = os5.homedir();
3370
3459
  if (process.platform === "win32") {
@@ -3402,15 +3491,15 @@ function runInstaller() {
3402
3491
  "-Command",
3403
3492
  "irm https://claude.ai/install.ps1 | iex"
3404
3493
  ] : ["-c", "curl -fsSL https://claude.ai/install.sh | bash"];
3405
- return new Promise((resolve2) => {
3494
+ return new Promise((resolve3) => {
3406
3495
  const proc = (0, import_child_process4.spawn)(cmd, args2, { stdio: "inherit" });
3407
3496
  proc.on("error", (err) => {
3408
3497
  console.error(`
3409
3498
  \u2717 Installer failed to launch: ${err.message}`);
3410
- resolve2(false);
3499
+ resolve3(false);
3411
3500
  });
3412
3501
  proc.on("exit", (code) => {
3413
- resolve2(code === 0);
3502
+ resolve3(code === 0);
3414
3503
  });
3415
3504
  });
3416
3505
  }
@@ -3444,6 +3533,7 @@ async function ensureClaudeInstalled() {
3444
3533
 
3445
3534
  // src/services/claude-resolver.ts
3446
3535
  var path7 = __toESM(require("path"));
3536
+ init_types();
3447
3537
  function buildClaudeLaunch(extraArgs = []) {
3448
3538
  const found = findInPath("claude") ?? findInPath("claude-code");
3449
3539
  if (!found) return null;
@@ -3703,6 +3793,7 @@ var fs5 = __toESM(require("fs"));
3703
3793
  var os6 = __toESM(require("os"));
3704
3794
  var path8 = __toESM(require("path"));
3705
3795
  var import_child_process5 = require("child_process");
3796
+ init_types();
3706
3797
  var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
3707
3798
  m,s=pty.openpty()
3708
3799
  try:
@@ -3758,17 +3849,17 @@ function parseUsageOutput(raw) {
3758
3849
  return { percent, resetAt };
3759
3850
  }
3760
3851
  async function fetchClaudeQuota() {
3761
- return new Promise((resolve2) => {
3852
+ return new Promise((resolve3) => {
3762
3853
  const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
3763
3854
  if (!claudeCmd) {
3764
- resolve2(null);
3855
+ resolve3(null);
3765
3856
  return;
3766
3857
  }
3767
3858
  const helperPath = path8.join(os6.tmpdir(), "codeam-quota-helper.py");
3768
3859
  fs5.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
3769
3860
  const python = findInPath("python3") ?? findInPath("python");
3770
3861
  if (!python) {
3771
- resolve2(null);
3862
+ resolve3(null);
3772
3863
  return;
3773
3864
  }
3774
3865
  const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
@@ -3795,13 +3886,13 @@ async function fetchClaudeQuota() {
3795
3886
  fs5.unlinkSync(helperPath);
3796
3887
  } catch {
3797
3888
  }
3798
- resolve2(result);
3889
+ resolve3(result);
3799
3890
  }, 5e3);
3800
3891
  }, 8e3);
3801
3892
  setTimeout(() => {
3802
3893
  if (!resolved) {
3803
3894
  resolved = true;
3804
- resolve2(null);
3895
+ resolve3(null);
3805
3896
  }
3806
3897
  try {
3807
3898
  proc.kill();
@@ -4446,6 +4537,7 @@ var ClaudeDeployStrategy = class {
4446
4537
 
4447
4538
  // src/agents/codex/runtime.ts
4448
4539
  var import_node_child_process = require("child_process");
4540
+ init_types();
4449
4541
 
4450
4542
  // src/agents/codex/history.ts
4451
4543
  var import_node_fs2 = __toESM(require("fs"));
@@ -5040,13 +5132,13 @@ var CodexRuntimeStrategy = class {
5040
5132
  }
5041
5133
  };
5042
5134
  async function installCodexViaNpm() {
5043
- return new Promise((resolve2, reject) => {
5135
+ return new Promise((resolve3, reject) => {
5044
5136
  const npm = process.platform === "win32" ? "npm.cmd" : "npm";
5045
5137
  const proc = (0, import_node_child_process.spawn)(npm, ["install", "-g", "@openai/codex"], {
5046
5138
  stdio: "inherit"
5047
5139
  });
5048
5140
  proc.on("close", (code) => {
5049
- if (code === 0) resolve2();
5141
+ if (code === 0) resolve3();
5050
5142
  else reject(new Error(`npm install -g @openai/codex exited ${code}`));
5051
5143
  });
5052
5144
  proc.on("error", reject);
@@ -5245,14 +5337,14 @@ var ChunkEmitter = class {
5245
5337
  "chunkEmitter",
5246
5338
  `send type=${body.type ?? "(clear)"} bytes=${payload.length} done=${body.done === true}`
5247
5339
  );
5248
- return new Promise((resolve2) => {
5340
+ return new Promise((resolve3) => {
5249
5341
  const attempt = (attemptsLeft) => {
5250
5342
  _transport2.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
5251
5343
  const tookMs = Date.now() - t0;
5252
5344
  if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
5253
5345
  process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
5254
5346
  log.info("chunkEmitter", `dead status=${statusCode} took=${tookMs}ms`);
5255
- resolve2({ dead: true });
5347
+ resolve3({ dead: true });
5256
5348
  return;
5257
5349
  }
5258
5350
  if (statusCode >= 400) {
@@ -5262,7 +5354,7 @@ var ChunkEmitter = class {
5262
5354
  } else {
5263
5355
  log.info("chunkEmitter", `ok status=${statusCode} took=${tookMs}ms`);
5264
5356
  }
5265
- resolve2({ dead: false });
5357
+ resolve3({ dead: false });
5266
5358
  }).catch((err) => {
5267
5359
  log.warn(
5268
5360
  "chunkEmitter",
@@ -5273,7 +5365,7 @@ var ChunkEmitter = class {
5273
5365
  const delay = 200 * (maxRetries - attemptsLeft + 1);
5274
5366
  setTimeout(() => attempt(attemptsLeft - 1), delay);
5275
5367
  } else {
5276
- resolve2({ dead: false });
5368
+ resolve3({ dead: false });
5277
5369
  }
5278
5370
  });
5279
5371
  };
@@ -5285,7 +5377,7 @@ var _transport2 = {
5285
5377
  post: _post
5286
5378
  };
5287
5379
  function _post(url, headers, payload) {
5288
- return new Promise((resolve2, reject) => {
5380
+ return new Promise((resolve3, reject) => {
5289
5381
  let settled = false;
5290
5382
  const u2 = new URL(url);
5291
5383
  const transport = u2.protocol === "https:" ? https3 : http3;
@@ -5309,7 +5401,7 @@ function _post(url, headers, payload) {
5309
5401
  res.on("end", () => {
5310
5402
  if (settled) return;
5311
5403
  settled = true;
5312
- resolve2({ statusCode: res.statusCode ?? 0, body: resData });
5404
+ resolve3({ statusCode: res.statusCode ?? 0, body: resData });
5313
5405
  });
5314
5406
  }
5315
5407
  );
@@ -5745,7 +5837,7 @@ function parseJsonl(filePath) {
5745
5837
  return messages;
5746
5838
  }
5747
5839
  function post(endpoint, body) {
5748
- return new Promise((resolve2) => {
5840
+ return new Promise((resolve3) => {
5749
5841
  const payload = JSON.stringify(body);
5750
5842
  const u2 = new URL(`${API_BASE4}${endpoint}`);
5751
5843
  const transport = u2.protocol === "https:" ? https4 : http4;
@@ -5766,17 +5858,17 @@ function post(endpoint, body) {
5766
5858
  res.resume();
5767
5859
  const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
5768
5860
  if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${res.statusCode}`);
5769
- resolve2(ok);
5861
+ resolve3(ok);
5770
5862
  }
5771
5863
  );
5772
5864
  req.on("error", (err) => {
5773
5865
  log.warn("history:post", `${endpoint} network error`, err);
5774
- resolve2(false);
5866
+ resolve3(false);
5775
5867
  });
5776
5868
  req.on("timeout", () => {
5777
5869
  log.warn("history:post", `${endpoint} timeout after 15s`);
5778
5870
  req.destroy();
5779
- resolve2(false);
5871
+ resolve3(false);
5780
5872
  });
5781
5873
  req.write(payload);
5782
5874
  req.end();
@@ -6258,7 +6350,7 @@ var _transport3 = {
6258
6350
  post: _post2
6259
6351
  };
6260
6352
  function _post2(url, headers, payload) {
6261
- return new Promise((resolve2, reject) => {
6353
+ return new Promise((resolve3, reject) => {
6262
6354
  let settled = false;
6263
6355
  const u2 = new URL(url);
6264
6356
  const lib = u2.protocol === "https:" ? https5 : http5;
@@ -6283,7 +6375,7 @@ function _post2(url, headers, payload) {
6283
6375
  res.on("end", () => {
6284
6376
  if (settled) return;
6285
6377
  settled = true;
6286
- resolve2({ statusCode: res.statusCode ?? 0, body });
6378
+ resolve3({ statusCode: res.statusCode ?? 0, body });
6287
6379
  });
6288
6380
  }
6289
6381
  );
@@ -6327,9 +6419,9 @@ var FileWatcherService = class {
6327
6419
  if (this.stopped) {
6328
6420
  throw new Error("FileWatcherService has already been stopped \u2014 re-instantiate to restart.");
6329
6421
  }
6330
- let chokidar;
6422
+ let chokidar2;
6331
6423
  try {
6332
- chokidar = require("chokidar");
6424
+ chokidar2 = require("chokidar");
6333
6425
  } catch (err) {
6334
6426
  log.warn(
6335
6427
  "fileWatcher",
@@ -6338,7 +6430,7 @@ var FileWatcherService = class {
6338
6430
  );
6339
6431
  return;
6340
6432
  }
6341
- const watcher = chokidar.watch(this.opts.workingDir, {
6433
+ const watcher = chokidar2.watch(this.opts.workingDir, {
6342
6434
  ignored: [
6343
6435
  /(^|[\\/])\../,
6344
6436
  // dot-files & dot-dirs (.git, .next, .expo, .DS_Store, …)
@@ -6591,12 +6683,12 @@ var _gitSeam = {
6591
6683
  run: _runGitImpl
6592
6684
  };
6593
6685
  async function _runGitImpl(cwd, args2, opts = {}) {
6594
- return new Promise((resolve2) => {
6686
+ return new Promise((resolve3) => {
6595
6687
  let proc;
6596
6688
  try {
6597
6689
  proc = (0, import_child_process7.spawn)("git", args2, { cwd, env: process.env });
6598
6690
  } catch {
6599
- resolve2(null);
6691
+ resolve3(null);
6600
6692
  return;
6601
6693
  }
6602
6694
  let stdout = "";
@@ -6607,13 +6699,13 @@ async function _runGitImpl(cwd, args2, opts = {}) {
6607
6699
  proc.stderr?.on("data", (c2) => {
6608
6700
  stderr += c2.toString();
6609
6701
  });
6610
- proc.on("error", () => resolve2(null));
6702
+ proc.on("error", () => resolve3(null));
6611
6703
  proc.on("close", (code) => {
6612
6704
  if (code === 0 || opts.allowNonZeroExit) {
6613
- resolve2(stdout);
6705
+ resolve3(stdout);
6614
6706
  } else {
6615
6707
  log.trace("fileWatcher", `git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`);
6616
- resolve2(null);
6708
+ resolve3(null);
6617
6709
  }
6618
6710
  });
6619
6711
  });
@@ -6633,7 +6725,7 @@ var _transport4 = {
6633
6725
  get: _get
6634
6726
  };
6635
6727
  function _post3(url, headers, payload) {
6636
- return new Promise((resolve2, reject) => {
6728
+ return new Promise((resolve3, reject) => {
6637
6729
  let settled = false;
6638
6730
  const u2 = new URL(url);
6639
6731
  const lib = u2.protocol === "https:" ? https6 : http6;
@@ -6658,7 +6750,7 @@ function _post3(url, headers, payload) {
6658
6750
  res.on("end", () => {
6659
6751
  if (settled) return;
6660
6752
  settled = true;
6661
- resolve2({ statusCode: res.statusCode ?? 0, body });
6753
+ resolve3({ statusCode: res.statusCode ?? 0, body });
6662
6754
  });
6663
6755
  }
6664
6756
  );
@@ -6675,7 +6767,7 @@ function _post3(url, headers, payload) {
6675
6767
  });
6676
6768
  }
6677
6769
  function _get(url, headers) {
6678
- return new Promise((resolve2, reject) => {
6770
+ return new Promise((resolve3, reject) => {
6679
6771
  let settled = false;
6680
6772
  const u2 = new URL(url);
6681
6773
  const lib = u2.protocol === "https:" ? https6 : http6;
@@ -6699,7 +6791,7 @@ function _get(url, headers) {
6699
6791
  res.on("end", () => {
6700
6792
  if (settled) return;
6701
6793
  settled = true;
6702
- resolve2({ statusCode: res.statusCode ?? 0, body });
6794
+ resolve3({ statusCode: res.statusCode ?? 0, body });
6703
6795
  });
6704
6796
  }
6705
6797
  );
@@ -7068,7 +7160,7 @@ function buildKeepAlive(ctx) {
7068
7160
  let timer = null;
7069
7161
  async function setIdleTimeout(minutes) {
7070
7162
  if (!ctx.inCodespace || !ctx.codespaceName) return;
7071
- await new Promise((resolve2) => {
7163
+ await new Promise((resolve3) => {
7072
7164
  const proc = (0, import_child_process8.spawn)(
7073
7165
  "gh",
7074
7166
  [
@@ -7082,8 +7174,8 @@ function buildKeepAlive(ctx) {
7082
7174
  { stdio: "ignore", detached: true }
7083
7175
  );
7084
7176
  proc.unref();
7085
- proc.on("exit", () => resolve2());
7086
- proc.on("error", () => resolve2());
7177
+ proc.on("exit", () => resolve3());
7178
+ proc.on("error", () => resolve3());
7087
7179
  });
7088
7180
  }
7089
7181
  return {
@@ -8461,7 +8553,7 @@ async function pair(args2 = []) {
8461
8553
  waitSpin.message(waitMessage());
8462
8554
  }, 1e3);
8463
8555
  countdownInterval.unref?.();
8464
- await new Promise((resolve2) => {
8556
+ await new Promise((resolve3) => {
8465
8557
  let stopPolling = null;
8466
8558
  function sigintHandler() {
8467
8559
  clearInterval(countdownInterval);
@@ -8488,7 +8580,7 @@ async function pair(args2 = []) {
8488
8580
  saveCliConfig({ ...loadCliConfig(), preferredAgent: agentId });
8489
8581
  showSuccess(`Paired with ${info.userName} (${info.plan})`);
8490
8582
  console.log("");
8491
- resolve2();
8583
+ resolve3();
8492
8584
  },
8493
8585
  () => {
8494
8586
  clearInterval(countdownInterval);
@@ -8521,12 +8613,12 @@ function readTokenFromArgs(args2) {
8521
8613
  }
8522
8614
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
8523
8615
  if (fileFlag) {
8524
- const path25 = fileFlag.slice("--token-file=".length);
8616
+ const path28 = fileFlag.slice("--token-file=".length);
8525
8617
  try {
8526
- const content = fs15.readFileSync(path25, "utf8").trim();
8527
- if (content.length === 0) fail(`--token-file ${path25} is empty`);
8618
+ const content = fs15.readFileSync(path28, "utf8").trim();
8619
+ if (content.length === 0) fail(`--token-file ${path28} is empty`);
8528
8620
  try {
8529
- fs15.unlinkSync(path25);
8621
+ fs15.unlinkSync(path28);
8530
8622
  } catch {
8531
8623
  }
8532
8624
  return content;
@@ -8744,12 +8836,12 @@ var GitHubCodespacesProvider = class {
8744
8836
  }
8745
8837
  if (!isAuthed) {
8746
8838
  resetStdinForChild();
8747
- await new Promise((resolve2, reject) => {
8839
+ await new Promise((resolve3, reject) => {
8748
8840
  const proc = (0, import_child_process12.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
8749
8841
  stdio: "inherit"
8750
8842
  });
8751
8843
  proc.on("exit", (code) => {
8752
- if (code === 0) resolve2();
8844
+ if (code === 0) resolve3();
8753
8845
  else reject(new Error("gh auth login failed."));
8754
8846
  });
8755
8847
  proc.on("error", reject);
@@ -8778,13 +8870,13 @@ var GitHubCodespacesProvider = class {
8778
8870
  }
8779
8871
  wt(noteLines.join("\n"), "One more permission needed");
8780
8872
  resetStdinForChild();
8781
- const refreshCode = await new Promise((resolve2, reject) => {
8873
+ const refreshCode = await new Promise((resolve3, reject) => {
8782
8874
  const proc = (0, import_child_process12.spawn)(
8783
8875
  "gh",
8784
8876
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
8785
8877
  { stdio: "inherit" }
8786
8878
  );
8787
- proc.on("exit", (code) => resolve2(code ?? 1));
8879
+ proc.on("exit", (code) => resolve3(code ?? 1));
8788
8880
  proc.on("error", reject);
8789
8881
  });
8790
8882
  if (refreshCode !== 0) {
@@ -8928,10 +9020,10 @@ var GitHubCodespacesProvider = class {
8928
9020
  if (q(proceed) || !proceed) return;
8929
9021
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
8930
9022
  resetStdinForChild();
8931
- const ok = await new Promise((resolve2) => {
9023
+ const ok = await new Promise((resolve3) => {
8932
9024
  const proc = (0, import_child_process12.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
8933
- proc.on("exit", (code) => resolve2(code === 0));
8934
- proc.on("error", () => resolve2(false));
9025
+ proc.on("exit", (code) => resolve3(code === 0));
9026
+ proc.on("error", () => resolve3(false));
8935
9027
  });
8936
9028
  if (ok) O2.success("gh installed");
8937
9029
  else O2.error("gh install failed");
@@ -8955,14 +9047,14 @@ var GitHubCodespacesProvider = class {
8955
9047
  "Expanding GitHub scopes"
8956
9048
  );
8957
9049
  resetStdinForChild();
8958
- await new Promise((resolve2, reject) => {
9050
+ await new Promise((resolve3, reject) => {
8959
9051
  const proc = (0, import_child_process12.spawn)(
8960
9052
  "gh",
8961
9053
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
8962
9054
  { stdio: "inherit" }
8963
9055
  );
8964
9056
  proc.on("exit", (code) => {
8965
- if (code === 0) resolve2();
9057
+ if (code === 0) resolve3();
8966
9058
  else reject(new Error(
8967
9059
  "gh auth refresh failed. Re-run `gh auth refresh -h github.com -s repo,read:org` manually."
8968
9060
  ));
@@ -9133,13 +9225,13 @@ var GitHubCodespacesProvider = class {
9133
9225
  }
9134
9226
  async streamCommand(workspaceId, command2) {
9135
9227
  resetStdinForChild();
9136
- return new Promise((resolve2, reject) => {
9228
+ return new Promise((resolve3, reject) => {
9137
9229
  const proc = (0, import_child_process12.spawn)(
9138
9230
  "gh",
9139
9231
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
9140
9232
  { stdio: "inherit" }
9141
9233
  );
9142
- proc.on("exit", (code) => resolve2({ code: code ?? 0 }));
9234
+ proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
9143
9235
  proc.on("error", reject);
9144
9236
  });
9145
9237
  }
@@ -9160,7 +9252,7 @@ var GitHubCodespacesProvider = class {
9160
9252
  "--",
9161
9253
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
9162
9254
  ];
9163
- await new Promise((resolve2, reject) => {
9255
+ await new Promise((resolve3, reject) => {
9164
9256
  const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
9165
9257
  stdio: ["ignore", "pipe", "pipe"],
9166
9258
  env: tarEnv
@@ -9180,7 +9272,7 @@ var GitHubCodespacesProvider = class {
9180
9272
  ssh.on("error", reject);
9181
9273
  ssh.on("exit", (code) => {
9182
9274
  if (code === 0) {
9183
- resolve2();
9275
+ resolve3();
9184
9276
  } else {
9185
9277
  const reason = (sshErr || tarErr || `exit ${code}`).trim().slice(0, 500);
9186
9278
  reject(new Error(`Remote tar failed: ${reason}`));
@@ -9198,7 +9290,7 @@ var GitHubCodespacesProvider = class {
9198
9290
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote(remotePath)}`);
9199
9291
  }
9200
9292
  const cmd = parts.join(" && ");
9201
- await new Promise((resolve2, reject) => {
9293
+ await new Promise((resolve3, reject) => {
9202
9294
  const proc = (0, import_child_process12.spawn)(
9203
9295
  "gh",
9204
9296
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
@@ -9210,7 +9302,7 @@ var GitHubCodespacesProvider = class {
9210
9302
  });
9211
9303
  proc.on("error", reject);
9212
9304
  proc.on("exit", (code) => {
9213
- if (code === 0) resolve2();
9305
+ if (code === 0) resolve3();
9214
9306
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
9215
9307
  });
9216
9308
  proc.stdin?.write(contents);
@@ -9300,10 +9392,10 @@ var GitpodProvider = class {
9300
9392
  "Authenticating Gitpod"
9301
9393
  );
9302
9394
  resetStdinForChild2();
9303
- await new Promise((resolve2, reject) => {
9395
+ await new Promise((resolve3, reject) => {
9304
9396
  const proc = (0, import_child_process13.spawn)("gitpod", ["login"], { stdio: "inherit" });
9305
9397
  proc.on("exit", (code) => {
9306
- if (code === 0) resolve2();
9398
+ if (code === 0) resolve3();
9307
9399
  else reject(new Error("gitpod login failed."));
9308
9400
  });
9309
9401
  proc.on("error", reject);
@@ -9452,13 +9544,13 @@ var GitpodProvider = class {
9452
9544
  }
9453
9545
  async streamCommand(workspaceId, command2) {
9454
9546
  resetStdinForChild2();
9455
- return new Promise((resolve2, reject) => {
9547
+ return new Promise((resolve3, reject) => {
9456
9548
  const proc = (0, import_child_process13.spawn)(
9457
9549
  "gitpod",
9458
9550
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
9459
9551
  { stdio: "inherit" }
9460
9552
  );
9461
- proc.on("exit", (code) => resolve2({ code: code ?? 0 }));
9553
+ proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
9462
9554
  proc.on("error", reject);
9463
9555
  });
9464
9556
  }
@@ -9472,7 +9564,7 @@ var GitpodProvider = class {
9472
9564
  tarArgs.push(".");
9473
9565
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
9474
9566
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
9475
- await new Promise((resolve2, reject) => {
9567
+ await new Promise((resolve3, reject) => {
9476
9568
  const tar = (0, import_child_process13.spawn)("tar", tarArgs, {
9477
9569
  stdio: ["ignore", "pipe", "pipe"],
9478
9570
  env: tarEnv
@@ -9493,7 +9585,7 @@ var GitpodProvider = class {
9493
9585
  tar.on("error", reject);
9494
9586
  ssh.on("error", reject);
9495
9587
  ssh.on("exit", (code) => {
9496
- if (code === 0) resolve2();
9588
+ if (code === 0) resolve3();
9497
9589
  else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
9498
9590
  });
9499
9591
  });
@@ -9508,7 +9600,7 @@ var GitpodProvider = class {
9508
9600
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote2(remotePath)}`);
9509
9601
  }
9510
9602
  const cmd = parts.join(" && ");
9511
- await new Promise((resolve2, reject) => {
9603
+ await new Promise((resolve3, reject) => {
9512
9604
  const proc = (0, import_child_process13.spawn)(
9513
9605
  "gitpod",
9514
9606
  ["workspace", "ssh", workspaceId, "--", cmd],
@@ -9520,7 +9612,7 @@ var GitpodProvider = class {
9520
9612
  });
9521
9613
  proc.on("error", reject);
9522
9614
  proc.on("exit", (code) => {
9523
- if (code === 0) resolve2();
9615
+ if (code === 0) resolve3();
9524
9616
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
9525
9617
  });
9526
9618
  proc.stdin?.write(contents);
@@ -9577,14 +9669,14 @@ var GitLabWorkspacesProvider = class {
9577
9669
  "Authenticating GitLab"
9578
9670
  );
9579
9671
  resetStdinForChild3();
9580
- await new Promise((resolve2, reject) => {
9672
+ await new Promise((resolve3, reject) => {
9581
9673
  const proc = (0, import_child_process14.spawn)(
9582
9674
  "glab",
9583
9675
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
9584
9676
  { stdio: "inherit" }
9585
9677
  );
9586
9678
  proc.on("exit", (code) => {
9587
- if (code === 0) resolve2();
9679
+ if (code === 0) resolve3();
9588
9680
  else reject(new Error("glab auth login failed."));
9589
9681
  });
9590
9682
  proc.on("error", reject);
@@ -9749,13 +9841,13 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9749
9841
  async streamCommand(workspaceId, command2) {
9750
9842
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
9751
9843
  resetStdinForChild3();
9752
- return new Promise((resolve2, reject) => {
9844
+ return new Promise((resolve3, reject) => {
9753
9845
  const proc = (0, import_child_process14.spawn)(
9754
9846
  "ssh",
9755
9847
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
9756
9848
  { stdio: "inherit" }
9757
9849
  );
9758
- proc.on("exit", (code) => resolve2({ code: code ?? 0 }));
9850
+ proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
9759
9851
  proc.on("error", reject);
9760
9852
  });
9761
9853
  }
@@ -9770,7 +9862,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9770
9862
  tarArgs.push(".");
9771
9863
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
9772
9864
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
9773
- await new Promise((resolve2, reject) => {
9865
+ await new Promise((resolve3, reject) => {
9774
9866
  const tar = (0, import_child_process14.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
9775
9867
  const ssh = (0, import_child_process14.spawn)(
9776
9868
  "ssh",
@@ -9788,7 +9880,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9788
9880
  tar.on("error", reject);
9789
9881
  ssh.on("error", reject);
9790
9882
  ssh.on("exit", (code) => {
9791
- if (code === 0) resolve2();
9883
+ if (code === 0) resolve3();
9792
9884
  else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
9793
9885
  });
9794
9886
  });
@@ -9801,7 +9893,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9801
9893
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
9802
9894
  }
9803
9895
  const cmd = parts.join(" && ");
9804
- await new Promise((resolve2, reject) => {
9896
+ await new Promise((resolve3, reject) => {
9805
9897
  const proc = (0, import_child_process14.spawn)(
9806
9898
  "ssh",
9807
9899
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
@@ -9813,7 +9905,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9813
9905
  });
9814
9906
  proc.on("error", reject);
9815
9907
  proc.on("exit", (code) => {
9816
- if (code === 0) resolve2();
9908
+ if (code === 0) resolve3();
9817
9909
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
9818
9910
  });
9819
9911
  proc.stdin?.write(contents);
@@ -9904,10 +9996,10 @@ var RailwayProvider = class {
9904
9996
  "Authenticating Railway"
9905
9997
  );
9906
9998
  resetStdinForChild4();
9907
- await new Promise((resolve2, reject) => {
9999
+ await new Promise((resolve3, reject) => {
9908
10000
  const proc = (0, import_child_process15.spawn)("railway", ["login"], { stdio: "inherit" });
9909
10001
  proc.on("exit", (code) => {
9910
- if (code === 0) resolve2();
10002
+ if (code === 0) resolve3();
9911
10003
  else reject(new Error("railway login failed."));
9912
10004
  });
9913
10005
  proc.on("error", reject);
@@ -10047,13 +10139,13 @@ var RailwayProvider = class {
10047
10139
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
10048
10140
  }
10049
10141
  resetStdinForChild4();
10050
- return new Promise((resolve2, reject) => {
10142
+ return new Promise((resolve3, reject) => {
10051
10143
  const proc = (0, import_child_process15.spawn)(
10052
10144
  "railway",
10053
10145
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
10054
10146
  { stdio: "inherit" }
10055
10147
  );
10056
- proc.on("exit", (code) => resolve2({ code: code ?? 0 }));
10148
+ proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
10057
10149
  proc.on("error", reject);
10058
10150
  });
10059
10151
  }
@@ -10071,7 +10163,7 @@ var RailwayProvider = class {
10071
10163
  tarArgs.push(".");
10072
10164
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
10073
10165
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
10074
- await new Promise((resolve2, reject) => {
10166
+ await new Promise((resolve3, reject) => {
10075
10167
  const tar = (0, import_child_process15.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
10076
10168
  const sh = (0, import_child_process15.spawn)(
10077
10169
  "railway",
@@ -10089,7 +10181,7 @@ var RailwayProvider = class {
10089
10181
  tar.on("error", reject);
10090
10182
  sh.on("error", reject);
10091
10183
  sh.on("exit", (code) => {
10092
- if (code === 0) resolve2();
10184
+ if (code === 0) resolve3();
10093
10185
  else reject(new Error(`Remote tar failed: ${(shErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
10094
10186
  });
10095
10187
  });
@@ -10105,7 +10197,7 @@ var RailwayProvider = class {
10105
10197
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
10106
10198
  }
10107
10199
  const cmd = parts.join(" && ");
10108
- await new Promise((resolve2, reject) => {
10200
+ await new Promise((resolve3, reject) => {
10109
10201
  const proc = (0, import_child_process15.spawn)(
10110
10202
  "railway",
10111
10203
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
@@ -10117,7 +10209,7 @@ var RailwayProvider = class {
10117
10209
  });
10118
10210
  proc.on("error", reject);
10119
10211
  proc.on("exit", (code) => {
10120
- if (code === 0) resolve2();
10212
+ if (code === 0) resolve3();
10121
10213
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
10122
10214
  });
10123
10215
  proc.stdin?.write(contents);
@@ -10630,74 +10722,427 @@ async function probeCodeamPair(provider, workspace) {
10630
10722
  }
10631
10723
  async function stopWorkspaceFromLocal(target) {
10632
10724
  if (target.provider.id === "github-codespaces") {
10633
- const { execFile: execFile7 } = await import("child_process");
10634
- const { promisify: promisify7 } = await import("util");
10635
- const execFileP7 = promisify7(execFile7);
10636
- await execFileP7("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
10725
+ const { execFile: execFile8 } = await import("child_process");
10726
+ const { promisify: promisify8 } = await import("util");
10727
+ const execFileP8 = promisify8(execFile8);
10728
+ await execFileP8("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
10637
10729
  return;
10638
10730
  }
10639
10731
  }
10640
10732
 
10641
- // src/commands/version.ts
10733
+ // src/commands/link.ts
10734
+ var import_node_child_process3 = require("child_process");
10735
+ var import_node_crypto = require("crypto");
10736
+ var fs18 = __toESM(require("fs"));
10737
+ var path26 = __toESM(require("path"));
10738
+ var import_chokidar = __toESM(require("chokidar"));
10642
10739
  var import_picocolors11 = __toESM(require("picocolors"));
10740
+
10741
+ // src/agents/claude/local-token.ts
10742
+ var import_node_child_process2 = require("child_process");
10743
+ var fs16 = __toESM(require("fs"));
10744
+ var os15 = __toESM(require("os"));
10745
+ var path24 = __toESM(require("path"));
10746
+ var import_node_util3 = require("util");
10747
+ var execFileP7 = (0, import_node_util3.promisify)(import_node_child_process2.execFile);
10748
+ var KEYCHAIN_SERVICE_NAMES = [
10749
+ "Claude Code-credentials",
10750
+ "claude-code-credentials",
10751
+ "Claude",
10752
+ "Anthropic Claude"
10753
+ ];
10754
+ function claudeCredentialsPaths() {
10755
+ const home = os15.homedir();
10756
+ return [
10757
+ path24.join(home, ".claude", ".credentials.json"),
10758
+ path24.join(home, ".config", "claude", ".credentials.json")
10759
+ ];
10760
+ }
10761
+ async function extractLocalClaudeToken() {
10762
+ for (const flat of claudeCredentialsPaths()) {
10763
+ if (!fs16.existsSync(flat)) continue;
10764
+ const credential = fs16.readFileSync(flat, "utf8").trim();
10765
+ if (credential.length > 0) {
10766
+ return { method: "oauth", credential, source: "flat-file" };
10767
+ }
10768
+ }
10769
+ if (process.platform === "darwin") {
10770
+ for (const service of KEYCHAIN_SERVICE_NAMES) {
10771
+ try {
10772
+ const { stdout } = await execFileP7(
10773
+ "security",
10774
+ ["find-generic-password", "-s", service, "-w"],
10775
+ { maxBuffer: 1024 * 1024 }
10776
+ );
10777
+ const credential = stdout.trim();
10778
+ if (credential.length > 0) {
10779
+ return { method: "oauth", credential, source: "macos-keychain" };
10780
+ }
10781
+ } catch {
10782
+ }
10783
+ }
10784
+ }
10785
+ return null;
10786
+ }
10787
+
10788
+ // src/agents/codex/local-token.ts
10789
+ var fs17 = __toESM(require("fs"));
10790
+ var os16 = __toESM(require("os"));
10791
+ var path25 = __toESM(require("path"));
10792
+ function codexCredentialsPath() {
10793
+ return path25.join(os16.homedir(), ".codex", "auth.json");
10794
+ }
10795
+ async function extractLocalCodexToken() {
10796
+ const file = codexCredentialsPath();
10797
+ if (!fs17.existsSync(file)) return null;
10798
+ const credential = fs17.readFileSync(file, "utf8").trim();
10799
+ if (credential.length === 0) return null;
10800
+ return { method: "oauth", credential, source: "flat-file" };
10801
+ }
10802
+ function codexCredentialsPaths() {
10803
+ return [codexCredentialsPath()];
10804
+ }
10805
+
10806
+ // src/commands/link.ts
10807
+ var AGENT_META = {
10808
+ claude: {
10809
+ internalId: "claude",
10810
+ publicId: "claude_code",
10811
+ binary: "claude",
10812
+ displayName: "Claude Code",
10813
+ vendor: "Anthropic",
10814
+ credentialsHint: "~/.claude/.credentials.json or the macOS Keychain",
10815
+ watchPaths: claudeCredentialsPaths,
10816
+ extract: extractLocalClaudeToken,
10817
+ ensureInstalled: ensureClaudeInstalled,
10818
+ launchLogin: () => {
10819
+ const child = (0, import_node_child_process3.spawn)("claude", [], { stdio: ["pipe", "inherit", "inherit"] });
10820
+ child.stdin?.write("/login\n");
10821
+ return child;
10822
+ }
10823
+ },
10824
+ codex: {
10825
+ internalId: "codex",
10826
+ publicId: "codex",
10827
+ binary: "codex",
10828
+ displayName: "Codex",
10829
+ vendor: "OpenAI",
10830
+ credentialsHint: "~/.codex/auth.json",
10831
+ watchPaths: codexCredentialsPaths,
10832
+ extract: extractLocalCodexToken,
10833
+ ensureInstalled: async () => {
10834
+ const { findInPath: findInPath2 } = await Promise.resolve().then(() => (init_types(), types_exports));
10835
+ if (findInPath2("codex")) return true;
10836
+ showError(
10837
+ "codex binary not found on PATH. Install it first (https://github.com/openai/codex-cli) then re-run `codeam link codex`."
10838
+ );
10839
+ return false;
10840
+ },
10841
+ launchLogin: () => {
10842
+ return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
10843
+ }
10844
+ }
10845
+ };
10846
+ function parseLinkArgs(args2) {
10847
+ const positional = args2.find((a) => !a.startsWith("--"));
10848
+ if (!positional) {
10849
+ throw new Error(
10850
+ `Usage: codeam link <agent>
10851
+ agent: ${Object.keys(AGENT_META).join(" | ")}`
10852
+ );
10853
+ }
10854
+ const normalised = positional === "claude_code" ? "claude" : positional;
10855
+ if (normalised !== "claude" && normalised !== "codex") {
10856
+ throw new Error(
10857
+ `Unknown agent "${positional}". Valid: ${Object.keys(AGENT_META).join(", ")}`
10858
+ );
10859
+ }
10860
+ const reuseExisting = args2.includes("--reuse-existing");
10861
+ const apiKeyArg = args2.find((a) => a.startsWith("--api-key="));
10862
+ const apiKey = apiKeyArg ? apiKeyArg.slice("--api-key=".length) : null;
10863
+ const tokenFileArg = args2.find((a) => a.startsWith("--token-file="));
10864
+ const tokenFile = tokenFileArg ? tokenFileArg.slice("--token-file=".length) : null;
10865
+ return { agent: normalised, reuseExisting, apiKey, tokenFile };
10866
+ }
10867
+ async function link(args2 = []) {
10868
+ const parsed = parseLinkArgs(args2);
10869
+ const meta = AGENT_META[parsed.agent];
10870
+ showIntro();
10871
+ console.log(
10872
+ import_picocolors11.default.bold(` Link ${meta.displayName}`) + import_picocolors11.default.dim(` \xB7 ${meta.vendor}`)
10873
+ );
10874
+ console.log("");
10875
+ const pluginId = (0, import_node_crypto.randomUUID)();
10876
+ const spin = dist_exports.spinner();
10877
+ spin.start("Requesting pairing code...");
10878
+ const pairing = await requestCode(pluginId);
10879
+ if (!pairing) {
10880
+ spin.stop("Failed");
10881
+ showError("Could not reach the server. Check your connection and try again.");
10882
+ process.exit(1);
10883
+ }
10884
+ spin.stop("Got pairing code");
10885
+ showPairingCode(pairing.code);
10886
+ console.log(import_picocolors11.default.dim(" Scan the QR or enter the code in CodeAgent Mobile."));
10887
+ console.log("");
10888
+ const waitSpin = dist_exports.spinner();
10889
+ const waitMsg = () => `Waiting for mobile pair... \xB7 expires in ${formatRemaining(pairing.expiresAt)}`;
10890
+ waitSpin.start(waitMsg());
10891
+ const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
10892
+ countdown.unref?.();
10893
+ const paired = await new Promise((resolve3, reject) => {
10894
+ let stopPoll = null;
10895
+ const sigint = () => {
10896
+ clearInterval(countdown);
10897
+ stopPoll?.();
10898
+ reject(new Error("cancelled"));
10899
+ };
10900
+ stopPoll = pollStatus(
10901
+ pluginId,
10902
+ (info) => {
10903
+ process.removeListener("SIGINT", sigint);
10904
+ clearInterval(countdown);
10905
+ waitSpin.stop("Paired");
10906
+ resolve3(info);
10907
+ },
10908
+ () => {
10909
+ clearInterval(countdown);
10910
+ waitSpin.stop("Timed out");
10911
+ reject(new Error("Pairing timed out after 5 minutes. Run codeam link again."));
10912
+ }
10913
+ );
10914
+ process.once("SIGINT", sigint);
10915
+ });
10916
+ if (!paired.pluginAuthToken) {
10917
+ showError(
10918
+ "Backend did not return a pluginAuthToken \u2014 upgrade api-v2 (deploy includes the link endpoint)."
10919
+ );
10920
+ process.exit(1);
10921
+ }
10922
+ addSession({
10923
+ id: paired.sessionId,
10924
+ pluginId,
10925
+ userName: paired.userName,
10926
+ userEmail: paired.userEmail,
10927
+ plan: paired.plan,
10928
+ pairedAt: Date.now(),
10929
+ pluginAuthToken: paired.pluginAuthToken,
10930
+ agent: meta.internalId
10931
+ });
10932
+ saveCliConfig({ ...loadCliConfig(), preferredAgent: meta.internalId });
10933
+ if (parsed.apiKey) {
10934
+ await uploadAndSucceed(meta, paired, pluginId, {
10935
+ method: "api_key",
10936
+ credential: parsed.apiKey.trim(),
10937
+ source: "manual"
10938
+ });
10939
+ return;
10940
+ }
10941
+ if (parsed.tokenFile) {
10942
+ const credential = fs18.readFileSync(path26.resolve(parsed.tokenFile), "utf8").trim();
10943
+ if (!credential) {
10944
+ showError(`--token-file ${parsed.tokenFile} is empty.`);
10945
+ process.exit(1);
10946
+ }
10947
+ await uploadAndSucceed(meta, paired, pluginId, {
10948
+ method: "oauth",
10949
+ credential,
10950
+ source: "manual"
10951
+ });
10952
+ return;
10953
+ }
10954
+ const installSpin = dist_exports.spinner();
10955
+ installSpin.start(`Checking that ${meta.binary} is installed...`);
10956
+ const installed = await meta.ensureInstalled();
10957
+ if (!installed) {
10958
+ installSpin.stop("Failed");
10959
+ showError(`Could not install ${meta.displayName}. Install it manually then re-run.`);
10960
+ process.exit(1);
10961
+ }
10962
+ installSpin.stop(`${meta.displayName} is installed`);
10963
+ const existing = await meta.extract();
10964
+ if (existing) {
10965
+ showInfo(`Found existing ${meta.displayName} credentials at ${import_picocolors11.default.bold(existing.source)}.`);
10966
+ await uploadAndSucceed(meta, paired, pluginId, existing);
10967
+ return;
10968
+ }
10969
+ if (parsed.reuseExisting) {
10970
+ showError(
10971
+ `--reuse-existing set, but no local ${meta.displayName} credentials were found at ${meta.credentialsHint}.`
10972
+ );
10973
+ process.exit(1);
10974
+ }
10975
+ showInfo(
10976
+ `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.`
10977
+ );
10978
+ console.log("");
10979
+ const captured = await captureFreshCredentials(meta);
10980
+ console.log("");
10981
+ await uploadAndSucceed(meta, paired, pluginId, captured);
10982
+ }
10983
+ async function captureFreshCredentials(meta) {
10984
+ const watcher = import_chokidar.default.watch(meta.watchPaths(), {
10985
+ persistent: true,
10986
+ ignoreInitial: false,
10987
+ awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
10988
+ });
10989
+ let child = null;
10990
+ let keychainPoll = null;
10991
+ const cleanup = () => {
10992
+ void watcher.close();
10993
+ if (keychainPoll) clearInterval(keychainPoll);
10994
+ if (child && !child.killed) {
10995
+ try {
10996
+ child.kill("SIGTERM");
10997
+ } catch {
10998
+ }
10999
+ }
11000
+ };
11001
+ try {
11002
+ const token = await new Promise((resolve3, reject) => {
11003
+ let settled = false;
11004
+ const tryExtract = async () => {
11005
+ if (settled) return;
11006
+ const t2 = await meta.extract();
11007
+ if (t2 && !settled) {
11008
+ settled = true;
11009
+ resolve3(t2);
11010
+ }
11011
+ };
11012
+ watcher.on("add", () => void tryExtract());
11013
+ watcher.on("change", () => void tryExtract());
11014
+ keychainPoll = setInterval(() => void tryExtract(), 2e3);
11015
+ keychainPoll.unref?.();
11016
+ const sigint = () => {
11017
+ if (settled) return;
11018
+ settled = true;
11019
+ reject(new Error("cancelled"));
11020
+ };
11021
+ process.once("SIGINT", sigint);
11022
+ setTimeout(() => {
11023
+ if (settled) return;
11024
+ settled = true;
11025
+ reject(new Error(`Timed out waiting for ${meta.displayName} sign-in (5 minutes).`));
11026
+ }, 5 * 6e4);
11027
+ child = meta.launchLogin();
11028
+ child.on("exit", () => {
11029
+ void tryExtract().then(() => {
11030
+ if (!settled) {
11031
+ settled = true;
11032
+ reject(
11033
+ new Error(
11034
+ `${meta.binary} exited but no credentials were written at ${meta.credentialsHint}.`
11035
+ )
11036
+ );
11037
+ }
11038
+ });
11039
+ });
11040
+ });
11041
+ cleanup();
11042
+ return token;
11043
+ } catch (err) {
11044
+ cleanup();
11045
+ throw err;
11046
+ }
11047
+ }
11048
+ async function uploadAndSucceed(meta, paired, pluginId, token) {
11049
+ if (!paired.pluginAuthToken) {
11050
+ showError("Missing pluginAuthToken; re-run codeam link.");
11051
+ process.exit(1);
11052
+ }
11053
+ const uploadSpin = dist_exports.spinner();
11054
+ uploadSpin.start("Sealing credential in your vault...");
11055
+ const result = await postLinkCredential({
11056
+ agentId: meta.publicId,
11057
+ sessionId: paired.sessionId,
11058
+ pluginId,
11059
+ pluginAuthToken: paired.pluginAuthToken,
11060
+ method: token.method,
11061
+ credential: token.credential
11062
+ });
11063
+ if (!result.ok) {
11064
+ uploadSpin.stop("Failed");
11065
+ if (result.status === 401) {
11066
+ showError("Pair token rejected by the backend (401). Re-run `codeam link`.");
11067
+ } else if (result.status === 404) {
11068
+ showError(
11069
+ `${meta.displayName} link endpoint not available on this backend (404). The api-v2 deployment may not yet include the route.`
11070
+ );
11071
+ } else {
11072
+ showError(`Upload failed: ${result.message}`);
11073
+ }
11074
+ process.exit(1);
11075
+ }
11076
+ uploadSpin.stop("Linked");
11077
+ console.log("");
11078
+ showSuccess(`${meta.displayName} is now linked to ${paired.userEmail || paired.userName}.`);
11079
+ showInfo(
11080
+ `Your codespaces and @codeagent mentions can now use ${meta.displayName} without you signing in again.`
11081
+ );
11082
+ console.log("");
11083
+ }
11084
+
11085
+ // src/commands/version.ts
11086
+ var import_picocolors12 = __toESM(require("picocolors"));
10643
11087
  function version() {
10644
- const v = true ? "2.15.8" : "unknown";
10645
- console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
11088
+ const v = true ? "2.16.1" : "unknown";
11089
+ console.log(`${import_picocolors12.default.bold("codeam-cli")} ${import_picocolors12.default.cyan(v)}`);
10646
11090
  }
10647
11091
 
10648
11092
  // src/commands/help.ts
10649
- var import_picocolors12 = __toESM(require("picocolors"));
11093
+ var import_picocolors13 = __toESM(require("picocolors"));
10650
11094
  function help() {
10651
11095
  const lines = [
10652
11096
  "",
10653
- ` ${import_picocolors12.default.bold(import_picocolors12.default.magenta("codeam-cli"))} ${import_picocolors12.default.dim("\u2014 remote-control AI coding agents from your phone")}`,
11097
+ ` ${import_picocolors13.default.bold(import_picocolors13.default.magenta("codeam-cli"))} ${import_picocolors13.default.dim("\u2014 remote-control AI coding agents from your phone")}`,
10654
11098
  "",
10655
- ` ${import_picocolors12.default.bold("Usage")}`,
10656
- ` ${import_picocolors12.default.cyan("codeam")} ${import_picocolors12.default.dim("[command]")}`,
11099
+ ` ${import_picocolors13.default.bold("Usage")}`,
11100
+ ` ${import_picocolors13.default.cyan("codeam")} ${import_picocolors13.default.dim("[command]")}`,
10657
11101
  "",
10658
- ` ${import_picocolors12.default.bold("Commands")}`,
10659
- ` ${import_picocolors12.default.white("codeam")} ${import_picocolors12.default.dim("start the active agent with mobile control")}`,
10660
- ` ${import_picocolors12.default.white("codeam <agent>")} ${import_picocolors12.default.dim("start a specific agent \u2014 e.g. claude, codex")}`,
10661
- ` ${import_picocolors12.default.white("codeam pair")} ${import_picocolors12.default.dim("pair a new mobile device (interactive)")}`,
10662
- ` ${import_picocolors12.default.white("codeam pair --agent <id>")} ${import_picocolors12.default.dim("pair non-interactively for a specific agent")}`,
10663
- ` ${import_picocolors12.default.white("codeam sessions")} ${import_picocolors12.default.dim("list paired devices")}`,
10664
- ` ${import_picocolors12.default.white("codeam sessions switch")} ${import_picocolors12.default.dim("switch the active paired session")}`,
10665
- ` ${import_picocolors12.default.white("codeam sessions delete <id>")} ${import_picocolors12.default.dim("remove a specific paired session")}`,
10666
- ` ${import_picocolors12.default.white("codeam status")} ${import_picocolors12.default.dim("show connection info")}`,
10667
- ` ${import_picocolors12.default.white("codeam logout")} ${import_picocolors12.default.dim("remove all paired sessions")}`,
10668
- ` ${import_picocolors12.default.white("codeam deploy")} ${import_picocolors12.default.dim("provision a cloud workspace (Codespaces) and pair it")}`,
10669
- ` ${import_picocolors12.default.white("codeam deploy ls | list")} ${import_picocolors12.default.dim("list deployed cloud workspaces")}`,
10670
- ` ${import_picocolors12.default.white("codeam deploy stop | remove")} ${import_picocolors12.default.dim("stop a deployed workspace session")}`,
11102
+ ` ${import_picocolors13.default.bold("Commands")}`,
11103
+ ` ${import_picocolors13.default.white("codeam")} ${import_picocolors13.default.dim("start the active agent with mobile control")}`,
11104
+ ` ${import_picocolors13.default.white("codeam <agent>")} ${import_picocolors13.default.dim("start a specific agent \u2014 e.g. claude, codex")}`,
11105
+ ` ${import_picocolors13.default.white("codeam pair")} ${import_picocolors13.default.dim("pair a new mobile device (interactive)")}`,
11106
+ ` ${import_picocolors13.default.white("codeam pair --agent <id>")} ${import_picocolors13.default.dim("pair non-interactively for a specific agent")}`,
11107
+ ` ${import_picocolors13.default.white("codeam sessions")} ${import_picocolors13.default.dim("list paired devices")}`,
11108
+ ` ${import_picocolors13.default.white("codeam sessions switch")} ${import_picocolors13.default.dim("switch the active paired session")}`,
11109
+ ` ${import_picocolors13.default.white("codeam sessions delete <id>")} ${import_picocolors13.default.dim("remove a specific paired session")}`,
11110
+ ` ${import_picocolors13.default.white("codeam status")} ${import_picocolors13.default.dim("show connection info")}`,
11111
+ ` ${import_picocolors13.default.white("codeam logout")} ${import_picocolors13.default.dim("remove all paired sessions")}`,
11112
+ ` ${import_picocolors13.default.white("codeam link <agent>")} ${import_picocolors13.default.dim("link an agent (claude, codex) to your CodeAgent account")}`,
11113
+ ` ${import_picocolors13.default.white("codeam deploy")} ${import_picocolors13.default.dim("provision a cloud workspace (Codespaces) and pair it")}`,
11114
+ ` ${import_picocolors13.default.white("codeam deploy ls | list")} ${import_picocolors13.default.dim("list deployed cloud workspaces")}`,
11115
+ ` ${import_picocolors13.default.white("codeam deploy stop | remove")} ${import_picocolors13.default.dim("stop a deployed workspace session")}`,
10671
11116
  "",
10672
- ` ${import_picocolors12.default.bold("Flags")}`,
10673
- ` ${import_picocolors12.default.white("-v, --version")} ${import_picocolors12.default.dim("print the CLI version")}`,
10674
- ` ${import_picocolors12.default.white("-h, --help")} ${import_picocolors12.default.dim("show this help")}`,
11117
+ ` ${import_picocolors13.default.bold("Flags")}`,
11118
+ ` ${import_picocolors13.default.white("-v, --version")} ${import_picocolors13.default.dim("print the CLI version")}`,
11119
+ ` ${import_picocolors13.default.white("-h, --help")} ${import_picocolors13.default.dim("show this help")}`,
10675
11120
  "",
10676
- ` ${import_picocolors12.default.bold("Links")}`,
10677
- ` ${import_picocolors12.default.dim("Docs:")} ${import_picocolors12.default.green("https://www.codeagent-mobile.com")}`,
10678
- ` ${import_picocolors12.default.dim("Issues:")} ${import_picocolors12.default.green("https://github.com/edgar-durand/codeagent-mobile-clients/issues")}`,
11121
+ ` ${import_picocolors13.default.bold("Links")}`,
11122
+ ` ${import_picocolors13.default.dim("Docs:")} ${import_picocolors13.default.green("https://www.codeagent-mobile.com")}`,
11123
+ ` ${import_picocolors13.default.dim("Issues:")} ${import_picocolors13.default.green("https://github.com/edgar-durand/codeagent-mobile-clients/issues")}`,
10679
11124
  ""
10680
11125
  ];
10681
11126
  process.stdout.write(lines.join("\n") + "\n");
10682
11127
  }
10683
11128
 
10684
11129
  // src/lib/updateNotifier.ts
10685
- var fs16 = __toESM(require("fs"));
10686
- var os15 = __toESM(require("os"));
10687
- var path24 = __toESM(require("path"));
11130
+ var fs19 = __toESM(require("fs"));
11131
+ var os17 = __toESM(require("os"));
11132
+ var path27 = __toESM(require("path"));
10688
11133
  var https7 = __toESM(require("https"));
10689
- var import_picocolors13 = __toESM(require("picocolors"));
11134
+ var import_picocolors14 = __toESM(require("picocolors"));
10690
11135
  var PKG_NAME = "codeam-cli";
10691
11136
  var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
10692
11137
  var TTL_MS = 24 * 60 * 60 * 1e3;
10693
11138
  var REQUEST_TIMEOUT_MS = 1500;
10694
11139
  function cachePath() {
10695
- const dir = path24.join(os15.homedir(), ".codeam");
10696
- return path24.join(dir, "update-check.json");
11140
+ const dir = path27.join(os17.homedir(), ".codeam");
11141
+ return path27.join(dir, "update-check.json");
10697
11142
  }
10698
11143
  function readCache() {
10699
11144
  try {
10700
- const raw = fs16.readFileSync(cachePath(), "utf8");
11145
+ const raw = fs19.readFileSync(cachePath(), "utf8");
10701
11146
  const parsed = JSON.parse(raw);
10702
11147
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
10703
11148
  return parsed;
@@ -10708,8 +11153,8 @@ function readCache() {
10708
11153
  function writeCache(cache) {
10709
11154
  try {
10710
11155
  const file = cachePath();
10711
- fs16.mkdirSync(path24.dirname(file), { recursive: true });
10712
- fs16.writeFileSync(file, JSON.stringify(cache));
11156
+ fs19.mkdirSync(path27.dirname(file), { recursive: true });
11157
+ fs19.writeFileSync(file, JSON.stringify(cache));
10713
11158
  } catch {
10714
11159
  }
10715
11160
  }
@@ -10727,14 +11172,14 @@ function compareSemver(a, b) {
10727
11172
  return 0;
10728
11173
  }
10729
11174
  function fetchLatest() {
10730
- return new Promise((resolve2) => {
11175
+ return new Promise((resolve3) => {
10731
11176
  const req = https7.get(
10732
11177
  REGISTRY_URL,
10733
11178
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
10734
11179
  (res) => {
10735
11180
  if (res.statusCode !== 200) {
10736
11181
  res.resume();
10737
- resolve2(null);
11182
+ resolve3(null);
10738
11183
  return;
10739
11184
  }
10740
11185
  let buf = "";
@@ -10746,30 +11191,30 @@ function fetchLatest() {
10746
11191
  try {
10747
11192
  const json = JSON.parse(buf);
10748
11193
  if (typeof json.version === "string") {
10749
- resolve2(json.version);
11194
+ resolve3(json.version);
10750
11195
  } else {
10751
- resolve2(null);
11196
+ resolve3(null);
10752
11197
  }
10753
11198
  } catch {
10754
- resolve2(null);
11199
+ resolve3(null);
10755
11200
  }
10756
11201
  });
10757
11202
  }
10758
11203
  );
10759
11204
  req.on("timeout", () => {
10760
11205
  req.destroy();
10761
- resolve2(null);
11206
+ resolve3(null);
10762
11207
  });
10763
- req.on("error", () => resolve2(null));
11208
+ req.on("error", () => resolve3(null));
10764
11209
  });
10765
11210
  }
10766
11211
  function notifyIfStale(currentVersion, latest) {
10767
11212
  if (compareSemver(latest, currentVersion) <= 0) return;
10768
- const arrow = import_picocolors13.default.dim("\u2192");
10769
- const cmd = import_picocolors13.default.cyan("npm install -g codeam-cli");
11213
+ const arrow = import_picocolors14.default.dim("\u2192");
11214
+ const cmd = import_picocolors14.default.cyan("npm install -g codeam-cli");
10770
11215
  const lines = [
10771
11216
  "",
10772
- ` ${import_picocolors13.default.yellow("\u25CF")} ${import_picocolors13.default.bold("Update available")} ${import_picocolors13.default.dim(currentVersion)} ${arrow} ${import_picocolors13.default.green(latest)}`,
11217
+ ` ${import_picocolors14.default.yellow("\u25CF")} ${import_picocolors14.default.bold("Update available")} ${import_picocolors14.default.dim(currentVersion)} ${arrow} ${import_picocolors14.default.green(latest)}`,
10773
11218
  ` Run ${cmd} to upgrade.`,
10774
11219
  ""
10775
11220
  ];
@@ -10780,7 +11225,7 @@ function checkForUpdates() {
10780
11225
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
10781
11226
  if (process.env.CI) return;
10782
11227
  if (!process.stdout.isTTY) return;
10783
- const current = true ? "2.15.8" : null;
11228
+ const current = true ? "2.16.1" : null;
10784
11229
  if (!current) return;
10785
11230
  const cache = readCache();
10786
11231
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -10818,6 +11263,8 @@ async function main() {
10818
11263
  return status();
10819
11264
  case "logout":
10820
11265
  return logout();
11266
+ case "link":
11267
+ return link(args);
10821
11268
  case "deploy":
10822
11269
  if (args[0] === "ls" || args[0] === "list") return deployList();
10823
11270
  if (args[0] === "stop" || args[0] === "remove") return deployStop();