codeam-cli 2.15.5 → 2.15.6

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 +11 -0
  2. package/dist/index.js +544 -70
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ 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.5] — 2026-05-20
8
+
9
+ ### Added
10
+
11
+ - **shared:** Add FileChangedEvent + PendingReviewHunkEvent wire types
12
+ - **cli:** Emit file-change + review-hunk events during paired sessions
13
+
14
+ ### CI
15
+
16
+ - **release:** Make CLI npm publish idempotent
17
+
7
18
  ## [2.15.1] — 2026-05-17
8
19
 
9
20
  ### Fixed
package/dist/index.js CHANGED
@@ -386,7 +386,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
386
386
  // package.json
387
387
  var package_default = {
388
388
  name: "codeam-cli",
389
- version: "2.15.5",
389
+ version: "2.15.6",
390
390
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
391
391
  type: "commonjs",
392
392
  main: "dist/index.js",
@@ -521,6 +521,34 @@ function vercelBypassHeader() {
521
521
  return token ? { "x-vercel-protection-bypass": token } : {};
522
522
  }
523
523
 
524
+ // src/lib/git-branch.ts
525
+ var import_child_process = require("child_process");
526
+ function detectCurrentBranch(cwd = process.cwd()) {
527
+ try {
528
+ const raw = _execSeam.exec("git branch --show-current", {
529
+ cwd,
530
+ // 1 s ceiling is comfortably above normal git latency (<50 ms
531
+ // on a healthy repo) and well below the pair POST's 10 s budget.
532
+ timeout: 1e3,
533
+ // Swallow stderr — non-git directories print "fatal: not a git
534
+ // repository" to stderr and we don't want that on the CLI's
535
+ // own stderr while pairing.
536
+ stdio: ["ignore", "pipe", "ignore"],
537
+ encoding: "utf8"
538
+ });
539
+ const trimmed = raw.trim();
540
+ return trimmed.length > 0 ? trimmed : null;
541
+ } catch {
542
+ return null;
543
+ }
544
+ }
545
+ var _execSeam = {
546
+ exec: (cmd, opts) => {
547
+ const out = (0, import_child_process.execSync)(cmd, opts);
548
+ return typeof out === "string" ? out : out.toString("utf8");
549
+ }
550
+ };
551
+
524
552
  // src/lib/poll-delay.ts
525
553
  var MAX_DELAY_MS = 3e4;
526
554
  function computePollDelay({ baseMs, failures }) {
@@ -535,12 +563,14 @@ async function requestCode(pluginId) {
535
563
  try {
536
564
  const runtime = process.env.CODESPACES === "true" ? "github-codespaces" : "local";
537
565
  const codespaceName = process.env.CODESPACE_NAME;
566
+ const branch = detectCurrentBranch();
538
567
  const result = await _transport.postJson(`${API_BASE}/api/pairing/code`, {
539
568
  pluginId,
540
569
  ideName: "Terminal (codeam-cli)",
541
570
  ideVersion: package_default.version,
542
571
  hostname: os2.hostname(),
543
572
  runtime,
573
+ branch,
544
574
  ...codespaceName ? { codespaceName } : {}
545
575
  });
546
576
  const data = result?.data;
@@ -983,7 +1013,7 @@ var CommandRelayService = class {
983
1013
  };
984
1014
 
985
1015
  // src/services/pty/unix.strategy.ts
986
- var import_child_process = require("child_process");
1016
+ var import_child_process2 = require("child_process");
987
1017
  var fs4 = __toESM(require("fs"));
988
1018
  var os4 = __toESM(require("os"));
989
1019
  var path4 = __toESM(require("path"));
@@ -1087,7 +1117,7 @@ var UnixPtyStrategy = class {
1087
1117
  const rows = process.stdout.rows || 50;
1088
1118
  this.helperPath = path4.join(os4.tmpdir(), "codeam-pty-helper.py");
1089
1119
  fs4.writeFileSync(this.helperPath, PYTHON_PTY_HELPER, { mode: 420 });
1090
- this.proc = (0, import_child_process.spawn)(python, [this.helperPath, cmd, ...args2], {
1120
+ this.proc = (0, import_child_process2.spawn)(python, [this.helperPath, cmd, ...args2], {
1091
1121
  stdio: ["pipe", "pipe", "inherit"],
1092
1122
  cwd,
1093
1123
  env: {
@@ -1152,7 +1182,7 @@ var UnixPtyStrategy = class {
1152
1182
  * are NOT interpreted by /bin/sh.
1153
1183
  */
1154
1184
  spawnDirect(cmd, cwd, args2 = []) {
1155
- this.proc = (0, import_child_process.spawn)(cmd, args2, {
1185
+ this.proc = (0, import_child_process2.spawn)(cmd, args2, {
1156
1186
  stdio: ["pipe", "inherit", "inherit"],
1157
1187
  cwd,
1158
1188
  env: process.env,
@@ -1224,7 +1254,7 @@ var UnixPtyStrategy = class {
1224
1254
  };
1225
1255
 
1226
1256
  // src/services/pty/windows.strategy.ts
1227
- var import_child_process2 = require("child_process");
1257
+ var import_child_process3 = require("child_process");
1228
1258
  var WindowsPtyStrategy = class {
1229
1259
  constructor(opts) {
1230
1260
  this.opts = opts;
@@ -1232,7 +1262,7 @@ var WindowsPtyStrategy = class {
1232
1262
  opts;
1233
1263
  proc = null;
1234
1264
  spawn(cmd, cwd, args2 = []) {
1235
- this.proc = (0, import_child_process2.spawn)(cmd, args2, {
1265
+ this.proc = (0, import_child_process3.spawn)(cmd, args2, {
1236
1266
  stdio: ["pipe", "pipe", "inherit"],
1237
1267
  cwd,
1238
1268
  env: {
@@ -1408,7 +1438,7 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
1408
1438
  };
1409
1439
 
1410
1440
  // src/services/claude-installer.ts
1411
- var import_child_process3 = require("child_process");
1441
+ var import_child_process4 = require("child_process");
1412
1442
  var path6 = __toESM(require("path"));
1413
1443
  var os5 = __toESM(require("os"));
1414
1444
 
@@ -3361,7 +3391,7 @@ function runInstaller() {
3361
3391
  "irm https://claude.ai/install.ps1 | iex"
3362
3392
  ] : ["-c", "curl -fsSL https://claude.ai/install.sh | bash"];
3363
3393
  return new Promise((resolve2) => {
3364
- const proc = (0, import_child_process3.spawn)(cmd, args2, { stdio: "inherit" });
3394
+ const proc = (0, import_child_process4.spawn)(cmd, args2, { stdio: "inherit" });
3365
3395
  proc.on("error", (err) => {
3366
3396
  console.error(`
3367
3397
  \u2717 Installer failed to launch: ${err.message}`);
@@ -3660,7 +3690,7 @@ var AgentService = class {
3660
3690
  var fs5 = __toESM(require("fs"));
3661
3691
  var os6 = __toESM(require("os"));
3662
3692
  var path8 = __toESM(require("path"));
3663
- var import_child_process4 = require("child_process");
3693
+ var import_child_process5 = require("child_process");
3664
3694
  var HELPER_SCRIPT = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
3665
3695
  m,s=pty.openpty()
3666
3696
  try:
@@ -3729,7 +3759,7 @@ async function fetchClaudeQuota() {
3729
3759
  resolve2(null);
3730
3760
  return;
3731
3761
  }
3732
- const proc = (0, import_child_process4.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
3762
+ const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
3733
3763
  stdio: ["pipe", "pipe", "ignore"],
3734
3764
  cwd: process.cwd(),
3735
3765
  env: { ...process.env, TERM: "dumb", COLUMNS: "120", LINES: "30" }
@@ -4192,12 +4222,12 @@ var os9 = __toESM(require("os"));
4192
4222
  var path11 = __toESM(require("path"));
4193
4223
 
4194
4224
  // src/agents/claude/credentials.ts
4195
- var import_child_process5 = require("child_process");
4225
+ var import_child_process6 = require("child_process");
4196
4226
  var fs7 = __toESM(require("fs"));
4197
4227
  var os8 = __toESM(require("os"));
4198
4228
  var path10 = __toESM(require("path"));
4199
4229
  var import_util = require("util");
4200
- var execFileP = (0, import_util.promisify)(import_child_process5.execFile);
4230
+ var execFileP = (0, import_util.promisify)(import_child_process6.execFile);
4201
4231
  async function detectLocalClaudeCredentials() {
4202
4232
  const localClaudeDir = path10.join(os8.homedir(), ".claude");
4203
4233
  const flat = path10.join(localClaudeDir, ".credentials.json");
@@ -6127,7 +6157,7 @@ var HistoryService = class _HistoryService {
6127
6157
  };
6128
6158
 
6129
6159
  // src/services/file-watcher.service.ts
6130
- var import_child_process6 = require("child_process");
6160
+ var import_child_process7 = require("child_process");
6131
6161
  var path15 = __toESM(require("path"));
6132
6162
 
6133
6163
  // src/services/file-watcher/diff-parser.ts
@@ -6552,7 +6582,7 @@ async function _runGitImpl(cwd, args2, opts = {}) {
6552
6582
  return new Promise((resolve2) => {
6553
6583
  let proc;
6554
6584
  try {
6555
- proc = (0, import_child_process6.spawn)("git", args2, { cwd, env: process.env });
6585
+ proc = (0, import_child_process7.spawn)("git", args2, { cwd, env: process.env });
6556
6586
  } catch {
6557
6587
  resolve2(null);
6558
6588
  return;
@@ -6580,6 +6610,432 @@ function _runGit(cwd, args2, opts = {}) {
6580
6610
  return _gitSeam.run(cwd, args2, opts);
6581
6611
  }
6582
6612
 
6613
+ // src/services/streaming-emitter.service.ts
6614
+ var import_crypto = require("crypto");
6615
+
6616
+ // src/services/streaming/transport.ts
6617
+ var http6 = __toESM(require("http"));
6618
+ var https6 = __toESM(require("https"));
6619
+ var _transport4 = {
6620
+ post: _post3,
6621
+ get: _get
6622
+ };
6623
+ function _post3(url, headers, payload) {
6624
+ return new Promise((resolve2, reject) => {
6625
+ let settled = false;
6626
+ const u2 = new URL(url);
6627
+ const lib = u2.protocol === "https:" ? https6 : http6;
6628
+ const req = lib.request(
6629
+ {
6630
+ hostname: u2.hostname,
6631
+ port: u2.port || (u2.protocol === "https:" ? 443 : 80),
6632
+ path: u2.pathname + u2.search,
6633
+ method: "POST",
6634
+ headers: {
6635
+ ...headers,
6636
+ ...vercelBypassHeader(),
6637
+ "Content-Length": Buffer.byteLength(payload)
6638
+ },
6639
+ timeout: 8e3
6640
+ },
6641
+ (res) => {
6642
+ let body = "";
6643
+ res.on("data", (c2) => {
6644
+ body += c2.toString();
6645
+ });
6646
+ res.on("end", () => {
6647
+ if (settled) return;
6648
+ settled = true;
6649
+ resolve2({ statusCode: res.statusCode ?? 0, body });
6650
+ });
6651
+ }
6652
+ );
6653
+ req.on("error", (err) => {
6654
+ if (settled) return;
6655
+ settled = true;
6656
+ reject(err);
6657
+ });
6658
+ req.on("timeout", () => {
6659
+ req.destroy();
6660
+ });
6661
+ req.write(payload);
6662
+ req.end();
6663
+ });
6664
+ }
6665
+ function _get(url, headers) {
6666
+ return new Promise((resolve2, reject) => {
6667
+ let settled = false;
6668
+ const u2 = new URL(url);
6669
+ const lib = u2.protocol === "https:" ? https6 : http6;
6670
+ const req = lib.request(
6671
+ {
6672
+ hostname: u2.hostname,
6673
+ port: u2.port || (u2.protocol === "https:" ? 443 : 80),
6674
+ path: u2.pathname + u2.search,
6675
+ method: "GET",
6676
+ headers: {
6677
+ ...headers,
6678
+ ...vercelBypassHeader()
6679
+ },
6680
+ timeout: 8e3
6681
+ },
6682
+ (res) => {
6683
+ let body = "";
6684
+ res.on("data", (c2) => {
6685
+ body += c2.toString();
6686
+ });
6687
+ res.on("end", () => {
6688
+ if (settled) return;
6689
+ settled = true;
6690
+ resolve2({ statusCode: res.statusCode ?? 0, body });
6691
+ });
6692
+ }
6693
+ );
6694
+ req.on("error", (err) => {
6695
+ if (settled) return;
6696
+ settled = true;
6697
+ reject(err);
6698
+ });
6699
+ req.on("timeout", () => {
6700
+ req.destroy();
6701
+ });
6702
+ req.end();
6703
+ });
6704
+ }
6705
+
6706
+ // src/services/streaming-emitter.service.ts
6707
+ var API_BASE6 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
6708
+ var TICK_MS = 50;
6709
+ var ANSWER_POLL_MS = 1500;
6710
+ var SELECTOR_STABLE_MS = 800;
6711
+ var MAX_RETRIES2 = 1;
6712
+ var RETRY_BACKOFF_MS2 = 200;
6713
+ var StreamingEmitterService = class {
6714
+ constructor(opts) {
6715
+ this.opts = opts;
6716
+ this.apiBase = opts.apiBaseUrl ?? API_BASE6;
6717
+ this.headers = {
6718
+ "Content-Type": "application/json",
6719
+ "X-Codeam-Protocol-Version": "2.0.0",
6720
+ "X-Plugin-Auth-Token": opts.pluginAuthToken
6721
+ };
6722
+ }
6723
+ opts;
6724
+ apiBase;
6725
+ headers;
6726
+ rawBuffer = "";
6727
+ active = false;
6728
+ tickTimer = null;
6729
+ answerPollTimer = null;
6730
+ /** Open chunk we're appending bytes to; cleared on kind change or stop. */
6731
+ activeChunk = null;
6732
+ /** Outstanding answer we're polling the backend for. Null when idle. */
6733
+ pendingAnswer = null;
6734
+ /**
6735
+ * Memoised selector signature so we don't fire `awaiting-answer` on
6736
+ * every tick while the user thinks. Cleared once an answer resolves.
6737
+ */
6738
+ lastSelectorSignature = null;
6739
+ selectorFirstSeenAt = 0;
6740
+ // ─── Lifecycle ─────────────────────────────────────────────────────
6741
+ start() {
6742
+ if (this.active) return;
6743
+ this.active = true;
6744
+ this.tickTimer = setInterval(() => this.tick(), TICK_MS);
6745
+ this.answerPollTimer = setInterval(() => {
6746
+ void this.pollPendingAnswer();
6747
+ }, ANSWER_POLL_MS);
6748
+ log.trace("streamingEmitter", `started session=${this.opts.sessionId.slice(0, 8)}`);
6749
+ }
6750
+ /**
6751
+ * Stop the emitter. Finalises the open chunk (if any) so the
6752
+ * backend doesn't leave a half-streamed message in the SSE
6753
+ * buffer. Idempotent.
6754
+ */
6755
+ async stop() {
6756
+ if (!this.active) return;
6757
+ this.active = false;
6758
+ if (this.tickTimer) {
6759
+ clearInterval(this.tickTimer);
6760
+ this.tickTimer = null;
6761
+ }
6762
+ if (this.answerPollTimer) {
6763
+ clearInterval(this.answerPollTimer);
6764
+ this.answerPollTimer = null;
6765
+ }
6766
+ if (this.activeChunk) {
6767
+ const finalContent = this.activeChunk.currentContent;
6768
+ const chunk = this.activeChunk;
6769
+ this.activeChunk = null;
6770
+ await this.postChunk({
6771
+ chunkId: chunk.chunkId,
6772
+ kind: chunk.kind,
6773
+ content: finalContent,
6774
+ isFinal: true
6775
+ });
6776
+ }
6777
+ log.trace("streamingEmitter", `stopped session=${this.opts.sessionId.slice(0, 8)}`);
6778
+ }
6779
+ // ─── Input pump ────────────────────────────────────────────────────
6780
+ /**
6781
+ * Forward a chunk of raw PTY bytes into the emitter. Safe to call
6782
+ * before `start()` — bytes accumulate in the internal buffer and the
6783
+ * first tick processes the backlog.
6784
+ */
6785
+ push(raw) {
6786
+ if (raw.length === 0) return;
6787
+ this.rawBuffer += raw;
6788
+ }
6789
+ // ─── Tick ──────────────────────────────────────────────────────────
6790
+ /**
6791
+ * Visible for tests. Runs one classify-and-emit pass against the
6792
+ * current PTY buffer.
6793
+ */
6794
+ /* @internal */
6795
+ _tickForTest() {
6796
+ this.tick();
6797
+ }
6798
+ tick() {
6799
+ if (!this.active) return;
6800
+ const lines = (this.opts.runtime.renderToLines ?? renderToLines)(this.rawBuffer);
6801
+ const selector = this.opts.runtime.detectInteractivePrompt(lines);
6802
+ if (selector) {
6803
+ this.maybeEmitAwaitingAnswer(selector);
6804
+ return;
6805
+ }
6806
+ this.lastSelectorSignature = null;
6807
+ this.selectorFirstSeenAt = 0;
6808
+ const groups = this.classifyLines(lines, this.opts.runtime);
6809
+ if (groups.length === 0) return;
6810
+ const last = groups[groups.length - 1];
6811
+ if (this.activeChunk && this.activeChunk.kind === last.kind) {
6812
+ this.activeChunk.currentContent = last.content;
6813
+ this.maybeFlushActive(false);
6814
+ return;
6815
+ }
6816
+ if (this.activeChunk) {
6817
+ const closing = this.activeChunk;
6818
+ this.activeChunk = null;
6819
+ void this.postChunk({
6820
+ chunkId: closing.chunkId,
6821
+ kind: closing.kind,
6822
+ content: closing.currentContent,
6823
+ isFinal: true
6824
+ });
6825
+ }
6826
+ this.activeChunk = {
6827
+ chunkId: (0, import_crypto.randomUUID)(),
6828
+ kind: last.kind,
6829
+ emittedContent: "",
6830
+ currentContent: last.content,
6831
+ lastEmitAt: 0
6832
+ };
6833
+ this.maybeFlushActive(false);
6834
+ }
6835
+ maybeFlushActive(force) {
6836
+ const chunk = this.activeChunk;
6837
+ if (!chunk) return;
6838
+ const now = Date.now();
6839
+ if (!force && now - chunk.lastEmitAt < TICK_MS) return;
6840
+ if (chunk.currentContent === chunk.emittedContent) return;
6841
+ chunk.emittedContent = chunk.currentContent;
6842
+ chunk.lastEmitAt = now;
6843
+ void this.postChunk({
6844
+ chunkId: chunk.chunkId,
6845
+ kind: chunk.kind,
6846
+ content: chunk.currentContent,
6847
+ isFinal: false
6848
+ });
6849
+ }
6850
+ // ─── Line classification ───────────────────────────────────────────
6851
+ /**
6852
+ * Walk the rendered screen lines and bucket them into kind-groups.
6853
+ *
6854
+ * Visible for tests so we can assert discrimination against fixture
6855
+ * inputs without standing up the full POST loop.
6856
+ */
6857
+ /* @internal */
6858
+ _classifyForTest(lines) {
6859
+ return this.classifyLines(lines, this.opts.runtime);
6860
+ }
6861
+ classifyLines(lines, runtime) {
6862
+ const parseLine2 = runtime.parseTuiChrome?.bind(runtime);
6863
+ const groups = [];
6864
+ let current = null;
6865
+ const flush = () => {
6866
+ if (!current) return;
6867
+ const text = current.lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
6868
+ if (text.length > 0) groups.push({ kind: current.kind, content: text });
6869
+ current = null;
6870
+ };
6871
+ for (const rawLine of lines) {
6872
+ const kind = classifyLine(rawLine, parseLine2, runtime);
6873
+ if (kind === null) continue;
6874
+ if (!current || current.kind !== kind) {
6875
+ flush();
6876
+ current = { kind, lines: [rawLine] };
6877
+ } else {
6878
+ current.lines.push(rawLine);
6879
+ }
6880
+ }
6881
+ flush();
6882
+ return groups;
6883
+ }
6884
+ // ─── Awaiting-answer + answer subscription ─────────────────────────
6885
+ maybeEmitAwaitingAnswer(selector) {
6886
+ const signature = `${selector.question}::${selector.options.join("|")}`;
6887
+ const now = Date.now();
6888
+ if (this.lastSelectorSignature !== signature) {
6889
+ this.lastSelectorSignature = signature;
6890
+ this.selectorFirstSeenAt = now;
6891
+ return;
6892
+ }
6893
+ if (this.pendingAnswer) return;
6894
+ if (now - this.selectorFirstSeenAt < SELECTOR_STABLE_MS) return;
6895
+ const questionId = (0, import_crypto.randomUUID)();
6896
+ this.pendingAnswer = {
6897
+ questionId,
6898
+ options: selector.options.length > 0 ? selector.options : void 0,
6899
+ fromIndex: selector.currentIndex
6900
+ };
6901
+ const event = {
6902
+ questionId,
6903
+ prompt: selector.question,
6904
+ ...selector.options.length > 0 ? { options: selector.options } : {}
6905
+ };
6906
+ void this.postAwaitingAnswer(event);
6907
+ }
6908
+ async pollPendingAnswer() {
6909
+ if (!this.active) return;
6910
+ if (!this.pendingAnswer) return;
6911
+ const questionId = this.pendingAnswer.questionId;
6912
+ try {
6913
+ const { statusCode, body } = await _transport4.get(
6914
+ `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/pending-answer?questionId=${encodeURIComponent(questionId)}`,
6915
+ this.headers
6916
+ );
6917
+ if (statusCode === 204 || statusCode === 404) {
6918
+ return;
6919
+ }
6920
+ if (statusCode < 200 || statusCode >= 300) {
6921
+ log.warn("streamingEmitter", `pending-answer status=${statusCode} body=${body.slice(0, 200)}`);
6922
+ return;
6923
+ }
6924
+ const parsed = parsePendingAnswerResponse(body);
6925
+ if (!parsed || parsed.questionId !== questionId) return;
6926
+ const pending = this.pendingAnswer;
6927
+ this.pendingAnswer = null;
6928
+ this.lastSelectorSignature = null;
6929
+ this.selectorFirstSeenAt = 0;
6930
+ this.deliverAnswer(parsed, pending);
6931
+ } catch (err) {
6932
+ log.trace("streamingEmitter", "pending-answer poll failed", err);
6933
+ }
6934
+ }
6935
+ deliverAnswer(answer, pending) {
6936
+ if (pending.options && pending.options.length > 0) {
6937
+ let targetIndex = answer.optionIndex ?? -1;
6938
+ if (targetIndex < 0) {
6939
+ targetIndex = pending.options.findIndex((o) => o === answer.answer);
6940
+ }
6941
+ if (targetIndex < 0 || targetIndex >= pending.options.length) {
6942
+ log.warn(
6943
+ "streamingEmitter",
6944
+ `answer label "${answer.answer}" not found in selector options \u2014 defaulting to 0`
6945
+ );
6946
+ targetIndex = 0;
6947
+ }
6948
+ this.opts.ptyInput.selectOption(targetIndex, pending.fromIndex ?? 0);
6949
+ return;
6950
+ }
6951
+ this.opts.ptyInput.sendCommand(answer.answer);
6952
+ }
6953
+ // ─── POSTs ─────────────────────────────────────────────────────────
6954
+ async postChunk(event) {
6955
+ await this.postWithRetries(
6956
+ `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`,
6957
+ event
6958
+ );
6959
+ }
6960
+ async postAwaitingAnswer(event) {
6961
+ await this.postWithRetries(
6962
+ `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/awaiting-answer`,
6963
+ event
6964
+ );
6965
+ }
6966
+ async postWithRetries(url, body) {
6967
+ const payload = JSON.stringify(body);
6968
+ for (let attempt = 0; attempt <= MAX_RETRIES2; attempt += 1) {
6969
+ try {
6970
+ const { statusCode, body: resBody } = await _transport4.post(url, this.headers, payload);
6971
+ if (statusCode >= 200 && statusCode < 300) {
6972
+ log.trace("streamingEmitter", `post ok url=${url} status=${statusCode}`);
6973
+ return;
6974
+ }
6975
+ if (statusCode === 410 || statusCode === 404) {
6976
+ log.warn("streamingEmitter", `session dead (status=${statusCode}) \u2014 disabling emitter`);
6977
+ this.active = false;
6978
+ if (this.tickTimer) {
6979
+ clearInterval(this.tickTimer);
6980
+ this.tickTimer = null;
6981
+ }
6982
+ if (this.answerPollTimer) {
6983
+ clearInterval(this.answerPollTimer);
6984
+ this.answerPollTimer = null;
6985
+ }
6986
+ return;
6987
+ }
6988
+ log.warn(
6989
+ "streamingEmitter",
6990
+ `post failed url=${url} status=${statusCode} attempt=${attempt + 1} body=${resBody.slice(0, 200)}`
6991
+ );
6992
+ } catch (err) {
6993
+ log.warn("streamingEmitter", `post error url=${url} attempt=${attempt + 1}`, err);
6994
+ }
6995
+ if (attempt < MAX_RETRIES2) {
6996
+ await sleep2(RETRY_BACKOFF_MS2 * (attempt + 1));
6997
+ }
6998
+ }
6999
+ }
7000
+ };
7001
+ function sleep2(ms) {
7002
+ return new Promise((r) => setTimeout(r, ms));
7003
+ }
7004
+ var TREE_CONTINUATION_RE = /^\s*└/;
7005
+ function classifyLine(line, parseChrome, runtime) {
7006
+ if (line.trim().length === 0) return null;
7007
+ if (TREE_CONTINUATION_RE.test(line)) return "tool_result";
7008
+ const step = parseChrome?.(line) ?? null;
7009
+ if (step) {
7010
+ if (step.tool === "thinking") return "thinking";
7011
+ return "tool_use";
7012
+ }
7013
+ const filtered = runtime.filterTuiOutput([line]);
7014
+ if (filtered.length === 0) return null;
7015
+ return "text";
7016
+ }
7017
+ function parsePendingAnswerResponse(body) {
7018
+ if (!body) return null;
7019
+ let parsed;
7020
+ try {
7021
+ parsed = JSON.parse(body);
7022
+ } catch {
7023
+ return null;
7024
+ }
7025
+ if (typeof parsed !== "object" || parsed === null) return null;
7026
+ const root = parsed;
7027
+ const candidate = root.data && typeof root.data === "object" ? root.data : root;
7028
+ const questionId = candidate.questionId;
7029
+ const answer = candidate.answer;
7030
+ const optionIndex = candidate.optionIndex;
7031
+ if (typeof questionId !== "string" || typeof answer !== "string") return null;
7032
+ const result = { questionId, answer };
7033
+ if (typeof optionIndex === "number" && Number.isInteger(optionIndex)) {
7034
+ result.optionIndex = optionIndex;
7035
+ }
7036
+ return result;
7037
+ }
7038
+
6583
7039
  // src/commands/start/quota-fetcher.ts
6584
7040
  var inProgress = false;
6585
7041
  function fetchQuotaUsage(runtime, historySvc) {
@@ -6595,13 +7051,13 @@ function fetchQuotaUsage(runtime, historySvc) {
6595
7051
  }
6596
7052
 
6597
7053
  // src/commands/start/keep-alive.ts
6598
- var import_child_process7 = require("child_process");
7054
+ var import_child_process8 = require("child_process");
6599
7055
  function buildKeepAlive(ctx) {
6600
7056
  let timer = null;
6601
7057
  async function setIdleTimeout(minutes) {
6602
7058
  if (!ctx.inCodespace || !ctx.codespaceName) return;
6603
7059
  await new Promise((resolve2) => {
6604
- const proc = (0, import_child_process7.spawn)(
7060
+ const proc = (0, import_child_process8.spawn)(
6605
7061
  "gh",
6606
7062
  [
6607
7063
  "api",
@@ -6641,8 +7097,8 @@ function buildKeepAlive(ctx) {
6641
7097
  var fs14 = __toESM(require("fs"));
6642
7098
  var os13 = __toESM(require("os"));
6643
7099
  var path19 = __toESM(require("path"));
6644
- var import_crypto2 = require("crypto");
6645
- var import_child_process10 = require("child_process");
7100
+ var import_crypto3 = require("crypto");
7101
+ var import_child_process11 = require("child_process");
6646
7102
 
6647
7103
  // src/lib/payload.ts
6648
7104
  var import_zod2 = require("zod");
@@ -6846,11 +7302,11 @@ async function writeProjectFile(rawPath, content) {
6846
7302
  }
6847
7303
 
6848
7304
  // src/services/project-ops.service.ts
6849
- var import_child_process8 = require("child_process");
7305
+ var import_child_process9 = require("child_process");
6850
7306
  var import_util2 = require("util");
6851
7307
  var fs13 = __toESM(require("fs/promises"));
6852
7308
  var path17 = __toESM(require("path"));
6853
- var execFileP2 = (0, import_util2.promisify)(import_child_process8.execFile);
7309
+ var execFileP2 = (0, import_util2.promisify)(import_child_process9.execFile);
6854
7310
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
6855
7311
  "node_modules",
6856
7312
  ".git",
@@ -7190,8 +7646,8 @@ async function jsSearchFiles(opts, cwd, cap) {
7190
7646
  }
7191
7647
 
7192
7648
  // src/services/terminal-ops.service.ts
7193
- var import_child_process9 = require("child_process");
7194
- var import_crypto = require("crypto");
7649
+ var import_child_process10 = require("child_process");
7650
+ var import_crypto2 = require("crypto");
7195
7651
  var import_path = __toESM(require("path"));
7196
7652
  var MAX_CONCURRENT_SESSIONS = 4;
7197
7653
  var nodePtyModule;
@@ -7312,7 +7768,7 @@ function createPythonSession(id, shell, cwd, env, cols, rows) {
7312
7768
  }
7313
7769
  let child;
7314
7770
  try {
7315
- child = (0, import_child_process9.spawn)(python, ["-c", PYTHON_TERMINAL_HELPER, shell], {
7771
+ child = (0, import_child_process10.spawn)(python, ["-c", PYTHON_TERMINAL_HELPER, shell], {
7316
7772
  cwd,
7317
7773
  env: { ...env, COLUMNS: String(cols), LINES: String(rows) },
7318
7774
  stdio: ["pipe", "pipe", "pipe"]
@@ -7367,7 +7823,7 @@ function openTerminal(opts) {
7367
7823
  };
7368
7824
  const cols = Math.max(1, Math.min(opts.cols ?? 80, 500));
7369
7825
  const rows = Math.max(1, Math.min(opts.rows ?? 24, 200));
7370
- const id = (0, import_crypto.randomUUID)();
7826
+ const id = (0, import_crypto2.randomUUID)();
7371
7827
  const ptyMod = loadNodePty2();
7372
7828
  if (ptyMod) {
7373
7829
  try {
@@ -7447,7 +7903,7 @@ function closeTerminal(sessionId) {
7447
7903
  function saveFilesTemp(files) {
7448
7904
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
7449
7905
  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}`);
7906
+ const tmpPath = path19.join(os13.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
7451
7907
  fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
7452
7908
  return tmpPath;
7453
7909
  });
@@ -7579,7 +8035,7 @@ var sessionTerminated = (ctx) => {
7579
8035
  } catch {
7580
8036
  }
7581
8037
  try {
7582
- const proc = (0, import_child_process10.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
8038
+ const proc = (0, import_child_process11.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
7583
8039
  detached: true,
7584
8040
  stdio: "ignore"
7585
8041
  });
@@ -7601,7 +8057,7 @@ var shutdownSession = async (ctx, cmd) => {
7601
8057
  }
7602
8058
  if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
7603
8059
  try {
7604
- const stopProc = (0, import_child_process10.spawn)(
8060
+ const stopProc = (0, import_child_process11.spawn)(
7605
8061
  "bash",
7606
8062
  ["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
7607
8063
  { detached: true, stdio: "ignore" }
@@ -7611,7 +8067,7 @@ var shutdownSession = async (ctx, cmd) => {
7611
8067
  }
7612
8068
  }
7613
8069
  try {
7614
- const proc = (0, import_child_process10.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
8070
+ const proc = (0, import_child_process11.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
7615
8071
  detached: true,
7616
8072
  stdio: "ignore"
7617
8073
  });
@@ -7836,22 +8292,34 @@ async function start(requestedAgent) {
7836
8292
  pluginId,
7837
8293
  pluginAuthToken: session.pluginAuthToken
7838
8294
  }) : null;
8295
+ let streamingEmitter = null;
7839
8296
  const claude = new AgentService(
7840
8297
  runtime,
7841
8298
  {
7842
8299
  cwd,
7843
8300
  onData(raw) {
7844
8301
  outputSvc.push(raw);
8302
+ streamingEmitter?.push(raw);
7845
8303
  },
7846
8304
  onExit(code) {
7847
8305
  process.removeListener("SIGINT", sigintHandler);
7848
8306
  outputSvc.dispose();
7849
8307
  relay.stop();
7850
8308
  void fileWatcher?.stop();
8309
+ void streamingEmitter?.stop();
7851
8310
  process.exit(code);
7852
8311
  }
7853
8312
  }
7854
8313
  );
8314
+ if (session.pluginAuthToken) {
8315
+ streamingEmitter = new StreamingEmitterService({
8316
+ sessionId: session.id,
8317
+ pluginId,
8318
+ pluginAuthToken: session.pluginAuthToken,
8319
+ runtime,
8320
+ ptyInput: claude
8321
+ });
8322
+ }
7855
8323
  const ctx = {
7856
8324
  outputSvc,
7857
8325
  claude,
@@ -7878,6 +8346,7 @@ async function start(requestedAgent) {
7878
8346
  outputSvc.dispose();
7879
8347
  relay.stop();
7880
8348
  void fileWatcher?.stop();
8349
+ void streamingEmitter?.stop();
7881
8350
  process.exit(0);
7882
8351
  }
7883
8352
  process.once("SIGINT", sigintHandler);
@@ -7888,6 +8357,7 @@ async function start(requestedAgent) {
7888
8357
  fileWatcher.start().catch(() => {
7889
8358
  });
7890
8359
  }
8360
+ streamingEmitter?.start();
7891
8361
  setTimeout(() => {
7892
8362
  historySvc.load().catch(() => {
7893
8363
  });
@@ -7896,7 +8366,7 @@ async function start(requestedAgent) {
7896
8366
  }
7897
8367
 
7898
8368
  // src/commands/pair.ts
7899
- var import_crypto3 = require("crypto");
8369
+ var import_crypto4 = require("crypto");
7900
8370
  var import_picocolors3 = __toESM(require("picocolors"));
7901
8371
 
7902
8372
  // src/ui/prompts.ts
@@ -7959,7 +8429,7 @@ async function pair(args2 = []) {
7959
8429
  const flagAgent = parseAgentFlag(args2);
7960
8430
  const agentId = flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
7961
8431
  showIntro();
7962
- const pluginId = (0, import_crypto3.randomUUID)();
8432
+ const pluginId = (0, import_crypto4.randomUUID)();
7963
8433
  const spin = dist_exports.spinner();
7964
8434
  spin.start("Requesting pairing code...");
7965
8435
  const result = await requestCode(pluginId);
@@ -8015,8 +8485,8 @@ async function pair(args2 = []) {
8015
8485
  // src/commands/pair-auto.ts
8016
8486
  var fs15 = __toESM(require("fs"));
8017
8487
  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";
8488
+ var import_crypto5 = require("crypto");
8489
+ var API_BASE7 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
8020
8490
  function fail(msg) {
8021
8491
  console.error(`
8022
8492
  ${msg}
@@ -8048,14 +8518,18 @@ function readTokenFromArgs(args2) {
8048
8518
  fail("codeam pair-auto requires --token-file=<path>, --token=<value>, or CODEAM_AUTO_TOKEN env");
8049
8519
  }
8050
8520
  async function claim(token, pluginId) {
8051
- const url = `${API_BASE6}/api/pairing/claim-auto-token`;
8521
+ const url = `${API_BASE7}/api/pairing/claim-auto-token`;
8052
8522
  const body = {
8053
8523
  token,
8054
8524
  pluginId,
8055
8525
  ideName: "codeam-cli (codespace)",
8056
8526
  ideVersion: process.env.npm_package_version ?? "unknown",
8057
8527
  hostname: os14.hostname(),
8058
- codespaceName: process.env.CODESPACE_NAME ?? ""
8528
+ codespaceName: process.env.CODESPACE_NAME ?? "",
8529
+ // Current git branch of the codespace's working directory, so the
8530
+ // backend can populate `PairedSession.branch` for the codespace pair.
8531
+ // `null` when detached HEAD / not a git repo.
8532
+ branch: detectCurrentBranch()
8059
8533
  };
8060
8534
  const res = await fetch(url, {
8061
8535
  method: "POST",
@@ -8077,7 +8551,7 @@ async function claim(token, pluginId) {
8077
8551
  }
8078
8552
  async function pairAuto(args2) {
8079
8553
  const token = readTokenFromArgs(args2);
8080
- const pluginId = (0, import_crypto4.randomUUID)();
8554
+ const pluginId = (0, import_crypto5.randomUUID)();
8081
8555
  console.log(" Claiming pairing token\u2026");
8082
8556
  const claimed = await claim(token, pluginId);
8083
8557
  if (!isKnownAgentId(claimed.agent)) {
@@ -8203,11 +8677,11 @@ async function logout() {
8203
8677
  var import_picocolors9 = __toESM(require("picocolors"));
8204
8678
 
8205
8679
  // src/services/providers/github-codespaces.ts
8206
- var import_child_process11 = require("child_process");
8680
+ var import_child_process12 = require("child_process");
8207
8681
  var import_util3 = require("util");
8208
8682
  var import_picocolors7 = __toESM(require("picocolors"));
8209
8683
  var path20 = __toESM(require("path"));
8210
- var execFileP3 = (0, import_util3.promisify)(import_child_process11.execFile);
8684
+ var execFileP3 = (0, import_util3.promisify)(import_child_process12.execFile);
8211
8685
  var MAX_BUFFER = 8 * 1024 * 1024;
8212
8686
  function resetStdinForChild() {
8213
8687
  if (process.stdin.isTTY) {
@@ -8251,7 +8725,7 @@ var GitHubCodespacesProvider = class {
8251
8725
  if (!isAuthed) {
8252
8726
  resetStdinForChild();
8253
8727
  await new Promise((resolve2, reject) => {
8254
- const proc = (0, import_child_process11.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
8728
+ const proc = (0, import_child_process12.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
8255
8729
  stdio: "inherit"
8256
8730
  });
8257
8731
  proc.on("exit", (code) => {
@@ -8285,7 +8759,7 @@ var GitHubCodespacesProvider = class {
8285
8759
  wt(noteLines.join("\n"), "One more permission needed");
8286
8760
  resetStdinForChild();
8287
8761
  const refreshCode = await new Promise((resolve2, reject) => {
8288
- const proc = (0, import_child_process11.spawn)(
8762
+ const proc = (0, import_child_process12.spawn)(
8289
8763
  "gh",
8290
8764
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
8291
8765
  { stdio: "inherit" }
@@ -8435,7 +8909,7 @@ var GitHubCodespacesProvider = class {
8435
8909
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
8436
8910
  resetStdinForChild();
8437
8911
  const ok = await new Promise((resolve2) => {
8438
- const proc = (0, import_child_process11.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
8912
+ const proc = (0, import_child_process12.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
8439
8913
  proc.on("exit", (code) => resolve2(code === 0));
8440
8914
  proc.on("error", () => resolve2(false));
8441
8915
  });
@@ -8462,7 +8936,7 @@ var GitHubCodespacesProvider = class {
8462
8936
  );
8463
8937
  resetStdinForChild();
8464
8938
  await new Promise((resolve2, reject) => {
8465
- const proc = (0, import_child_process11.spawn)(
8939
+ const proc = (0, import_child_process12.spawn)(
8466
8940
  "gh",
8467
8941
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
8468
8942
  { stdio: "inherit" }
@@ -8640,7 +9114,7 @@ var GitHubCodespacesProvider = class {
8640
9114
  async streamCommand(workspaceId, command2) {
8641
9115
  resetStdinForChild();
8642
9116
  return new Promise((resolve2, reject) => {
8643
- const proc = (0, import_child_process11.spawn)(
9117
+ const proc = (0, import_child_process12.spawn)(
8644
9118
  "gh",
8645
9119
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
8646
9120
  { stdio: "inherit" }
@@ -8667,11 +9141,11 @@ var GitHubCodespacesProvider = class {
8667
9141
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
8668
9142
  ];
8669
9143
  await new Promise((resolve2, reject) => {
8670
- const tar = (0, import_child_process11.spawn)("tar", tarArgs, {
9144
+ const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
8671
9145
  stdio: ["ignore", "pipe", "pipe"],
8672
9146
  env: tarEnv
8673
9147
  });
8674
- const ssh = (0, import_child_process11.spawn)("gh", sshArgs, {
9148
+ const ssh = (0, import_child_process12.spawn)("gh", sshArgs, {
8675
9149
  stdio: [tar.stdout, "pipe", "pipe"]
8676
9150
  });
8677
9151
  let tarErr = "";
@@ -8705,7 +9179,7 @@ var GitHubCodespacesProvider = class {
8705
9179
  }
8706
9180
  const cmd = parts.join(" && ");
8707
9181
  await new Promise((resolve2, reject) => {
8708
- const proc = (0, import_child_process11.spawn)(
9182
+ const proc = (0, import_child_process12.spawn)(
8709
9183
  "gh",
8710
9184
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
8711
9185
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -8763,11 +9237,11 @@ function shellQuote(s) {
8763
9237
  }
8764
9238
 
8765
9239
  // src/services/providers/gitpod.ts
8766
- var import_child_process12 = require("child_process");
9240
+ var import_child_process13 = require("child_process");
8767
9241
  var import_util4 = require("util");
8768
9242
  var path21 = __toESM(require("path"));
8769
9243
  var import_picocolors8 = __toESM(require("picocolors"));
8770
- var execFileP4 = (0, import_util4.promisify)(import_child_process12.execFile);
9244
+ var execFileP4 = (0, import_util4.promisify)(import_child_process13.execFile);
8771
9245
  var MAX_BUFFER2 = 8 * 1024 * 1024;
8772
9246
  function resetStdinForChild2() {
8773
9247
  if (process.stdin.isTTY) {
@@ -8807,7 +9281,7 @@ var GitpodProvider = class {
8807
9281
  );
8808
9282
  resetStdinForChild2();
8809
9283
  await new Promise((resolve2, reject) => {
8810
- const proc = (0, import_child_process12.spawn)("gitpod", ["login"], { stdio: "inherit" });
9284
+ const proc = (0, import_child_process13.spawn)("gitpod", ["login"], { stdio: "inherit" });
8811
9285
  proc.on("exit", (code) => {
8812
9286
  if (code === 0) resolve2();
8813
9287
  else reject(new Error("gitpod login failed."));
@@ -8959,7 +9433,7 @@ var GitpodProvider = class {
8959
9433
  async streamCommand(workspaceId, command2) {
8960
9434
  resetStdinForChild2();
8961
9435
  return new Promise((resolve2, reject) => {
8962
- const proc = (0, import_child_process12.spawn)(
9436
+ const proc = (0, import_child_process13.spawn)(
8963
9437
  "gitpod",
8964
9438
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
8965
9439
  { stdio: "inherit" }
@@ -8979,11 +9453,11 @@ var GitpodProvider = class {
8979
9453
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
8980
9454
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
8981
9455
  await new Promise((resolve2, reject) => {
8982
- const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
9456
+ const tar = (0, import_child_process13.spawn)("tar", tarArgs, {
8983
9457
  stdio: ["ignore", "pipe", "pipe"],
8984
9458
  env: tarEnv
8985
9459
  });
8986
- const ssh = (0, import_child_process12.spawn)(
9460
+ const ssh = (0, import_child_process13.spawn)(
8987
9461
  "gitpod",
8988
9462
  ["workspace", "ssh", workspaceId, "--", remoteCmd],
8989
9463
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -9015,7 +9489,7 @@ var GitpodProvider = class {
9015
9489
  }
9016
9490
  const cmd = parts.join(" && ");
9017
9491
  await new Promise((resolve2, reject) => {
9018
- const proc = (0, import_child_process12.spawn)(
9492
+ const proc = (0, import_child_process13.spawn)(
9019
9493
  "gitpod",
9020
9494
  ["workspace", "ssh", workspaceId, "--", cmd],
9021
9495
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -9039,10 +9513,10 @@ function shellQuote2(s) {
9039
9513
  }
9040
9514
 
9041
9515
  // src/services/providers/gitlab-workspaces.ts
9042
- var import_child_process13 = require("child_process");
9516
+ var import_child_process14 = require("child_process");
9043
9517
  var import_util5 = require("util");
9044
9518
  var path22 = __toESM(require("path"));
9045
- var execFileP5 = (0, import_util5.promisify)(import_child_process13.execFile);
9519
+ var execFileP5 = (0, import_util5.promisify)(import_child_process14.execFile);
9046
9520
  var MAX_BUFFER3 = 8 * 1024 * 1024;
9047
9521
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
9048
9522
  function resetStdinForChild3() {
@@ -9084,7 +9558,7 @@ var GitLabWorkspacesProvider = class {
9084
9558
  );
9085
9559
  resetStdinForChild3();
9086
9560
  await new Promise((resolve2, reject) => {
9087
- const proc = (0, import_child_process13.spawn)(
9561
+ const proc = (0, import_child_process14.spawn)(
9088
9562
  "glab",
9089
9563
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
9090
9564
  { stdio: "inherit" }
@@ -9256,7 +9730,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9256
9730
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
9257
9731
  resetStdinForChild3();
9258
9732
  return new Promise((resolve2, reject) => {
9259
- const proc = (0, import_child_process13.spawn)(
9733
+ const proc = (0, import_child_process14.spawn)(
9260
9734
  "ssh",
9261
9735
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
9262
9736
  { stdio: "inherit" }
@@ -9277,8 +9751,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9277
9751
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
9278
9752
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
9279
9753
  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)(
9754
+ const tar = (0, import_child_process14.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
9755
+ const ssh = (0, import_child_process14.spawn)(
9282
9756
  "ssh",
9283
9757
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
9284
9758
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -9308,7 +9782,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
9308
9782
  }
9309
9783
  const cmd = parts.join(" && ");
9310
9784
  await new Promise((resolve2, reject) => {
9311
- const proc = (0, import_child_process13.spawn)(
9785
+ const proc = (0, import_child_process14.spawn)(
9312
9786
  "ssh",
9313
9787
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
9314
9788
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -9367,10 +9841,10 @@ function shellQuote3(s) {
9367
9841
  }
9368
9842
 
9369
9843
  // src/services/providers/railway.ts
9370
- var import_child_process14 = require("child_process");
9844
+ var import_child_process15 = require("child_process");
9371
9845
  var import_util6 = require("util");
9372
9846
  var path23 = __toESM(require("path"));
9373
- var execFileP6 = (0, import_util6.promisify)(import_child_process14.execFile);
9847
+ var execFileP6 = (0, import_util6.promisify)(import_child_process15.execFile);
9374
9848
  var MAX_BUFFER4 = 8 * 1024 * 1024;
9375
9849
  function resetStdinForChild4() {
9376
9850
  if (process.stdin.isTTY) {
@@ -9411,7 +9885,7 @@ var RailwayProvider = class {
9411
9885
  );
9412
9886
  resetStdinForChild4();
9413
9887
  await new Promise((resolve2, reject) => {
9414
- const proc = (0, import_child_process14.spawn)("railway", ["login"], { stdio: "inherit" });
9888
+ const proc = (0, import_child_process15.spawn)("railway", ["login"], { stdio: "inherit" });
9415
9889
  proc.on("exit", (code) => {
9416
9890
  if (code === 0) resolve2();
9417
9891
  else reject(new Error("railway login failed."));
@@ -9554,7 +10028,7 @@ var RailwayProvider = class {
9554
10028
  }
9555
10029
  resetStdinForChild4();
9556
10030
  return new Promise((resolve2, reject) => {
9557
- const proc = (0, import_child_process14.spawn)(
10031
+ const proc = (0, import_child_process15.spawn)(
9558
10032
  "railway",
9559
10033
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
9560
10034
  { stdio: "inherit" }
@@ -9578,8 +10052,8 @@ var RailwayProvider = class {
9578
10052
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
9579
10053
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
9580
10054
  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)(
10055
+ const tar = (0, import_child_process15.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
10056
+ const sh = (0, import_child_process15.spawn)(
9583
10057
  "railway",
9584
10058
  ["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
9585
10059
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -9612,7 +10086,7 @@ var RailwayProvider = class {
9612
10086
  }
9613
10087
  const cmd = parts.join(" && ");
9614
10088
  await new Promise((resolve2, reject) => {
9615
- const proc = (0, import_child_process14.spawn)(
10089
+ const proc = (0, import_child_process15.spawn)(
9616
10090
  "railway",
9617
10091
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
9618
10092
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -10147,7 +10621,7 @@ async function stopWorkspaceFromLocal(target) {
10147
10621
  // src/commands/version.ts
10148
10622
  var import_picocolors11 = __toESM(require("picocolors"));
10149
10623
  function version() {
10150
- const v = true ? "2.15.5" : "unknown";
10624
+ const v = true ? "2.15.6" : "unknown";
10151
10625
  console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
10152
10626
  }
10153
10627
 
@@ -10191,7 +10665,7 @@ function help() {
10191
10665
  var fs16 = __toESM(require("fs"));
10192
10666
  var os15 = __toESM(require("os"));
10193
10667
  var path24 = __toESM(require("path"));
10194
- var https6 = __toESM(require("https"));
10668
+ var https7 = __toESM(require("https"));
10195
10669
  var import_picocolors13 = __toESM(require("picocolors"));
10196
10670
  var PKG_NAME = "codeam-cli";
10197
10671
  var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
@@ -10234,7 +10708,7 @@ function compareSemver(a, b) {
10234
10708
  }
10235
10709
  function fetchLatest() {
10236
10710
  return new Promise((resolve2) => {
10237
- const req = https6.get(
10711
+ const req = https7.get(
10238
10712
  REGISTRY_URL,
10239
10713
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
10240
10714
  (res) => {
@@ -10286,7 +10760,7 @@ function checkForUpdates() {
10286
10760
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
10287
10761
  if (process.env.CI) return;
10288
10762
  if (!process.stdout.isTTY) return;
10289
- const current = true ? "2.15.5" : null;
10763
+ const current = true ? "2.15.6" : null;
10290
10764
  if (!current) return;
10291
10765
  const cache = readCache();
10292
10766
  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.6",
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",