@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.
- package/dist/catalog.d.ts +4 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +29 -0
- package/dist/catalog.js.map +1 -0
- package/dist/codex-adapter.d.ts +171 -0
- package/dist/codex-adapter.d.ts.map +1 -0
- package/dist/codex-adapter.js +911 -0
- package/dist/codex-adapter.js.map +1 -0
- package/dist/codex-spawn-args.d.ts +44 -0
- package/dist/codex-spawn-args.d.ts.map +1 -0
- package/dist/codex-spawn-args.js +117 -0
- package/dist/codex-spawn-args.js.map +1 -0
- package/dist/detect.d.ts +17 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +32 -0
- package/dist/detect.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/json-rpc-client.d.ts +37 -0
- package/dist/json-rpc-client.d.ts.map +1 -0
- package/dist/json-rpc-client.js +121 -0
- package/dist/json-rpc-client.js.map +1 -0
- package/dist/output-mapper.d.ts +15 -0
- package/dist/output-mapper.d.ts.map +1 -0
- package/dist/output-mapper.js +111 -0
- package/dist/output-mapper.js.map +1 -0
- package/dist/permission-mapper.d.ts +52 -0
- package/dist/permission-mapper.d.ts.map +1 -0
- package/dist/permission-mapper.js +146 -0
- package/dist/permission-mapper.js.map +1 -0
- package/package.json +32 -0
|
@@ -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"}
|
package/dist/catalog.js
ADDED
|
@@ -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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"}
|