@workbench-ai/agent-driver-openai-codex 0.0.44

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.
@@ -0,0 +1,12 @@
1
+ import type { ProviderIntegrationCatalog, ProviderIntegrationUpdate } from "@workbench-ai/agent-driver";
2
+ export declare function listCodexIntegrations(codexHomeDir: string): Promise<ProviderIntegrationCatalog>;
3
+ export declare function updateCodexIntegrations(args: {
4
+ codexHomeDir: string;
5
+ update: ProviderIntegrationUpdate;
6
+ }): Promise<ProviderIntegrationCatalog>;
7
+ export declare function projectCodexIntegrations(args: {
8
+ sourceCodexHomeDir: string;
9
+ targetCodexHomeDir: string;
10
+ }): Promise<void>;
11
+ export declare function clearCodexLegacySkillRoots(codexHomeDir: string): Promise<void>;
12
+ //# sourceMappingURL=integrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../src/integrations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAE1B,yBAAyB,EAC1B,MAAM,4BAA4B,CAAC;AAYpC,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,0BAA0B,CAAC,CAOrC;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,yBAAyB,CAAC;CACnC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAStC;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB;AAED,wBAAsB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpF"}
@@ -0,0 +1,233 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ export async function listCodexIntegrations(codexHomeDir) {
4
+ const integrations = await listCodexMcpIntegrations(codexHomeDir);
5
+ return {
6
+ providerId: "openai/codex",
7
+ providerLabel: "OpenAI Codex",
8
+ integrations: integrations.map(toIntegrationEntry),
9
+ };
10
+ }
11
+ export async function updateCodexIntegrations(args) {
12
+ const integrations = await listCodexMcpIntegrations(args.codexHomeDir);
13
+ validateRequestedIds(integrations, args.update.enabledIds);
14
+ await applyCodexIntegrationSelection(args.codexHomeDir, integrations, new Set(args.update.enabledIds));
15
+ return await listCodexIntegrations(args.codexHomeDir);
16
+ }
17
+ export async function projectCodexIntegrations(args) {
18
+ const integrations = await listCodexMcpIntegrations(args.sourceCodexHomeDir);
19
+ await appendEnabledCodexMcpServers(args.sourceCodexHomeDir, args.targetCodexHomeDir, integrations.filter((integration) => integration.enabled));
20
+ }
21
+ export async function clearCodexLegacySkillRoots(codexHomeDir) {
22
+ await Promise.all([
23
+ fs.rm(path.join(codexHomeDir, "skills"), { recursive: true, force: true }),
24
+ fs.rm(path.join(codexHomeDir, ".disabled-skills"), { recursive: true, force: true }),
25
+ ]);
26
+ }
27
+ function toIntegrationEntry(integration) {
28
+ return {
29
+ id: integration.id,
30
+ label: integration.label,
31
+ enabled: integration.enabled,
32
+ };
33
+ }
34
+ async function listCodexMcpIntegrations(codexHomeDir) {
35
+ const configPath = path.join(codexHomeDir, "config.toml");
36
+ const raw = await readFileOrEmpty(configPath);
37
+ if (!raw.trim()) {
38
+ return [];
39
+ }
40
+ const lines = raw.split(/\n/u);
41
+ const sections = parseTomlSections(lines);
42
+ const groups = new Map();
43
+ for (const section of sections) {
44
+ if (section.path[0] !== "mcp_servers" || !section.path[1]) {
45
+ continue;
46
+ }
47
+ const integrationId = section.path[1];
48
+ const existing = groups.get(integrationId) ?? {
49
+ id: integrationId,
50
+ label: integrationId,
51
+ enabled: true,
52
+ ranges: [],
53
+ };
54
+ existing.ranges.push({
55
+ start: section.start,
56
+ end: section.end,
57
+ });
58
+ if (section.path.length === 2) {
59
+ existing.enabled = parseCodexMcpEnabled(lines, section.start, section.end);
60
+ }
61
+ groups.set(integrationId, existing);
62
+ }
63
+ return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
64
+ }
65
+ async function applyCodexIntegrationSelection(codexHomeDir, integrations, enabledIds) {
66
+ const configPath = path.join(codexHomeDir, "config.toml");
67
+ const raw = await readFileOrEmpty(configPath);
68
+ const lines = raw.split(/\n/u);
69
+ const sections = parseTomlSections(lines);
70
+ const rootRanges = integrations
71
+ .map((integration) => {
72
+ const range = sections.find((section) => section.path[0] === "mcp_servers" &&
73
+ section.path[1] === integration.id &&
74
+ section.path.length === 2);
75
+ return range ? { integration, range } : null;
76
+ })
77
+ .filter((entry) => entry !== null)
78
+ .sort((left, right) => right.range.start - left.range.start);
79
+ for (const entry of rootRanges) {
80
+ const shouldEnable = enabledIds.has(entry.integration.id);
81
+ const updatedSection = setCodexMcpEnabled(lines.slice(entry.range.start, entry.range.end + 1), shouldEnable);
82
+ lines.splice(entry.range.start, entry.range.end - entry.range.start + 1, ...updatedSection);
83
+ }
84
+ await fs.mkdir(codexHomeDir, { recursive: true });
85
+ await fs.writeFile(configPath, `${trimTrailingEmptyLines(lines).join("\n")}\n`, "utf8");
86
+ }
87
+ async function appendEnabledCodexMcpServers(sourceCodexHomeDir, targetCodexHomeDir, enabledIntegrations) {
88
+ if (enabledIntegrations.length === 0) {
89
+ return;
90
+ }
91
+ const sourceRaw = await readFileOrEmpty(path.join(sourceCodexHomeDir, "config.toml"));
92
+ if (!sourceRaw.trim()) {
93
+ return;
94
+ }
95
+ const sourceLines = sourceRaw.split(/\n/u);
96
+ const sections = parseTomlSections(sourceLines);
97
+ const sectionLookup = new Map(sections
98
+ .filter((section) => section.path[0] === "mcp_servers" && section.path[1])
99
+ .map((section) => [sectionKey(section), section]));
100
+ const blocks = enabledIntegrations
101
+ .map((integration) => integration.ranges
102
+ .map((range) => {
103
+ const section = sectionLookup.get(`${integration.id}:${range.start}:${range.end}`);
104
+ if (!section) {
105
+ return "";
106
+ }
107
+ return sourceLines.slice(section.start, section.end + 1).join("\n").trimEnd();
108
+ })
109
+ .filter(Boolean)
110
+ .join("\n\n"))
111
+ .filter(Boolean);
112
+ if (blocks.length === 0) {
113
+ return;
114
+ }
115
+ const targetConfigPath = path.join(targetCodexHomeDir, "config.toml");
116
+ const targetRaw = await readFileOrEmpty(targetConfigPath);
117
+ const prefix = targetRaw.trimEnd();
118
+ const suffix = blocks.join("\n\n");
119
+ await fs.mkdir(targetCodexHomeDir, { recursive: true });
120
+ await fs.writeFile(targetConfigPath, `${prefix ? `${prefix}\n\n` : ""}${suffix}\n`, "utf8");
121
+ }
122
+ function parseCodexMcpEnabled(lines, start, end) {
123
+ for (let index = start + 1; index <= end; index += 1) {
124
+ const match = lines[index]?.match(/^\s*enabled\s*=\s*(true|false)\s*(?:#.*)?$/u);
125
+ if (!match) {
126
+ continue;
127
+ }
128
+ return match[1] !== "false";
129
+ }
130
+ return true;
131
+ }
132
+ function setCodexMcpEnabled(sectionLines, enabled) {
133
+ const nextLines = [...sectionLines];
134
+ const enabledLine = `enabled = ${enabled ? "true" : "false"}`;
135
+ for (let index = 1; index < nextLines.length; index += 1) {
136
+ if (/^\s*\[.+\]\s*$/u.test(nextLines[index] ?? "")) {
137
+ nextLines.splice(index, 0, enabledLine);
138
+ return nextLines;
139
+ }
140
+ if (/^\s*enabled\s*=/u.test(nextLines[index] ?? "")) {
141
+ nextLines[index] = enabledLine;
142
+ return nextLines;
143
+ }
144
+ }
145
+ nextLines.splice(1, 0, enabledLine);
146
+ return nextLines;
147
+ }
148
+ function parseTomlSections(lines) {
149
+ const sections = [];
150
+ for (let index = 0; index < lines.length; index += 1) {
151
+ const match = lines[index]?.match(/^\s*\[(.+)\]\s*$/u);
152
+ const rawPath = match?.[1];
153
+ if (!rawPath) {
154
+ continue;
155
+ }
156
+ const parsedPath = parseTomlPath(rawPath);
157
+ if (parsedPath.length === 0) {
158
+ continue;
159
+ }
160
+ const previous = sections.at(-1);
161
+ if (previous) {
162
+ previous.end = index - 1;
163
+ }
164
+ sections.push({
165
+ path: parsedPath,
166
+ start: index,
167
+ end: lines.length - 1,
168
+ });
169
+ }
170
+ return sections;
171
+ }
172
+ function parseTomlPath(raw) {
173
+ const segments = [];
174
+ let current = "";
175
+ let inQuotes = false;
176
+ let escaped = false;
177
+ for (const char of raw.trim()) {
178
+ if (escaped) {
179
+ current += char;
180
+ escaped = false;
181
+ continue;
182
+ }
183
+ if (char === "\\") {
184
+ escaped = true;
185
+ continue;
186
+ }
187
+ if (char === "\"") {
188
+ inQuotes = !inQuotes;
189
+ continue;
190
+ }
191
+ if (char === "." && !inQuotes) {
192
+ if (current.trim().length > 0) {
193
+ segments.push(current.trim());
194
+ }
195
+ current = "";
196
+ continue;
197
+ }
198
+ current += char;
199
+ }
200
+ if (current.trim().length > 0) {
201
+ segments.push(current.trim());
202
+ }
203
+ return segments;
204
+ }
205
+ function sectionKey(section) {
206
+ return `${section.path[1] ?? ""}:${section.start}:${section.end}`;
207
+ }
208
+ async function readFileOrEmpty(filePath) {
209
+ try {
210
+ return await fs.readFile(filePath, "utf8");
211
+ }
212
+ catch (error) {
213
+ if (error.code === "ENOENT") {
214
+ return "";
215
+ }
216
+ throw error;
217
+ }
218
+ }
219
+ function trimTrailingEmptyLines(lines) {
220
+ const nextLines = [...lines];
221
+ while (nextLines.length > 0 && nextLines.at(-1)?.trim() === "") {
222
+ nextLines.pop();
223
+ }
224
+ return nextLines;
225
+ }
226
+ function validateRequestedIds(integrations, requestedIds) {
227
+ const knownIds = new Set(integrations.map((integration) => integration.id));
228
+ const unknown = requestedIds.filter((id) => !knownIds.has(id));
229
+ if (unknown.length === 0) {
230
+ return;
231
+ }
232
+ throw new Error(`Unknown integration ids: ${unknown.join(", ")}`);
233
+ }
@@ -0,0 +1,2 @@
1
+ export declare const codexHarnessPackageVersion: string;
2
+ //# sourceMappingURL=package-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-version.d.ts","sourceRoot":"","sources":["../src/package-version.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,0BAA0B,QAAsB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ const packageJson = JSON.parse(readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf8"));
4
+ export const codexHarnessPackageVersion = packageJson.version;
@@ -0,0 +1,28 @@
1
+ export declare const codexWorkbenchProviderAuth: {
2
+ readonly apiKey: {
3
+ readonly envName: "OPENAI_API_KEY";
4
+ };
5
+ readonly profile: {
6
+ readonly required: readonly [".codex/auth.json"];
7
+ readonly optional: readonly [];
8
+ };
9
+ readonly harnessDefaults: {
10
+ readonly config: {
11
+ readonly sandbox_mode: "danger-full-access";
12
+ };
13
+ };
14
+ readonly toHarnessAuth: (auth: {
15
+ kind: "profile" | "api_key" | "bedrock";
16
+ root?: string;
17
+ }) => {
18
+ strategy: string;
19
+ ref: string;
20
+ path?: undefined;
21
+ } | {
22
+ strategy: string;
23
+ path: string;
24
+ ref?: undefined;
25
+ } | null;
26
+ readonly staleErrorPatterns: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
27
+ };
28
+ //# sourceMappingURL=workbench-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workbench-auth.d.ts","sourceRoot":"","sources":["../src/workbench-auth.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;mCAajB;QAAE,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;CA+BtE,CAAC"}
@@ -0,0 +1,45 @@
1
+ export const codexWorkbenchProviderAuth = {
2
+ apiKey: {
3
+ envName: "OPENAI_API_KEY",
4
+ },
5
+ profile: {
6
+ required: [".codex/auth.json"],
7
+ optional: [],
8
+ },
9
+ harnessDefaults: {
10
+ config: {
11
+ sandbox_mode: "danger-full-access",
12
+ },
13
+ },
14
+ toHarnessAuth(auth) {
15
+ if (auth.kind === "api_key") {
16
+ return {
17
+ strategy: "secret_ref",
18
+ ref: "OPENAI_API_KEY",
19
+ };
20
+ }
21
+ if (auth.kind === "bedrock") {
22
+ return null;
23
+ }
24
+ if (!auth.root) {
25
+ return null;
26
+ }
27
+ return {
28
+ strategy: "profile_path",
29
+ path: auth.root,
30
+ };
31
+ },
32
+ staleErrorPatterns: [
33
+ /not logged in/iu,
34
+ /login required/iu,
35
+ /authentication required/iu,
36
+ /failed to authenticate/iu,
37
+ /authentication_error/iu,
38
+ /api error:\s*401/iu,
39
+ /invalid.*session/iu,
40
+ /invalid bearer token/iu,
41
+ /session.*expired/iu,
42
+ /oauth.*expired/iu,
43
+ /unauthorized/iu,
44
+ ],
45
+ };
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@workbench-ai/agent-driver-openai-codex",
3
+ "version": "0.0.44",
4
+ "type": "module",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/workbench-ai/workbench-monorepo.git",
8
+ "directory": "products/agent-drivers/packages/openai-codex"
9
+ },
10
+ "publishConfig": {
11
+ "registry": "https://registry.npmjs.org/",
12
+ "access": "public"
13
+ },
14
+ "main": "dist/index.js",
15
+ "types": "dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./workbench-auth": {
22
+ "types": "./dist/workbench-auth.d.ts",
23
+ "default": "./dist/workbench-auth.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "dependencies": {
30
+ "zod": "^4.1.5",
31
+ "@workbench-ai/agent-driver": "0.0.44"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24.3.1",
35
+ "prettier": "^3.6.2",
36
+ "typescript": "^5.9.2"
37
+ },
38
+ "scripts": {
39
+ "build": "rm -rf dist && tsc -p tsconfig.json",
40
+ "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
41
+ "lint": "tsc -p tsconfig.json --noEmit",
42
+ "format": "prettier --check 'src/**/*.ts'"
43
+ }
44
+ }