cyrus-codex-runner 0.2.64-test.6 → 0.2.64

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 (55) hide show
  1. package/dist/CodexEventMapper.d.ts +56 -0
  2. package/dist/CodexEventMapper.d.ts.map +1 -0
  3. package/dist/CodexEventMapper.js +469 -0
  4. package/dist/CodexEventMapper.js.map +1 -0
  5. package/dist/CodexRunner.d.ts +37 -46
  6. package/dist/CodexRunner.d.ts.map +1 -1
  7. package/dist/CodexRunner.js +136 -851
  8. package/dist/CodexRunner.js.map +1 -1
  9. package/dist/CodexSkillStager.d.ts +42 -0
  10. package/dist/CodexSkillStager.d.ts.map +1 -0
  11. package/dist/CodexSkillStager.js +182 -0
  12. package/dist/CodexSkillStager.js.map +1 -0
  13. package/dist/backend/AppServerCodexBackend.d.ts +74 -0
  14. package/dist/backend/AppServerCodexBackend.d.ts.map +1 -0
  15. package/dist/backend/AppServerCodexBackend.js +352 -0
  16. package/dist/backend/AppServerCodexBackend.js.map +1 -0
  17. package/dist/backend/appServerClient.d.ts +75 -0
  18. package/dist/backend/appServerClient.d.ts.map +1 -0
  19. package/dist/backend/appServerClient.js +223 -0
  20. package/dist/backend/appServerClient.js.map +1 -0
  21. package/dist/backend/appServerEvents.d.ts +9 -0
  22. package/dist/backend/appServerEvents.d.ts.map +1 -0
  23. package/dist/backend/appServerEvents.js +110 -0
  24. package/dist/backend/appServerEvents.js.map +1 -0
  25. package/dist/backend/appServerProcess.d.ts +38 -0
  26. package/dist/backend/appServerProcess.d.ts.map +1 -0
  27. package/dist/backend/appServerProcess.js +283 -0
  28. package/dist/backend/appServerProcess.js.map +1 -0
  29. package/dist/backend/codexBinary.d.ts +24 -0
  30. package/dist/backend/codexBinary.d.ts.map +1 -0
  31. package/dist/backend/codexBinary.js +44 -0
  32. package/dist/backend/codexBinary.js.map +1 -0
  33. package/dist/backend/types.d.ts +210 -0
  34. package/dist/backend/types.d.ts.map +1 -0
  35. package/dist/backend/types.js +2 -0
  36. package/dist/backend/types.js.map +1 -0
  37. package/dist/config/CodexConfigBuilder.d.ts +40 -0
  38. package/dist/config/CodexConfigBuilder.d.ts.map +1 -0
  39. package/dist/config/CodexConfigBuilder.js +182 -0
  40. package/dist/config/CodexConfigBuilder.js.map +1 -0
  41. package/dist/config/mcpConfigTranslator.d.ts +17 -0
  42. package/dist/config/mcpConfigTranslator.d.ts.map +1 -0
  43. package/dist/config/mcpConfigTranslator.js +245 -0
  44. package/dist/config/mcpConfigTranslator.js.map +1 -0
  45. package/dist/config/sandboxPolicy.d.ts +43 -0
  46. package/dist/config/sandboxPolicy.d.ts.map +1 -0
  47. package/dist/config/sandboxPolicy.js +56 -0
  48. package/dist/config/sandboxPolicy.js.map +1 -0
  49. package/dist/index.d.ts +3 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/types.d.ts +9 -7
  54. package/dist/types.d.ts.map +1 -1
  55. package/package.json +5 -4
@@ -0,0 +1,210 @@
1
+ import type { EventEmitter } from "node:events";
2
+ import type { ApprovalMode, ModelReasoningEffort, SandboxMode, WebSearchMode } from "@openai/codex-sdk";
3
+ import type { CodexConfigOverrides } from "../types.js";
4
+ /**
5
+ * Backend-neutral item shape consumed by {@link CodexEventMapper}.
6
+ *
7
+ * The {@link AppServerCodexBackend} translates the app-server protocol's
8
+ * camelCase item payloads into this normalized shape. The mapper depends only
9
+ * on this type — never on a concrete transport's wire format (Dependency
10
+ * Inversion).
11
+ */
12
+ export type NormalizedCodexItem = {
13
+ type: "agent_message";
14
+ id: string;
15
+ text: string;
16
+ } | {
17
+ type: "reasoning";
18
+ id: string;
19
+ text?: string;
20
+ } | {
21
+ type: "user_message";
22
+ id: string;
23
+ } | {
24
+ type: "command_execution";
25
+ id: string;
26
+ command: string;
27
+ aggregated_output: string;
28
+ exit_code?: number;
29
+ status: "in_progress" | "completed" | "failed";
30
+ } | {
31
+ type: "file_change";
32
+ id: string;
33
+ changes: {
34
+ path: string;
35
+ kind: "add" | "delete" | "update";
36
+ }[];
37
+ status: "completed" | "failed";
38
+ } | {
39
+ type: "mcp_tool_call";
40
+ id: string;
41
+ server: string;
42
+ tool: string;
43
+ arguments: unknown;
44
+ result?: {
45
+ content?: unknown[];
46
+ structured_content?: unknown;
47
+ };
48
+ error?: {
49
+ message: string;
50
+ };
51
+ status: "in_progress" | "completed" | "failed";
52
+ } | {
53
+ type: "web_search";
54
+ id: string;
55
+ query: string;
56
+ action?: Record<string, unknown>;
57
+ } | {
58
+ type: "todo_list";
59
+ id: string;
60
+ items: {
61
+ text: string;
62
+ completed: boolean;
63
+ }[];
64
+ } | {
65
+ type: "error";
66
+ id: string;
67
+ message: string;
68
+ } | {
69
+ type: "unknown";
70
+ id: string;
71
+ };
72
+ /** Token usage for a completed turn, in backend-neutral form. */
73
+ export interface NormalizedUsage {
74
+ input_tokens: number;
75
+ output_tokens: number;
76
+ cached_input_tokens: number;
77
+ }
78
+ /**
79
+ * Backend-neutral lifecycle/stream event. Both backends emit this; the mapper
80
+ * turns it into Cyrus `SDKMessage`s.
81
+ */
82
+ export type NormalizedCodexEvent = {
83
+ kind: "thread-started";
84
+ threadId: string;
85
+ } | {
86
+ kind: "turn-started";
87
+ } | {
88
+ kind: "item-started";
89
+ item: NormalizedCodexItem;
90
+ } | {
91
+ kind: "item-completed";
92
+ item: NormalizedCodexItem;
93
+ } | {
94
+ kind: "turn-completed";
95
+ usage: NormalizedUsage;
96
+ } | {
97
+ kind: "turn-failed";
98
+ message: string;
99
+ } | {
100
+ kind: "error";
101
+ message: string;
102
+ };
103
+ /** A single piece of user input for a turn or a steer. */
104
+ export type CodexUserInput = {
105
+ type: "text";
106
+ text: string;
107
+ } | {
108
+ type: "local_image";
109
+ path: string;
110
+ };
111
+ /** A single filesystem access grant in a Codex permission profile. */
112
+ export type CodexFileSystemAccess = "read" | "write" | "deny";
113
+ /**
114
+ * Resolved per-thread sandbox decision.
115
+ * - `workspace-mode`: the coarse Codex sandbox mode (broad reads, writes limited
116
+ * to cwd + `writableRoots` + tmp). The default when there are no explicit Cyrus
117
+ * sandbox settings — sent via `thread/start.sandbox` + `config.sandbox_workspace_write`.
118
+ * - `profile`: a granular per-thread permission profile (restricted reads) derived
119
+ * from Cyrus sandbox settings. `filesystem` is a flattened map of path →
120
+ * read/write/deny, where keys are either absolute paths or Codex special-path
121
+ * tokens (`:minimal` = platform defaults, `:workspace_roots` = cwd/worktree,
122
+ * `:tmpdir`, `:slash_tmp`). Sent via `thread/start.permissions` (the profile id)
123
+ * + `config.permissions.<id>` (the profile body); the profile persists per-thread
124
+ * and cannot be combined with `thread/start.sandbox`.
125
+ */
126
+ export type ResolvedCodexSandbox = {
127
+ kind: "workspace-mode";
128
+ mode: SandboxMode;
129
+ writableRoots: string[];
130
+ networkAccess: boolean;
131
+ } | {
132
+ kind: "profile";
133
+ profileId: string;
134
+ filesystem: Record<string, CodexFileSystemAccess>;
135
+ networkAccess: boolean;
136
+ };
137
+ /**
138
+ * Fully-resolved, transport-neutral run configuration produced by
139
+ * {@link CodexConfigBuilder}; the backend maps it onto `thread/start` +
140
+ * `turn/start` params for the app-server.
141
+ */
142
+ export interface ResolvedCodexConfig {
143
+ model?: string;
144
+ sandbox: ResolvedCodexSandbox;
145
+ workingDirectory?: string;
146
+ approvalPolicy: ApprovalMode;
147
+ skipGitRepoCheck: boolean;
148
+ modelReasoningEffort?: ModelReasoningEffort;
149
+ webSearchMode?: WebSearchMode;
150
+ /** Maps to Codex `developer_instructions` (appended system prompt). */
151
+ developerInstructions?: string;
152
+ /** Global Codex config overrides (e.g. `mcp_servers`). */
153
+ configOverrides?: CodexConfigOverrides;
154
+ /** Environment override; when set the child does not inherit ambient env. */
155
+ env?: Record<string, string>;
156
+ codexHome: string;
157
+ codexPath?: string;
158
+ outputSchema?: unknown;
159
+ /** Existing thread id to resume rather than start fresh. */
160
+ resumeSessionId?: string;
161
+ }
162
+ /** Events emitted by a {@link CodexBackend}. */
163
+ export interface CodexBackendEvents {
164
+ event: (event: NormalizedCodexEvent) => void;
165
+ }
166
+ export declare interface CodexBackend {
167
+ on<K extends keyof CodexBackendEvents>(event: K, listener: CodexBackendEvents[K]): this;
168
+ emit<K extends keyof CodexBackendEvents>(event: K, ...args: Parameters<CodexBackendEvents[K]>): boolean;
169
+ }
170
+ /**
171
+ * Transport strategy for driving Codex.
172
+ *
173
+ * Implementations are responsible only for (a) establishing/maintaining a
174
+ * Codex session and (b) translating their wire protocol into
175
+ * {@link NormalizedCodexEvent}s. Config assembly, skill staging, MCP
176
+ * translation, and SDKMessage mapping live in dedicated collaborators.
177
+ */
178
+ export interface CodexBackend extends EventEmitter {
179
+ /**
180
+ * Whether this backend can inject input into an already-running turn
181
+ * (`turn/steer`). When false, mid-turn input must be delivered as a new turn.
182
+ */
183
+ readonly supportsSteer: boolean;
184
+ /**
185
+ * Open the session: connect/spawn as needed, then start or resume the thread.
186
+ * Resolves with the resolved thread id (may differ from a requested resume id
187
+ * only if the backend allocates one). Implementations should emit a
188
+ * `thread-started` event when the id is known.
189
+ */
190
+ open(config: ResolvedCodexConfig): Promise<{
191
+ threadId: string;
192
+ }>;
193
+ /**
194
+ * Run a single turn with the given input. Resolves when the turn reaches a
195
+ * terminal state (a `turn-completed` or `turn-failed` event has been emitted).
196
+ */
197
+ runTurn(input: CodexUserInput[]): Promise<void>;
198
+ /**
199
+ * Inject input into the currently-active turn. Only present when
200
+ * {@link supportsSteer} is true. Rejects if no turn is active.
201
+ */
202
+ steer?(input: CodexUserInput[]): Promise<void>;
203
+ /** Whether a turn is currently in flight. */
204
+ isTurnActive(): boolean;
205
+ /** Cancel the active turn (if any) without tearing down the session. */
206
+ interrupt(): Promise<void>;
207
+ /** Tear down the session and release all resources. */
208
+ close(): Promise<void>;
209
+ }
210
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import type { ResolvedCodexConfig } from "../backend/types.js";
2
+ import type { CodexRunnerConfig } from "../types.js";
3
+ /**
4
+ * Assembles a transport-neutral {@link ResolvedCodexConfig} from a
5
+ * {@link CodexRunnerConfig}. Single responsibility: configuration resolution
6
+ * (model fallback, sandbox, reasoning effort, MCP translation, env, home dir).
7
+ * Produces no side effects beyond ensuring the Codex home directory exists.
8
+ */
9
+ export declare class CodexConfigBuilder {
10
+ private readonly config;
11
+ constructor(config: CodexRunnerConfig);
12
+ build(): Promise<ResolvedCodexConfig>;
13
+ /**
14
+ * Network intent for the sandbox. Defaults to enabled (so common remote
15
+ * workflows — git/gh — work without danger-full-access); honors an explicit
16
+ * `sandbox_workspace_write.network_access` in the passed-through overrides.
17
+ */
18
+ private resolveNetworkAccess;
19
+ private getAdditionalDirectories;
20
+ private resolveCodexHome;
21
+ private buildEnvOverride;
22
+ /**
23
+ * Global Codex config overrides — currently just MCP servers. Sandbox
24
+ * (writable/readable roots, network) is owned by {@link resolveCodexSandbox}
25
+ * and `developer_instructions` is surfaced on
26
+ * {@link ResolvedCodexConfig.developerInstructions}, so neither is injected
27
+ * here. Any caller-supplied `sandbox_workspace_write` is dropped (its
28
+ * `network_access` is folded into the sandbox decision via
29
+ * {@link resolveNetworkAccess}) to keep a single source of truth.
30
+ */
31
+ private buildConfigOverrides;
32
+ /**
33
+ * If the configured model is unreachable via the OpenAI API, swap to the
34
+ * fallback model before starting. Skipped when there is no API key (Codex
35
+ * native auth handles access) or when the user has a ChatGPT subscription.
36
+ */
37
+ private resolveModelWithFallback;
38
+ private hasCodexSubscription;
39
+ }
40
+ //# sourceMappingURL=CodexConfigBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodexConfigBuilder.d.ts","sourceRoot":"","sources":["../../src/config/CodexConfigBuilder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAGX,iBAAiB,EACjB,MAAM,aAAa,CAAC;AAWrB;;;;;GAKG;AACH,qBAAa,kBAAkB;IAClB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,iBAAiB;IAEhD,KAAK,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAoC3C;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;IAgBxB;;;;;;;;OAQG;IACH,OAAO,CAAC,oBAAoB;IA6B5B;;;;OAIG;YACW,wBAAwB;YAqCxB,oBAAoB;CAuBlC"}
@@ -0,0 +1,182 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { buildCodexMcpServersConfig } from "./mcpConfigTranslator.js";
5
+ import { resolveCodexSandbox } from "./sandboxPolicy.js";
6
+ function getDefaultReasoningEffortForModel(model) {
7
+ // All gpt-5 variants (including plain "gpt-5") reject xhigh; pin to "high".
8
+ return /^gpt-5/i.test(model || "") ? "high" : undefined;
9
+ }
10
+ /**
11
+ * Assembles a transport-neutral {@link ResolvedCodexConfig} from a
12
+ * {@link CodexRunnerConfig}. Single responsibility: configuration resolution
13
+ * (model fallback, sandbox, reasoning effort, MCP translation, env, home dir).
14
+ * Produces no side effects beyond ensuring the Codex home directory exists.
15
+ */
16
+ export class CodexConfigBuilder {
17
+ config;
18
+ constructor(config) {
19
+ this.config = config;
20
+ }
21
+ async build() {
22
+ await this.resolveModelWithFallback();
23
+ const codexHome = this.resolveCodexHome();
24
+ const reasoningEffort = this.config.modelReasoningEffort ??
25
+ getDefaultReasoningEffortForModel(this.config.model);
26
+ const webSearchMode = this.config.webSearchMode ??
27
+ (this.config.includeWebSearch ? "live" : undefined);
28
+ return {
29
+ model: this.config.model,
30
+ sandbox: resolveCodexSandbox({
31
+ mode: this.config.sandbox || "workspace-write",
32
+ workingDirectory: this.config.workingDirectory,
33
+ writableRoots: this.getAdditionalDirectories(),
34
+ networkAccess: this.resolveNetworkAccess(),
35
+ sandboxSettings: this.config.sandboxSettings,
36
+ }),
37
+ workingDirectory: this.config.workingDirectory,
38
+ approvalPolicy: this.config.askForApproval || "never",
39
+ skipGitRepoCheck: this.config.skipGitRepoCheck ?? true,
40
+ modelReasoningEffort: reasoningEffort,
41
+ webSearchMode,
42
+ developerInstructions: (this.config.appendSystemPrompt ?? "").trim() || undefined,
43
+ configOverrides: this.buildConfigOverrides(),
44
+ env: this.buildEnvOverride(codexHome),
45
+ codexHome,
46
+ codexPath: this.config.codexPath,
47
+ outputSchema: this.config.outputSchema,
48
+ resumeSessionId: this.config.resumeSessionId,
49
+ };
50
+ }
51
+ /**
52
+ * Network intent for the sandbox. Defaults to enabled (so common remote
53
+ * workflows — git/gh — work without danger-full-access); honors an explicit
54
+ * `sandbox_workspace_write.network_access` in the passed-through overrides.
55
+ */
56
+ resolveNetworkAccess() {
57
+ const sww = this.config.configOverrides?.sandbox_workspace_write;
58
+ if (sww &&
59
+ typeof sww === "object" &&
60
+ !Array.isArray(sww) &&
61
+ typeof sww.network_access === "boolean") {
62
+ return sww.network_access;
63
+ }
64
+ return true;
65
+ }
66
+ getAdditionalDirectories() {
67
+ const workingDirectory = this.config.workingDirectory;
68
+ const uniqueDirectories = new Set();
69
+ for (const directory of this.config.allowedDirectories || []) {
70
+ if (!directory || directory === workingDirectory) {
71
+ continue;
72
+ }
73
+ uniqueDirectories.add(directory);
74
+ }
75
+ return [...uniqueDirectories];
76
+ }
77
+ resolveCodexHome() {
78
+ const codexHome = this.config.codexHome ||
79
+ process.env.CODEX_HOME ||
80
+ join(homedir(), ".codex");
81
+ mkdirSync(codexHome, { recursive: true });
82
+ return codexHome;
83
+ }
84
+ buildEnvOverride(codexHome) {
85
+ if (!this.config.codexHome) {
86
+ return undefined;
87
+ }
88
+ const env = {};
89
+ for (const [key, value] of Object.entries(process.env)) {
90
+ if (typeof value === "string") {
91
+ env[key] = value;
92
+ }
93
+ }
94
+ env.CODEX_HOME = codexHome;
95
+ return env;
96
+ }
97
+ /**
98
+ * Global Codex config overrides — currently just MCP servers. Sandbox
99
+ * (writable/readable roots, network) is owned by {@link resolveCodexSandbox}
100
+ * and `developer_instructions` is surfaced on
101
+ * {@link ResolvedCodexConfig.developerInstructions}, so neither is injected
102
+ * here. Any caller-supplied `sandbox_workspace_write` is dropped (its
103
+ * `network_access` is folded into the sandbox decision via
104
+ * {@link resolveNetworkAccess}) to keep a single source of truth.
105
+ */
106
+ buildConfigOverrides() {
107
+ const { sandbox_workspace_write: _dropped, ...rest } = this.config.configOverrides ?? {};
108
+ const configOverrides = { ...rest };
109
+ const mcpServers = buildCodexMcpServersConfig({
110
+ workingDirectory: this.config.workingDirectory,
111
+ mcpConfigPath: this.config.mcpConfigPath,
112
+ mcpConfig: this.config.mcpConfig,
113
+ allowedTools: this.config.allowedTools,
114
+ });
115
+ if (mcpServers) {
116
+ const existingMcpServers = configOverrides.mcp_servers;
117
+ configOverrides.mcp_servers =
118
+ existingMcpServers &&
119
+ typeof existingMcpServers === "object" &&
120
+ !Array.isArray(existingMcpServers)
121
+ ? {
122
+ ...existingMcpServers,
123
+ ...mcpServers,
124
+ }
125
+ : mcpServers;
126
+ }
127
+ return Object.keys(configOverrides).length > 0
128
+ ? configOverrides
129
+ : undefined;
130
+ }
131
+ /**
132
+ * If the configured model is unreachable via the OpenAI API, swap to the
133
+ * fallback model before starting. Skipped when there is no API key (Codex
134
+ * native auth handles access) or when the user has a ChatGPT subscription.
135
+ */
136
+ async resolveModelWithFallback() {
137
+ const model = this.config.model;
138
+ const fallback = this.config.fallbackModel;
139
+ if (!model || !fallback || fallback === model)
140
+ return;
141
+ const apiKey = process.env.OPENAI_API_KEY;
142
+ if (!apiKey)
143
+ return;
144
+ if (await this.hasCodexSubscription())
145
+ return;
146
+ const baseUrl = (process.env.OPENAI_BASE_URL ||
147
+ process.env.OPENAI_API_BASE ||
148
+ "https://api.openai.com/v1").replace(/\/+$/, "");
149
+ try {
150
+ const response = await fetch(`${baseUrl}/models/${encodeURIComponent(model)}`, {
151
+ method: "GET",
152
+ headers: { Authorization: `Bearer ${apiKey}` },
153
+ signal: AbortSignal.timeout(10_000),
154
+ });
155
+ if (response.status === 404) {
156
+ console.log(`[CodexRunner] Model "${model}" not found (404), falling back to "${fallback}"`);
157
+ this.config.model = fallback;
158
+ }
159
+ }
160
+ catch {
161
+ // Network error or timeout — proceed with the original model and let
162
+ // the backend surface any downstream failure.
163
+ }
164
+ }
165
+ async hasCodexSubscription() {
166
+ const codexBin = this.config.codexPath || "codex";
167
+ try {
168
+ const { execFile } = await import("node:child_process");
169
+ const { promisify } = await import("node:util");
170
+ const execFileAsync = promisify(execFile);
171
+ const { stdout, stderr } = await execFileAsync(codexBin, ["login", "status"], { timeout: 5_000 });
172
+ const result = /logged in using chatgpt/i.test(stdout + stderr);
173
+ console.log(`[CodexRunner] hasCodexSubscription: ${result} (stdout: "${stdout.trim()}"${stderr.trim() ? `, stderr: "${stderr.trim()}"` : ""})`);
174
+ return result;
175
+ }
176
+ catch (error) {
177
+ console.warn(`[CodexRunner] hasCodexSubscription error (returning false): ${error instanceof Error ? error.message : String(error)}`);
178
+ return false;
179
+ }
180
+ }
181
+ }
182
+ //# sourceMappingURL=CodexConfigBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodexConfigBuilder.js","sourceRoot":"","sources":["../../src/config/CodexConfigBuilder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { McpServerConfig } from "cyrus-core";
2
+ import type { CodexConfigOverrides } from "../types.js";
3
+ /** Inputs the MCP translator needs from a runner config. */
4
+ export interface McpTranslationInput {
5
+ workingDirectory?: string;
6
+ mcpConfigPath?: string | string[];
7
+ mcpConfig?: Record<string, McpServerConfig>;
8
+ allowedTools?: string[];
9
+ }
10
+ /**
11
+ * Translate Cyrus MCP server configs (file-based + inline) and Cyrus
12
+ * `allowedTools` semantics into Codex-native `mcp_servers` config overrides.
13
+ *
14
+ * Reference: {@link https://platform.openai.com/docs/docs-mcp}
15
+ */
16
+ export declare function buildCodexMcpServersConfig(input: McpTranslationInput): Record<string, CodexConfigOverrides> | undefined;
17
+ //# sourceMappingURL=mcpConfigTranslator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpConfigTranslator.d.ts","sourceRoot":"","sources":["../../src/config/mcpConfigTranslator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAUxD,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAqPD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACzC,KAAK,EAAE,mBAAmB,GACxB,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,SAAS,CA2FlD"}