codeam-cli 2.15.5 → 2.15.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.15.6] — 2026-05-20
8
+
9
+ ### Added
10
+
11
+ - **cli:** Send current git branch on pair
12
+ - **vsc-plugin:** Emit file-change + review-hunk events from Path B (direct claude)
13
+ - **jetbrains-plugin:** Emit file-change + review-hunk events from Path B
14
+ - **cli:** Epic C — emit streaming chunks + subscribe to answer channel
15
+
16
+ ## [2.15.5] — 2026-05-20
17
+
18
+ ### Added
19
+
20
+ - **shared:** Add FileChangedEvent + PendingReviewHunkEvent wire types
21
+ - **cli:** Emit file-change + review-hunk events during paired sessions
22
+
23
+ ### CI
24
+
25
+ - **release:** Make CLI npm publish idempotent
26
+
7
27
  ## [2.15.1] — 2026-05-17
8
28
 
9
29
  ### Fixed
package/README.md CHANGED
@@ -111,7 +111,7 @@ Adding more cloud backends (Gitpod, Coder, your own SSH host, …) is a single n
111
111
 
112
112
  | Variable | Default | Effect |
113
113
  |---|---|---|
114
- | `CODEAM_API_URL` | `https://codeagent-mobile-api.vercel.app` | Override the backend relay URL. Useful for hitting a staging environment or self-hosted backend. |
114
+ | `CODEAM_API_URL` | `https://api.codeagent-mobile.com` | Override the backend relay URL. Useful for hitting a staging environment or self-hosted backend. |
115
115
  | `CODEAM_DISABLE_UPDATE_CHECK` | unset | Set to `1` to suppress the "update available" banner. The check also auto-skips on non-TTY stdout, when `CI=true`, and during tests. |
116
116
  | `CODEAM_AUTO_TOKEN` | unset | One-shot pairing token consumed by `codeam pair-auto`. Used by the `codeam deploy` bootstrap; see *Advanced / scripted pairing* below. |
117
117
 
package/dist/index.js CHANGED
@@ -284,6 +284,9 @@ function isKnownAgentId(id) {
284
284
  return id === "claude" || id === "codex" || id === "copilot";
285
285
  }
286
286
 
287
+ // ../../packages/shared/src/api-url.ts
288
+ var DEFAULT_API_BASE_URL = "https://api.codeagent-mobile.com";
289
+
287
290
  // src/config.ts
288
291
  var fs = __toESM(require("fs"));
289
292
  var os = __toESM(require("os"));
@@ -386,7 +389,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
386
389
  // package.json
387
390
  var package_default = {
388
391
  name: "codeam-cli",
389
- version: "2.15.5",
392
+ version: "2.15.7",
390
393
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
391
394
  type: "commonjs",
392
395
  main: "dist/index.js",
@@ -521,6 +524,34 @@ function vercelBypassHeader() {
521
524
  return token ? { "x-vercel-protection-bypass": token } : {};
522
525
  }
523
526
 
527
+ // src/lib/git-branch.ts
528
+ var import_child_process = require("child_process");
529
+ function detectCurrentBranch(cwd = process.cwd()) {
530
+ try {
531
+ const raw = _execSeam.exec("git branch --show-current", {
532
+ cwd,
533
+ // 1 s ceiling is comfortably above normal git latency (<50 ms
534
+ // on a healthy repo) and well below the pair POST's 10 s budget.
535
+ timeout: 1e3,
536
+ // Swallow stderr — non-git directories print "fatal: not a git
537
+ // repository" to stderr and we don't want that on the CLI's
538
+ // own stderr while pairing.
539
+ stdio: ["ignore", "pipe", "ignore"],
540
+ encoding: "utf8"
541
+ });
542
+ const trimmed = raw.trim();
543
+ return trimmed.length > 0 ? trimmed : null;
544
+ } catch {
545
+ return null;
546
+ }
547
+ }
548
+ var _execSeam = {
549
+ exec: (cmd, opts) => {
550
+ const out = (0, import_child_process.execSync)(cmd, opts);
551
+ return typeof out === "string" ? out : out.toString("utf8");
552
+ }
553
+ };
554
+
524
555
  // src/lib/poll-delay.ts
525
556
  var MAX_DELAY_MS = 3e4;
526
557
  function computePollDelay({ baseMs, failures }) {
@@ -530,17 +561,19 @@ function computePollDelay({ baseMs, failures }) {
530
561
  }
531
562
 
532
563
  // src/services/pairing.service.ts
533
- var API_BASE = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
564
+ var API_BASE = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
534
565
  async function requestCode(pluginId) {
535
566
  try {
536
567
  const runtime = process.env.CODESPACES === "true" ? "github-codespaces" : "local";
537
568
  const codespaceName = process.env.CODESPACE_NAME;
569
+ const branch = detectCurrentBranch();
538
570
  const result = await _transport.postJson(`${API_BASE}/api/pairing/code`, {
539
571
  pluginId,
540
572
  ideName: "Terminal (codeam-cli)",
541
573
  ideVersion: package_default.version,
542
574
  hostname: os2.hostname(),
543
575
  runtime,
576
+ branch,
544
577
  ...codespaceName ? { codespaceName } : {}
545
578
  });
546
579
  const data = result?.data;
@@ -748,7 +781,7 @@ var log = {
748
781
  };
749
782
 
750
783
  // src/services/command-relay.service.ts
751
- var API_BASE2 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
784
+ var API_BASE2 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
752
785
  var CommandRelayService = class {
753
786
  constructor(pluginId, onCommand, agentMeta) {
754
787
  this.pluginId = pluginId;
@@ -983,7 +1016,7 @@ var CommandRelayService = class {
983
1016
  };
984
1017
 
985
1018
  // src/services/pty/unix.strategy.ts
986
- var import_child_process = require("child_process");
1019
+ var import_child_process2 = require("child_process");
987
1020
  var fs4 = __toESM(require("fs"));
988
1021
  var os4 = __toESM(require("os"));
989
1022
  var path4 = __toESM(require("path"));
@@ -1087,7 +1120,7 @@ var UnixPtyStrategy = class {
1087
1120
  const rows = process.stdout.rows || 50;
1088
1121
  this.helperPath = path4.join(os4.tmpdir(), "codeam-pty-helper.py");
1089
1122
  fs4.writeFileSync(this.helperPath, PYTHON_PTY_HELPER, { mode: 420 });
1090
- this.proc = (0, import_child_process.spawn)(python, [this.helperPath, cmd, ...args2], {
1123
+ this.proc = (0, import_child_process2.spawn)(python, [this.helperPath, cmd, ...args2], {
1091
1124
  stdio: ["pipe", "pipe", "inherit"],
1092
1125
  cwd,
1093
1126
  env: {
@@ -1152,7 +1185,7 @@ var UnixPtyStrategy = class {
1152
1185
  * are NOT interpreted by /bin/sh.
1153
1186
  */
1154
1187
  spawnDirect(cmd, cwd, args2 = []) {
1155
- this.proc = (0, import_child_process.spawn)(cmd, args2, {
1188
+ this.proc = (0, import_child_process2.spawn)(cmd, args2, {
1156
1189
  stdio: ["pipe", "inherit", "inherit"],
1157
1190
  cwd,
1158
1191
  env: process.env,
@@ -1224,7 +1257,7 @@ var UnixPtyStrategy = class {
1224
1257
  };
1225
1258
 
1226
1259
  // src/services/pty/windows.strategy.ts
1227
- var import_child_process2 = require("child_process");
1260
+ var import_child_process3 = require("child_process");
1228
1261
  var WindowsPtyStrategy = class {
1229
1262
  constructor(opts) {
1230
1263
  this.opts = opts;
@@ -1232,7 +1265,7 @@ var WindowsPtyStrategy = class {
1232
1265
  opts;
1233
1266
  proc = null;
1234
1267
  spawn(cmd, cwd, args2 = []) {
1235
- this.proc = (0, import_child_process2.spawn)(cmd, args2, {
1268
+ this.proc = (0, import_child_process3.spawn)(cmd, args2, {
1236
1269
  stdio: ["pipe", "pipe", "inherit"],
1237
1270
  cwd,
1238
1271
  env: {
@@ -1408,7 +1441,7 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
1408
1441
  };
1409
1442
 
1410
1443
  // src/services/claude-installer.ts
1411
- var import_child_process3 = require("child_process");
1444
+ var import_child_process4 = require("child_process");
1412
1445
  var path6 = __toESM(require("path"));
1413
1446
  var os5 = __toESM(require("os"));
1414
1447
 
@@ -3361,7 +3394,7 @@ function runInstaller() {
3361
3394
  "irm https://claude.ai/install.ps1 | iex"
3362
3395
  ] : ["-c", "curl -fsSL https://claude.ai/install.sh | bash"];
3363
3396
  return new Promise((resolve2) => {
3364
- const proc = (0, import_child_process3.spawn)(cmd, args2, { stdio: "inherit" });
3397
+ const proc = (0, import_child_process4.spawn)(cmd, args2, { stdio: "inherit" });
3365
3398
  proc.on("error", (err) => {
3366
3399
  console.error(`
3367
3400
  \u2717 Installer failed to launch: ${err.message}`);
@@ -3660,7 +3693,7 @@ var AgentService = class {
3660
3693
  var fs5 = __toESM(require("fs"));
3661
3694
  var os6 = __toESM(require("os"));
3662
3695
  var path8 = __toESM(require("path"));
3663
- var import_child_process4 = require("child_process");
3696
+ var import_child_process5 = require("child_process");
3664
3697
  var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
3665
3698
  m,s=pty.openpty()
3666
3699
  try:
@@ -3729,7 +3762,7 @@ async function fetchClaudeQuota() {
3729
3762
  resolve2(null);
3730
3763
  return;
3731
3764
  }
3732
- const proc = (0, import_child_process4.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
3765
+ const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
3733
3766
  stdio: ["pipe", "pipe", "ignore"],
3734
3767
  cwd: process.cwd(),
3735
3768
  env: { ...process.env, TERM: "dumb", COLUMNS: "120", LINES: "30" }
@@ -4192,12 +4225,12 @@ var os9 = __toESM(require("os"));
4192
4225
  var path11 = __toESM(require("path"));
4193
4226
 
4194
4227
  // src/agents/claude/credentials.ts
4195
- var import_child_process5 = require("child_process");
4228
+ var import_child_process6 = require("child_process");
4196
4229
  var fs7 = __toESM(require("fs"));
4197
4230
  var os8 = __toESM(require("os"));
4198
4231
  var path10 = __toESM(require("path"));
4199
4232
  var import_util = require("util");
4200
- var execFileP = (0, import_util.promisify)(import_child_process5.execFile);
4233
+ var execFileP = (0, import_util.promisify)(import_child_process6.execFile);
4201
4234
  async function detectLocalClaudeCredentials() {
4202
4235
  const localClaudeDir = path10.join(os8.homedir(), ".claude");
4203
4236
  const flat = path10.join(localClaudeDir, ".credentials.json");
@@ -5164,7 +5197,7 @@ var ChromeStepTracker = class {
5164
5197
  // src/services/output/chunk-emitter.ts
5165
5198
  var https3 = __toESM(require("https"));
5166
5199
  var http3 = __toESM(require("http"));
5167
- var API_BASE3 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
5200
+ var API_BASE3 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
5168
5201
  var ChunkEmitter = class {
5169
5202
  constructor(opts) {
5170
5203
  this.opts = opts;
@@ -5653,7 +5686,7 @@ var historyRecordSchema = import_zod.z.object({
5653
5686
  content: import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.unknown())]).optional()
5654
5687
  }).passthrough().optional()
5655
5688
  }).passthrough();
5656
- var API_BASE4 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
5689
+ var API_BASE4 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
5657
5690
  function extractText2(content) {
5658
5691
  if (typeof content === "string") return content;
5659
5692
  if (Array.isArray(content)) {
@@ -6127,7 +6160,7 @@ var HistoryService = class _HistoryService {
6127
6160
  };
6128
6161
 
6129
6162
  // src/services/file-watcher.service.ts
6130
- var import_child_process6 = require("child_process");
6163
+ var import_child_process7 = require("child_process");
6131
6164
  var path15 = __toESM(require("path"));
6132
6165
 
6133
6166
  // src/services/file-watcher/diff-parser.ts
@@ -6259,7 +6292,7 @@ function _post2(url, headers, payload) {
6259
6292
  }
6260
6293
 
6261
6294
  // src/services/file-watcher.service.ts
6262
- var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
6295
+ var API_BASE5 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
6263
6296
  var DEBOUNCE_MS = 250;
6264
6297
  var MAX_RETRIES = 2;
6265
6298
  var RETRY_BACKOFF_MS = 300;
@@ -6552,7 +6585,7 @@ async function _runGitImpl(cwd, args2, opts = {}) {
6552
6585
  return new Promise((resolve2) => {
6553
6586
  let proc;
6554
6587
  try {
6555
- proc = (0, import_child_process6.spawn)("git", args2, { cwd, env: process.env });
6588
+ proc = (0, import_child_process7.spawn)("git", args2, { cwd, env: process.env });
6556
6589
  } catch {
6557
6590
  resolve2(null);
6558
6591
  return;
@@ -6580,6 +6613,432 @@ function _runGit(cwd, args2, opts = {}) {
6580
6613
  return _gitSeam.run(cwd, args2, opts);
6581
6614
  }
6582
6615
 
6616
+ // src/services/streaming-emitter.service.ts
6617
+ var import_crypto = require("crypto");
6618
+
6619
+ // src/services/streaming/transport.ts
6620
+ var http6 = __toESM(require("http"));
6621
+ var https6 = __toESM(require("https"));
6622
+ var _transport4 = {
6623
+ post: _post3,
6624
+ get: _get
6625
+ };
6626
+ function _post3(url, headers, payload) {
6627
+ return new Promise((resolve2, reject) => {
6628
+ let settled = false;
6629
+ const u2 = new URL(url);
6630
+ const lib = u2.protocol === "https:" ? https6 : http6;
6631
+ const req = lib.request(
6632
+ {
6633
+ hostname: u2.hostname,
6634
+ port: u2.port || (u2.protocol === "https:" ? 443 : 80),
6635
+ path: u2.pathname + u2.search,
6636
+ method: "POST",
6637
+ headers: {
6638
+ ...headers,
6639
+ ...vercelBypassHeader(),
6640
+ "Content-Length": Buffer.byteLength(payload)
6641
+ },
6642
+ timeout: 8e3
6643
+ },
6644
+ (res) => {
6645
+ let body = "";
6646
+ res.on("data", (c2) => {
6647
+ body += c2.toString();
6648
+ });
6649
+ res.on("end", () => {
6650
+ if (settled) return;
6651
+ settled = true;
6652
+ resolve2({ statusCode: res.statusCode ?? 0, body });
6653
+ });
6654
+ }
6655
+ );
6656
+ req.on("error", (err) => {
6657
+ if (settled) return;
6658
+ settled = true;
6659
+ reject(err);
6660
+ });
6661
+ req.on("timeout", () => {
6662
+ req.destroy();
6663
+ });
6664
+ req.write(payload);
6665
+ req.end();
6666
+ });
6667
+ }
6668
+ function _get(url, headers) {
6669
+ return new Promise((resolve2, reject) => {
6670
+ let settled = false;
6671
+ const u2 = new URL(url);
6672
+ const lib = u2.protocol === "https:" ? https6 : http6;
6673
+ const req = lib.request(
6674
+ {
6675
+ hostname: u2.hostname,
6676
+ port: u2.port || (u2.protocol === "https:" ? 443 : 80),
6677
+ path: u2.pathname + u2.search,
6678
+ method: "GET",
6679
+ headers: {
6680
+ ...headers,
6681
+ ...vercelBypassHeader()
6682
+ },
6683
+ timeout: 8e3
6684
+ },
6685
+ (res) => {
6686
+ let body = "";
6687
+ res.on("data", (c2) => {
6688
+ body += c2.toString();
6689
+ });
6690
+ res.on("end", () => {
6691
+ if (settled) return;
6692
+ settled = true;
6693
+ resolve2({ statusCode: res.statusCode ?? 0, body });
6694
+ });
6695
+ }
6696
+ );
6697
+ req.on("error", (err) => {
6698
+ if (settled) return;
6699
+ settled = true;
6700
+ reject(err);
6701
+ });
6702
+ req.on("timeout", () => {
6703
+ req.destroy();
6704
+ });
6705
+ req.end();
6706
+ });
6707
+ }
6708
+
6709
+ // src/services/streaming-emitter.service.ts
6710
+ var API_BASE6 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
6711
+ var TICK_MS = 50;
6712
+ var ANSWER_POLL_MS = 1500;
6713
+ var SELECTOR_STABLE_MS = 800;
6714
+ var MAX_RETRIES2 = 1;
6715
+ var RETRY_BACKOFF_MS2 = 200;
6716
+ var StreamingEmitterService = class {
6717
+ constructor(opts) {
6718
+ this.opts = opts;
6719
+ this.apiBase = opts.apiBaseUrl ?? API_BASE6;
6720
+ this.headers = {
6721
+ "Content-Type": "application/json",
6722
+ "X-Codeam-Protocol-Version": "2.0.0",
6723
+ "X-Plugin-Auth-Token": opts.pluginAuthToken
6724
+ };
6725
+ }
6726
+ opts;
6727
+ apiBase;
6728
+ headers;
6729
+ rawBuffer = "";
6730
+ active = false;
6731
+ tickTimer = null;
6732
+ answerPollTimer = null;
6733
+ /** Open chunk we're appending bytes to; cleared on kind change or stop. */
6734
+ activeChunk = null;
6735
+ /** Outstanding answer we're polling the backend for. Null when idle. */
6736
+ pendingAnswer = null;
6737
+ /**
6738
+ * Memoised selector signature so we don't fire `awaiting-answer` on
6739
+ * every tick while the user thinks. Cleared once an answer resolves.
6740
+ */
6741
+ lastSelectorSignature = null;
6742
+ selectorFirstSeenAt = 0;
6743
+ // ─── Lifecycle ─────────────────────────────────────────────────────
6744
+ start() {
6745
+ if (this.active) return;
6746
+ this.active = true;
6747
+ this.tickTimer = setInterval(() => this.tick(), TICK_MS);
6748
+ this.answerPollTimer = setInterval(() => {
6749
+ void this.pollPendingAnswer();
6750
+ }, ANSWER_POLL_MS);
6751
+ log.trace("streamingEmitter", `started session=${this.opts.sessionId.slice(0, 8)}`);
6752
+ }
6753
+ /**
6754
+ * Stop the emitter. Finalises the open chunk (if any) so the
6755
+ * backend doesn't leave a half-streamed message in the SSE
6756
+ * buffer. Idempotent.
6757
+ */
6758
+ async stop() {
6759
+ if (!this.active) return;
6760
+ this.active = false;
6761
+ if (this.tickTimer) {
6762
+ clearInterval(this.tickTimer);
6763
+ this.tickTimer = null;
6764
+ }
6765
+ if (this.answerPollTimer) {
6766
+ clearInterval(this.answerPollTimer);
6767
+ this.answerPollTimer = null;
6768
+ }
6769
+ if (this.activeChunk) {
6770
+ const finalContent = this.activeChunk.currentContent;
6771
+ const chunk = this.activeChunk;
6772
+ this.activeChunk = null;
6773
+ await this.postChunk({
6774
+ chunkId: chunk.chunkId,
6775
+ kind: chunk.kind,
6776
+ content: finalContent,
6777
+ isFinal: true
6778
+ });
6779
+ }
6780
+ log.trace("streamingEmitter", `stopped session=${this.opts.sessionId.slice(0, 8)}`);
6781
+ }
6782
+ // ─── Input pump ────────────────────────────────────────────────────
6783
+ /**
6784
+ * Forward a chunk of raw PTY bytes into the emitter. Safe to call
6785
+ * before `start()` — bytes accumulate in the internal buffer and the
6786
+ * first tick processes the backlog.
6787
+ */
6788
+ push(raw) {
6789
+ if (raw.length === 0) return;
6790
+ this.rawBuffer += raw;
6791
+ }
6792
+ // ─── Tick ──────────────────────────────────────────────────────────
6793
+ /**
6794
+ * Visible for tests. Runs one classify-and-emit pass against the
6795
+ * current PTY buffer.
6796
+ */
6797
+ /* @internal */
6798
+ _tickForTest() {
6799
+ this.tick();
6800
+ }
6801
+ tick() {
6802
+ if (!this.active) return;
6803
+ const lines = (this.opts.runtime.renderToLines ?? renderToLines)(this.rawBuffer);
6804
+ const selector = this.opts.runtime.detectInteractivePrompt(lines);
6805
+ if (selector) {
6806
+ this.maybeEmitAwaitingAnswer(selector);
6807
+ return;
6808
+ }
6809
+ this.lastSelectorSignature = null;
6810
+ this.selectorFirstSeenAt = 0;
6811
+ const groups = this.classifyLines(lines, this.opts.runtime);
6812
+ if (groups.length === 0) return;
6813
+ const last = groups[groups.length - 1];
6814
+ if (this.activeChunk && this.activeChunk.kind === last.kind) {
6815
+ this.activeChunk.currentContent = last.content;
6816
+ this.maybeFlushActive(false);
6817
+ return;
6818
+ }
6819
+ if (this.activeChunk) {
6820
+ const closing = this.activeChunk;
6821
+ this.activeChunk = null;
6822
+ void this.postChunk({
6823
+ chunkId: closing.chunkId,
6824
+ kind: closing.kind,
6825
+ content: closing.currentContent,
6826
+ isFinal: true
6827
+ });
6828
+ }
6829
+ this.activeChunk = {
6830
+ chunkId: (0, import_crypto.randomUUID)(),
6831
+ kind: last.kind,
6832
+ emittedContent: "",
6833
+ currentContent: last.content,
6834
+ lastEmitAt: 0
6835
+ };
6836
+ this.maybeFlushActive(false);
6837
+ }
6838
+ maybeFlushActive(force) {
6839
+ const chunk = this.activeChunk;
6840
+ if (!chunk) return;
6841
+ const now = Date.now();
6842
+ if (!force && now - chunk.lastEmitAt < TICK_MS) return;
6843
+ if (chunk.currentContent === chunk.emittedContent) return;
6844
+ chunk.emittedContent = chunk.currentContent;
6845
+ chunk.lastEmitAt = now;
6846
+ void this.postChunk({
6847
+ chunkId: chunk.chunkId,
6848
+ kind: chunk.kind,
6849
+ content: chunk.currentContent,
6850
+ isFinal: false
6851
+ });
6852
+ }
6853
+ // ─── Line classification ───────────────────────────────────────────
6854
+ /**
6855
+ * Walk the rendered screen lines and bucket them into kind-groups.
6856
+ *
6857
+ * Visible for tests so we can assert discrimination against fixture
6858
+ * inputs without standing up the full POST loop.
6859
+ */
6860
+ /* @internal */
6861
+ _classifyForTest(lines) {
6862
+ return this.classifyLines(lines, this.opts.runtime);
6863
+ }
6864
+ classifyLines(lines, runtime) {
6865
+ const parseLine2 = runtime.parseTuiChrome?.bind(runtime);
6866
+ const groups = [];
6867
+ let current = null;
6868
+ const flush = () => {
6869
+ if (!current) return;
6870
+ const text = current.lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
6871
+ if (text.length > 0) groups.push({ kind: current.kind, content: text });
6872
+ current = null;
6873
+ };
6874
+ for (const rawLine of lines) {
6875
+ const kind = classifyLine(rawLine, parseLine2, runtime);
6876
+ if (kind === null) continue;
6877
+ if (!current || current.kind !== kind) {
6878
+ flush();
6879
+ current = { kind, lines: [rawLine] };
6880
+ } else {
6881
+ current.lines.push(rawLine);
6882
+ }
6883
+ }
6884
+ flush();
6885
+ return groups;
6886
+ }
6887
+ // ─── Awaiting-answer + answer subscription ─────────────────────────
6888
+ maybeEmitAwaitingAnswer(selector) {
6889
+ const signature = `${selector.question}::${selector.options.join("|")}`;
6890
+ const now = Date.now();
6891
+ if (this.lastSelectorSignature !== signature) {
6892
+ this.lastSelectorSignature = signature;
6893
+ this.selectorFirstSeenAt = now;
6894
+ return;
6895
+ }
6896
+ if (this.pendingAnswer) return;
6897
+ if (now - this.selectorFirstSeenAt < SELECTOR_STABLE_MS) return;
6898
+ const questionId = (0, import_crypto.randomUUID)();
6899
+ this.pendingAnswer = {
6900
+ questionId,
6901
+ options: selector.options.length > 0 ? selector.options : void 0,
6902
+ fromIndex: selector.currentIndex
6903
+ };
6904
+ const event = {
6905
+ questionId,
6906
+ prompt: selector.question,
6907
+ ...selector.options.length > 0 ? { options: selector.options } : {}
6908
+ };
6909
+ void this.postAwaitingAnswer(event);
6910
+ }
6911
+ async pollPendingAnswer() {
6912
+ if (!this.active) return;
6913
+ if (!this.pendingAnswer) return;
6914
+ const questionId = this.pendingAnswer.questionId;
6915
+ try {
6916
+ const { statusCode, body } = await _transport4.get(
6917
+ `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/pending-answer?questionId=${encodeURIComponent(questionId)}`,
6918
+ this.headers
6919
+ );
6920
+ if (statusCode === 204 || statusCode === 404) {
6921
+ return;
6922
+ }
6923
+ if (statusCode < 200 || statusCode >= 300) {
6924
+ log.warn("streamingEmitter", `pending-answer status=${statusCode} body=${body.slice(0, 200)}`);
6925
+ return;
6926
+ }
6927
+ const parsed = parsePendingAnswerResponse(body);
6928
+ if (!parsed || parsed.questionId !== questionId) return;
6929
+ const pending = this.pendingAnswer;
6930
+ this.pendingAnswer = null;
6931
+ this.lastSelectorSignature = null;
6932
+ this.selectorFirstSeenAt = 0;
6933
+ this.deliverAnswer(parsed, pending);
6934
+ } catch (err) {
6935
+ log.trace("streamingEmitter", "pending-answer poll failed", err);
6936
+ }
6937
+ }
6938
+ deliverAnswer(answer, pending) {
6939
+ if (pending.options && pending.options.length > 0) {
6940
+ let targetIndex = answer.optionIndex ?? -1;
6941
+ if (targetIndex < 0) {
6942
+ targetIndex = pending.options.findIndex((o) => o === answer.answer);
6943
+ }
6944
+ if (targetIndex < 0 || targetIndex >= pending.options.length) {
6945
+ log.warn(
6946
+ "streamingEmitter",
6947
+ `answer label "${answer.answer}" not found in selector options \u2014 defaulting to 0`
6948
+ );
6949
+ targetIndex = 0;
6950
+ }
6951
+ this.opts.ptyInput.selectOption(targetIndex, pending.fromIndex ?? 0);
6952
+ return;
6953
+ }
6954
+ this.opts.ptyInput.sendCommand(answer.answer);
6955
+ }
6956
+ // ─── POSTs ─────────────────────────────────────────────────────────
6957
+ async postChunk(event) {
6958
+ await this.postWithRetries(
6959
+ `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`,
6960
+ event
6961
+ );
6962
+ }
6963
+ async postAwaitingAnswer(event) {
6964
+ await this.postWithRetries(
6965
+ `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/awaiting-answer`,
6966
+ event
6967
+ );
6968
+ }
6969
+ async postWithRetries(url, body) {
6970
+ const payload = JSON.stringify(body);
6971
+ for (let attempt = 0; attempt <= MAX_RETRIES2; attempt += 1) {
6972
+ try {
6973
+ const { statusCode, body: resBody } = await _transport4.post(url, this.headers, payload);
6974
+ if (statusCode >= 200 && statusCode < 300) {
6975
+ log.trace("streamingEmitter", `post ok url=${url} status=${statusCode}`);
6976
+ return;
6977
+ }
6978
+ if (statusCode === 410 || statusCode === 404) {
6979
+ log.warn("streamingEmitter", `session dead (status=${statusCode}) \u2014 disabling emitter`);
6980
+ this.active = false;
6981
+ if (this.tickTimer) {
6982
+ clearInterval(this.tickTimer);
6983
+ this.tickTimer = null;
6984
+ }
6985
+ if (this.answerPollTimer) {
6986
+ clearInterval(this.answerPollTimer);
6987
+ this.answerPollTimer = null;
6988
+ }
6989
+ return;
6990
+ }
6991
+ log.warn(
6992
+ "streamingEmitter",
6993
+ `post failed url=${url} status=${statusCode} attempt=${attempt + 1} body=${resBody.slice(0, 200)}`
6994
+ );
6995
+ } catch (err) {
6996
+ log.warn("streamingEmitter", `post error url=${url} attempt=${attempt + 1}`, err);
6997
+ }
6998
+ if (attempt < MAX_RETRIES2) {
6999
+ await sleep2(RETRY_BACKOFF_MS2 * (attempt + 1));
7000
+ }
7001
+ }
7002
+ }
7003
+ };
7004
+ function sleep2(ms) {
7005
+ return new Promise((r) => setTimeout(r, ms));
7006
+ }
7007
+ var TREE_CONTINUATION_RE = /^\s*└/;
7008
+ function classifyLine(line, parseChrome, runtime) {
7009
+ if (line.trim().length === 0) return null;
7010
+ if (TREE_CONTINUATION_RE.test(line)) return "tool_result";
7011
+ const step = parseChrome?.(line) ?? null;
7012
+ if (step) {
7013
+ if (step.tool === "thinking") return "thinking";
7014
+ return "tool_use";
7015
+ }
7016
+ const filtered = runtime.filterTuiOutput([line]);
7017
+ if (filtered.length === 0) return null;
7018
+ return "text";
7019
+ }
7020
+ function parsePendingAnswerResponse(body) {
7021
+ if (!body) return null;
7022
+ let parsed;
7023
+ try {
7024
+ parsed = JSON.parse(body);
7025
+ } catch {
7026
+ return null;
7027
+ }
7028
+ if (typeof parsed !== "object" || parsed === null) return null;
7029
+ const root = parsed;
7030
+ const candidate = root.data && typeof root.data === "object" ? root.data : root;
7031
+ const questionId = candidate.questionId;
7032
+ const answer = candidate.answer;
7033
+ const optionIndex = candidate.optionIndex;
7034
+ if (typeof questionId !== "string" || typeof answer !== "string") return null;
7035
+ const result = { questionId, answer };
7036
+ if (typeof optionIndex === "number" && Number.isInteger(optionIndex)) {
7037
+ result.optionIndex = optionIndex;
7038
+ }
7039
+ return result;
7040
+ }
7041
+
6583
7042
  // src/commands/start/quota-fetcher.ts
6584
7043
  var inProgress = false;
6585
7044
  function fetchQuotaUsage(runtime, historySvc) {
@@ -6595,13 +7054,13 @@ function fetchQuotaUsage(runtime, historySvc) {
6595
7054
  }
6596
7055
 
6597
7056
  // src/commands/start/keep-alive.ts
6598
- var import_child_process7 = require("child_process");
7057
+ var import_child_process8 = require("child_process");
6599
7058
  function buildKeepAlive(ctx) {
6600
7059
  let timer = null;
6601
7060
  async function setIdleTimeout(minutes) {
6602
7061
  if (!ctx.inCodespace || !ctx.codespaceName) return;
6603
7062
  await new Promise((resolve2) => {
6604
- const proc = (0, import_child_process7.spawn)(
7063
+ const proc = (0, import_child_process8.spawn)(
6605
7064
  "gh",
6606
7065
  [
6607
7066
  "api",
@@ -6641,8 +7100,8 @@ function buildKeepAlive(ctx) {
6641
7100
  var fs14 = __toESM(require("fs"));
6642
7101
  var os13 = __toESM(require("os"));
6643
7102
  var path19 = __toESM(require("path"));
6644
- var import_crypto2 = require("crypto");
6645
- var import_child_process10 = require("child_process");
7103
+ var import_crypto3 = require("crypto");
7104
+ var import_child_process11 = require("child_process");
6646
7105
 
6647
7106
  // src/lib/payload.ts
6648
7107
  var import_zod2 = require("zod");
@@ -6846,11 +7305,11 @@ async function writeProjectFile(rawPath, content) {
6846
7305
  }
6847
7306
 
6848
7307
  // src/services/project-ops.service.ts
6849
- var import_child_process8 = require("child_process");
7308
+ var import_child_process9 = require("child_process");
6850
7309
  var import_util2 = require("util");
6851
7310
  var fs13 = __toESM(require("fs/promises"));
6852
7311
  var path17 = __toESM(require("path"));
6853
- var execFileP2 = (0, import_util2.promisify)(import_child_process8.execFile);
7312
+ var execFileP2 = (0, import_util2.promisify)(import_child_process9.execFile);
6854
7313
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
6855
7314
  "node_modules",
6856
7315
  ".git",
@@ -7190,8 +7649,8 @@ async function jsSearchFiles(opts, cwd, cap) {
7190
7649
  }
7191
7650
 
7192
7651
  // src/services/terminal-ops.service.ts
7193
- var import_child_process9 = require("child_process");
7194
- var import_crypto = require("crypto");
7652
+ var import_child_process10 = require("child_process");
7653
+ var import_crypto2 = require("crypto");
7195
7654
  var import_path = __toESM(require("path"));
7196
7655
  var MAX_CONCURRENT_SESSIONS = 4;
7197
7656
  var nodePtyModule;
@@ -7312,7 +7771,7 @@ function createPythonSession(id, shell, cwd, env, cols, rows) {
7312
7771
  }
7313
7772
  let child;
7314
7773
  try {
7315
- child = (0, import_child_process9.spawn)(python, ["-c", PYTHON_TERMINAL_HELPER, shell], {
7774
+ child = (0, import_child_process10.spawn)(python, ["-c", PYTHON_TERMINAL_HELPER, shell], {
7316
7775
  cwd,
7317
7776
  env: { ...env, COLUMNS: String(cols), LINES: String(rows) },
7318
7777
  stdio: ["pipe", "pipe", "pipe"]
@@ -7367,7 +7826,7 @@ function openTerminal(opts) {
7367
7826
  };
7368
7827
  const cols = Math.max(1, Math.min(opts.cols ?? 80, 500));
7369
7828
  const rows = Math.max(1, Math.min(opts.rows ?? 24, 200));
7370
- const id = (0, import_crypto.randomUUID)();
7829
+ const id = (0, import_crypto2.randomUUID)();
7371
7830
  const ptyMod = loadNodePty2();
7372
7831
  if (ptyMod) {
7373
7832
  try {
@@ -7447,7 +7906,7 @@ function closeTerminal(sessionId) {
7447
7906
  function saveFilesTemp(files) {
7448
7907
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
7449
7908
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
7450
- const tmpPath = path19.join(os13.tmpdir(), `codeam-${(0, import_crypto2.randomUUID)()}-${safeName}`);
7909
+ const tmpPath = path19.join(os13.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
7451
7910
  fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
7452
7911
  return tmpPath;
7453
7912
  });
@@ -7579,7 +8038,7 @@ var sessionTerminated = (ctx) => {
7579
8038
  } catch {
7580
8039
  }
7581
8040
  try {
7582
- const proc = (0, import_child_process10.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
8041
+ const proc = (0, import_child_process11.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
7583
8042
  detached: true,
7584
8043
  stdio: "ignore"
7585
8044
  });
@@ -7601,7 +8060,7 @@ var shutdownSession = async (ctx, cmd) => {
7601
8060
  }
7602
8061
  if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
7603
8062
  try {
7604
- const stopProc = (0, import_child_process10.spawn)(
8063
+ const stopProc = (0, import_child_process11.spawn)(
7605
8064
  "bash",
7606
8065
  ["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
7607
8066
  { detached: true, stdio: "ignore" }
@@ -7611,7 +8070,7 @@ var shutdownSession = async (ctx, cmd) => {
7611
8070
  }
7612
8071
  }
7613
8072
  try {
7614
- const proc = (0, import_child_process10.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
8073
+ const proc = (0, import_child_process11.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
7615
8074
  detached: true,
7616
8075
  stdio: "ignore"
7617
8076
  });
@@ -7836,22 +8295,34 @@ async function start(requestedAgent) {
7836
8295
  pluginId,
7837
8296
  pluginAuthToken: session.pluginAuthToken
7838
8297
  }) : null;
8298
+ let streamingEmitter = null;
7839
8299
  const claude = new AgentService(
7840
8300
  runtime,
7841
8301
  {
7842
8302
  cwd,
7843
8303
  onData(raw) {
7844
8304
  outputSvc.push(raw);
8305
+ streamingEmitter?.push(raw);
7845
8306
  },
7846
8307
  onExit(code) {
7847
8308
  process.removeListener("SIGINT", sigintHandler);
7848
8309
  outputSvc.dispose();
7849
8310
  relay.stop();
7850
8311
  void fileWatcher?.stop();
8312
+ void streamingEmitter?.stop();
7851
8313
  process.exit(code);
7852
8314
  }
7853
8315
  }
7854
8316
  );
8317
+ if (session.pluginAuthToken) {
8318
+ streamingEmitter = new StreamingEmitterService({
8319
+ sessionId: session.id,
8320
+ pluginId,
8321
+ pluginAuthToken: session.pluginAuthToken,
8322
+ runtime,
8323
+ ptyInput: claude
8324
+ });
8325
+ }
7855
8326
  const ctx = {
7856
8327
  outputSvc,
7857
8328
  claude,
@@ -7878,6 +8349,7 @@ async function start(requestedAgent) {
7878
8349
  outputSvc.dispose();
7879
8350
  relay.stop();
7880
8351
  void fileWatcher?.stop();
8352
+ void streamingEmitter?.stop();
7881
8353
  process.exit(0);
7882
8354
  }
7883
8355
  process.once("SIGINT", sigintHandler);
@@ -7888,6 +8360,7 @@ async function start(requestedAgent) {
7888
8360
  fileWatcher.start().catch(() => {
7889
8361
  });
7890
8362
  }
8363
+ streamingEmitter?.start();
7891
8364
  setTimeout(() => {
7892
8365
  historySvc.load().catch(() => {
7893
8366
  });
@@ -7896,7 +8369,7 @@ async function start(requestedAgent) {
7896
8369
  }
7897
8370
 
7898
8371
  // src/commands/pair.ts
7899
- var import_crypto3 = require("crypto");
8372
+ var import_crypto4 = require("crypto");
7900
8373
  var import_picocolors3 = __toESM(require("picocolors"));
7901
8374
 
7902
8375
  // src/ui/prompts.ts
@@ -7959,7 +8432,7 @@ async function pair(args2 = []) {
7959
8432
  const flagAgent = parseAgentFlag(args2);
7960
8433
  const agentId = flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
7961
8434
  showIntro();
7962
- const pluginId = (0, import_crypto3.randomUUID)();
8435
+ const pluginId = (0, import_crypto4.randomUUID)();
7963
8436
  const spin = dist_exports.spinner();
7964
8437
  spin.start("Requesting pairing code...");
7965
8438
  const result = await requestCode(pluginId);
@@ -8015,8 +8488,8 @@ async function pair(args2 = []) {
8015
8488
  // src/commands/pair-auto.ts
8016
8489
  var fs15 = __toESM(require("fs"));
8017
8490
  var os14 = __toESM(require("os"));
8018
- var import_crypto4 = require("crypto");
8019
- var API_BASE6 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
8491
+ var import_crypto5 = require("crypto");
8492
+ var API_BASE7 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
8020
8493
  function fail(msg) {
8021
8494
  console.error(`
8022
8495
  ${msg}
@@ -8048,14 +8521,18 @@ function readTokenFromArgs(args2) {
8048
8521
  fail("codeam pair-auto requires --token-file=<path>, --token=<value>, or CODEAM_AUTO_TOKEN env");
8049
8522
  }
8050
8523
  async function claim(token, pluginId) {
8051
- const url = `${API_BASE6}/api/pairing/claim-auto-token`;
8524
+ const url = `${API_BASE7}/api/pairing/claim-auto-token`;
8052
8525
  const body = {
8053
8526
  token,
8054
8527
  pluginId,
8055
8528
  ideName: "codeam-cli (codespace)",
8056
8529
  ideVersion: process.env.npm_package_version ?? "unknown",
8057
8530
  hostname: os14.hostname(),
8058
- codespaceName: process.env.CODESPACE_NAME ?? ""
8531
+ codespaceName: process.env.CODESPACE_NAME ?? "",
8532
+ // Current git branch of the codespace's working directory, so the
8533
+ // backend can populate `PairedSession.branch` for the codespace pair.
8534
+ // `null` when detached HEAD / not a git repo.
8535
+ branch: detectCurrentBranch()
8059
8536
  };
8060
8537
  const res = await fetch(url, {
8061
8538
  method: "POST",
@@ -8077,7 +8554,7 @@ async function claim(token, pluginId) {
8077
8554
  }
8078
8555
  async function pairAuto(args2) {
8079
8556
  const token = readTokenFromArgs(args2);
8080
- const pluginId = (0, import_crypto4.randomUUID)();
8557
+ const pluginId = (0, import_crypto5.randomUUID)();
8081
8558
  console.log(" Claiming pairing token\u2026");
8082
8559
  const claimed = await claim(token, pluginId);
8083
8560
  if (!isKnownAgentId(claimed.agent)) {
@@ -8203,11 +8680,11 @@ async function logout() {
8203
8680
  var import_picocolors9 = __toESM(require("picocolors"));
8204
8681
 
8205
8682
  // src/services/providers/github-codespaces.ts
8206
- var import_child_process11 = require("child_process");
8683
+ var import_child_process12 = require("child_process");
8207
8684
  var import_util3 = require("util");
8208
8685
  var import_picocolors7 = __toESM(require("picocolors"));
8209
8686
  var path20 = __toESM(require("path"));
8210
- var execFileP3 = (0, import_util3.promisify)(import_child_process11.execFile);
8687
+ var execFileP3 = (0, import_util3.promisify)(import_child_process12.execFile);
8211
8688
  var MAX_BUFFER = 8 * 1024 * 1024;
8212
8689
  function resetStdinForChild() {
8213
8690
  if (process.stdin.isTTY) {
@@ -8251,7 +8728,7 @@ var GitHubCodespacesProvider = class {
8251
8728
  if (!isAuthed) {
8252
8729
  resetStdinForChild();
8253
8730
  await new Promise((resolve2, reject) => {
8254
- const proc = (0, import_child_process11.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
8731
+ const proc = (0, import_child_process12.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
8255
8732
  stdio: "inherit"
8256
8733
  });
8257
8734
  proc.on("exit", (code) => {
@@ -8285,7 +8762,7 @@ var GitHubCodespacesProvider = class {
8285
8762
  wt(noteLines.join("\n"), "One more permission needed");
8286
8763
  resetStdinForChild();
8287
8764
  const refreshCode = await new Promise((resolve2, reject) => {
8288
- const proc = (0, import_child_process11.spawn)(
8765
+ const proc = (0, import_child_process12.spawn)(
8289
8766
  "gh",
8290
8767
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
8291
8768
  { stdio: "inherit" }
@@ -8435,7 +8912,7 @@ var GitHubCodespacesProvider = class {
8435
8912
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
8436
8913
  resetStdinForChild();
8437
8914
  const ok = await new Promise((resolve2) => {
8438
- const proc = (0, import_child_process11.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
8915
+ const proc = (0, import_child_process12.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
8439
8916
  proc.on("exit", (code) => resolve2(code === 0));
8440
8917
  proc.on("error", () => resolve2(false));
8441
8918
  });
@@ -8462,7 +8939,7 @@ var GitHubCodespacesProvider = class {
8462
8939
  );
8463
8940
  resetStdinForChild();
8464
8941
  await new Promise((resolve2, reject) => {
8465
- const proc = (0, import_child_process11.spawn)(
8942
+ const proc = (0, import_child_process12.spawn)(
8466
8943
  "gh",
8467
8944
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
8468
8945
  { stdio: "inherit" }
@@ -8640,7 +9117,7 @@ var GitHubCodespacesProvider = class {
8640
9117
  async streamCommand(workspaceId, command2) {
8641
9118
  resetStdinForChild();
8642
9119
  return new Promise((resolve2, reject) => {
8643
- const proc = (0, import_child_process11.spawn)(
9120
+ const proc = (0, import_child_process12.spawn)(
8644
9121
  "gh",
8645
9122
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
8646
9123
  { stdio: "inherit" }
@@ -8667,11 +9144,11 @@ var GitHubCodespacesProvider = class {
8667
9144
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
8668
9145
  ];
8669
9146
  await new Promise((resolve2, reject) => {
8670
- const tar = (0, import_child_process11.spawn)("tar", tarArgs, {
9147
+ const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
8671
9148
  stdio: ["ignore", "pipe", "pipe"],
8672
9149
  env: tarEnv
8673
9150
  });
8674
- const ssh = (0, import_child_process11.spawn)("gh", sshArgs, {
9151
+ const ssh = (0, import_child_process12.spawn)("gh", sshArgs, {
8675
9152
  stdio: [tar.stdout, "pipe", "pipe"]
8676
9153
  });
8677
9154
  let tarErr = "";
@@ -8705,7 +9182,7 @@ var GitHubCodespacesProvider = class {
8705
9182
  }
8706
9183
  const cmd = parts.join(" && ");
8707
9184
  await new Promise((resolve2, reject) => {
8708
- const proc = (0, import_child_process11.spawn)(
9185
+ const proc = (0, import_child_process12.spawn)(
8709
9186
  "gh",
8710
9187
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
8711
9188
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -8763,11 +9240,11 @@ function shellQuote(s) {
8763
9240
  }
8764
9241
 
8765
9242
  // src/services/providers/gitpod.ts
8766
- var import_child_process12 = require("child_process");
9243
+ var import_child_process13 = require("child_process");
8767
9244
  var import_util4 = require("util");
8768
9245
  var path21 = __toESM(require("path"));
8769
9246
  var import_picocolors8 = __toESM(require("picocolors"));
8770
- var execFileP4 = (0, import_util4.promisify)(import_child_process12.execFile);
9247
+ var execFileP4 = (0, import_util4.promisify)(import_child_process13.execFile);
8771
9248
  var MAX_BUFFER2 = 8 * 1024 * 1024;
8772
9249
  function resetStdinForChild2() {
8773
9250
  if (process.stdin.isTTY) {
@@ -8807,7 +9284,7 @@ var GitpodProvider = class {
8807
9284
  );
8808
9285
  resetStdinForChild2();
8809
9286
  await new Promise((resolve2, reject) => {
8810
- const proc = (0, import_child_process12.spawn)("gitpod", ["login"], { stdio: "inherit" });
9287
+ const proc = (0, import_child_process13.spawn)("gitpod", ["login"], { stdio: "inherit" });
8811
9288
  proc.on("exit", (code) => {
8812
9289
  if (code === 0) resolve2();
8813
9290
  else reject(new Error("gitpod login failed."));
@@ -8959,7 +9436,7 @@ var GitpodProvider = class {
8959
9436
  async streamCommand(workspaceId, command2) {
8960
9437
  resetStdinForChild2();
8961
9438
  return new Promise((resolve2, reject) => {
8962
- const proc = (0, import_child_process12.spawn)(
9439
+ const proc = (0, import_child_process13.spawn)(
8963
9440
  "gitpod",
8964
9441
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
8965
9442
  { stdio: "inherit" }
@@ -8979,11 +9456,11 @@ var GitpodProvider = class {
8979
9456
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
8980
9457
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
8981
9458
  await new Promise((resolve2, reject) => {
8982
- const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
9459
+ const tar = (0, import_child_process13.spawn)("tar", tarArgs, {
8983
9460
  stdio: ["ignore", "pipe", "pipe"],
8984
9461
  env: tarEnv
8985
9462
  });
8986
- const ssh = (0, import_child_process12.spawn)(
9463
+ const ssh = (0, import_child_process13.spawn)(
8987
9464
  "gitpod",
8988
9465
  ["workspace", "ssh", workspaceId, "--", remoteCmd],
8989
9466
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -9015,7 +9492,7 @@ var GitpodProvider = class {
9015
9492
  }
9016
9493
  const cmd = parts.join(" && ");
9017
9494
  await new Promise((resolve2, reject) => {
9018
- const proc = (0, import_child_process12.spawn)(
9495
+ const proc = (0, import_child_process13.spawn)(
9019
9496
  "gitpod",
9020
9497
  ["workspace", "ssh", workspaceId, "--", cmd],
9021
9498
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -9039,10 +9516,10 @@ function shellQuote2(s) {
9039
9516
  }
9040
9517
 
9041
9518
  // src/services/providers/gitlab-workspaces.ts
9042
- var import_child_process13 = require("child_process");
9519
+ var import_child_process14 = require("child_process");
9043
9520
  var import_util5 = require("util");
9044
9521
  var path22 = __toESM(require("path"));
9045
- var execFileP5 = (0, import_util5.promisify)(import_child_process13.execFile);
9522
+ var execFileP5 = (0, import_util5.promisify)(import_child_process14.execFile);
9046
9523
  var MAX_BUFFER3 = 8 * 1024 * 1024;
9047
9524
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
9048
9525
  function resetStdinForChild3() {
@@ -9084,7 +9561,7 @@ var GitLabWorkspacesProvider = class {
9084
9561
  );
9085
9562
  resetStdinForChild3();
9086
9563
  await new Promise((resolve2, reject) => {
9087
- const proc = (0, import_child_process13.spawn)(
9564
+ const proc = (0, import_child_process14.spawn)(
9088
9565
  "glab",
9089
9566
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
9090
9567
  { stdio: "inherit" }
@@ -9256,7 +9733,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9256
9733
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
9257
9734
  resetStdinForChild3();
9258
9735
  return new Promise((resolve2, reject) => {
9259
- const proc = (0, import_child_process13.spawn)(
9736
+ const proc = (0, import_child_process14.spawn)(
9260
9737
  "ssh",
9261
9738
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
9262
9739
  { stdio: "inherit" }
@@ -9277,8 +9754,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9277
9754
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
9278
9755
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
9279
9756
  await new Promise((resolve2, reject) => {
9280
- const tar = (0, import_child_process13.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
9281
- const ssh = (0, import_child_process13.spawn)(
9757
+ const tar = (0, import_child_process14.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
9758
+ const ssh = (0, import_child_process14.spawn)(
9282
9759
  "ssh",
9283
9760
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
9284
9761
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -9308,7 +9785,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9308
9785
  }
9309
9786
  const cmd = parts.join(" && ");
9310
9787
  await new Promise((resolve2, reject) => {
9311
- const proc = (0, import_child_process13.spawn)(
9788
+ const proc = (0, import_child_process14.spawn)(
9312
9789
  "ssh",
9313
9790
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
9314
9791
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -9367,10 +9844,10 @@ function shellQuote3(s) {
9367
9844
  }
9368
9845
 
9369
9846
  // src/services/providers/railway.ts
9370
- var import_child_process14 = require("child_process");
9847
+ var import_child_process15 = require("child_process");
9371
9848
  var import_util6 = require("util");
9372
9849
  var path23 = __toESM(require("path"));
9373
- var execFileP6 = (0, import_util6.promisify)(import_child_process14.execFile);
9850
+ var execFileP6 = (0, import_util6.promisify)(import_child_process15.execFile);
9374
9851
  var MAX_BUFFER4 = 8 * 1024 * 1024;
9375
9852
  function resetStdinForChild4() {
9376
9853
  if (process.stdin.isTTY) {
@@ -9411,7 +9888,7 @@ var RailwayProvider = class {
9411
9888
  );
9412
9889
  resetStdinForChild4();
9413
9890
  await new Promise((resolve2, reject) => {
9414
- const proc = (0, import_child_process14.spawn)("railway", ["login"], { stdio: "inherit" });
9891
+ const proc = (0, import_child_process15.spawn)("railway", ["login"], { stdio: "inherit" });
9415
9892
  proc.on("exit", (code) => {
9416
9893
  if (code === 0) resolve2();
9417
9894
  else reject(new Error("railway login failed."));
@@ -9554,7 +10031,7 @@ var RailwayProvider = class {
9554
10031
  }
9555
10032
  resetStdinForChild4();
9556
10033
  return new Promise((resolve2, reject) => {
9557
- const proc = (0, import_child_process14.spawn)(
10034
+ const proc = (0, import_child_process15.spawn)(
9558
10035
  "railway",
9559
10036
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
9560
10037
  { stdio: "inherit" }
@@ -9578,8 +10055,8 @@ var RailwayProvider = class {
9578
10055
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
9579
10056
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
9580
10057
  await new Promise((resolve2, reject) => {
9581
- const tar = (0, import_child_process14.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
9582
- const sh = (0, import_child_process14.spawn)(
10058
+ const tar = (0, import_child_process15.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
10059
+ const sh = (0, import_child_process15.spawn)(
9583
10060
  "railway",
9584
10061
  ["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
9585
10062
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -9612,7 +10089,7 @@ var RailwayProvider = class {
9612
10089
  }
9613
10090
  const cmd = parts.join(" && ");
9614
10091
  await new Promise((resolve2, reject) => {
9615
- const proc = (0, import_child_process14.spawn)(
10092
+ const proc = (0, import_child_process15.spawn)(
9616
10093
  "railway",
9617
10094
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
9618
10095
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -10147,7 +10624,7 @@ async function stopWorkspaceFromLocal(target) {
10147
10624
  // src/commands/version.ts
10148
10625
  var import_picocolors11 = __toESM(require("picocolors"));
10149
10626
  function version() {
10150
- const v = true ? "2.15.5" : "unknown";
10627
+ const v = true ? "2.15.7" : "unknown";
10151
10628
  console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
10152
10629
  }
10153
10630
 
@@ -10191,7 +10668,7 @@ function help() {
10191
10668
  var fs16 = __toESM(require("fs"));
10192
10669
  var os15 = __toESM(require("os"));
10193
10670
  var path24 = __toESM(require("path"));
10194
- var https6 = __toESM(require("https"));
10671
+ var https7 = __toESM(require("https"));
10195
10672
  var import_picocolors13 = __toESM(require("picocolors"));
10196
10673
  var PKG_NAME = "codeam-cli";
10197
10674
  var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
@@ -10234,7 +10711,7 @@ function compareSemver(a, b) {
10234
10711
  }
10235
10712
  function fetchLatest() {
10236
10713
  return new Promise((resolve2) => {
10237
- const req = https6.get(
10714
+ const req = https7.get(
10238
10715
  REGISTRY_URL,
10239
10716
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
10240
10717
  (res) => {
@@ -10286,7 +10763,7 @@ function checkForUpdates() {
10286
10763
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
10287
10764
  if (process.env.CI) return;
10288
10765
  if (!process.stdout.isTTY) return;
10289
- const current = true ? "2.15.5" : null;
10766
+ const current = true ? "2.15.7" : null;
10290
10767
  if (!current) return;
10291
10768
  const cache = readCache();
10292
10769
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.15.5",
3
+ "version": "2.15.7",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",