ai-sdk-provider-codex-cli 1.2.2 → 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/README.md CHANGED
@@ -54,7 +54,7 @@ npm i ai ai-sdk-provider-codex-cli
54
54
  npm i ai@^5.0.0 ai-sdk-provider-codex-cli@ai-sdk-v5
55
55
  ```
56
56
 
57
- > **⚠️ Codex CLI Version**: Requires the current stable Codex CLI **0.130.x** for full support of both provider modes (`codexExec` and `codexAppServer`). This package pins its optional `@openai/codex` dependency to `^0.130.0`, the latest non-alpha release validated for this maintenance update. If you supply your own Codex CLI (global install or custom `codexPath`), check it with `codex --version` and upgrade if needed.
57
+ > **⚠️ Codex CLI Version**: Requires the current stable Codex CLI **0.144.x** for full support of both provider modes (`codexExec` and `codexAppServer`). This package pins its optional `@openai/codex` dependency to `^0.144.0`, the latest non-alpha release line validated for this maintenance update. If you supply your own Codex CLI (global install or custom `codexPath`), check it with `codex --version` and upgrade if needed.
58
58
  >
59
59
  > ```bash
60
60
  > npm i -g @openai/codex@latest
@@ -90,7 +90,7 @@ import { createCodexAppServer } from 'ai-sdk-provider-codex-cli';
90
90
 
91
91
  const provider = createCodexAppServer({
92
92
  defaultSettings: {
93
- minCodexVersion: '0.130.0',
93
+ minCodexVersion: '0.144.0',
94
94
  autoApprove: false,
95
95
  personality: 'pragmatic',
96
96
  },
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;
@@ -4667,7 +4668,7 @@ var AppServerRpcClient = class extends events.EventEmitter {
4667
4668
  if (this.serverCapabilities?.modelList === false) {
4668
4669
  throw new UnsupportedFeatureError({
4669
4670
  feature: "model/list",
4670
- minCodexVersion: this.settings.minCodexVersion ?? "0.130.0",
4671
+ minCodexVersion: this.settings.minCodexVersion ?? "0.144.0",
4671
4672
  serverVersion: this.serverVersion
4672
4673
  });
4673
4674
  }
@@ -4680,7 +4681,7 @@ var AppServerRpcClient = class extends events.EventEmitter {
4680
4681
  if (error instanceof JsonRpcRequestError && error.code === -32601) {
4681
4682
  throw new UnsupportedFeatureError({
4682
4683
  feature: "model/list",
4683
- minCodexVersion: this.settings.minCodexVersion ?? "0.130.0",
4684
+ minCodexVersion: this.settings.minCodexVersion ?? "0.144.0",
4684
4685
  serverVersion: this.serverVersion
4685
4686
  });
4686
4687
  }
@@ -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.130.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,
@@ -4859,7 +4894,7 @@ var AppServerRpcClient = class extends events.EventEmitter {
4859
4894
  return;
4860
4895
  }
4861
4896
  this.serverVersion = detected;
4862
- const minVersion = this.settings.minCodexVersion ?? "0.130.0";
4897
+ const minVersion = this.settings.minCodexVersion ?? "0.144.0";
4863
4898
  const compared = compareSemver(detected, minVersion);
4864
4899
  if (compared === void 0) {
4865
4900
  this.logger.warn(
@@ -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.d.cts CHANGED
@@ -722,7 +722,7 @@ interface CodexAppServerProvider extends ProviderV3 {
722
722
  * @example
723
723
  * ```ts
724
724
  * const provider = createCodexAppServer({
725
- * defaultSettings: { minCodexVersion: '0.130.0' },
725
+ * defaultSettings: { minCodexVersion: '0.144.0' },
726
726
  * });
727
727
  *
728
728
  * try {
package/dist/index.d.ts CHANGED
@@ -722,7 +722,7 @@ interface CodexAppServerProvider extends ProviderV3 {
722
722
  * @example
723
723
  * ```ts
724
724
  * const provider = createCodexAppServer({
725
- * defaultSettings: { minCodexVersion: '0.130.0' },
725
+ * defaultSettings: { minCodexVersion: '0.144.0' },
726
726
  * });
727
727
  *
728
728
  * try {
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;
@@ -4659,7 +4660,7 @@ var AppServerRpcClient = class extends EventEmitter {
4659
4660
  if (this.serverCapabilities?.modelList === false) {
4660
4661
  throw new UnsupportedFeatureError({
4661
4662
  feature: "model/list",
4662
- minCodexVersion: this.settings.minCodexVersion ?? "0.130.0",
4663
+ minCodexVersion: this.settings.minCodexVersion ?? "0.144.0",
4663
4664
  serverVersion: this.serverVersion
4664
4665
  });
4665
4666
  }
@@ -4672,7 +4673,7 @@ var AppServerRpcClient = class extends EventEmitter {
4672
4673
  if (error instanceof JsonRpcRequestError && error.code === -32601) {
4673
4674
  throw new UnsupportedFeatureError({
4674
4675
  feature: "model/list",
4675
- minCodexVersion: this.settings.minCodexVersion ?? "0.130.0",
4676
+ minCodexVersion: this.settings.minCodexVersion ?? "0.144.0",
4676
4677
  serverVersion: this.serverVersion
4677
4678
  });
4678
4679
  }
@@ -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.130.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,
@@ -4851,7 +4886,7 @@ var AppServerRpcClient = class extends EventEmitter {
4851
4886
  return;
4852
4887
  }
4853
4888
  this.serverVersion = detected;
4854
- const minVersion = this.settings.minCodexVersion ?? "0.130.0";
4889
+ const minVersion = this.settings.minCodexVersion ?? "0.144.0";
4855
4890
  const compared = compareSemver(detected, minVersion);
4856
4891
  if (compared === void 0) {
4857
4892
  this.logger.warn(
@@ -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.2.2",
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",
@@ -67,7 +67,7 @@
67
67
  "jsonc-parser": "^3.3.1"
68
68
  },
69
69
  "optionalDependencies": {
70
- "@openai/codex": "^0.130.0"
70
+ "@openai/codex": "^0.144.0"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@eslint/js": "^9.14.0",