ai-sdk-provider-codex-cli 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4573,6 +4573,7 @@ var AppServerRpcClient = class extends events.EventEmitter {
4573
4573
  activeRequestContextsByTurn = /* @__PURE__ */ new Map();
4574
4574
  completedTurnIds = /* @__PURE__ */ new Set();
4575
4575
  lastStderr = "";
4576
+ lastCrashHadStderr = false;
4576
4577
  idleTimer;
4577
4578
  serverCapabilities;
4578
4579
  expectedExitSignal;
@@ -4779,6 +4780,7 @@ var AppServerRpcClient = class extends events.EventEmitter {
4779
4780
  const base = resolveCodexPath2(this.settings.codexPath);
4780
4781
  const args = [...base.args, "app-server", "--listen", "stdio://"];
4781
4782
  this.lastStderr = "";
4783
+ this.lastCrashHadStderr = false;
4782
4784
  this.expectedExitSignal = void 0;
4783
4785
  this.child = child_process.spawn(base.cmd, args, {
4784
4786
  stdio: ["pipe", "pipe", "pipe"],
@@ -4789,18 +4791,23 @@ var AppServerRpcClient = class extends events.EventEmitter {
4789
4791
  },
4790
4792
  cwd: this.settings.cwd
4791
4793
  });
4792
- this.child.stderr.setEncoding("utf8");
4793
- this.child.stderr.on("data", (chunk) => {
4794
- this.lastStderr += String(chunk);
4795
- if (this.lastStderr.length > 4e3) {
4796
- this.lastStderr = this.lastStderr.slice(-4e3);
4794
+ const child = this.child;
4795
+ let stderrBuf = "";
4796
+ child.stderr.setEncoding("utf8");
4797
+ child.stderr.on("data", (chunk) => {
4798
+ stderrBuf = (stderrBuf + String(chunk)).slice(-4e3);
4799
+ if (this.child === child) {
4800
+ this.lastStderr = stderrBuf;
4797
4801
  }
4798
4802
  });
4799
- this.child.on("error", (error) => {
4803
+ child.on("error", (error) => {
4800
4804
  this.logger.error(`[codex-app-server] process error: ${String(error)}`);
4801
- this.handleCrash(error);
4805
+ const base2 = String(error);
4806
+ const message = this.withStderrTail(base2, stderrBuf);
4807
+ this.lastCrashHadStderr = message !== base2;
4808
+ this.handleCrash(new Error(message));
4802
4809
  });
4803
- this.child.on("exit", (code, signal) => {
4810
+ child.on("exit", (code, signal) => {
4804
4811
  const message = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
4805
4812
  const expected = this.state === "closed" || signal !== null && signal === this.expectedExitSignal;
4806
4813
  this.expectedExitSignal = void 0;
@@ -4808,11 +4815,29 @@ var AppServerRpcClient = class extends events.EventEmitter {
4808
4815
  this.logger.info(`[codex-app-server] ${message}`);
4809
4816
  return;
4810
4817
  }
4811
- this.logger.warn(`[codex-app-server] ${message}`);
4812
- this.handleCrash(new Error(message));
4818
+ const crashedPending = this.markCrashed();
4819
+ if (!crashedPending) return;
4820
+ let settled = false;
4821
+ const finish = () => {
4822
+ if (settled) return;
4823
+ settled = true;
4824
+ child.off("close", finish);
4825
+ clearTimeout(timer);
4826
+ const base2 = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
4827
+ const crashMessage = this.withStderrTail(base2, stderrBuf);
4828
+ if (this.child === void 0) {
4829
+ this.lastStderr = stderrBuf;
4830
+ this.lastCrashHadStderr = crashMessage !== base2;
4831
+ }
4832
+ this.logger.warn(`[codex-app-server] ${crashMessage}`);
4833
+ this.rejectCrashedPending(crashedPending, new Error(crashMessage));
4834
+ };
4835
+ child.once("close", finish);
4836
+ const timer = setTimeout(finish, 120);
4837
+ timer.unref?.();
4813
4838
  });
4814
4839
  this.stdoutReader = readline__default.default.createInterface({
4815
- input: this.child.stdout,
4840
+ input: child.stdout,
4816
4841
  crlfDelay: Infinity
4817
4842
  });
4818
4843
  this.stdoutReader.on("line", (line) => this.handleLine(line));
@@ -4834,12 +4859,22 @@ var AppServerRpcClient = class extends events.EventEmitter {
4834
4859
  this.settings.connectionTimeoutMs ?? this.requestTimeoutMs
4835
4860
  );
4836
4861
  } catch (error) {
4837
- const message = String(error?.message ?? error);
4838
- if (message.includes("ENOENT") || message.includes("unknown subcommand")) {
4862
+ const raw = String(error?.message ?? error);
4863
+ if (raw.includes("unknown subcommand") || this.lastStderr.includes("unknown subcommand")) {
4864
+ throw new Error(
4865
+ this.withStderrTail(
4866
+ "codex app-server requires codex CLI >= 0.144.0. Run 'codex --version' to check."
4867
+ )
4868
+ );
4869
+ }
4870
+ if (raw.includes("ENOENT") || this.lastStderr.includes("ENOENT")) {
4839
4871
  throw new Error(
4840
- "codex app-server requires codex CLI >= 0.144.0. Run 'codex --version' to check."
4872
+ this.withStderrTail(
4873
+ "codex app-server failed to start: codex executable not found (ENOENT). Check that the codex CLI is installed and its native binary is intact \u2014 a corrupted @openai/codex npm install can cause this. Run 'codex --version' to verify."
4874
+ )
4841
4875
  );
4842
4876
  }
4877
+ const message = this.lastCrashHadStderr ? raw : this.withStderrTail(raw);
4843
4878
  throw createAPICallError({
4844
4879
  message: `Failed to initialize codex app-server: ${message}`,
4845
4880
  stderr: this.lastStderr,
@@ -4901,8 +4936,8 @@ var AppServerRpcClient = class extends events.EventEmitter {
4901
4936
  }
4902
4937
  this.writeQueue = Promise.resolve();
4903
4938
  }
4904
- handleCrash(error) {
4905
- if (this.state === "closed") return;
4939
+ markCrashed() {
4940
+ if (this.state === "closed" || this.state === "error") return void 0;
4906
4941
  this.state = "error";
4907
4942
  this.clearIdleTimer();
4908
4943
  this.stdoutReader?.close();
@@ -4912,11 +4947,9 @@ var AppServerRpcClient = class extends events.EventEmitter {
4912
4947
  this.child.kill("SIGTERM");
4913
4948
  this.child = void 0;
4914
4949
  }
4915
- for (const [id, pending] of this.pending.entries()) {
4916
- clearTimeout(pending.timer);
4917
- pending.reject(
4918
- new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
4919
- );
4950
+ const pending = new Map(this.pending);
4951
+ for (const [, entry] of pending) {
4952
+ clearTimeout(entry.timer);
4920
4953
  }
4921
4954
  this.pending.clear();
4922
4955
  this.threadLocks.clear();
@@ -4926,6 +4959,41 @@ var AppServerRpcClient = class extends events.EventEmitter {
4926
4959
  this.completedTurnIds.clear();
4927
4960
  this.serverCapabilities = void 0;
4928
4961
  this.writeQueue = Promise.resolve();
4962
+ return pending;
4963
+ }
4964
+ rejectCrashedPending(pending, error) {
4965
+ for (const [id, entry] of pending) {
4966
+ entry.reject(
4967
+ new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
4968
+ );
4969
+ }
4970
+ }
4971
+ handleCrash(error) {
4972
+ const pending = this.markCrashed();
4973
+ if (!pending) return;
4974
+ this.rejectCrashedPending(pending, error);
4975
+ }
4976
+ stderrExcerpt(raw) {
4977
+ if (!raw) return "";
4978
+ const escape = String.fromCharCode(27);
4979
+ const ansiEscapeSequence = new RegExp(
4980
+ `${escape}(?:\\[[0-?]*[ -/]*[@-~]|\\][^\\u0007]*(?:\\u0007|${escape}\\\\))`,
4981
+ "g"
4982
+ );
4983
+ const withoutAnsi = raw.replace(ansiEscapeSequence, "");
4984
+ const withoutControls = Array.from(withoutAnsi).filter((character) => {
4985
+ const code = character.charCodeAt(0);
4986
+ return character === "\n" || code >= 32 && (code < 127 || code > 159);
4987
+ }).join("");
4988
+ const excerpt = withoutControls.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-5).join("; ");
4989
+ if (excerpt.length > 600) {
4990
+ return `\u2026${excerpt.slice(-600)}`;
4991
+ }
4992
+ return excerpt;
4993
+ }
4994
+ withStderrTail(message, raw = this.lastStderr) {
4995
+ const excerpt = this.stderrExcerpt(raw);
4996
+ return excerpt ? `${message} | stderr (tail): ${excerpt}` : message;
4929
4997
  }
4930
4998
  handleLine(line) {
4931
4999
  const trimmed = line.trim();
package/dist/index.js CHANGED
@@ -4565,6 +4565,7 @@ var AppServerRpcClient = class extends EventEmitter {
4565
4565
  activeRequestContextsByTurn = /* @__PURE__ */ new Map();
4566
4566
  completedTurnIds = /* @__PURE__ */ new Set();
4567
4567
  lastStderr = "";
4568
+ lastCrashHadStderr = false;
4568
4569
  idleTimer;
4569
4570
  serverCapabilities;
4570
4571
  expectedExitSignal;
@@ -4771,6 +4772,7 @@ var AppServerRpcClient = class extends EventEmitter {
4771
4772
  const base = resolveCodexPath2(this.settings.codexPath);
4772
4773
  const args = [...base.args, "app-server", "--listen", "stdio://"];
4773
4774
  this.lastStderr = "";
4775
+ this.lastCrashHadStderr = false;
4774
4776
  this.expectedExitSignal = void 0;
4775
4777
  this.child = spawn(base.cmd, args, {
4776
4778
  stdio: ["pipe", "pipe", "pipe"],
@@ -4781,18 +4783,23 @@ var AppServerRpcClient = class extends EventEmitter {
4781
4783
  },
4782
4784
  cwd: this.settings.cwd
4783
4785
  });
4784
- this.child.stderr.setEncoding("utf8");
4785
- this.child.stderr.on("data", (chunk) => {
4786
- this.lastStderr += String(chunk);
4787
- if (this.lastStderr.length > 4e3) {
4788
- this.lastStderr = this.lastStderr.slice(-4e3);
4786
+ const child = this.child;
4787
+ let stderrBuf = "";
4788
+ child.stderr.setEncoding("utf8");
4789
+ child.stderr.on("data", (chunk) => {
4790
+ stderrBuf = (stderrBuf + String(chunk)).slice(-4e3);
4791
+ if (this.child === child) {
4792
+ this.lastStderr = stderrBuf;
4789
4793
  }
4790
4794
  });
4791
- this.child.on("error", (error) => {
4795
+ child.on("error", (error) => {
4792
4796
  this.logger.error(`[codex-app-server] process error: ${String(error)}`);
4793
- this.handleCrash(error);
4797
+ const base2 = String(error);
4798
+ const message = this.withStderrTail(base2, stderrBuf);
4799
+ this.lastCrashHadStderr = message !== base2;
4800
+ this.handleCrash(new Error(message));
4794
4801
  });
4795
- this.child.on("exit", (code, signal) => {
4802
+ child.on("exit", (code, signal) => {
4796
4803
  const message = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
4797
4804
  const expected = this.state === "closed" || signal !== null && signal === this.expectedExitSignal;
4798
4805
  this.expectedExitSignal = void 0;
@@ -4800,11 +4807,29 @@ var AppServerRpcClient = class extends EventEmitter {
4800
4807
  this.logger.info(`[codex-app-server] ${message}`);
4801
4808
  return;
4802
4809
  }
4803
- this.logger.warn(`[codex-app-server] ${message}`);
4804
- this.handleCrash(new Error(message));
4810
+ const crashedPending = this.markCrashed();
4811
+ if (!crashedPending) return;
4812
+ let settled = false;
4813
+ const finish = () => {
4814
+ if (settled) return;
4815
+ settled = true;
4816
+ child.off("close", finish);
4817
+ clearTimeout(timer);
4818
+ const base2 = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
4819
+ const crashMessage = this.withStderrTail(base2, stderrBuf);
4820
+ if (this.child === void 0) {
4821
+ this.lastStderr = stderrBuf;
4822
+ this.lastCrashHadStderr = crashMessage !== base2;
4823
+ }
4824
+ this.logger.warn(`[codex-app-server] ${crashMessage}`);
4825
+ this.rejectCrashedPending(crashedPending, new Error(crashMessage));
4826
+ };
4827
+ child.once("close", finish);
4828
+ const timer = setTimeout(finish, 120);
4829
+ timer.unref?.();
4805
4830
  });
4806
4831
  this.stdoutReader = readline.createInterface({
4807
- input: this.child.stdout,
4832
+ input: child.stdout,
4808
4833
  crlfDelay: Infinity
4809
4834
  });
4810
4835
  this.stdoutReader.on("line", (line) => this.handleLine(line));
@@ -4826,12 +4851,22 @@ var AppServerRpcClient = class extends EventEmitter {
4826
4851
  this.settings.connectionTimeoutMs ?? this.requestTimeoutMs
4827
4852
  );
4828
4853
  } catch (error) {
4829
- const message = String(error?.message ?? error);
4830
- if (message.includes("ENOENT") || message.includes("unknown subcommand")) {
4854
+ const raw = String(error?.message ?? error);
4855
+ if (raw.includes("unknown subcommand") || this.lastStderr.includes("unknown subcommand")) {
4856
+ throw new Error(
4857
+ this.withStderrTail(
4858
+ "codex app-server requires codex CLI >= 0.144.0. Run 'codex --version' to check."
4859
+ )
4860
+ );
4861
+ }
4862
+ if (raw.includes("ENOENT") || this.lastStderr.includes("ENOENT")) {
4831
4863
  throw new Error(
4832
- "codex app-server requires codex CLI >= 0.144.0. Run 'codex --version' to check."
4864
+ this.withStderrTail(
4865
+ "codex app-server failed to start: codex executable not found (ENOENT). Check that the codex CLI is installed and its native binary is intact \u2014 a corrupted @openai/codex npm install can cause this. Run 'codex --version' to verify."
4866
+ )
4833
4867
  );
4834
4868
  }
4869
+ const message = this.lastCrashHadStderr ? raw : this.withStderrTail(raw);
4835
4870
  throw createAPICallError({
4836
4871
  message: `Failed to initialize codex app-server: ${message}`,
4837
4872
  stderr: this.lastStderr,
@@ -4893,8 +4928,8 @@ var AppServerRpcClient = class extends EventEmitter {
4893
4928
  }
4894
4929
  this.writeQueue = Promise.resolve();
4895
4930
  }
4896
- handleCrash(error) {
4897
- if (this.state === "closed") return;
4931
+ markCrashed() {
4932
+ if (this.state === "closed" || this.state === "error") return void 0;
4898
4933
  this.state = "error";
4899
4934
  this.clearIdleTimer();
4900
4935
  this.stdoutReader?.close();
@@ -4904,11 +4939,9 @@ var AppServerRpcClient = class extends EventEmitter {
4904
4939
  this.child.kill("SIGTERM");
4905
4940
  this.child = void 0;
4906
4941
  }
4907
- for (const [id, pending] of this.pending.entries()) {
4908
- clearTimeout(pending.timer);
4909
- pending.reject(
4910
- new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
4911
- );
4942
+ const pending = new Map(this.pending);
4943
+ for (const [, entry] of pending) {
4944
+ clearTimeout(entry.timer);
4912
4945
  }
4913
4946
  this.pending.clear();
4914
4947
  this.threadLocks.clear();
@@ -4918,6 +4951,41 @@ var AppServerRpcClient = class extends EventEmitter {
4918
4951
  this.completedTurnIds.clear();
4919
4952
  this.serverCapabilities = void 0;
4920
4953
  this.writeQueue = Promise.resolve();
4954
+ return pending;
4955
+ }
4956
+ rejectCrashedPending(pending, error) {
4957
+ for (const [id, entry] of pending) {
4958
+ entry.reject(
4959
+ new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
4960
+ );
4961
+ }
4962
+ }
4963
+ handleCrash(error) {
4964
+ const pending = this.markCrashed();
4965
+ if (!pending) return;
4966
+ this.rejectCrashedPending(pending, error);
4967
+ }
4968
+ stderrExcerpt(raw) {
4969
+ if (!raw) return "";
4970
+ const escape = String.fromCharCode(27);
4971
+ const ansiEscapeSequence = new RegExp(
4972
+ `${escape}(?:\\[[0-?]*[ -/]*[@-~]|\\][^\\u0007]*(?:\\u0007|${escape}\\\\))`,
4973
+ "g"
4974
+ );
4975
+ const withoutAnsi = raw.replace(ansiEscapeSequence, "");
4976
+ const withoutControls = Array.from(withoutAnsi).filter((character) => {
4977
+ const code = character.charCodeAt(0);
4978
+ return character === "\n" || code >= 32 && (code < 127 || code > 159);
4979
+ }).join("");
4980
+ const excerpt = withoutControls.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-5).join("; ");
4981
+ if (excerpt.length > 600) {
4982
+ return `\u2026${excerpt.slice(-600)}`;
4983
+ }
4984
+ return excerpt;
4985
+ }
4986
+ withStderrTail(message, raw = this.lastStderr) {
4987
+ const excerpt = this.stderrExcerpt(raw);
4988
+ return excerpt ? `${message} | stderr (tail): ${excerpt}` : message;
4921
4989
  }
4922
4990
  handleLine(line) {
4923
4991
  const trimmed = line.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-sdk-provider-codex-cli",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "AI SDK v6 provider for OpenAI Codex CLI (use ChatGPT Plus/Pro subscription)",
5
5
  "keywords": [
6
6
  "ai-sdk",