oh-my-codex 0.16.1 → 0.16.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/Cargo.lock +5 -5
- package/Cargo.toml +1 -1
- package/README.md +2 -2
- package/dist/cli/__tests__/doctor-warning-copy.test.js +37 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +2 -2
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +173 -3
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +58 -0
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +1 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +8 -0
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +82 -2
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +12 -0
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +161 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +14 -9
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +44 -2
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +109 -19
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +17 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +3 -4
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +34 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +72 -6
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +54 -15
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/ultragoal.d.ts +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +26 -8
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +68 -5
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/cli/update.d.ts +10 -2
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +99 -5
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +269 -2
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +60 -2
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +59 -1
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/wiki-config-contract.test.js +2 -1
- package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +52 -4
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +268 -7
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/generator.d.ts +13 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +207 -12
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +37 -25
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +29 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +20 -4
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +1 -0
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +52 -0
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +148 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js +3 -0
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
- package/dist/hooks/__tests__/wiki-docs-contract.test.js +5 -4
- package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +2 -4
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +145 -6
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/__tests__/wiki-server.test.js +97 -1
- package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
- package/dist/mcp/wiki-server.d.ts.map +1 -1
- package/dist/mcp/wiki-server.js +11 -2
- package/dist/mcp/wiki-server.js.map +1 -1
- package/dist/planning/__tests__/artifacts.test.js +64 -0
- package/dist/planning/__tests__/artifacts.test.js.map +1 -1
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.d.ts +2 -0
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.d.ts.map +1 -0
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +90 -0
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +1 -0
- package/dist/planning/artifacts.d.ts +7 -2
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +62 -8
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/planning/context-pack-status.d.ts +6 -0
- package/dist/planning/context-pack-status.d.ts.map +1 -1
- package/dist/planning/context-pack-status.js +25 -0
- package/dist/planning/context-pack-status.js.map +1 -1
- package/dist/ralph/persistence.d.ts +1 -1
- package/dist/ralph/persistence.d.ts.map +1 -1
- package/dist/ralph/persistence.js +8 -2
- package/dist/ralph/persistence.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +190 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-execution-surface.d.ts +1 -1
- package/dist/scripts/codex-execution-surface.d.ts.map +1 -1
- package/dist/scripts/codex-execution-surface.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +18 -3
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-dispatcher.d.ts +7 -0
- package/dist/scripts/notify-dispatcher.d.ts.map +1 -0
- package/dist/scripts/notify-dispatcher.js +58 -0
- package/dist/scripts/notify-dispatcher.js.map +1 -0
- package/dist/scripts/notify-fallback-watcher.js +4 -0
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +96 -8
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +6 -2
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/visual-verdict.js +3 -3
- package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
- package/dist/scripts/notify-hook.js +124 -0
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +79 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +10 -18
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +1 -20
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +1 -0
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +28 -18
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +1 -0
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/approved-execution.test.js +45 -1
- package/dist/team/__tests__/approved-execution.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +173 -19
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +37 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/approved-execution.d.ts +1 -0
- package/dist/team/approved-execution.d.ts.map +1 -1
- package/dist/team/approved-execution.js +50 -0
- package/dist/team/approved-execution.js.map +1 -1
- package/dist/team/delivery-log.d.ts.map +1 -1
- package/dist/team/delivery-log.js +8 -1
- package/dist/team/delivery-log.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +104 -18
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/state/mailbox.d.ts +1 -0
- package/dist/team/state/mailbox.d.ts.map +1 -1
- package/dist/team/state/mailbox.js +10 -1
- package/dist/team/state/mailbox.js.map +1 -1
- package/dist/team/state-root.js +1 -1
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +2 -2
- package/dist/team/state.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +7 -2
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +17 -4
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +81 -7
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +8 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +4 -0
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +72 -4
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/paths.d.ts +3 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +6 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/wiki/__tests__/ingest.test.js +35 -1
- package/dist/wiki/__tests__/ingest.test.js.map +1 -1
- package/dist/wiki/__tests__/lint.test.js +14 -1
- package/dist/wiki/__tests__/lint.test.js.map +1 -1
- package/dist/wiki/__tests__/query.test.js +28 -3
- package/dist/wiki/__tests__/query.test.js.map +1 -1
- package/dist/wiki/__tests__/session-hooks.test.js +30 -2
- package/dist/wiki/__tests__/session-hooks.test.js.map +1 -1
- package/dist/wiki/__tests__/storage.test.js +62 -22
- package/dist/wiki/__tests__/storage.test.js.map +1 -1
- package/dist/wiki/index.d.ts +2 -2
- package/dist/wiki/index.d.ts.map +1 -1
- package/dist/wiki/index.js +2 -2
- package/dist/wiki/index.js.map +1 -1
- package/dist/wiki/ingest.js +2 -2
- package/dist/wiki/ingest.js.map +1 -1
- package/dist/wiki/lifecycle.d.ts +5 -0
- package/dist/wiki/lifecycle.d.ts.map +1 -1
- package/dist/wiki/lifecycle.js +31 -4
- package/dist/wiki/lifecycle.js.map +1 -1
- package/dist/wiki/lint.d.ts.map +1 -1
- package/dist/wiki/lint.js +12 -8
- package/dist/wiki/lint.js.map +1 -1
- package/dist/wiki/query.d.ts.map +1 -1
- package/dist/wiki/query.js +3 -2
- package/dist/wiki/query.js.map +1 -1
- package/dist/wiki/storage.d.ts +4 -0
- package/dist/wiki/storage.d.ts.map +1 -1
- package/dist/wiki/storage.js +54 -18
- package/dist/wiki/storage.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/plan/SKILL.md +4 -4
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +11 -7
- package/plugins/oh-my-codex/skills/wiki/SKILL.md +5 -5
- package/prompts/planner.md +1 -1
- package/skills/deep-interview/SKILL.md +2 -2
- package/skills/plan/SKILL.md +4 -4
- package/skills/ralplan/SKILL.md +2 -2
- package/skills/ultragoal/SKILL.md +11 -7
- package/skills/wiki/SKILL.md +5 -5
- package/src/scripts/__tests__/codex-native-hook.test.ts +218 -1
- package/src/scripts/codex-execution-surface.ts +2 -0
- package/src/scripts/codex-native-hook.ts +22 -3
- package/src/scripts/notify-dispatcher.ts +74 -0
- package/src/scripts/notify-fallback-watcher.ts +6 -2
- package/src/scripts/notify-hook/ralph-session-resume.ts +117 -8
- package/src/scripts/notify-hook/state-io.ts +4 -2
- package/src/scripts/notify-hook/visual-verdict.ts +3 -3
- package/src/scripts/notify-hook.ts +116 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { afterEach, describe, it, mock } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { existsSync, mkdirSync, utimesSync } from "node:fs";
|
|
4
|
-
import { chmod, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { chmod, lstat, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -450,6 +450,32 @@ describe("cleanupPostLaunchModeStateFiles", () => {
|
|
|
450
450
|
}
|
|
451
451
|
assert.deepEqual(warnings, []);
|
|
452
452
|
});
|
|
453
|
+
it("marks active Ralph state cancelled with interrupted metadata during postLaunch cleanup", async () => {
|
|
454
|
+
const wd = await mkdtemp(join(tmpdir(), "omx-postlaunch-ralph-interrupted-"));
|
|
455
|
+
const sessionId = "sess-postlaunch-ralph-interrupted";
|
|
456
|
+
const sessionStateDir = join(wd, ".omx", "state", "sessions", sessionId);
|
|
457
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
458
|
+
await writeFile(join(sessionStateDir, "ralph-state.json"), JSON.stringify({
|
|
459
|
+
active: true,
|
|
460
|
+
mode: "ralph",
|
|
461
|
+
current_phase: "executing",
|
|
462
|
+
owner_omx_session_id: sessionId,
|
|
463
|
+
}, null, 2), "utf-8");
|
|
464
|
+
try {
|
|
465
|
+
await cleanupPostLaunchModeStateFiles(wd, sessionId, {
|
|
466
|
+
now: () => new Date("2026-05-09T08:00:00.000Z"),
|
|
467
|
+
});
|
|
468
|
+
const ralph = JSON.parse(await readFile(join(sessionStateDir, "ralph-state.json"), "utf-8"));
|
|
469
|
+
assert.equal(ralph.active, false);
|
|
470
|
+
assert.equal(ralph.current_phase, "cancelled");
|
|
471
|
+
assert.equal(ralph.completed_at, "2026-05-09T08:00:00.000Z");
|
|
472
|
+
assert.equal(ralph.interrupted_at, "2026-05-09T08:00:00.000Z");
|
|
473
|
+
assert.equal(ralph.stop_reason, "session_exit");
|
|
474
|
+
}
|
|
475
|
+
finally {
|
|
476
|
+
await rm(wd, { recursive: true, force: true });
|
|
477
|
+
}
|
|
478
|
+
});
|
|
453
479
|
it("does not cancel root mode state during session-scoped postLaunch cleanup", async () => {
|
|
454
480
|
const wd = await mkdtemp(join(tmpdir(), "omx-postlaunch-root-preserve-"));
|
|
455
481
|
const sessionId = "sess-postlaunch-root-preserve";
|
|
@@ -1089,6 +1115,7 @@ describe("project launch scope helpers", () => {
|
|
|
1089
1115
|
].join("\n");
|
|
1090
1116
|
await writeFile(configPath, originalConfig);
|
|
1091
1117
|
await writeFile(join(projectCodexHome, "agents", "planner.toml"), 'name = "planner"\n');
|
|
1118
|
+
await writeFile(join(projectCodexHome, "hooks.json"), '{"hooks":{}}\n');
|
|
1092
1119
|
await writeFile(join(projectCodexHome, "state_5.sqlite"), "state db placeholder");
|
|
1093
1120
|
await writeFile(join(projectCodexHome, "state_5.sqlite-wal"), "state db wal placeholder");
|
|
1094
1121
|
await writeFile(join(projectCodexHome, "logs_2.sqlite-shm"), "logs db shm placeholder");
|
|
@@ -1101,6 +1128,8 @@ describe("project launch scope helpers", () => {
|
|
|
1101
1128
|
assert.equal(prepared.runtimeCodexHomeForCleanup, runtimeCodexHome);
|
|
1102
1129
|
assert.equal(await readFile(join(runtimeCodexHome, "config.toml"), "utf-8"), originalConfig);
|
|
1103
1130
|
assert.equal(await readFile(join(runtimeCodexHome, "agents", "planner.toml"), "utf-8"), 'name = "planner"\n');
|
|
1131
|
+
assert.equal(await readFile(join(runtimeCodexHome, "hooks.json"), "utf-8"), '{"hooks":{}}\n');
|
|
1132
|
+
assert.equal((await lstat(join(runtimeCodexHome, "hooks.json"))).isSymbolicLink(), false);
|
|
1104
1133
|
assert.equal(existsSync(join(runtimeCodexHome, "state_5.sqlite")), false);
|
|
1105
1134
|
assert.equal(existsSync(join(runtimeCodexHome, "state_5.sqlite-wal")), false);
|
|
1106
1135
|
assert.equal(existsSync(join(runtimeCodexHome, "logs_2.sqlite-shm")), false);
|
|
@@ -1116,6 +1145,36 @@ describe("project launch scope helpers", () => {
|
|
|
1116
1145
|
await rm(wd, { recursive: true, force: true });
|
|
1117
1146
|
}
|
|
1118
1147
|
});
|
|
1148
|
+
it("rewrites setup-owned hook trust state for the runtime CODEX_HOME mirror", async () => {
|
|
1149
|
+
const wd = await mkdtemp(join(tmpdir(), "omx-launch-runtime-hook-trust-"));
|
|
1150
|
+
try {
|
|
1151
|
+
const projectCodexHome = join(wd, ".codex");
|
|
1152
|
+
await mkdir(join(wd, ".omx"), { recursive: true });
|
|
1153
|
+
await mkdir(projectCodexHome, { recursive: true });
|
|
1154
|
+
await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "project" }));
|
|
1155
|
+
await writeFile(join(projectCodexHome, "hooks.json"), '{"hooks":{}}\n');
|
|
1156
|
+
await writeFile(join(projectCodexHome, "config.toml"), [
|
|
1157
|
+
"[features]",
|
|
1158
|
+
"codex_hooks = true",
|
|
1159
|
+
"",
|
|
1160
|
+
"# OMX-owned Codex hook trust state",
|
|
1161
|
+
"# Trusts only setup-managed codex-native-hook.js wrappers.",
|
|
1162
|
+
`[hooks.state."${join(projectCodexHome, "hooks.json")}:pre_tool_use:0:0"]`,
|
|
1163
|
+
'trusted_hash = "stale"',
|
|
1164
|
+
"# End OMX-owned Codex hook trust state",
|
|
1165
|
+
"",
|
|
1166
|
+
].join("\n"));
|
|
1167
|
+
await prepareCodexHomeForLaunch(wd, "session-trust", {});
|
|
1168
|
+
const runtimeCodexHome = runtimeCodexHomePath(wd, "session-trust");
|
|
1169
|
+
const runtimeConfig = await readFile(join(runtimeCodexHome, "config.toml"), "utf-8");
|
|
1170
|
+
assert.match(runtimeConfig, new RegExp(`\\[hooks\\.state\\."${join(runtimeCodexHome, "hooks.json").replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:pre_tool_use:0:0"\\]`));
|
|
1171
|
+
assert.doesNotMatch(runtimeConfig, /trusted_hash = "stale"/);
|
|
1172
|
+
assert.doesNotMatch(runtimeConfig, new RegExp(join(projectCodexHome, "hooks.json").replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
|
1173
|
+
}
|
|
1174
|
+
finally {
|
|
1175
|
+
await rm(wd, { recursive: true, force: true });
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1119
1178
|
it("uses boxed runtime root for project-scope CODEX_HOME mirrors", async () => {
|
|
1120
1179
|
const source = await mkdtemp(join(tmpdir(), "omx-launch-boxed-source-"));
|
|
1121
1180
|
const boxedRoot = await mkdtemp(join(tmpdir(), "omx-launch-boxed-root-"));
|
|
@@ -1499,9 +1558,12 @@ describe("detached tmux new-session sequencing", () => {
|
|
|
1499
1558
|
assert.match(leaderCmd, /wait "\$omx_codex_pid";/);
|
|
1500
1559
|
assert.match(leaderCmd, /kill -TERM "\$omx_codex_pid"/);
|
|
1501
1560
|
assert.match(leaderCmd, /releaseTmuxExtendedKeysLease/);
|
|
1502
|
-
assert.match(leaderCmd, /if \[ "\$status" -
|
|
1561
|
+
assert.match(leaderCmd, /if \[ "\$status" -eq 0 \]; then/);
|
|
1503
1562
|
assert.match(leaderCmd, /tmux kill-session -t/);
|
|
1504
1563
|
assert.match(leaderCmd, /"omx-demo"/);
|
|
1564
|
+
assert.match(leaderCmd, /codex exited immediately with code 0/);
|
|
1565
|
+
assert.match(leaderCmd, /codex exited with code/);
|
|
1566
|
+
assert.match(leaderCmd, /detached tmux session is being kept open/);
|
|
1505
1567
|
assert.match(leaderCmd, /exit \$status/);
|
|
1506
1568
|
});
|
|
1507
1569
|
it("buildDetachedSessionBootstrapSteps finalizes postLaunch inside the detached leader when a session id is available", () => {
|
|
@@ -1514,7 +1576,7 @@ describe("detached tmux new-session sequencing", () => {
|
|
|
1514
1576
|
assert.match(leaderCmd, /\/tmp\/project\/\.codex-project/);
|
|
1515
1577
|
assert.match(leaderCmd, /\/tmp\/project\/\.omx\/runtime\/codex-home\/omx-session-123/);
|
|
1516
1578
|
const helperIndex = leaderCmd.indexOf("runDetachedSessionPostLaunch");
|
|
1517
|
-
const signalGateIndex = leaderCmd.indexOf('if [ "$status" -
|
|
1579
|
+
const signalGateIndex = leaderCmd.indexOf('if [ "$status" -eq 0 ]');
|
|
1518
1580
|
assert.ok(helperIndex >= 0);
|
|
1519
1581
|
assert.ok(signalGateIndex >= 0);
|
|
1520
1582
|
assert.ok(helperIndex < signalGateIndex, "detached postLaunch helper must run before the signal-derived tmux kill-session gate");
|
|
@@ -1684,6 +1746,114 @@ exit 0
|
|
|
1684
1746
|
await rm(cwd, { recursive: true, force: true });
|
|
1685
1747
|
}
|
|
1686
1748
|
});
|
|
1749
|
+
it("detached leader command keeps child startup errors visible instead of killing the session", async () => {
|
|
1750
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-error-"));
|
|
1751
|
+
const fakeBin = join(cwd, "bin");
|
|
1752
|
+
const logPath = join(cwd, "leader.log");
|
|
1753
|
+
try {
|
|
1754
|
+
await mkdir(fakeBin, { recursive: true });
|
|
1755
|
+
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
1756
|
+
printf 'codex-stderr: unsupported startup flag\\n' >&2
|
|
1757
|
+
exit 42
|
|
1758
|
+
`);
|
|
1759
|
+
await chmod(join(fakeBin, "codex"), 0o755);
|
|
1760
|
+
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
1761
|
+
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
1762
|
+
case "$1" in
|
|
1763
|
+
display-message)
|
|
1764
|
+
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
1765
|
+
printf '/tmp/tmux-test.sock\\n'
|
|
1766
|
+
else
|
|
1767
|
+
printf '0\\n'
|
|
1768
|
+
fi
|
|
1769
|
+
;;
|
|
1770
|
+
show-options)
|
|
1771
|
+
printf 'off\\n'
|
|
1772
|
+
;;
|
|
1773
|
+
set-option|kill-session)
|
|
1774
|
+
;;
|
|
1775
|
+
esac
|
|
1776
|
+
exit 0
|
|
1777
|
+
`);
|
|
1778
|
+
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
1779
|
+
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--bad-startup-flag"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
1780
|
+
const leaderCmd = steps[0]?.args.at(-1);
|
|
1781
|
+
assert.equal(typeof leaderCmd, "string");
|
|
1782
|
+
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
1783
|
+
cwd,
|
|
1784
|
+
env: {
|
|
1785
|
+
...process.env,
|
|
1786
|
+
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
1787
|
+
HOME: cwd,
|
|
1788
|
+
},
|
|
1789
|
+
input: "\n",
|
|
1790
|
+
encoding: "utf-8",
|
|
1791
|
+
});
|
|
1792
|
+
assert.equal(result.status, 42);
|
|
1793
|
+
assert.match(result.stderr, /codex-stderr: unsupported startup flag/);
|
|
1794
|
+
assert.match(result.stderr, /codex exited with code 42 during startup/);
|
|
1795
|
+
assert.match(result.stderr, /detached tmux session is being kept open/);
|
|
1796
|
+
const log = await readFile(logPath, "utf-8");
|
|
1797
|
+
assert.doesNotMatch(log, /tmux:kill-session -t omx-demo/);
|
|
1798
|
+
}
|
|
1799
|
+
finally {
|
|
1800
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1801
|
+
}
|
|
1802
|
+
});
|
|
1803
|
+
it("detached leader command keeps immediate zero-code exits visible instead of silently closing", async () => {
|
|
1804
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-zero-"));
|
|
1805
|
+
const fakeBin = join(cwd, "bin");
|
|
1806
|
+
const logPath = join(cwd, "leader.log");
|
|
1807
|
+
try {
|
|
1808
|
+
await mkdir(fakeBin, { recursive: true });
|
|
1809
|
+
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
1810
|
+
printf 'codex-started-then-quit\\n' >&2
|
|
1811
|
+
exit 0
|
|
1812
|
+
`);
|
|
1813
|
+
await chmod(join(fakeBin, "codex"), 0o755);
|
|
1814
|
+
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
1815
|
+
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
1816
|
+
case "$1" in
|
|
1817
|
+
display-message)
|
|
1818
|
+
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
1819
|
+
printf '/tmp/tmux-test.sock\\n'
|
|
1820
|
+
else
|
|
1821
|
+
printf '0\\n'
|
|
1822
|
+
fi
|
|
1823
|
+
;;
|
|
1824
|
+
show-options)
|
|
1825
|
+
printf 'off\\n'
|
|
1826
|
+
;;
|
|
1827
|
+
set-option|kill-session)
|
|
1828
|
+
;;
|
|
1829
|
+
esac
|
|
1830
|
+
exit 0
|
|
1831
|
+
`);
|
|
1832
|
+
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
1833
|
+
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
1834
|
+
const leaderCmd = steps[0]?.args.at(-1);
|
|
1835
|
+
assert.equal(typeof leaderCmd, "string");
|
|
1836
|
+
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
1837
|
+
cwd,
|
|
1838
|
+
env: {
|
|
1839
|
+
...process.env,
|
|
1840
|
+
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
1841
|
+
HOME: cwd,
|
|
1842
|
+
},
|
|
1843
|
+
input: "\n",
|
|
1844
|
+
encoding: "utf-8",
|
|
1845
|
+
});
|
|
1846
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
1847
|
+
assert.match(result.stderr, /codex-started-then-quit/);
|
|
1848
|
+
assert.match(result.stderr, /codex exited immediately with code 0 during startup/);
|
|
1849
|
+
assert.match(result.stderr, /detached tmux session is being kept open/);
|
|
1850
|
+
const log = await readFile(logPath, "utf-8");
|
|
1851
|
+
assert.match(log, /tmux:kill-session -t omx-demo/);
|
|
1852
|
+
}
|
|
1853
|
+
finally {
|
|
1854
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1855
|
+
}
|
|
1856
|
+
});
|
|
1687
1857
|
it("detached leader command terminates codex child on external SIGHUP", async () => {
|
|
1688
1858
|
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-hup-"));
|
|
1689
1859
|
const fakeBin = join(cwd, "bin");
|