@fixy/codex-adapter 0.0.2 → 0.0.3
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/.omc/state/last-tool-error.json +7 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +171 -1
- package/dist/index.js.map +1 -1
- package/dist/parse.d.ts +6 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +56 -0
- package/dist/parse.js.map +1 -0
- package/package.json +6 -2
- package/src/index.ts +201 -1
- package/src/parse.ts +65 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tool_name": "Bash",
|
|
3
|
+
"tool_input_preview": "{\"command\":\"pnpm tsc --noEmit 2>&1\",\"description\":\"Typecheck codex-adapter directly\"}",
|
|
4
|
+
"error": "Exit code 2\nsrc/index.ts(51,14): error TS2591: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.\n[rerun: b14]",
|
|
5
|
+
"timestamp": "2026-04-12T11:48:20.179Z",
|
|
6
|
+
"retry_count": 1
|
|
7
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EAIZ,MAAM,YAAY,CAAC;AA6LpB,wBAAgB,kBAAkB,IAAI,WAAW,CAEhD;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,172 @@
|
|
|
1
|
-
|
|
1
|
+
// packages/codex-adapter/src/index.ts
|
|
2
|
+
import { buildInheritedEnv, redactEnvForLogs, resolveCommand, runChildProcess, } from '@fixy/adapter-utils';
|
|
3
|
+
import { parseCodexStreamJson } from './parse.js';
|
|
4
|
+
// Codex CLI writes skill-loader errors to stderr on startup — suppress them.
|
|
5
|
+
const CODEX_NOISE_RE = /^[0-9TZ:.+-]+ (ERROR|WARN) codex_/;
|
|
6
|
+
function filterCodexNoise(stderr) {
|
|
7
|
+
return stderr
|
|
8
|
+
.split('\n')
|
|
9
|
+
.filter((line) => !CODEX_NOISE_RE.test(line))
|
|
10
|
+
.join('\n')
|
|
11
|
+
.trim();
|
|
12
|
+
}
|
|
13
|
+
class CodexAdapter {
|
|
14
|
+
id = 'codex';
|
|
15
|
+
name = 'Codex CLI';
|
|
16
|
+
async probe() {
|
|
17
|
+
let resolvedCommand;
|
|
18
|
+
try {
|
|
19
|
+
resolvedCommand = await resolveCommand('codex');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {
|
|
23
|
+
available: false,
|
|
24
|
+
version: null,
|
|
25
|
+
authStatus: 'unknown',
|
|
26
|
+
detail: 'codex CLI not found in PATH',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const result = await runChildProcess({
|
|
31
|
+
command: resolvedCommand,
|
|
32
|
+
args: ['--version'],
|
|
33
|
+
cwd: process.cwd(),
|
|
34
|
+
env: buildInheritedEnv(),
|
|
35
|
+
timeoutMs: 10_000,
|
|
36
|
+
});
|
|
37
|
+
const version = result.stdout.trim() || null;
|
|
38
|
+
return {
|
|
39
|
+
available: true,
|
|
40
|
+
version,
|
|
41
|
+
authStatus: 'unknown',
|
|
42
|
+
detail: null,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
47
|
+
return {
|
|
48
|
+
available: false,
|
|
49
|
+
version: null,
|
|
50
|
+
authStatus: 'unknown',
|
|
51
|
+
detail,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async execute(ctx) {
|
|
56
|
+
const resolvedCommand = await resolveCommand('codex');
|
|
57
|
+
const env = buildInheritedEnv({});
|
|
58
|
+
let args;
|
|
59
|
+
if (ctx.session) {
|
|
60
|
+
// Resume existing session: codex exec resume <thread_id> --json ...
|
|
61
|
+
args = [
|
|
62
|
+
'exec',
|
|
63
|
+
'resume',
|
|
64
|
+
ctx.session.sessionId,
|
|
65
|
+
'--json',
|
|
66
|
+
'--skip-git-repo-check',
|
|
67
|
+
'--full-auto',
|
|
68
|
+
ctx.prompt,
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// New session: codex exec --json --skip-git-repo-check --full-auto <prompt>
|
|
73
|
+
args = [
|
|
74
|
+
'exec',
|
|
75
|
+
'--json',
|
|
76
|
+
'--skip-git-repo-check',
|
|
77
|
+
'--full-auto',
|
|
78
|
+
ctx.prompt,
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
ctx.onMeta({
|
|
82
|
+
resolvedCommand,
|
|
83
|
+
args,
|
|
84
|
+
cwd: ctx.threadContext.worktreePath,
|
|
85
|
+
env: redactEnvForLogs(env),
|
|
86
|
+
});
|
|
87
|
+
// Buffer partial stdout lines so we can parse JSONL events and forward
|
|
88
|
+
// only the agent text instead of raw JSON to the terminal.
|
|
89
|
+
let stdoutLineBuffer = '';
|
|
90
|
+
const forwardJsonLine = (line) => {
|
|
91
|
+
try {
|
|
92
|
+
const obj = JSON.parse(line);
|
|
93
|
+
if (typeof obj === 'object' &&
|
|
94
|
+
obj !== null &&
|
|
95
|
+
obj['type'] === 'item.completed') {
|
|
96
|
+
const item = obj['item'];
|
|
97
|
+
if (typeof item === 'object' &&
|
|
98
|
+
item !== null &&
|
|
99
|
+
item['type'] === 'agent_message') {
|
|
100
|
+
const text = item['text'];
|
|
101
|
+
if (typeof text === 'string' && text.length > 0) {
|
|
102
|
+
ctx.onLog('stdout', text + '\n');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Not JSON — forward as-is (shouldn't happen with --json flag)
|
|
109
|
+
if (line.length > 0)
|
|
110
|
+
ctx.onLog('stdout', line + '\n');
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const result = await runChildProcess({
|
|
114
|
+
command: resolvedCommand,
|
|
115
|
+
args,
|
|
116
|
+
cwd: ctx.threadContext.worktreePath,
|
|
117
|
+
env,
|
|
118
|
+
signal: ctx.signal,
|
|
119
|
+
onLog: (stream, chunk) => {
|
|
120
|
+
if (stream === 'stderr') {
|
|
121
|
+
const filtered = filterCodexNoise(chunk);
|
|
122
|
+
if (filtered.length > 0)
|
|
123
|
+
ctx.onLog('stderr', filtered);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// stdout: buffer and parse JSONL line-by-line
|
|
127
|
+
stdoutLineBuffer += chunk;
|
|
128
|
+
const lines = stdoutLineBuffer.split('\n');
|
|
129
|
+
stdoutLineBuffer = lines.pop() ?? '';
|
|
130
|
+
for (const line of lines) {
|
|
131
|
+
const trimmed = line.trim();
|
|
132
|
+
if (trimmed.length > 0)
|
|
133
|
+
forwardJsonLine(trimmed);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
onSpawn: ctx.onSpawn,
|
|
137
|
+
});
|
|
138
|
+
// Flush any remaining buffered line
|
|
139
|
+
if (stdoutLineBuffer.trim().length > 0) {
|
|
140
|
+
forwardJsonLine(stdoutLineBuffer.trim());
|
|
141
|
+
}
|
|
142
|
+
const parsed = parseCodexStreamJson(result.stdout);
|
|
143
|
+
const warnings = [];
|
|
144
|
+
const filteredStderr = filterCodexNoise(result.stderr ?? '');
|
|
145
|
+
if (filteredStderr.length > 0) {
|
|
146
|
+
warnings.push(filteredStderr);
|
|
147
|
+
}
|
|
148
|
+
let errorMessage = null;
|
|
149
|
+
if (result.exitCode !== 0 && !result.timedOut) {
|
|
150
|
+
const stderrMsg = filteredStderr;
|
|
151
|
+
errorMessage =
|
|
152
|
+
stderrMsg.length > 0
|
|
153
|
+
? `codex exited with code ${result.exitCode}: ${stderrMsg}`
|
|
154
|
+
: `codex exited with code ${result.exitCode}`;
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
exitCode: result.exitCode,
|
|
158
|
+
signal: result.signal,
|
|
159
|
+
timedOut: result.timedOut,
|
|
160
|
+
summary: parsed.summary,
|
|
161
|
+
session: parsed.sessionId ? { sessionId: parsed.sessionId, params: {} } : null,
|
|
162
|
+
patches: [],
|
|
163
|
+
warnings,
|
|
164
|
+
errorMessage,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export function createCodexAdapter() {
|
|
169
|
+
return new CodexAdapter();
|
|
170
|
+
}
|
|
171
|
+
export { parseCodexStreamJson } from './parse.js';
|
|
2
172
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAkBD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CA2CtE"}
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function tryParseJson(line) {
|
|
2
|
+
try {
|
|
3
|
+
const parsed = JSON.parse(line);
|
|
4
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
5
|
+
return parsed;
|
|
6
|
+
}
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function asString(value, fallback) {
|
|
14
|
+
return typeof value === 'string' && value.length > 0 ? value : fallback;
|
|
15
|
+
}
|
|
16
|
+
export function parseCodexStreamJson(stdout) {
|
|
17
|
+
if (stdout.length === 0) {
|
|
18
|
+
return { sessionId: null, summary: '' };
|
|
19
|
+
}
|
|
20
|
+
let sessionId = null;
|
|
21
|
+
const agentTexts = [];
|
|
22
|
+
const lines = stdout.split('\n');
|
|
23
|
+
for (const rawLine of lines) {
|
|
24
|
+
const line = rawLine.trim();
|
|
25
|
+
if (line.length === 0)
|
|
26
|
+
continue;
|
|
27
|
+
const obj = tryParseJson(line);
|
|
28
|
+
if (obj === null)
|
|
29
|
+
continue;
|
|
30
|
+
const type = obj['type'];
|
|
31
|
+
if (type === 'thread.started') {
|
|
32
|
+
// {"type":"thread.started","thread_id":"<uuid>"}
|
|
33
|
+
const tid = asString(obj['thread_id'], '');
|
|
34
|
+
if (tid !== '')
|
|
35
|
+
sessionId = tid;
|
|
36
|
+
}
|
|
37
|
+
else if (type === 'item.completed') {
|
|
38
|
+
// {"type":"item.completed","item":{"id":"...","type":"agent_message","text":"..."}}
|
|
39
|
+
const item = obj['item'];
|
|
40
|
+
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
|
|
41
|
+
const it = item;
|
|
42
|
+
if (it['type'] === 'agent_message') {
|
|
43
|
+
const text = asString(it['text'], '');
|
|
44
|
+
if (text !== '')
|
|
45
|
+
agentTexts.push(text);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const summary = agentTexts.join('\n\n').trim();
|
|
51
|
+
if (summary === '' && sessionId === null) {
|
|
52
|
+
return { sessionId: null, summary: stdout };
|
|
53
|
+
}
|
|
54
|
+
return { sessionId, summary };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAKA,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5E,OAAO,MAAiC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc,EAAE,QAAgB;IAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC1C,CAAC;IAED,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEjC,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAEhC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,GAAG,KAAK,IAAI;YAAE,SAAS;QAE3B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QAEzB,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;YAC9B,iDAAiD;YACjD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3C,IAAI,GAAG,KAAK,EAAE;gBAAE,SAAS,GAAG,GAAG,CAAC;QAClC,CAAC;aAAM,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;YACrC,oFAAoF;YACpF,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;YACzB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtE,MAAM,EAAE,GAAG,IAA+B,CAAC;gBAC3C,IAAI,EAAE,CAAC,MAAM,CAAC,KAAK,eAAe,EAAE,CAAC;oBACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;oBACtC,IAAI,IAAI,KAAK,EAAE;wBAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAE/C,IAAI,OAAO,KAAK,EAAE,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACzC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAC9C,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fixy/codex-adapter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@fixy/
|
|
14
|
+
"@fixy/core": "0.0.3",
|
|
15
|
+
"@fixy/adapter-utils": "0.0.3"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^20.0.0"
|
|
15
19
|
},
|
|
16
20
|
"license": "MIT",
|
|
17
21
|
"scripts": {
|
package/src/index.ts
CHANGED
|
@@ -1 +1,201 @@
|
|
|
1
|
-
|
|
1
|
+
// packages/codex-adapter/src/index.ts
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
FixyAdapter,
|
|
5
|
+
FixyProbeResult,
|
|
6
|
+
FixyExecutionContext,
|
|
7
|
+
FixyExecutionResult,
|
|
8
|
+
} from '@fixy/core';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
buildInheritedEnv,
|
|
12
|
+
redactEnvForLogs,
|
|
13
|
+
resolveCommand,
|
|
14
|
+
runChildProcess,
|
|
15
|
+
} from '@fixy/adapter-utils';
|
|
16
|
+
|
|
17
|
+
import { parseCodexStreamJson } from './parse.js';
|
|
18
|
+
|
|
19
|
+
// Codex CLI writes skill-loader errors to stderr on startup — suppress them.
|
|
20
|
+
const CODEX_NOISE_RE = /^[0-9TZ:.+-]+ (ERROR|WARN) codex_/;
|
|
21
|
+
|
|
22
|
+
function filterCodexNoise(stderr: string): string {
|
|
23
|
+
return stderr
|
|
24
|
+
.split('\n')
|
|
25
|
+
.filter((line) => !CODEX_NOISE_RE.test(line))
|
|
26
|
+
.join('\n')
|
|
27
|
+
.trim();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class CodexAdapter implements FixyAdapter {
|
|
31
|
+
readonly id = 'codex';
|
|
32
|
+
readonly name = 'Codex CLI';
|
|
33
|
+
|
|
34
|
+
async probe(): Promise<FixyProbeResult> {
|
|
35
|
+
let resolvedCommand: string;
|
|
36
|
+
try {
|
|
37
|
+
resolvedCommand = await resolveCommand('codex');
|
|
38
|
+
} catch {
|
|
39
|
+
return {
|
|
40
|
+
available: false,
|
|
41
|
+
version: null,
|
|
42
|
+
authStatus: 'unknown',
|
|
43
|
+
detail: 'codex CLI not found in PATH',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const result = await runChildProcess({
|
|
49
|
+
command: resolvedCommand,
|
|
50
|
+
args: ['--version'],
|
|
51
|
+
cwd: process.cwd(),
|
|
52
|
+
env: buildInheritedEnv(),
|
|
53
|
+
timeoutMs: 10_000,
|
|
54
|
+
});
|
|
55
|
+
const version = result.stdout.trim() || null;
|
|
56
|
+
return {
|
|
57
|
+
available: true,
|
|
58
|
+
version,
|
|
59
|
+
authStatus: 'unknown',
|
|
60
|
+
detail: null,
|
|
61
|
+
};
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
64
|
+
return {
|
|
65
|
+
available: false,
|
|
66
|
+
version: null,
|
|
67
|
+
authStatus: 'unknown',
|
|
68
|
+
detail,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async execute(ctx: FixyExecutionContext): Promise<FixyExecutionResult> {
|
|
74
|
+
const resolvedCommand = await resolveCommand('codex');
|
|
75
|
+
const env = buildInheritedEnv({});
|
|
76
|
+
|
|
77
|
+
let args: string[];
|
|
78
|
+
|
|
79
|
+
if (ctx.session) {
|
|
80
|
+
// Resume existing session: codex exec resume <thread_id> --json ...
|
|
81
|
+
args = [
|
|
82
|
+
'exec',
|
|
83
|
+
'resume',
|
|
84
|
+
ctx.session.sessionId,
|
|
85
|
+
'--json',
|
|
86
|
+
'--skip-git-repo-check',
|
|
87
|
+
'--full-auto',
|
|
88
|
+
ctx.prompt,
|
|
89
|
+
];
|
|
90
|
+
} else {
|
|
91
|
+
// New session: codex exec --json --skip-git-repo-check --full-auto <prompt>
|
|
92
|
+
args = [
|
|
93
|
+
'exec',
|
|
94
|
+
'--json',
|
|
95
|
+
'--skip-git-repo-check',
|
|
96
|
+
'--full-auto',
|
|
97
|
+
ctx.prompt,
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
ctx.onMeta({
|
|
102
|
+
resolvedCommand,
|
|
103
|
+
args,
|
|
104
|
+
cwd: ctx.threadContext.worktreePath,
|
|
105
|
+
env: redactEnvForLogs(env),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Buffer partial stdout lines so we can parse JSONL events and forward
|
|
109
|
+
// only the agent text instead of raw JSON to the terminal.
|
|
110
|
+
let stdoutLineBuffer = '';
|
|
111
|
+
|
|
112
|
+
const forwardJsonLine = (line: string): void => {
|
|
113
|
+
try {
|
|
114
|
+
const obj = JSON.parse(line);
|
|
115
|
+
if (
|
|
116
|
+
typeof obj === 'object' &&
|
|
117
|
+
obj !== null &&
|
|
118
|
+
obj['type'] === 'item.completed'
|
|
119
|
+
) {
|
|
120
|
+
const item = obj['item'];
|
|
121
|
+
if (
|
|
122
|
+
typeof item === 'object' &&
|
|
123
|
+
item !== null &&
|
|
124
|
+
item['type'] === 'agent_message'
|
|
125
|
+
) {
|
|
126
|
+
const text = item['text'];
|
|
127
|
+
if (typeof text === 'string' && text.length > 0) {
|
|
128
|
+
ctx.onLog('stdout', text + '\n');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch {
|
|
133
|
+
// Not JSON — forward as-is (shouldn't happen with --json flag)
|
|
134
|
+
if (line.length > 0) ctx.onLog('stdout', line + '\n');
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const result = await runChildProcess({
|
|
139
|
+
command: resolvedCommand,
|
|
140
|
+
args,
|
|
141
|
+
cwd: ctx.threadContext.worktreePath,
|
|
142
|
+
env,
|
|
143
|
+
signal: ctx.signal,
|
|
144
|
+
onLog: (stream, chunk) => {
|
|
145
|
+
if (stream === 'stderr') {
|
|
146
|
+
const filtered = filterCodexNoise(chunk);
|
|
147
|
+
if (filtered.length > 0) ctx.onLog('stderr', filtered);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// stdout: buffer and parse JSONL line-by-line
|
|
151
|
+
stdoutLineBuffer += chunk;
|
|
152
|
+
const lines = stdoutLineBuffer.split('\n');
|
|
153
|
+
stdoutLineBuffer = lines.pop() ?? '';
|
|
154
|
+
for (const line of lines) {
|
|
155
|
+
const trimmed = line.trim();
|
|
156
|
+
if (trimmed.length > 0) forwardJsonLine(trimmed);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
onSpawn: ctx.onSpawn,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Flush any remaining buffered line
|
|
163
|
+
if (stdoutLineBuffer.trim().length > 0) {
|
|
164
|
+
forwardJsonLine(stdoutLineBuffer.trim());
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const parsed = parseCodexStreamJson(result.stdout);
|
|
168
|
+
|
|
169
|
+
const warnings: string[] = [];
|
|
170
|
+
const filteredStderr = filterCodexNoise(result.stderr ?? '');
|
|
171
|
+
if (filteredStderr.length > 0) {
|
|
172
|
+
warnings.push(filteredStderr);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let errorMessage: string | null = null;
|
|
176
|
+
if (result.exitCode !== 0 && !result.timedOut) {
|
|
177
|
+
const stderrMsg = filteredStderr;
|
|
178
|
+
errorMessage =
|
|
179
|
+
stderrMsg.length > 0
|
|
180
|
+
? `codex exited with code ${result.exitCode}: ${stderrMsg}`
|
|
181
|
+
: `codex exited with code ${result.exitCode}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
exitCode: result.exitCode,
|
|
186
|
+
signal: result.signal,
|
|
187
|
+
timedOut: result.timedOut,
|
|
188
|
+
summary: parsed.summary,
|
|
189
|
+
session: parsed.sessionId ? { sessionId: parsed.sessionId, params: {} } : null,
|
|
190
|
+
patches: [],
|
|
191
|
+
warnings,
|
|
192
|
+
errorMessage,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function createCodexAdapter(): FixyAdapter {
|
|
198
|
+
return new CodexAdapter();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export { parseCodexStreamJson } from './parse.js';
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface CodexStreamResult {
|
|
2
|
+
sessionId: string | null;
|
|
3
|
+
summary: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function tryParseJson(line: string): Record<string, unknown> | null {
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(line);
|
|
9
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
10
|
+
return parsed as Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function asString(value: unknown, fallback: string): string {
|
|
19
|
+
return typeof value === 'string' && value.length > 0 ? value : fallback;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function parseCodexStreamJson(stdout: string): CodexStreamResult {
|
|
23
|
+
if (stdout.length === 0) {
|
|
24
|
+
return { sessionId: null, summary: '' };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let sessionId: string | null = null;
|
|
28
|
+
const agentTexts: string[] = [];
|
|
29
|
+
|
|
30
|
+
const lines = stdout.split('\n');
|
|
31
|
+
|
|
32
|
+
for (const rawLine of lines) {
|
|
33
|
+
const line = rawLine.trim();
|
|
34
|
+
if (line.length === 0) continue;
|
|
35
|
+
|
|
36
|
+
const obj = tryParseJson(line);
|
|
37
|
+
if (obj === null) continue;
|
|
38
|
+
|
|
39
|
+
const type = obj['type'];
|
|
40
|
+
|
|
41
|
+
if (type === 'thread.started') {
|
|
42
|
+
// {"type":"thread.started","thread_id":"<uuid>"}
|
|
43
|
+
const tid = asString(obj['thread_id'], '');
|
|
44
|
+
if (tid !== '') sessionId = tid;
|
|
45
|
+
} else if (type === 'item.completed') {
|
|
46
|
+
// {"type":"item.completed","item":{"id":"...","type":"agent_message","text":"..."}}
|
|
47
|
+
const item = obj['item'];
|
|
48
|
+
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
|
|
49
|
+
const it = item as Record<string, unknown>;
|
|
50
|
+
if (it['type'] === 'agent_message') {
|
|
51
|
+
const text = asString(it['text'], '');
|
|
52
|
+
if (text !== '') agentTexts.push(text);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const summary = agentTexts.join('\n\n').trim();
|
|
59
|
+
|
|
60
|
+
if (summary === '' && sessionId === null) {
|
|
61
|
+
return { sessionId: null, summary: stdout };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { sessionId, summary };
|
|
65
|
+
}
|