ai-sdk-provider-codex-cli 2.1.0 → 2.1.2
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.d.ts +14 -0
- package/dist/index.js +133 -27
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -900,7 +900,21 @@ declare class ExecLanguageModel implements LanguageModelV4 {
|
|
|
900
900
|
private mergeSettings;
|
|
901
901
|
private getItemType;
|
|
902
902
|
private buildArgs;
|
|
903
|
+
/**
|
|
904
|
+
* Apply MCP server settings as `-c` config overrides.
|
|
905
|
+
*
|
|
906
|
+
* Returns environment variables that must be set on the spawned codex
|
|
907
|
+
* process. Inline HTTP `bearerToken` secrets are routed through the child
|
|
908
|
+
* environment (via `bearer_token_env_var`) instead of argv, because command
|
|
909
|
+
* lines are readable by any local process (`ps`, /proc/<pid>/cmdline).
|
|
910
|
+
*/
|
|
903
911
|
private applyMcpSettings;
|
|
912
|
+
/**
|
|
913
|
+
* Pick a child-env variable name to carry an inline MCP bearer token,
|
|
914
|
+
* avoiding collisions between server names that normalize identically
|
|
915
|
+
* (e.g. 'foo-bar' and 'foo_bar').
|
|
916
|
+
*/
|
|
917
|
+
private allocateBearerTokenEnvVar;
|
|
904
918
|
private addConfigOverride;
|
|
905
919
|
/**
|
|
906
920
|
* Serialize a config override value into a CLI-safe string.
|
package/dist/index.js
CHANGED
|
@@ -1443,7 +1443,7 @@ var ExecLanguageModel = class {
|
|
|
1443
1443
|
if (settings.webSearch) {
|
|
1444
1444
|
args.push("-c", "tools.web_search=true");
|
|
1445
1445
|
}
|
|
1446
|
-
this.applyMcpSettings(args, settings);
|
|
1446
|
+
const mcpSecretEnv = this.applyMcpSettings(args, settings);
|
|
1447
1447
|
if (settings.color) {
|
|
1448
1448
|
args.push("--color", settings.color);
|
|
1449
1449
|
}
|
|
@@ -1491,6 +1491,7 @@ var ExecLanguageModel = class {
|
|
|
1491
1491
|
const env = {
|
|
1492
1492
|
...process.env,
|
|
1493
1493
|
...settings.env || {},
|
|
1494
|
+
...mcpSecretEnv,
|
|
1494
1495
|
RUST_LOG: process.env.RUST_LOG || "error"
|
|
1495
1496
|
};
|
|
1496
1497
|
let lastMessagePath = settings.outputLastMessageFile;
|
|
@@ -1516,11 +1517,20 @@ var ExecLanguageModel = class {
|
|
|
1516
1517
|
tempImagePaths: tempImagePaths.length > 0 ? tempImagePaths : void 0
|
|
1517
1518
|
};
|
|
1518
1519
|
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Apply MCP server settings as `-c` config overrides.
|
|
1522
|
+
*
|
|
1523
|
+
* Returns environment variables that must be set on the spawned codex
|
|
1524
|
+
* process. Inline HTTP `bearerToken` secrets are routed through the child
|
|
1525
|
+
* environment (via `bearer_token_env_var`) instead of argv, because command
|
|
1526
|
+
* lines are readable by any local process (`ps`, /proc/<pid>/cmdline).
|
|
1527
|
+
*/
|
|
1519
1528
|
applyMcpSettings(args, settings) {
|
|
1520
1529
|
if (settings.rmcpClient) {
|
|
1521
1530
|
this.addConfigOverride(args, "features.rmcp_client", true);
|
|
1522
1531
|
}
|
|
1523
|
-
if (!settings.mcpServers) return;
|
|
1532
|
+
if (!settings.mcpServers) return void 0;
|
|
1533
|
+
const secretEnv = {};
|
|
1524
1534
|
for (const [rawName, server] of Object.entries(settings.mcpServers)) {
|
|
1525
1535
|
const name = assertValidMcpServerName(rawName);
|
|
1526
1536
|
const prefix = `mcp_servers.${name}`;
|
|
@@ -1546,15 +1556,43 @@ var ExecLanguageModel = class {
|
|
|
1546
1556
|
if (server.cwd) this.addConfigOverride(args, `${prefix}.cwd`, server.cwd);
|
|
1547
1557
|
} else {
|
|
1548
1558
|
this.addConfigOverride(args, `${prefix}.url`, server.url);
|
|
1549
|
-
|
|
1559
|
+
const hasExplicitAuthorizationHeader = Object.keys(server.httpHeaders ?? {}).some(
|
|
1560
|
+
(key) => key.toLowerCase() === "authorization"
|
|
1561
|
+
);
|
|
1562
|
+
if (server.bearerTokenEnvVar) {
|
|
1550
1563
|
this.addConfigOverride(args, `${prefix}.bearer_token_env_var`, server.bearerTokenEnvVar);
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1564
|
+
if (server.bearerToken !== void 0) {
|
|
1565
|
+
this.logger.warn(
|
|
1566
|
+
`[codex-cli] MCP server '${name}': both bearerToken and bearerTokenEnvVar are set; using bearerTokenEnvVar and ignoring the inline token.`
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
} else if (server.bearerToken !== void 0 && !hasExplicitAuthorizationHeader) {
|
|
1570
|
+
const envVarName = this.allocateBearerTokenEnvVar(name, secretEnv);
|
|
1571
|
+
secretEnv[envVarName] = server.bearerToken;
|
|
1572
|
+
this.addConfigOverride(args, `${prefix}.bearer_token_env_var`, envVarName);
|
|
1573
|
+
}
|
|
1574
|
+
if (server.httpHeaders !== void 0)
|
|
1575
|
+
this.addConfigOverride(args, `${prefix}.http_headers`, server.httpHeaders);
|
|
1554
1576
|
if (server.envHttpHeaders !== void 0)
|
|
1555
1577
|
this.addConfigOverride(args, `${prefix}.env_http_headers`, server.envHttpHeaders);
|
|
1556
1578
|
}
|
|
1557
1579
|
}
|
|
1580
|
+
return Object.keys(secretEnv).length > 0 ? secretEnv : void 0;
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Pick a child-env variable name to carry an inline MCP bearer token,
|
|
1584
|
+
* avoiding collisions between server names that normalize identically
|
|
1585
|
+
* (e.g. 'foo-bar' and 'foo_bar').
|
|
1586
|
+
*/
|
|
1587
|
+
allocateBearerTokenEnvVar(serverName, secretEnv) {
|
|
1588
|
+
const base = `CODEX_MCP_${serverName.toUpperCase().replace(/-/g, "_")}_BEARER_TOKEN`;
|
|
1589
|
+
let candidate = base;
|
|
1590
|
+
let suffix = 2;
|
|
1591
|
+
while (candidate in secretEnv) {
|
|
1592
|
+
candidate = `${base}_${suffix}`;
|
|
1593
|
+
suffix += 1;
|
|
1594
|
+
}
|
|
1595
|
+
return candidate;
|
|
1558
1596
|
}
|
|
1559
1597
|
addConfigOverride(args, key, value) {
|
|
1560
1598
|
assertValidConfigOverrideKey(key);
|
|
@@ -4827,6 +4865,7 @@ var AppServerRpcClient = class extends EventEmitter {
|
|
|
4827
4865
|
activeRequestContextsByTurn = /* @__PURE__ */ new Map();
|
|
4828
4866
|
completedTurnIds = /* @__PURE__ */ new Set();
|
|
4829
4867
|
lastStderr = "";
|
|
4868
|
+
lastCrashHadStderr = false;
|
|
4830
4869
|
idleTimer;
|
|
4831
4870
|
serverCapabilities;
|
|
4832
4871
|
expectedExitSignal;
|
|
@@ -5033,6 +5072,7 @@ var AppServerRpcClient = class extends EventEmitter {
|
|
|
5033
5072
|
const base = resolveCodexPath2(this.settings.codexPath);
|
|
5034
5073
|
const args = [...base.args, "app-server", "--listen", "stdio://"];
|
|
5035
5074
|
this.lastStderr = "";
|
|
5075
|
+
this.lastCrashHadStderr = false;
|
|
5036
5076
|
this.expectedExitSignal = void 0;
|
|
5037
5077
|
this.child = spawn(base.cmd, args, {
|
|
5038
5078
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -5043,18 +5083,23 @@ var AppServerRpcClient = class extends EventEmitter {
|
|
|
5043
5083
|
},
|
|
5044
5084
|
cwd: this.settings.cwd
|
|
5045
5085
|
});
|
|
5046
|
-
this.child
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5086
|
+
const child = this.child;
|
|
5087
|
+
let stderrBuf = "";
|
|
5088
|
+
child.stderr.setEncoding("utf8");
|
|
5089
|
+
child.stderr.on("data", (chunk) => {
|
|
5090
|
+
stderrBuf = (stderrBuf + String(chunk)).slice(-4e3);
|
|
5091
|
+
if (this.child === child) {
|
|
5092
|
+
this.lastStderr = stderrBuf;
|
|
5051
5093
|
}
|
|
5052
5094
|
});
|
|
5053
|
-
|
|
5095
|
+
child.on("error", (error) => {
|
|
5054
5096
|
this.logger.error(`[codex-app-server] process error: ${String(error)}`);
|
|
5055
|
-
|
|
5097
|
+
const base2 = String(error);
|
|
5098
|
+
const message = this.withStderrTail(base2, stderrBuf);
|
|
5099
|
+
this.lastCrashHadStderr = message !== base2;
|
|
5100
|
+
this.handleCrash(new Error(message));
|
|
5056
5101
|
});
|
|
5057
|
-
|
|
5102
|
+
child.on("exit", (code, signal) => {
|
|
5058
5103
|
const message = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
|
|
5059
5104
|
const expected = this.state === "closed" || signal !== null && signal === this.expectedExitSignal;
|
|
5060
5105
|
this.expectedExitSignal = void 0;
|
|
@@ -5062,11 +5107,29 @@ var AppServerRpcClient = class extends EventEmitter {
|
|
|
5062
5107
|
this.logger.info(`[codex-app-server] ${message}`);
|
|
5063
5108
|
return;
|
|
5064
5109
|
}
|
|
5065
|
-
this.
|
|
5066
|
-
|
|
5110
|
+
const crashedPending = this.markCrashed();
|
|
5111
|
+
if (!crashedPending) return;
|
|
5112
|
+
let settled = false;
|
|
5113
|
+
const finish = () => {
|
|
5114
|
+
if (settled) return;
|
|
5115
|
+
settled = true;
|
|
5116
|
+
child.off("close", finish);
|
|
5117
|
+
clearTimeout(timer);
|
|
5118
|
+
const base2 = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
|
|
5119
|
+
const crashMessage = this.withStderrTail(base2, stderrBuf);
|
|
5120
|
+
if (this.child === void 0) {
|
|
5121
|
+
this.lastStderr = stderrBuf;
|
|
5122
|
+
this.lastCrashHadStderr = crashMessage !== base2;
|
|
5123
|
+
}
|
|
5124
|
+
this.logger.warn(`[codex-app-server] ${crashMessage}`);
|
|
5125
|
+
this.rejectCrashedPending(crashedPending, new Error(crashMessage));
|
|
5126
|
+
};
|
|
5127
|
+
child.once("close", finish);
|
|
5128
|
+
const timer = setTimeout(finish, 120);
|
|
5129
|
+
timer.unref?.();
|
|
5067
5130
|
});
|
|
5068
5131
|
this.stdoutReader = readline.createInterface({
|
|
5069
|
-
input:
|
|
5132
|
+
input: child.stdout,
|
|
5070
5133
|
crlfDelay: Infinity
|
|
5071
5134
|
});
|
|
5072
5135
|
this.stdoutReader.on("line", (line) => this.handleLine(line));
|
|
@@ -5088,12 +5151,22 @@ var AppServerRpcClient = class extends EventEmitter {
|
|
|
5088
5151
|
this.settings.connectionTimeoutMs ?? this.requestTimeoutMs
|
|
5089
5152
|
);
|
|
5090
5153
|
} catch (error) {
|
|
5091
|
-
const
|
|
5092
|
-
if (
|
|
5154
|
+
const raw = String(error?.message ?? error);
|
|
5155
|
+
if (raw.includes("unknown subcommand") || this.lastStderr.includes("unknown subcommand")) {
|
|
5156
|
+
throw new Error(
|
|
5157
|
+
this.withStderrTail(
|
|
5158
|
+
"codex app-server requires codex CLI >= 0.144.0. Run 'codex --version' to check."
|
|
5159
|
+
)
|
|
5160
|
+
);
|
|
5161
|
+
}
|
|
5162
|
+
if (raw.includes("ENOENT") || this.lastStderr.includes("ENOENT")) {
|
|
5093
5163
|
throw new Error(
|
|
5094
|
-
|
|
5164
|
+
this.withStderrTail(
|
|
5165
|
+
"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."
|
|
5166
|
+
)
|
|
5095
5167
|
);
|
|
5096
5168
|
}
|
|
5169
|
+
const message = this.lastCrashHadStderr ? raw : this.withStderrTail(raw);
|
|
5097
5170
|
throw createAPICallError({
|
|
5098
5171
|
message: `Failed to initialize codex app-server: ${message}`,
|
|
5099
5172
|
stderr: this.lastStderr,
|
|
@@ -5155,8 +5228,8 @@ var AppServerRpcClient = class extends EventEmitter {
|
|
|
5155
5228
|
}
|
|
5156
5229
|
this.writeQueue = Promise.resolve();
|
|
5157
5230
|
}
|
|
5158
|
-
|
|
5159
|
-
if (this.state === "closed") return;
|
|
5231
|
+
markCrashed() {
|
|
5232
|
+
if (this.state === "closed" || this.state === "error") return void 0;
|
|
5160
5233
|
this.state = "error";
|
|
5161
5234
|
this.clearIdleTimer();
|
|
5162
5235
|
this.stdoutReader?.close();
|
|
@@ -5166,11 +5239,9 @@ var AppServerRpcClient = class extends EventEmitter {
|
|
|
5166
5239
|
this.child.kill("SIGTERM");
|
|
5167
5240
|
this.child = void 0;
|
|
5168
5241
|
}
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
|
|
5173
|
-
);
|
|
5242
|
+
const pending = new Map(this.pending);
|
|
5243
|
+
for (const [, entry] of pending) {
|
|
5244
|
+
clearTimeout(entry.timer);
|
|
5174
5245
|
}
|
|
5175
5246
|
this.pending.clear();
|
|
5176
5247
|
this.threadLocks.clear();
|
|
@@ -5180,6 +5251,41 @@ var AppServerRpcClient = class extends EventEmitter {
|
|
|
5180
5251
|
this.completedTurnIds.clear();
|
|
5181
5252
|
this.serverCapabilities = void 0;
|
|
5182
5253
|
this.writeQueue = Promise.resolve();
|
|
5254
|
+
return pending;
|
|
5255
|
+
}
|
|
5256
|
+
rejectCrashedPending(pending, error) {
|
|
5257
|
+
for (const [id, entry] of pending) {
|
|
5258
|
+
entry.reject(
|
|
5259
|
+
new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
|
|
5260
|
+
);
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
handleCrash(error) {
|
|
5264
|
+
const pending = this.markCrashed();
|
|
5265
|
+
if (!pending) return;
|
|
5266
|
+
this.rejectCrashedPending(pending, error);
|
|
5267
|
+
}
|
|
5268
|
+
stderrExcerpt(raw) {
|
|
5269
|
+
if (!raw) return "";
|
|
5270
|
+
const escape = String.fromCharCode(27);
|
|
5271
|
+
const ansiEscapeSequence = new RegExp(
|
|
5272
|
+
`${escape}(?:\\[[0-?]*[ -/]*[@-~]|\\][^\\u0007]*(?:\\u0007|${escape}\\\\))`,
|
|
5273
|
+
"g"
|
|
5274
|
+
);
|
|
5275
|
+
const withoutAnsi = raw.replace(ansiEscapeSequence, "");
|
|
5276
|
+
const withoutControls = Array.from(withoutAnsi).filter((character) => {
|
|
5277
|
+
const code = character.charCodeAt(0);
|
|
5278
|
+
return character === "\n" || code >= 32 && (code < 127 || code > 159);
|
|
5279
|
+
}).join("");
|
|
5280
|
+
const excerpt = withoutControls.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-5).join("; ");
|
|
5281
|
+
if (excerpt.length > 600) {
|
|
5282
|
+
return `\u2026${excerpt.slice(-600)}`;
|
|
5283
|
+
}
|
|
5284
|
+
return excerpt;
|
|
5285
|
+
}
|
|
5286
|
+
withStderrTail(message, raw = this.lastStderr) {
|
|
5287
|
+
const excerpt = this.stderrExcerpt(raw);
|
|
5288
|
+
return excerpt ? `${message} | stderr (tail): ${excerpt}` : message;
|
|
5183
5289
|
}
|
|
5184
5290
|
handleLine(line) {
|
|
5185
5291
|
const trimmed = line.trim();
|