oh-my-codex 0.20.2 → 0.20.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 +7 -6
- package/Cargo.toml +1 -1
- package/README.md +26 -0
- package/crates/omx-runtime/Cargo.toml +1 -0
- package/crates/omx-runtime/src/main.rs +25 -2
- package/crates/omx-runtime/tests/execution.rs +219 -0
- package/crates/omx-runtime-core/src/dispatch.rs +96 -10
- package/crates/omx-runtime-core/src/engine.rs +839 -29
- package/crates/omx-runtime-core/src/lib.rs +8 -0
- package/dist/agents/__tests__/definitions.test.js +81 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +25 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/auth/hotswap.d.ts +6 -0
- package/dist/auth/hotswap.d.ts.map +1 -1
- package/dist/auth/hotswap.js +85 -15
- package/dist/auth/hotswap.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/cli/__tests__/auth.test.js +60 -6
- package/dist/cli/__tests__/auth.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +103 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +136 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +258 -4
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +287 -1
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +4 -1
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/native-hook-claim-journal.test.js +84 -6
- package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +3 -3
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +56 -171
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan.test.js +37 -111
- package/dist/cli/__tests__/ralplan.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +11 -10
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-search-help.test.js +25 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +42 -4
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +105 -7
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +46 -0
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/doctor.d.ts +3 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +15 -2
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +4 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +138 -38
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.js +2 -2
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/native-hook-claim-journal.d.ts +16 -5
- package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
- package/dist/cli/native-hook-claim-journal.js +42 -29
- package/dist/cli/native-hook-claim-journal.js.map +1 -1
- package/dist/cli/ralph.js +2 -2
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/ralplan.d.ts +4 -8
- package/dist/cli/ralplan.d.ts.map +1 -1
- package/dist/cli/ralplan.js +19 -112
- package/dist/cli/ralplan.js.map +1 -1
- package/dist/cli/session-search.d.ts +5 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +87 -0
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/setup.d.ts +5 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +80 -31
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/uninstall.d.ts +9 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +61 -19
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +330 -1
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +2 -2
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +67 -3
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +4 -20
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +499 -119
- package/dist/config/generator.js.map +1 -1
- package/dist/config/models.d.ts +12 -2
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +15 -1
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +2 -2
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +4 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +238 -543
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +96 -9
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +155 -23
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +348 -2
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/agents-overlay.js +1 -1
- package/dist/hooks/session.d.ts +26 -2
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +169 -13
- package/dist/hooks/session.js.map +1 -1
- package/dist/leader/__tests__/contract.test.js +57 -7
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +16 -0
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +79 -4
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
- package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
- package/dist/mcp/__tests__/wiki-server.test.js +18 -0
- package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
- package/dist/mcp/code-intel-server.d.ts.map +1 -1
- package/dist/mcp/code-intel-server.js +13 -3
- package/dist/mcp/code-intel-server.js.map +1 -1
- package/dist/mcp/wiki-server.d.ts +11 -0
- package/dist/mcp/wiki-server.d.ts.map +1 -1
- package/dist/mcp/wiki-server.js +9 -2
- package/dist/mcp/wiki-server.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/notifications/temp-contract.d.ts.map +1 -1
- package/dist/notifications/temp-contract.js +4 -0
- package/dist/notifications/temp-contract.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +8 -8
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +134 -10
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
- package/dist/ralplan/__tests__/runtime.test.js +8 -4
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +58 -50
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
- package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
- package/dist/ralplan/documented-leader-preflight.js +58 -0
- package/dist/ralplan/documented-leader-preflight.js.map +1 -0
- package/dist/ralplan/runtime.d.ts +1 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +11 -7
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/__tests__/bridge.test.js +42 -1
- package/dist/runtime/__tests__/bridge.test.js.map +1 -1
- package/dist/runtime/bridge.d.ts +12 -0
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +52 -1
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +2620 -487
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +46 -173
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +39 -159
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +254 -2
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +4 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +11013 -1153
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +82 -108
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +74 -3
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +234 -79
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +37 -28
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +28 -2
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +1 -0
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +48 -3
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +2796 -13
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/state/__tests__/mode-state-context.test.js +54 -0
- package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +2 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +5 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +59 -0
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +24 -245
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
- package/dist/subagents/tracker.d.ts +1 -82
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +3 -275
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +91 -1
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/idle-nudge.test.js +45 -15
- package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
- package/dist/team/__tests__/mcp-comm.test.js +93 -27
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +110 -3
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime-cli.test.js +9 -0
- package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +3204 -335
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +2359 -142
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +804 -36
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/team-ops-contract.test.js +6 -0
- package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +3049 -784
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/__tests__/worktree.test.js +6 -0
- package/dist/team/__tests__/worktree.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +15 -9
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/exact-pane.d.ts +23 -0
- package/dist/team/exact-pane.d.ts.map +1 -0
- package/dist/team/exact-pane.js +133 -0
- package/dist/team/exact-pane.js.map +1 -0
- package/dist/team/idle-nudge.d.ts +12 -1
- package/dist/team/idle-nudge.d.ts.map +1 -1
- package/dist/team/idle-nudge.js +41 -14
- package/dist/team/idle-nudge.js.map +1 -1
- package/dist/team/model-contract.d.ts +2 -5
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +59 -17
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/runtime.d.ts +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +1244 -180
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts +13 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +1430 -384
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +26 -8
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/locks.d.ts.map +1 -1
- package/dist/team/state/locks.js +61 -13
- package/dist/team/state/locks.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +4 -3
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state.d.ts +67 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +636 -129
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +5 -0
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +4 -0
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +123 -23
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +1250 -291
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +4 -0
- package/dist/team/worktree.js.map +1 -1
- package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
- package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
- package/dist/utils/__tests__/file-durability.test.js +57 -0
- package/dist/utils/__tests__/file-durability.test.js.map +1 -0
- package/dist/utils/file-durability.d.ts +15 -0
- package/dist/utils/file-durability.d.ts.map +1 -0
- package/dist/utils/file-durability.js +37 -0
- package/dist/utils/file-durability.js.map +1 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/dist/wiki/lint.d.ts +4 -1
- package/dist/wiki/lint.d.ts.map +1 -1
- package/dist/wiki/lint.js +3 -2
- package/dist/wiki/lint.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/hooks/codex-native-hook.mjs +24 -0
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +3 -2
- package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
- package/skills/ralph/SKILL.md +6 -4
- package/skills/ralplan/SKILL.md +3 -2
- package/skills/team/SKILL.md +13 -9
- package/src/scripts/__tests__/codex-native-hook.test.ts +2826 -497
- package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
- package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +48 -185
- package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +44 -177
- package/src/scripts/__tests__/smoke-packed-install.test.ts +320 -2
- package/src/scripts/codex-native-hook.ts +10715 -1069
- package/src/scripts/notify-fallback-watcher.ts +84 -115
- package/src/scripts/notify-hook/auto-nudge.ts +71 -3
- package/src/scripts/notify-hook/team-dispatch.ts +242 -75
- package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
- package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
- package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
- package/src/scripts/notify-hook/team-worker.ts +39 -26
- package/src/scripts/notify-hook/tmux-injection.ts +28 -2
- package/src/scripts/notify-hook.ts +1 -0
- package/src/scripts/smoke-packed-install.ts +3196 -14
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
- package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
- package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
- package/dist/subagents/adapted-role-binding.d.ts +0 -17
- package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
- package/dist/subagents/adapted-role-binding.js +0 -75
- package/dist/subagents/adapted-role-binding.js.map +0 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { describe, it } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import {
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { once } from 'node:events';
|
|
4
5
|
import { existsSync } from 'node:fs';
|
|
6
|
+
import { mkdir, mkdtemp, readFile, readdir, rename, rm, rmdir, symlink, writeFile } from 'node:fs/promises';
|
|
5
7
|
import { tmpdir } from 'node:os';
|
|
6
8
|
import { join } from 'node:path';
|
|
7
|
-
import { __createDefaultPidProbeForTests, appendPromptSessionProvenanceRejection, __resetSessionPointerTransactionDependenciesForTests, __setSessionPointerTransactionDependenciesForTests, isSessionPointerLaunchAbort, isSessionStale, readSessionPointer, readSessionState, readUsableSessionState, reconcileNativeSessionStart, resetSessionMetrics, resolveSessionPointerContext, writeSessionEnd, writeSessionStart, } from '../session.js';
|
|
9
|
+
import { __createDefaultPidProbeForTests, appendPromptSessionProvenanceRejection, __resetSessionPointerTransactionDependenciesForTests, __setSessionPointerTransactionDependenciesForTests, inspectSessionPointerLock, isSessionPointerLaunchAbort, isSessionStale, readSessionPointer, readSessionState, readUsableSessionState, reconcileNativeSessionStart, recoverSessionPointerLock, resetSessionMetrics, resolveSessionPointerContext, writeSessionEnd, writeSessionStart, } from '../session.js';
|
|
8
10
|
function todayIsoDate() {
|
|
9
11
|
return new Date().toISOString().slice(0, 10);
|
|
10
12
|
}
|
|
@@ -68,6 +70,15 @@ function validLockOwner(overrides = {}) {
|
|
|
68
70
|
...overrides,
|
|
69
71
|
};
|
|
70
72
|
}
|
|
73
|
+
async function linuxProcessStartTicks(pid) {
|
|
74
|
+
const stat = await readFile(`/proc/${pid}/stat`, 'utf-8');
|
|
75
|
+
const closingParen = stat.lastIndexOf(')');
|
|
76
|
+
assert.notEqual(closingParen, -1);
|
|
77
|
+
const fieldsAfterCommand = stat.slice(closingParen + 2).trim().split(/\s+/);
|
|
78
|
+
const startTicks = Number.parseInt(fieldsAfterCommand[19] ?? '', 10);
|
|
79
|
+
assert.equal(Number.isInteger(startTicks), true);
|
|
80
|
+
return startTicks;
|
|
81
|
+
}
|
|
71
82
|
describe('session lifecycle manager', () => {
|
|
72
83
|
it('resets session metrics files with zeroed counters', async () => {
|
|
73
84
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-metrics-'));
|
|
@@ -155,6 +166,55 @@ describe('session lifecycle manager', () => {
|
|
|
155
166
|
await rm(cwd, { recursive: true, force: true });
|
|
156
167
|
}
|
|
157
168
|
});
|
|
169
|
+
it('emits the session-end warning only after successful end finalization', async () => {
|
|
170
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-durability-'));
|
|
171
|
+
const originalWrite = process.stderr.write;
|
|
172
|
+
const warnings = [];
|
|
173
|
+
process.stderr.write = ((value) => {
|
|
174
|
+
warnings.push(value);
|
|
175
|
+
return true;
|
|
176
|
+
});
|
|
177
|
+
try {
|
|
178
|
+
await writeSessionEnd(cwd, 'sess-end-durability', {
|
|
179
|
+
platform: 'win32',
|
|
180
|
+
regularFileSync: async () => { throw codedError('EPERM'); },
|
|
181
|
+
});
|
|
182
|
+
assert.deepEqual(warnings, [
|
|
183
|
+
'[omx] warning: Windows EPERM regular-file fsync unsupported in session pointer end; operation succeeded with degraded durability.\n',
|
|
184
|
+
]);
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
process.stderr.write = originalWrite;
|
|
188
|
+
await rm(cwd, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
it('keeps session-end durability warnings silent when finalization or release fails', async () => {
|
|
192
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-durability-failure-'));
|
|
193
|
+
const originalWrite = process.stderr.write;
|
|
194
|
+
const warnings = [];
|
|
195
|
+
process.stderr.write = ((value) => {
|
|
196
|
+
warnings.push(value);
|
|
197
|
+
return true;
|
|
198
|
+
});
|
|
199
|
+
const regularFileSync = async () => { throw codedError('EPERM'); };
|
|
200
|
+
try {
|
|
201
|
+
await withPointerDependencies({
|
|
202
|
+
fs: {
|
|
203
|
+
rmdir: async () => { throw new Error('release failure'); },
|
|
204
|
+
},
|
|
205
|
+
}, async () => {
|
|
206
|
+
await assert.rejects(writeSessionEnd(cwd, 'sess-end-release-failure', {
|
|
207
|
+
platform: 'win32',
|
|
208
|
+
regularFileSync,
|
|
209
|
+
}));
|
|
210
|
+
});
|
|
211
|
+
assert.deepEqual(warnings, []);
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
process.stderr.write = originalWrite;
|
|
215
|
+
await rm(cwd, { recursive: true, force: true });
|
|
216
|
+
}
|
|
217
|
+
});
|
|
158
218
|
it('does not delete the current session pointer when ending a different session', async () => {
|
|
159
219
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-owner-'));
|
|
160
220
|
try {
|
|
@@ -659,6 +719,231 @@ describe('session pointer transaction', () => {
|
|
|
659
719
|
}
|
|
660
720
|
}
|
|
661
721
|
});
|
|
722
|
+
it('atomically quarantines dead pre-rename temporary owner evidence and stays idempotent', async () => {
|
|
723
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-dead-temp-'));
|
|
724
|
+
try {
|
|
725
|
+
const context = resolveSessionPointerContext(cwd);
|
|
726
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
727
|
+
await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner()), 'utf-8');
|
|
728
|
+
await withPointerDependencies({ token: () => SUCCESSOR_TOKEN, probePid: () => 'dead' }, async () => {
|
|
729
|
+
const inspected = await inspectSessionPointerLock(cwd);
|
|
730
|
+
assert.equal(inspected.status, 'dead');
|
|
731
|
+
assert.equal(inspected.evidenceSource, 'owner-temp');
|
|
732
|
+
assert.equal(inspected.safeToRecover, true);
|
|
733
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
734
|
+
assert.equal(recovered.recovered, true);
|
|
735
|
+
assert.equal(recovered.action, 'quarantined');
|
|
736
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
737
|
+
assert.equal(existsSync(recovered.quarantinePath), true);
|
|
738
|
+
const repeated = await recoverSessionPointerLock(cwd);
|
|
739
|
+
assert.equal(repeated.recovered, false);
|
|
740
|
+
assert.equal(repeated.action, 'none');
|
|
741
|
+
assert.equal(repeated.status, 'absent');
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
finally {
|
|
745
|
+
await rm(cwd, { recursive: true, force: true });
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
it('does not recover a dead canonical owner because owner.json is not an exact acquisition claim', async () => {
|
|
749
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-dead-canonical-'));
|
|
750
|
+
try {
|
|
751
|
+
const context = resolveSessionPointerContext(cwd);
|
|
752
|
+
await writeLockOwner(cwd, validLockOwner());
|
|
753
|
+
await withPointerDependencies({ token: () => SUCCESSOR_TOKEN, probePid: () => 'dead' }, async () => {
|
|
754
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
755
|
+
assert.equal(recovered.status, 'dead');
|
|
756
|
+
assert.equal(recovered.evidenceSource, 'owner.json');
|
|
757
|
+
assert.equal(recovered.safeToRecover, false);
|
|
758
|
+
assert.equal(recovered.recovered, false);
|
|
759
|
+
assert.equal(recovered.action, 'none');
|
|
760
|
+
assert.equal(existsSync(context.lockPath), true);
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
finally {
|
|
764
|
+
await rm(cwd, { recursive: true, force: true });
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
it('distinguishes a paused live pre-rename owner from the same owner after SIGKILL', async (t) => {
|
|
768
|
+
if (process.platform !== 'linux') {
|
|
769
|
+
t.skip('Linux process start identity is required for deterministic PID reuse protection.');
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-sigkill-'));
|
|
773
|
+
const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1_000)'], { stdio: 'ignore' });
|
|
774
|
+
try {
|
|
775
|
+
assert.ok(child.pid);
|
|
776
|
+
await once(child, 'spawn');
|
|
777
|
+
const context = resolveSessionPointerContext(cwd);
|
|
778
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
779
|
+
await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner({
|
|
780
|
+
pid: child.pid,
|
|
781
|
+
pid_start_ticks: await linuxProcessStartTicks(child.pid),
|
|
782
|
+
})), 'utf-8');
|
|
783
|
+
const paused = await recoverSessionPointerLock(cwd);
|
|
784
|
+
assert.equal(paused.status, 'live');
|
|
785
|
+
assert.equal(paused.recovered, false);
|
|
786
|
+
assert.equal(paused.action, 'none');
|
|
787
|
+
assert.equal(existsSync(context.lockPath), true);
|
|
788
|
+
child.kill('SIGKILL');
|
|
789
|
+
const [exitCode, signal] = await once(child, 'exit');
|
|
790
|
+
assert.equal(exitCode, null);
|
|
791
|
+
assert.equal(signal, 'SIGKILL');
|
|
792
|
+
const orphaned = await recoverSessionPointerLock(cwd);
|
|
793
|
+
assert.equal(orphaned.status, 'dead');
|
|
794
|
+
assert.equal(orphaned.recovered, true);
|
|
795
|
+
assert.equal(orphaned.action, 'quarantined');
|
|
796
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
797
|
+
}
|
|
798
|
+
finally {
|
|
799
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
800
|
+
child.kill('SIGKILL');
|
|
801
|
+
await rm(cwd, { recursive: true, force: true });
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
it('fails closed for live pre-rename, PID reuse, malformed, and ambiguous lock evidence', async () => {
|
|
805
|
+
const cases = [
|
|
806
|
+
{ name: 'live-temp', files: [[`owner.${TEST_TOKEN}.tmp`, JSON.stringify(validLockOwner())]], probePid: 'alive', identity: matchingProcessIdentity() },
|
|
807
|
+
{ name: 'reused', files: [['owner.json', JSON.stringify(validLockOwner())]], probePid: 'alive', identity: { status: 'matching', startTicks: 2 } },
|
|
808
|
+
{ name: 'malformed', files: [['owner.json', '{']], probePid: 'dead' },
|
|
809
|
+
{ name: 'ambiguous', files: [['owner.json', JSON.stringify(validLockOwner())], [`owner.${SUCCESSOR_TOKEN}.tmp`, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN }))]], probePid: 'dead' },
|
|
810
|
+
];
|
|
811
|
+
for (const testCase of cases) {
|
|
812
|
+
const cwd = await mkdtemp(join(tmpdir(), `omx-session-lock-recovery-${testCase.name}-`));
|
|
813
|
+
try {
|
|
814
|
+
const context = resolveSessionPointerContext(cwd);
|
|
815
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
816
|
+
await Promise.all(testCase.files.map(async ([name, contents]) => await writeFile(join(context.lockPath, name), contents, 'utf-8')));
|
|
817
|
+
await withPointerDependencies({
|
|
818
|
+
token: () => SUCCESSOR_TOKEN,
|
|
819
|
+
probePid: () => testCase.probePid,
|
|
820
|
+
readProcessIdentity: () => testCase.identity ?? matchingProcessIdentity(),
|
|
821
|
+
}, async () => {
|
|
822
|
+
const before = await readdir(context.lockPath);
|
|
823
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
824
|
+
assert.equal(recovered.recovered, false);
|
|
825
|
+
assert.equal(recovered.action, 'none');
|
|
826
|
+
assert.equal(recovered.safeToRecover, false);
|
|
827
|
+
assert.deepEqual(await readdir(context.lockPath), before);
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
finally {
|
|
831
|
+
await rm(cwd, { recursive: true, force: true });
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
it('claims stale evidence before quarantine so a successor cannot be mistaken for it', async () => {
|
|
836
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-race-'));
|
|
837
|
+
try {
|
|
838
|
+
const context = resolveSessionPointerContext(cwd);
|
|
839
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
840
|
+
await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner()), 'utf-8');
|
|
841
|
+
const renames = [];
|
|
842
|
+
let successorBlocked = false;
|
|
843
|
+
await withPointerDependencies({
|
|
844
|
+
token: () => SUCCESSOR_TOKEN,
|
|
845
|
+
probePid: () => 'dead',
|
|
846
|
+
fs: {
|
|
847
|
+
rename: async (from, to) => {
|
|
848
|
+
renames.push([from, to]);
|
|
849
|
+
await rename(from, to);
|
|
850
|
+
if (from.endsWith(`owner.${TEST_TOKEN}.tmp`)) {
|
|
851
|
+
await assert.rejects(mkdir(context.lockPath), { code: 'EEXIST' });
|
|
852
|
+
successorBlocked = true;
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
}, async () => {
|
|
857
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
858
|
+
assert.equal(recovered.recovered, true);
|
|
859
|
+
});
|
|
860
|
+
assert.equal(successorBlocked, true);
|
|
861
|
+
assert.match(renames[0][0], /owner\.transaction_token_123456\.tmp$/);
|
|
862
|
+
assert.match(renames[0][1], new RegExp(`owner\\.${TEST_TOKEN}\\.${SUCCESSOR_TOKEN}\\.recovery$`));
|
|
863
|
+
assert.match(renames[1][0], new RegExp(`owner\\.${TEST_TOKEN}\\.${SUCCESSOR_TOKEN}\\.recovery$`));
|
|
864
|
+
assert.equal(renames[1][1].includes('.quarantine.'), true);
|
|
865
|
+
}
|
|
866
|
+
finally {
|
|
867
|
+
await rm(cwd, { recursive: true, force: true });
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
it('does not claim a successor lock when the inspected orphan is displaced before the exact temp rename', async () => {
|
|
871
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-successor-race-'));
|
|
872
|
+
try {
|
|
873
|
+
const context = resolveSessionPointerContext(cwd);
|
|
874
|
+
const staleTemp = join(context.lockPath, `owner.${TEST_TOKEN}.tmp`);
|
|
875
|
+
const successorTemp = join(context.lockPath, `owner.${SUCCESSOR_TOKEN}.tmp`);
|
|
876
|
+
const displacedPath = `${context.lockPath}.displaced`;
|
|
877
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
878
|
+
await writeFile(staleTemp, JSON.stringify(validLockOwner()), 'utf-8');
|
|
879
|
+
let displaced = false;
|
|
880
|
+
await withPointerDependencies({
|
|
881
|
+
token: () => SUCCESSOR_TOKEN,
|
|
882
|
+
probePid: () => 'dead',
|
|
883
|
+
fs: {
|
|
884
|
+
rename: async (from, to) => {
|
|
885
|
+
if (!displaced && from === staleTemp) {
|
|
886
|
+
displaced = true;
|
|
887
|
+
await rename(context.lockPath, displacedPath);
|
|
888
|
+
await mkdir(context.lockPath);
|
|
889
|
+
await writeFile(successorTemp, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })), 'utf-8');
|
|
890
|
+
}
|
|
891
|
+
await rename(from, to);
|
|
892
|
+
},
|
|
893
|
+
},
|
|
894
|
+
}, async () => {
|
|
895
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
896
|
+
assert.equal(recovered.recovered, false);
|
|
897
|
+
assert.equal(recovered.action, 'none');
|
|
898
|
+
assert.match(recovered.reason, /changed before recovery claim/i);
|
|
899
|
+
});
|
|
900
|
+
assert.equal(displaced, true);
|
|
901
|
+
assert.deepEqual(await readdir(context.lockPath), [`owner.${SUCCESSOR_TOKEN}.tmp`]);
|
|
902
|
+
assert.equal(await readFile(successorTemp, 'utf-8'), JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })));
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
await rm(cwd, { recursive: true, force: true });
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
it('does not quarantine a successor live lock displaced into the canonical path after claim revalidation', async () => {
|
|
909
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-post-claim-race-'));
|
|
910
|
+
try {
|
|
911
|
+
const context = resolveSessionPointerContext(cwd);
|
|
912
|
+
const staleTemp = join(context.lockPath, `owner.${TEST_TOKEN}.tmp`);
|
|
913
|
+
const successorTemp = join(context.lockPath, `owner.${SUCCESSOR_TOKEN}.tmp`);
|
|
914
|
+
const displacedPath = `${context.lockPath}.claimed-displaced`;
|
|
915
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
916
|
+
await writeFile(staleTemp, JSON.stringify(validLockOwner()), 'utf-8');
|
|
917
|
+
let displaced = false;
|
|
918
|
+
await withPointerDependencies({
|
|
919
|
+
token: () => SUCCESSOR_TOKEN,
|
|
920
|
+
probePid: () => 'dead',
|
|
921
|
+
fs: {
|
|
922
|
+
rmdir: async (path) => {
|
|
923
|
+
if (!displaced && path === context.lockPath) {
|
|
924
|
+
displaced = true;
|
|
925
|
+
await rename(context.lockPath, displacedPath);
|
|
926
|
+
await mkdir(context.lockPath);
|
|
927
|
+
await writeFile(successorTemp, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })), 'utf-8');
|
|
928
|
+
}
|
|
929
|
+
await rmdir(path);
|
|
930
|
+
},
|
|
931
|
+
},
|
|
932
|
+
}, async () => {
|
|
933
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
934
|
+
assert.equal(recovered.recovered, false);
|
|
935
|
+
assert.equal(recovered.action, 'none');
|
|
936
|
+
assert.match(recovered.reason, /canonical lock directory was not empty/i);
|
|
937
|
+
assert.ok(recovered.quarantinePath);
|
|
938
|
+
});
|
|
939
|
+
assert.equal(displaced, true);
|
|
940
|
+
assert.deepEqual(await readdir(context.lockPath), [`owner.${SUCCESSOR_TOKEN}.tmp`]);
|
|
941
|
+
assert.equal(await readFile(successorTemp, 'utf-8'), JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })));
|
|
942
|
+
}
|
|
943
|
+
finally {
|
|
944
|
+
await rm(cwd, { recursive: true, force: true });
|
|
945
|
+
}
|
|
946
|
+
});
|
|
662
947
|
it('retries a positively matching live owner with the bounded 25/50/100 schedule before timing out', async () => {
|
|
663
948
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-timeout-'));
|
|
664
949
|
try {
|
|
@@ -729,6 +1014,65 @@ describe('session pointer transaction', () => {
|
|
|
729
1014
|
await rm(cwd, { recursive: true, force: true });
|
|
730
1015
|
}
|
|
731
1016
|
});
|
|
1017
|
+
it('publishes the owner and pointer on Windows when regular-file fsync reports EPERM', async () => {
|
|
1018
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-windows-eperm-'));
|
|
1019
|
+
let syncCalls = 0;
|
|
1020
|
+
const regularFileSync = async (platform) => {
|
|
1021
|
+
assert.equal(platform, 'win32');
|
|
1022
|
+
syncCalls += 1;
|
|
1023
|
+
throw codedError('EPERM');
|
|
1024
|
+
};
|
|
1025
|
+
try {
|
|
1026
|
+
const state = await writeSessionStart(cwd, 'sess-windows-eperm', {
|
|
1027
|
+
platform: 'win32',
|
|
1028
|
+
regularFileSync,
|
|
1029
|
+
});
|
|
1030
|
+
const context = resolveSessionPointerContext(cwd);
|
|
1031
|
+
assert.equal(state.session_id, 'sess-windows-eperm');
|
|
1032
|
+
assert.equal(syncCalls, 2, 'owner publication and pointer publication must both attempt fsync');
|
|
1033
|
+
assert.equal(existsSync(join(context.lockPath, 'owner.json')), false, 'lock owner is removed after commit');
|
|
1034
|
+
assert.equal(JSON.parse(await readFile(context.sessionPath, 'utf8')).session_id, 'sess-windows-eperm');
|
|
1035
|
+
}
|
|
1036
|
+
finally {
|
|
1037
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
it('emits one post-release warning for a degraded session start and stays silent for synced or failed transactions', async () => {
|
|
1041
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-durability-warning-'));
|
|
1042
|
+
const originalWrite = process.stderr.write;
|
|
1043
|
+
const warnings = [];
|
|
1044
|
+
process.stderr.write = ((value) => {
|
|
1045
|
+
warnings.push({ value, lockExists: existsSync(resolveSessionPointerContext(cwd).lockPath) });
|
|
1046
|
+
return true;
|
|
1047
|
+
});
|
|
1048
|
+
const regularFileSync = async () => { throw codedError('EPERM'); };
|
|
1049
|
+
try {
|
|
1050
|
+
await writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32', regularFileSync });
|
|
1051
|
+
assert.deepEqual(warnings, [{
|
|
1052
|
+
value: '[omx] warning: Windows EPERM regular-file fsync unsupported in session pointer start/reconcile; operation succeeded with degraded durability.\n',
|
|
1053
|
+
lockExists: false,
|
|
1054
|
+
}]);
|
|
1055
|
+
warnings.length = 0;
|
|
1056
|
+
await writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32' });
|
|
1057
|
+
assert.deepEqual(warnings, []);
|
|
1058
|
+
await withPointerDependencies({
|
|
1059
|
+
fs: {
|
|
1060
|
+
rename: async (from, to) => {
|
|
1061
|
+
if (to === resolveSessionPointerContext(cwd).sessionPath)
|
|
1062
|
+
throw new Error('rename failure');
|
|
1063
|
+
await rename(from, to);
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
}, async () => {
|
|
1067
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32', regularFileSync }));
|
|
1068
|
+
});
|
|
1069
|
+
assert.deepEqual(warnings, []);
|
|
1070
|
+
}
|
|
1071
|
+
finally {
|
|
1072
|
+
process.stderr.write = originalWrite;
|
|
1073
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
732
1076
|
it('types state-directory, pointer-read, owner-sync, owner-rename, and invalid-token failures before commit', async () => {
|
|
733
1077
|
const failures = [
|
|
734
1078
|
{
|
|
@@ -767,6 +1111,7 @@ describe('session pointer transaction', () => {
|
|
|
767
1111
|
openAndSync: async (path) => {
|
|
768
1112
|
if (path === join(context.lockPath, `owner.${TEST_TOKEN}.tmp`))
|
|
769
1113
|
throw new Error('owner sync failure');
|
|
1114
|
+
return 'synced';
|
|
770
1115
|
},
|
|
771
1116
|
},
|
|
772
1117
|
}),
|
|
@@ -838,6 +1183,7 @@ describe('session pointer transaction', () => {
|
|
|
838
1183
|
openAndSync: async (path) => {
|
|
839
1184
|
if (phase.fail === 'sync' && path === tempPath)
|
|
840
1185
|
throw new Error('pointer sync failure');
|
|
1186
|
+
return 'synced';
|
|
841
1187
|
},
|
|
842
1188
|
rename: async (from, to) => {
|
|
843
1189
|
if (phase.fail === 'rename' && from === tempPath && to === context.sessionPath) {
|