@mclawnet/codex-adapter 0.1.0

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,4 @@
1
+ import type { ModelDescriptor, ModeDescriptor } from "@mclawnet/shared";
2
+ export declare const CODEX_MODELS: ModelDescriptor[];
3
+ export declare const CODEX_MODES: ModeDescriptor[];
4
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAKxE,eAAO,MAAM,YAAY,EAAE,eAAe,EAkBzC,CAAC;AAGF,eAAO,MAAM,WAAW,EAAE,cAAc,EAIvC,CAAC"}
@@ -0,0 +1,29 @@
1
+ // PROVISIONAL — not verified against `codex --help` on a real install.
2
+ // Update once we have a way to query codex's accepted model list at runtime.
3
+ // `code` values must be ones OpenAI's API accepts (codex CLI passes via -c model="...").
4
+ export const CODEX_MODELS = [
5
+ {
6
+ code: "gpt-4o",
7
+ label: "GPT-4o",
8
+ tier: "premium",
9
+ contextWindow: 128_000,
10
+ supportsVision: true,
11
+ supportsToolUse: true,
12
+ default: true,
13
+ },
14
+ {
15
+ code: "gpt-4o-mini",
16
+ label: "GPT-4o mini",
17
+ tier: "standard",
18
+ contextWindow: 128_000,
19
+ supportsVision: true,
20
+ supportsToolUse: true,
21
+ },
22
+ ];
23
+ // codex 的 mode = sandbox_mode
24
+ export const CODEX_MODES = [
25
+ { code: "read-only", label: "Read Only", description: "No FS writes" },
26
+ { code: "workspace-write", label: "Workspace Write", description: "Write inside cwd only", default: true },
27
+ { code: "full-access", label: "Full Access", description: "No sandbox" },
28
+ ];
29
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAEA,uEAAuE;AACvE,6EAA6E;AAC7E,yFAAyF;AACzF,MAAM,CAAC,MAAM,YAAY,GAAsB;IAC7C;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,SAAS;QACf,aAAa,EAAE,OAAO;QACtB,cAAc,EAAE,IAAI;QACpB,eAAe,EAAE,IAAI;QACrB,OAAO,EAAE,IAAI;KACd;IACD;QACE,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,aAAa;QACpB,IAAI,EAAE,UAAU;QAChB,aAAa,EAAE,OAAO;QACtB,cAAc,EAAE,IAAI;QACpB,eAAe,EAAE,IAAI;KACtB;CACF,CAAC;AAEF,8BAA8B;AAC9B,MAAM,CAAC,MAAM,WAAW,GAAqB;IAC3C,EAAE,IAAI,EAAE,WAAW,EAAQ,KAAK,EAAE,WAAW,EAAQ,WAAW,EAAE,cAAc,EAAE;IAClF,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,uBAAuB,EAAE,OAAO,EAAE,IAAI,EAAE;IAC1G,EAAE,IAAI,EAAE,aAAa,EAAM,KAAK,EAAE,aAAa,EAAM,WAAW,EAAE,YAAY,EAAE;CACjF,CAAC"}
@@ -0,0 +1,171 @@
1
+ import { type ChildProcess } from "node:child_process";
2
+ import { EventEmitter } from "node:events";
3
+ import type { BackendAdapter, BackendProcess, PermissionDecision, PermissionRequest, SpawnOptions } from "@mclawnet/backend-types";
4
+ import type { BackendManifestEntry } from "@mclawnet/shared";
5
+ import { JsonRpcClient } from "./json-rpc-client.js";
6
+ import { type ApprovalWireFamily } from "./permission-mapper.js";
7
+ import { type DetectResult } from "./detect.js";
8
+ /** Test-only: clear the resolved-bin cache between cases. */
9
+ export declare function __resetResolvedCodexBinCache(): void;
10
+ /** Test-only: how many times resolveCodexBin actually ran (i.e. cache misses). */
11
+ export declare function __getResolveCodexBinCallCount(): number;
12
+ /**
13
+ * Resolve how to spawn codex without invoking cmd.exe on Windows.
14
+ *
15
+ * codex.cmd is an npm shim that dispatches to the real Rust .exe (or in
16
+ * some installs, a node-shimmed cli.js). Spawning the .cmd directly:
17
+ * - requires `shell: true` post-CVE-2024-27980, which would also have to
18
+ * deal with cmd.exe's 8191-char command-line cap.
19
+ * - drops the child's stdio attachment in subtle ways through cmd.exe.
20
+ *
21
+ * So we parse the shim and spawn the underlying .exe (or `node cli.js`)
22
+ * directly. Mirrors claude-adapter's resolveSpawnTarget pattern.
23
+ */
24
+ export declare function resolveSpawnTarget(codexBin: string): {
25
+ command: string;
26
+ prefixArgs: string[];
27
+ };
28
+ /**
29
+ * Per-process state wrapper. Holds the JSON-RPC client + pending approval
30
+ * resolvers so `respondToPermission` can find the right server-initiated
31
+ * request id to reply to.
32
+ */
33
+ export declare class CodexProcess extends EventEmitter implements BackendProcess {
34
+ readonly id: string;
35
+ readonly workDir: string;
36
+ pid?: number;
37
+ proc?: ChildProcess;
38
+ private killed;
39
+ /** callId → JSON-RPC server-request id we still owe a result to. */
40
+ pendingApprovals: Map<string, number>;
41
+ /** callId → originating wire family (drives v2/legacy/mcp reply shape). */
42
+ approvalMethods: Map<string, ApprovalWireFamily>;
43
+ /** Monotonic counter for approval resolver keys. */
44
+ private nextResolverKey;
45
+ /** Resolver registered by the rpc client for each pending approval. */
46
+ approvalResolvers: Map<number, (reply: Record<string, string>) => void>;
47
+ rpc?: JsonRpcClient;
48
+ backendSessionId?: string;
49
+ /**
50
+ * Thread id to resume on agent restart. When set, handshake() sends
51
+ * `thread/resume { threadId }` instead of `thread/start { cwd }`.
52
+ * Set from SpawnOptions.resumeId in CodexAdapter.spawn(). Must NOT be
53
+ * passed as `--resume` CLI flag — app-server rejects it.
54
+ */
55
+ resumeId?: string;
56
+ /** True once `initialize` round-trip + `thread/start` (or `thread/resume`) have completed. */
57
+ handshakeComplete: boolean;
58
+ /** Inputs queued by send() while the handshake is still in flight. */
59
+ pendingInputs: string[];
60
+ /**
61
+ * Per-itemId accumulator of assistant text already emitted via
62
+ * `item/agentMessage/delta` chunks. Consulted on `item/completed` with
63
+ * `item.type === "agentMessage"` to decide what (if anything) of the
64
+ * completed payload still needs to be emitted. Without this, codex
65
+ * delivers the same assistant text twice (deltas + final) and the UI
66
+ * renders "Hello worldHello world".
67
+ *
68
+ * Entries are cleared per item when the matching `item/completed` fires.
69
+ * We do NOT clear the whole map on `turn/completed`: codex's wire
70
+ * doesn't strictly guarantee that every item/completed precedes its
71
+ * parent turn/completed, and a trailing item/completed against an empty
72
+ * accumulator would re-emit the full text (reactivating the bug). To
73
+ * cap unbounded growth from interrupted turns that never fire
74
+ * completed, the map is size-bounded with FIFO eviction; in practice a
75
+ * single turn produces 1-2 agentMessage items so the cap is generous.
76
+ */
77
+ assistantTextSeen: Map<string, string>;
78
+ /** Temp file for briefing injection; cleaned up on kill(). */
79
+ briefingFile?: string;
80
+ /**
81
+ * Set by CodexAdapter.spawn() so handshake() can surface real process-exit
82
+ * info if the codex CLI died (e.g. arg parse error). Without this, a dead
83
+ * process leads to a generic "handshake timeout" instead of the actual
84
+ * "error: unexpected argument …" stderr message.
85
+ *
86
+ * Default returns (null, "") for tests that construct CodexProcess
87
+ * directly without going through spawn() (e.g. attachRpc mock pipes).
88
+ * spawn() overrides these with real getters wired to the child process.
89
+ */
90
+ getExitInfo: () => {
91
+ code: number | null;
92
+ signal: string | null;
93
+ } | null;
94
+ getStderr: () => string;
95
+ /** Allocate a resolver key. Monotonic; never reused. */
96
+ allocResolverKey(): number;
97
+ constructor(sessionId: string, workDir: string, proc?: ChildProcess);
98
+ kill(): Promise<void>;
99
+ isAlive(): boolean;
100
+ }
101
+ export interface CodexAdapterOptions {
102
+ /** Override path to the codex binary. Default: lookup in PATH. */
103
+ codexBin?: string;
104
+ /**
105
+ * Max time to wait for codex handshake (initialize + thread/start|resume)
106
+ * to complete before emitting an error. Default: 15s. Used to detect the
107
+ * case where the codex process dies mid-handshake (CLI parse error,
108
+ * crash) — without this, pendingInputs stack up forever and the swarm
109
+ * silently stalls.
110
+ */
111
+ handshakeTimeoutMs?: number;
112
+ /**
113
+ * Override the CLI detection probe. Defaults to `detectCodexInstall`.
114
+ * Exposed for tests (ESM module exports aren't configurable, so we
115
+ * inject the dependency instead of spying on the module).
116
+ */
117
+ detect?: () => Promise<DetectResult>;
118
+ }
119
+ /**
120
+ * CodexAdapter — BackendAdapter wrapping `codex app-server --listen stdio://`.
121
+ *
122
+ * M3.S3 scope: spawn/stop/send/onOutput plus the permission flow
123
+ * (`onPermissionRequest` / `respondToPermission`). Resume + token-budget +
124
+ * MCP plumbing land in follow-up slices.
125
+ */
126
+ export declare class CodexAdapter implements BackendAdapter {
127
+ readonly type = "codex";
128
+ private codexBin;
129
+ private handshakeTimeoutMs;
130
+ private detect;
131
+ constructor(options?: CodexAdapterOptions);
132
+ spawn(options: SpawnOptions): Promise<CodexProcess>;
133
+ private buildMcpServerConfig;
134
+ /**
135
+ * Bind a CodexProcess to a JSON-RPC duplex. Public so tests can inject a
136
+ * mocked stdin/stdout pair without spawning a real subprocess.
137
+ *
138
+ * Drives the v2 handshake automatically: `initialize` → `thread/start`.
139
+ * Once `thread/start` resolves, `cp.backendSessionId` is set and a
140
+ * `session_started` event fires so the agent's hub bridge can persist it.
141
+ */
142
+ attachRpc(cp: CodexProcess, stdin: NodeJS.WritableStream, stdout: NodeJS.ReadableStream): void;
143
+ private handshake;
144
+ private wireRpc;
145
+ private handleServerRequest;
146
+ private handleNotification;
147
+ /**
148
+ * Selective notification logging. The previous catch-all `log.debug("codex
149
+ * notification")` fired for every streaming `item/agentMessage/delta` chunk
150
+ * (many per turn), drowning the actually useful events. Instead, log only
151
+ * structural events at INFO and surface unrecognised methods as WARN so the
152
+ * `{kind:"raw"}` degradation isn't silent.
153
+ */
154
+ private logBackendOutput;
155
+ stop(process: BackendProcess): Promise<void>;
156
+ send(process: BackendProcess, input: string): void;
157
+ private dispatchTurn;
158
+ onOutput(process: BackendProcess, handler: (msg: unknown) => void): void;
159
+ onPermissionRequest(process: BackendProcess, handler: (req: PermissionRequest) => void): void;
160
+ respondToPermission(process: BackendProcess, decision: PermissionDecision): Promise<void>;
161
+ onTurnComplete(process: BackendProcess, handler: (info: {
162
+ backendSessionId?: string;
163
+ }) => void): void;
164
+ onSessionStarted(process: BackendProcess, handler: (info: {
165
+ backendSessionId: string;
166
+ }) => void): void;
167
+ onError(process: BackendProcess, handler: (err: Error) => void): void;
168
+ onExit(process: BackendProcess, handler: (code: number | null) => void): void;
169
+ getManifest(): Promise<BackendManifestEntry>;
170
+ }
171
+ //# sourceMappingURL=codex-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-adapter.d.ts","sourceRoot":"","sources":["../src/codex-adapter.ts"],"names":[],"mappings":"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"}