@lyupro/codex-bridge 0.5.3 → 0.5.4
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/CHANGELOG.md +71 -0
- package/README.md +152 -152
- package/bin/codex-bridge.mjs +188 -174
- package/cli/doctor-format.mjs +23 -0
- package/cli/doctor-hooks.mjs +114 -0
- package/cli/doctor-installation.mjs +111 -0
- package/cli/doctor-runs.mjs +55 -0
- package/cli/doctor.mjs +150 -399
- package/cli/host-contract.mjs +135 -0
- package/cli/hosts.mjs +4 -3
- package/cli/install.mjs +31 -8
- package/cli/probe-contract.mjs +174 -0
- package/package.json +49 -49
- package/scripts/run-tests.mjs +4 -1
- package/src/agents/codex-build.md +265 -265
- package/src/agents/codex-review.md +219 -219
- package/src/agents/codex-scout.md +232 -232
- package/src/home/lib/brand-home.mjs +39 -0
- package/src/home/lib/meta/reply.mjs +27 -0
- package/src/home/lib/run-config.mjs +18 -6
- package/src/home/lib/runner/codex-args.mjs +154 -0
- package/src/home/lib/runner/codex-cmd.mjs +268 -398
- package/src/home/lib/runner/launcher.mjs +18 -30
- package/src/home/lib/runner/worker-order.mjs +56 -0
- package/src/home/lib/write-meta.mjs +11 -3
|
@@ -2,15 +2,9 @@
|
|
|
2
2
|
* The half the caller can kill: every preparation and every refusal that costs no quota,
|
|
3
3
|
* then the spawn of the worker and the immediate return; a repeated call waits for the reply.
|
|
4
4
|
*
|
|
5
|
-
* The order it leaves for the worker is worker.json in the run folder — after the split
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* and `args`;
|
|
9
|
-
* worker.mjs reads `repo`, `agent`, `args`, `is_git_repo` and `budget_minutes`. The extra fields
|
|
10
|
-
* are deliberate: `slug`, `order_id`, `launcher_pid` and `scope_new` are what a run folder read
|
|
11
|
-
* back months later needs in order to explain itself — which order it belonged to and which new
|
|
12
|
-
* paths it declared included. Nothing may be dropped from this shape without changing worker.mjs
|
|
13
|
-
* and saying so out loud.
|
|
5
|
+
* The order it leaves for the worker is worker.json in the run folder — after the split that file
|
|
6
|
+
* is the only connection between the two halves. Its shape and the reason for every field live in
|
|
7
|
+
* worker-order.mjs, which writes it.
|
|
14
8
|
*/
|
|
15
9
|
import fs from 'node:fs';
|
|
16
10
|
import path from 'node:path';
|
|
@@ -37,7 +31,9 @@ import { continuationRefusal } from './continuation.mjs';
|
|
|
37
31
|
import { SCHEMAS } from './schemas.mjs';
|
|
38
32
|
import { INSTRUCTIONS } from './prompts.mjs';
|
|
39
33
|
import { git, headSha, branchName, worktreeSnapshot, reviewScope } from './git-state.mjs';
|
|
40
|
-
import { codexArgs,
|
|
34
|
+
import { codexArgs, runMode, runProfile } from './codex-args.mjs';
|
|
35
|
+
import { writeWorkerOrder } from './worker-order.mjs';
|
|
36
|
+
import { requireCodex, unsafeForCmd } from './codex-cmd.mjs';
|
|
41
37
|
import { runsRoot } from './runs-root.mjs';
|
|
42
38
|
import { resolveProjectRunsDir } from './project-dir.mjs';
|
|
43
39
|
import { cleanupRetention } from '../retention.mjs';
|
|
@@ -344,26 +340,18 @@ export async function launcher(argv = process.argv.slice(2)) {
|
|
|
344
340
|
console.log(reply);
|
|
345
341
|
return 1;
|
|
346
342
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
budget_minutes: RUN_ENV?.budgets?.[runMode(opts.agent)],
|
|
360
|
-
scope_new: opts.scopeNewPatterns,
|
|
361
|
-
args: codexArgv,
|
|
362
|
-
},
|
|
363
|
-
null,
|
|
364
|
-
2,
|
|
365
|
-
)}\n`,
|
|
366
|
-
);
|
|
343
|
+
writeWorkerOrder(runDir, {
|
|
344
|
+
agent: opts.agent,
|
|
345
|
+
slug: opts.slug,
|
|
346
|
+
orderId: opts.orderId,
|
|
347
|
+
repo: repoRoot,
|
|
348
|
+
isGitRepo,
|
|
349
|
+
launcherPid: process.pid,
|
|
350
|
+
budgetMinutes: RUN_ENV?.budgets?.[runMode(opts.agent)],
|
|
351
|
+
scopeNew: opts.scopeNewPatterns,
|
|
352
|
+
profile: runProfile({ ...opts, repo: repoRoot }),
|
|
353
|
+
args: codexArgv,
|
|
354
|
+
});
|
|
367
355
|
|
|
368
356
|
// detached + unref + no stdio: the worker leaves the caller's process group, so a Ctrl+C
|
|
369
357
|
// or a timeout kill aimed at the dispatcher's shell does not reach it, and it holds no
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The order the launcher leaves for the worker: worker.json in the run folder.
|
|
3
|
+
*
|
|
4
|
+
* After the launcher/worker split this file is the only connection between the two halves, and the
|
|
5
|
+
* worker never re-reads the CLI or the configuration — a run that consulted config.json twice could
|
|
6
|
+
* end up honouring two different limits. It carries `agent`, `slug`, `order_id`, `repo`,
|
|
7
|
+
* `is_git_repo`, `launcher_pid`, `budget_minutes`, `scope_new`, `profile` and `args`; worker.mjs
|
|
8
|
+
* reads `repo`, `agent`, `args`, `is_git_repo` and `budget_minutes`. The rest is deliberate: those
|
|
9
|
+
* fields are what a run folder read back months later needs in order to explain itself — which
|
|
10
|
+
* order it belonged to, which new paths it declared included, and which worker was actually
|
|
11
|
+
* ordered. Nothing may be dropped from this shape without changing worker.mjs and saying so out
|
|
12
|
+
* loud; `docs/worker-contract.md` is the document that says it.
|
|
13
|
+
*
|
|
14
|
+
* It lives beside launcher.mjs rather than inside it because writing this contract is its own job,
|
|
15
|
+
* and the launcher had grown past the size limit while the contract was buried in the middle of it.
|
|
16
|
+
*/
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
|
|
20
|
+
export function workerOrder({
|
|
21
|
+
agent,
|
|
22
|
+
slug,
|
|
23
|
+
orderId,
|
|
24
|
+
repo,
|
|
25
|
+
isGitRepo,
|
|
26
|
+
launcherPid,
|
|
27
|
+
budgetMinutes,
|
|
28
|
+
scopeNew,
|
|
29
|
+
profile,
|
|
30
|
+
args,
|
|
31
|
+
}) {
|
|
32
|
+
return {
|
|
33
|
+
agent,
|
|
34
|
+
slug,
|
|
35
|
+
order_id: orderId,
|
|
36
|
+
repo,
|
|
37
|
+
is_git_repo: isGitRepo,
|
|
38
|
+
launcher_pid: launcherPid,
|
|
39
|
+
// The mode's wall-clock budget, resolved by the launcher and never re-read by the worker.
|
|
40
|
+
budget_minutes: budgetMinutes,
|
|
41
|
+
scope_new: scopeNew,
|
|
42
|
+
// Which worker was ordered, and where each half of that answer came from. Recorded separately
|
|
43
|
+
// from args because an absent flag says nothing: a run without `-m` looked exactly like a run
|
|
44
|
+
// whose configured model had never been read, which is how a pinned profile went unnoticed for
|
|
45
|
+
// three releases (2026-08-26).
|
|
46
|
+
profile,
|
|
47
|
+
args,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function writeWorkerOrder(runDir, fields) {
|
|
52
|
+
fs.writeFileSync(
|
|
53
|
+
path.join(runDir, 'worker.json'),
|
|
54
|
+
`${JSON.stringify(workerOrder(fields), null, 2)}\n`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -28,7 +28,7 @@ import { splitRunChanges } from './meta/environment.mjs';
|
|
|
28
28
|
import { readEvents } from './meta/events.mjs';
|
|
29
29
|
import { writeStatus } from './meta/run-state.mjs';
|
|
30
30
|
import { resolveStatus } from './meta/verdict.mjs';
|
|
31
|
-
import { AGENTS, failReply, limitReply } from './meta/reply.mjs';
|
|
31
|
+
import { AGENTS, failReply, limitReply, withProfileRow } from './meta/reply.mjs';
|
|
32
32
|
|
|
33
33
|
function readRunnerVersion() {
|
|
34
34
|
try {
|
|
@@ -122,7 +122,12 @@ export function collect(runDir, agent, exitCode) {
|
|
|
122
122
|
tokens: events.tokens,
|
|
123
123
|
tokens_reported: events.tokens !== null,
|
|
124
124
|
usage: events.usage,
|
|
125
|
-
|
|
125
|
+
// The ordered profile, straight from worker.json; null for runs made before it was recorded.
|
|
126
|
+
// `model` stays for readers that already know it, and now answers for a run that pinned
|
|
127
|
+
// nothing instead of going quiet: an absent -m used to be indistinguishable from a model
|
|
128
|
+
// the operator had configured and never received (2026-08-26).
|
|
129
|
+
profile: worker?.profile ?? null,
|
|
130
|
+
model: worker?.profile?.model || argValue('-m'),
|
|
126
131
|
sandbox: argValue('--sandbox'),
|
|
127
132
|
// Null for runs made before env.json existed: absence is not the same as "both off".
|
|
128
133
|
env: readJson(path.join(runDir, 'env.json')),
|
|
@@ -143,7 +148,10 @@ export function collect(runDir, agent, exitCode) {
|
|
|
143
148
|
file: (name) => path.join(runDir, name),
|
|
144
149
|
};
|
|
145
150
|
const reply =
|
|
146
|
-
|
|
151
|
+
withProfileRow(
|
|
152
|
+
status === 'OK' ? cfg.reply(ctx) : status === 'LIMIT' ? limitReply(ctx, meta) : failReply(ctx, meta),
|
|
153
|
+
meta,
|
|
154
|
+
);
|
|
147
155
|
|
|
148
156
|
return { meta, reply: reply.join('\n') };
|
|
149
157
|
}
|