oh-my-codex 0.18.13 → 0.18.15
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 +6 -6
- package/Cargo.toml +1 -1
- package/README.md +41 -18
- package/dist/agents/__tests__/native-config.test.js +62 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +15 -2
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/fsm.test.js +7 -0
- package/dist/autopilot/__tests__/fsm.test.js.map +1 -1
- package/dist/autopilot/__tests__/planner-routing.test.d.ts +2 -0
- package/dist/autopilot/__tests__/planner-routing.test.d.ts.map +1 -0
- package/dist/autopilot/__tests__/planner-routing.test.js +74 -0
- package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js +315 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/completion-gate.js +1 -1
- package/dist/autopilot/completion-gate.js.map +1 -1
- package/dist/autopilot/fsm.d.ts +2 -2
- package/dist/autopilot/fsm.d.ts.map +1 -1
- package/dist/autopilot/fsm.js +3 -0
- package/dist/autopilot/fsm.js.map +1 -1
- package/dist/autopilot/planner-routing.d.ts +21 -0
- package/dist/autopilot/planner-routing.d.ts.map +1 -0
- package/dist/autopilot/planner-routing.js +59 -0
- package/dist/autopilot/planner-routing.js.map +1 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +30 -0
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/autoresearch/goal.d.ts.map +1 -1
- package/dist/autoresearch/goal.js +10 -1
- package/dist/autoresearch/goal.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-goal.test.js +33 -0
- package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +3 -0
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +30 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js +158 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js +33 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-spark-routing.test.js +13 -0
- package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +132 -2
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +5 -0
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +40 -0
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +4 -0
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +7 -1
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/performance-goal.test.js +22 -0
- package/dist/cli/__tests__/performance-goal.test.js.map +1 -1
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts +2 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts.map +1 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +64 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -0
- package/dist/cli/__tests__/resume.test.js +96 -0
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +162 -0
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/session-search.test.js +36 -0
- package/dist/cli/__tests__/session-search.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +70 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +33 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +17 -3
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +37 -0
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +81 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/url.test.d.ts +2 -0
- package/dist/cli/__tests__/url.test.d.ts.map +1 -0
- package/dist/cli/__tests__/url.test.js +82 -0
- package/dist/cli/__tests__/url.test.js.map +1 -0
- package/dist/cli/autoresearch-goal.d.ts.map +1 -1
- package/dist/cli/autoresearch-goal.js +2 -1
- package/dist/cli/autoresearch-goal.js.map +1 -1
- package/dist/cli/autoresearch-intake.d.ts.map +1 -1
- package/dist/cli/autoresearch-intake.js +2 -0
- package/dist/cli/autoresearch-intake.js.map +1 -1
- package/dist/cli/doctor.d.ts +39 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +460 -40
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +71 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/performance-goal.d.ts.map +1 -1
- package/dist/cli/performance-goal.js +4 -2
- package/dist/cli/performance-goal.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +1 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +88 -67
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/project-runtime-codex-homes.d.ts +2 -0
- package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -1
- package/dist/cli/project-runtime-codex-homes.js +112 -5
- package/dist/cli/project-runtime-codex-homes.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +15 -6
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +5 -1
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +23 -4
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/url.d.ts +8 -0
- package/dist/cli/url.d.ts.map +1 -0
- package/dist/cli/url.js +69 -0
- package/dist/cli/url.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +20 -4
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +21 -1
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +1 -4
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/models.d.ts +5 -0
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +23 -0
- package/dist/config/models.js.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +2 -0
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.js +10 -0
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +7 -0
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +46 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +100 -0
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +75 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +225 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +75 -0
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +563 -0
- 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 +187 -12
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +225 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +75 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +4 -0
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +137 -2
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +107 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +17 -0
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +21 -7
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +9 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +43 -5
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/types.d.ts +6 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +24 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +53 -0
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +2 -0
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +32 -6
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +11 -5
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-autopilot-gates.test.js +48 -0
- package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +1 -0
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/http-client.test.js +9 -0
- package/dist/notifications/__tests__/http-client.test.js.map +1 -1
- package/dist/notifications/http-client.js +1 -1
- package/dist/notifications/http-client.js.map +1 -1
- package/dist/performance-goal/artifacts.d.ts.map +1 -1
- package/dist/performance-goal/artifacts.js +10 -1
- package/dist/performance-goal/artifacts.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +112 -0
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +23 -0
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +133 -21
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +1251 -13
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +601 -24
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +1 -0
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +279 -86
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +87 -8
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/run-compiled-ci.d.ts +2 -0
- package/dist/scripts/run-compiled-ci.d.ts.map +1 -0
- package/dist/scripts/run-compiled-ci.js +77 -0
- package/dist/scripts/run-compiled-ci.js.map +1 -0
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +1 -0
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/session-history/search.d.ts.map +1 -1
- package/dist/session-history/search.js +24 -9
- package/dist/session-history/search.js.map +1 -1
- package/dist/sidecar/__tests__/args.test.d.ts +2 -0
- package/dist/sidecar/__tests__/args.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/args.test.js +18 -0
- package/dist/sidecar/__tests__/args.test.js.map +1 -0
- package/dist/sidecar/index.d.ts.map +1 -1
- package/dist/sidecar/index.js +12 -4
- package/dist/sidecar/index.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +107 -3
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +57 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +22 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +57 -1
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +1468 -110
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +63 -0
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +24 -1
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +512 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +42 -9
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/model-contract.d.ts +16 -0
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +41 -1
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +116 -30
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +9 -1
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/types.d.ts +2 -0
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state.d.ts +3 -0
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +20 -0
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts +38 -3
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +275 -36
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +26 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +388 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +3 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +23 -0
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +275 -12
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/url-reader/__tests__/url-reader.test.d.ts +2 -0
- package/dist/url-reader/__tests__/url-reader.test.d.ts.map +1 -0
- package/dist/url-reader/__tests__/url-reader.test.js +433 -0
- package/dist/url-reader/__tests__/url-reader.test.js.map +1 -0
- package/dist/url-reader/index.d.ts +4 -0
- package/dist/url-reader/index.d.ts.map +1 -0
- package/dist/url-reader/index.js +522 -0
- package/dist/url-reader/index.js.map +1 -0
- package/dist/url-reader/types.d.ts +41 -0
- package/dist/url-reader/types.d.ts.map +1 -0
- package/dist/url-reader/types.js +2 -0
- package/dist/url-reader/types.js.map +1 -0
- package/dist/utils/__tests__/agents-md.test.js +39 -1
- package/dist/utils/__tests__/agents-md.test.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +28 -0
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/agents-md.d.ts +4 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +28 -0
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/agents-model-table.d.ts +7 -3
- package/dist/utils/agents-model-table.d.ts.map +1 -1
- package/dist/utils/agents-model-table.js +16 -8
- package/dist/utils/agents-model-table.js.map +1 -1
- package/dist/utils/platform-command.d.ts +7 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +12 -1
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +4 -0
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/vscode/__tests__/index.test.d.ts +2 -0
- package/dist/vscode/__tests__/index.test.d.ts.map +1 -0
- package/dist/vscode/__tests__/index.test.js +236 -0
- package/dist/vscode/__tests__/index.test.js.map +1 -0
- package/dist/vscode/index.d.ts +76 -0
- package/dist/vscode/index.d.ts.map +1 -0
- package/dist/vscode/index.js +196 -0
- package/dist/vscode/index.js.map +1 -0
- package/package.json +3 -3
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +59 -8
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +9 -5
- package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +3 -0
- package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +3 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +22 -4
- package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +61 -0
- package/skills/autopilot/SKILL.md +9 -5
- package/skills/autoresearch-goal/SKILL.md +3 -0
- package/skills/ecomode/SKILL.md +1 -1
- package/skills/ecomode/references/agent-tiers.md +61 -0
- package/skills/performance-goal/SKILL.md +3 -0
- package/skills/ralplan/SKILL.md +1 -1
- package/skills/ultragoal/SKILL.md +22 -4
- package/skills/ultrawork/SKILL.md +2 -2
- package/skills/ultrawork/references/agent-tiers.md +61 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +1560 -181
- package/src/scripts/codex-native-hook.ts +663 -24
- package/src/scripts/notify-hook/team-leader-nudge.ts +298 -91
- package/src/scripts/notify-hook/team-tmux-guard.ts +92 -8
- package/src/scripts/notify-hook/tmux-injection.ts +1 -1
- package/src/scripts/run-compiled-ci.ts +85 -0
- package/src/scripts/sync-plugin-mirror.ts +1 -0
- package/templates/AGENTS.md +6 -0
|
@@ -18,6 +18,9 @@ const coverageRun = process.env.NODE_V8_COVERAGE ? true : false;
|
|
|
18
18
|
const skipSlowLifecycleUnderCoverage = coverageRun
|
|
19
19
|
? 'covered by the team-state-runtime lane; skipped under c8 to keep the coverage gate bounded around slow process-lifecycle waits'
|
|
20
20
|
: false;
|
|
21
|
+
function escapeRegExp(value) {
|
|
22
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
23
|
+
}
|
|
21
24
|
async function initRepo() {
|
|
22
25
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-worktree-repo-'));
|
|
23
26
|
execFileSync('git', ['init'], { cwd, stdio: 'ignore' });
|
|
@@ -446,6 +449,7 @@ describe('runtime', () => {
|
|
|
446
449
|
assert.deepEqual(args, ['--no-alt-screen', '--model', expectedLowComplexityModel()]);
|
|
447
450
|
});
|
|
448
451
|
it('resolveWorkerLaunchArgsFromEnv reads low-complexity model from config when present', async () => {
|
|
452
|
+
// Intentional legacy model fixture: verifies explicit low-complexity config survives worker launch resolution.
|
|
449
453
|
await withIsolatedDefaultModelEnvAsync(async () => {
|
|
450
454
|
const previousCodexHome = process.env.CODEX_HOME;
|
|
451
455
|
const tempCodexHome = await mkdtemp(join(tmpdir(), 'omx-codex-home-'));
|
|
@@ -1242,6 +1246,271 @@ process.on('SIGTERM', () => process.exit(0));`);
|
|
|
1242
1246
|
assert.equal(shouldPrekillInteractiveShutdownProcessTrees('leader:0'), false);
|
|
1243
1247
|
assert.equal(shouldPrekillInteractiveShutdownProcessTrees('omx-team-alpha'), true);
|
|
1244
1248
|
});
|
|
1249
|
+
it('startTeam tags interactive panes with the derived tmux-pane leader identity when session id is unavailable', async () => {
|
|
1250
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-pane-derived-owner-'));
|
|
1251
|
+
const prevTmux = process.env.TMUX;
|
|
1252
|
+
const prevTmuxPane = process.env.TMUX_PANE;
|
|
1253
|
+
const prevSessionId = process.env.OMX_SESSION_ID;
|
|
1254
|
+
const prevCodexSessionId = process.env.CODEX_SESSION_ID;
|
|
1255
|
+
const prevGenericSessionId = process.env.SESSION_ID;
|
|
1256
|
+
const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1257
|
+
const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
1258
|
+
const prevSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
1259
|
+
let runtime = null;
|
|
1260
|
+
try {
|
|
1261
|
+
await withMockTmuxFixture({
|
|
1262
|
+
dirPrefix: 'omx-runtime-pane-derived-owner-bin-',
|
|
1263
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
1264
|
+
set -eu
|
|
1265
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
1266
|
+
case "\${1:-}" in
|
|
1267
|
+
-V)
|
|
1268
|
+
echo "tmux 3.4"
|
|
1269
|
+
exit 0
|
|
1270
|
+
;;
|
|
1271
|
+
display-message)
|
|
1272
|
+
case "$*" in
|
|
1273
|
+
*"#{window_width}"*)
|
|
1274
|
+
echo "120"
|
|
1275
|
+
;;
|
|
1276
|
+
*)
|
|
1277
|
+
echo "leader:0 %1"
|
|
1278
|
+
;;
|
|
1279
|
+
esac
|
|
1280
|
+
exit 0
|
|
1281
|
+
;;
|
|
1282
|
+
list-panes)
|
|
1283
|
+
case "$*" in
|
|
1284
|
+
*"-t %2 -F #{pane_pid}"*)
|
|
1285
|
+
echo "2000002222"
|
|
1286
|
+
;;
|
|
1287
|
+
*"pane_current_command"*)
|
|
1288
|
+
printf "%%1\\tnode\\t'codex'\\n"
|
|
1289
|
+
;;
|
|
1290
|
+
*)
|
|
1291
|
+
printf "%%1\\n"
|
|
1292
|
+
;;
|
|
1293
|
+
esac
|
|
1294
|
+
exit 0
|
|
1295
|
+
;;
|
|
1296
|
+
split-window)
|
|
1297
|
+
case "$*" in
|
|
1298
|
+
*" -h "*)
|
|
1299
|
+
echo "%2"
|
|
1300
|
+
;;
|
|
1301
|
+
*)
|
|
1302
|
+
echo "%3"
|
|
1303
|
+
;;
|
|
1304
|
+
esac
|
|
1305
|
+
exit 0
|
|
1306
|
+
;;
|
|
1307
|
+
set-option|resize-pane|select-layout|set-window-option|select-pane|set-hook|run-shell|send-keys|kill-pane|kill-session)
|
|
1308
|
+
exit 0
|
|
1309
|
+
;;
|
|
1310
|
+
*)
|
|
1311
|
+
exit 0
|
|
1312
|
+
;;
|
|
1313
|
+
esac
|
|
1314
|
+
`,
|
|
1315
|
+
binaries: [{
|
|
1316
|
+
name: 'gemini',
|
|
1317
|
+
content: '#!/bin/sh\nexit 0\n',
|
|
1318
|
+
}],
|
|
1319
|
+
env: {
|
|
1320
|
+
OMX_SESSION_ID: undefined,
|
|
1321
|
+
CODEX_SESSION_ID: undefined,
|
|
1322
|
+
SESSION_ID: undefined,
|
|
1323
|
+
},
|
|
1324
|
+
}, async ({ tmuxLogPath }) => {
|
|
1325
|
+
process.env.TMUX = 'leader-session,stub,0';
|
|
1326
|
+
process.env.TMUX_PANE = '%1';
|
|
1327
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
1328
|
+
process.env.OMX_TEAM_WORKER_CLI = 'gemini';
|
|
1329
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
|
|
1330
|
+
runtime = await withoutTeamWorkerEnv(() => startTeam('pane-derived-owner', 'derived pane owner', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
|
|
1331
|
+
const manifest = JSON.parse(await readFile(join(cwd, '.omx', 'state', 'team', runtime.teamName, 'manifest.v2.json'), 'utf-8'));
|
|
1332
|
+
assert.equal(manifest.leader?.session_id, '%1');
|
|
1333
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1334
|
+
assert.match(tmuxLog, /set-option -p -t %1 @omx_pane_instance_id %1/);
|
|
1335
|
+
assert.match(tmuxLog, /set-option -p -t %2 @omx_pane_instance_id %1/);
|
|
1336
|
+
assert.match(tmuxLog, /set-option -p -t %3 @omx_pane_instance_id %1/);
|
|
1337
|
+
assert.match(tmuxLog, /exec env OMX_SESSION_ID='%1' OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%1' .*hud --watch/);
|
|
1338
|
+
await shutdownTeam(runtime.teamName, cwd, { force: true });
|
|
1339
|
+
runtime = null;
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
finally {
|
|
1343
|
+
const runtimeToShutdown = runtime;
|
|
1344
|
+
if (runtimeToShutdown) {
|
|
1345
|
+
await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
|
|
1346
|
+
}
|
|
1347
|
+
if (typeof prevTmux === 'string')
|
|
1348
|
+
process.env.TMUX = prevTmux;
|
|
1349
|
+
else
|
|
1350
|
+
delete process.env.TMUX;
|
|
1351
|
+
if (typeof prevTmuxPane === 'string')
|
|
1352
|
+
process.env.TMUX_PANE = prevTmuxPane;
|
|
1353
|
+
else
|
|
1354
|
+
delete process.env.TMUX_PANE;
|
|
1355
|
+
if (typeof prevSessionId === 'string')
|
|
1356
|
+
process.env.OMX_SESSION_ID = prevSessionId;
|
|
1357
|
+
else
|
|
1358
|
+
delete process.env.OMX_SESSION_ID;
|
|
1359
|
+
if (typeof prevCodexSessionId === 'string')
|
|
1360
|
+
process.env.CODEX_SESSION_ID = prevCodexSessionId;
|
|
1361
|
+
else
|
|
1362
|
+
delete process.env.CODEX_SESSION_ID;
|
|
1363
|
+
if (typeof prevGenericSessionId === 'string')
|
|
1364
|
+
process.env.SESSION_ID = prevGenericSessionId;
|
|
1365
|
+
else
|
|
1366
|
+
delete process.env.SESSION_ID;
|
|
1367
|
+
if (typeof prevLaunchMode === 'string')
|
|
1368
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
|
|
1369
|
+
else
|
|
1370
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1371
|
+
if (typeof prevWorkerCli === 'string')
|
|
1372
|
+
process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
|
|
1373
|
+
else
|
|
1374
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
1375
|
+
if (typeof prevSkipReadyWait === 'string')
|
|
1376
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = prevSkipReadyWait;
|
|
1377
|
+
else
|
|
1378
|
+
delete process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
1379
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
it('startTeam keeps logical session id out of team shutdown ownership tags', async () => {
|
|
1383
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-pane-owner-env-isolated-'));
|
|
1384
|
+
const prevTmux = process.env.TMUX;
|
|
1385
|
+
const prevTmuxPane = process.env.TMUX_PANE;
|
|
1386
|
+
const prevSessionId = process.env.OMX_SESSION_ID;
|
|
1387
|
+
const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1388
|
+
const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
1389
|
+
const prevSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
1390
|
+
let runtime = null;
|
|
1391
|
+
try {
|
|
1392
|
+
await withMockTmuxFixture({
|
|
1393
|
+
dirPrefix: 'omx-runtime-pane-owner-env-isolated-bin-',
|
|
1394
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
1395
|
+
set -eu
|
|
1396
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
1397
|
+
case "\${1:-}" in
|
|
1398
|
+
-V)
|
|
1399
|
+
echo "tmux 3.4"
|
|
1400
|
+
exit 0
|
|
1401
|
+
;;
|
|
1402
|
+
display-message)
|
|
1403
|
+
case "$*" in
|
|
1404
|
+
*"#{window_width}"*)
|
|
1405
|
+
echo "120"
|
|
1406
|
+
;;
|
|
1407
|
+
*)
|
|
1408
|
+
echo "leader:0 %1"
|
|
1409
|
+
;;
|
|
1410
|
+
esac
|
|
1411
|
+
exit 0
|
|
1412
|
+
;;
|
|
1413
|
+
list-panes)
|
|
1414
|
+
case "$*" in
|
|
1415
|
+
*"-t %2 -F #{pane_pid}"*)
|
|
1416
|
+
echo "2000002222"
|
|
1417
|
+
;;
|
|
1418
|
+
*"pane_current_command"*)
|
|
1419
|
+
printf "%%1\\tnode\\t'codex'\\n"
|
|
1420
|
+
;;
|
|
1421
|
+
*)
|
|
1422
|
+
printf "%%1\\n"
|
|
1423
|
+
;;
|
|
1424
|
+
esac
|
|
1425
|
+
exit 0
|
|
1426
|
+
;;
|
|
1427
|
+
split-window)
|
|
1428
|
+
case "$*" in
|
|
1429
|
+
*" -h "*)
|
|
1430
|
+
echo "%2"
|
|
1431
|
+
;;
|
|
1432
|
+
*)
|
|
1433
|
+
echo "%3"
|
|
1434
|
+
;;
|
|
1435
|
+
esac
|
|
1436
|
+
exit 0
|
|
1437
|
+
;;
|
|
1438
|
+
show-option)
|
|
1439
|
+
case "$*" in
|
|
1440
|
+
*)
|
|
1441
|
+
exit 1
|
|
1442
|
+
;;
|
|
1443
|
+
esac
|
|
1444
|
+
exit 0
|
|
1445
|
+
;;
|
|
1446
|
+
set-option|resize-pane|select-layout|set-window-option|select-pane|set-hook|run-shell|send-keys|kill-pane|kill-session)
|
|
1447
|
+
exit 0
|
|
1448
|
+
;;
|
|
1449
|
+
*)
|
|
1450
|
+
exit 0
|
|
1451
|
+
;;
|
|
1452
|
+
esac
|
|
1453
|
+
`,
|
|
1454
|
+
binaries: [{
|
|
1455
|
+
name: 'gemini',
|
|
1456
|
+
content: '#!/bin/sh\nexit 0\n',
|
|
1457
|
+
}],
|
|
1458
|
+
}, async ({ tmuxLogPath }) => {
|
|
1459
|
+
process.env.TMUX = 'leader-session,stub,0';
|
|
1460
|
+
process.env.TMUX_PANE = '%1';
|
|
1461
|
+
process.env.OMX_SESSION_ID = 'logical-session-from-env';
|
|
1462
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
1463
|
+
process.env.OMX_TEAM_WORKER_CLI = 'gemini';
|
|
1464
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
|
|
1465
|
+
runtime = await withoutTeamWorkerEnv(() => startTeam('pane-owner-env-isolated', 'env session must not be pane owner', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
|
|
1466
|
+
const manifest = JSON.parse(await readFile(join(cwd, '.omx', 'state', 'team', runtime.teamName, 'manifest.v2.json'), 'utf-8'));
|
|
1467
|
+
assert.equal(manifest.leader?.session_id, 'logical-session-from-env');
|
|
1468
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1469
|
+
assert.match(tmuxLog, /set-option -t leader @omx_instance_id logical-session-from-env/);
|
|
1470
|
+
assert.match(tmuxLog, /set-option -p -t %1 @omx_pane_instance_id logical-session-from-env/);
|
|
1471
|
+
assert.match(tmuxLog, /set-option -p -t %2 @omx_pane_instance_id logical-session-from-env/);
|
|
1472
|
+
assert.match(tmuxLog, /set-option -p -t %3 @omx_pane_instance_id logical-session-from-env/);
|
|
1473
|
+
assert.match(tmuxLog, /set-option -p -t %1 @omx_team_pane_owner_id team:pane-owner-env-isolat-[a-f0-9]{8}/);
|
|
1474
|
+
assert.match(tmuxLog, /set-option -p -t %2 @omx_team_pane_owner_id team:pane-owner-env-isolat-[a-f0-9]{8}/);
|
|
1475
|
+
assert.match(tmuxLog, /set-option -p -t %3 @omx_team_pane_owner_id team:pane-owner-env-isolat-[a-f0-9]{8}/);
|
|
1476
|
+
assert.doesNotMatch(tmuxLog, /set-option -p -t %1 @omx_team_pane_owner_id logical-session-from-env/);
|
|
1477
|
+
assert.match(tmuxLog, /exec env OMX_SESSION_ID='logical-session-from-env' OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%1' .*hud --watch/);
|
|
1478
|
+
await shutdownTeam(runtime.teamName, cwd, { force: true });
|
|
1479
|
+
runtime = null;
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
finally {
|
|
1483
|
+
const runtimeToShutdown = runtime;
|
|
1484
|
+
if (runtimeToShutdown) {
|
|
1485
|
+
await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
|
|
1486
|
+
}
|
|
1487
|
+
if (typeof prevTmux === 'string')
|
|
1488
|
+
process.env.TMUX = prevTmux;
|
|
1489
|
+
else
|
|
1490
|
+
delete process.env.TMUX;
|
|
1491
|
+
if (typeof prevTmuxPane === 'string')
|
|
1492
|
+
process.env.TMUX_PANE = prevTmuxPane;
|
|
1493
|
+
else
|
|
1494
|
+
delete process.env.TMUX_PANE;
|
|
1495
|
+
if (typeof prevSessionId === 'string')
|
|
1496
|
+
process.env.OMX_SESSION_ID = prevSessionId;
|
|
1497
|
+
else
|
|
1498
|
+
delete process.env.OMX_SESSION_ID;
|
|
1499
|
+
if (typeof prevLaunchMode === 'string')
|
|
1500
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
|
|
1501
|
+
else
|
|
1502
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1503
|
+
if (typeof prevWorkerCli === 'string')
|
|
1504
|
+
process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
|
|
1505
|
+
else
|
|
1506
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
1507
|
+
if (typeof prevSkipReadyWait === 'string')
|
|
1508
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = prevSkipReadyWait;
|
|
1509
|
+
else
|
|
1510
|
+
delete process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
1511
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1245
1514
|
it('startTeam accepts native Windows tmux clients even when TMUX env vars are absent', async () => {
|
|
1246
1515
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-win32-no-env-'));
|
|
1247
1516
|
const prevTmux = process.env.TMUX;
|
|
@@ -1395,10 +1664,12 @@ esac
|
|
|
1395
1664
|
hudPaneId: '%4',
|
|
1396
1665
|
resizeHookName: 'resize-hook',
|
|
1397
1666
|
resizeHookTarget: 'leader:0',
|
|
1667
|
+
teamPaneOwnerId: 'team:team-pane-persist-race',
|
|
1398
1668
|
}, workerPaneIds);
|
|
1399
1669
|
assert.equal(config.tmux_session, 'leader:0');
|
|
1400
1670
|
assert.equal(config.leader_pane_id, '%1');
|
|
1401
1671
|
assert.equal(config.hud_pane_id, '%4');
|
|
1672
|
+
assert.equal(config.tmux_pane_owner_id, 'team:team-pane-persist-race');
|
|
1402
1673
|
assert.deepEqual(workerPaneIds, ['%2', '%3']);
|
|
1403
1674
|
assert.equal(config.workers[0]?.pane_id, '%2');
|
|
1404
1675
|
assert.equal(config.workers[1]?.pane_id, '%3');
|
|
@@ -3315,6 +3586,10 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
3315
3586
|
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
3316
3587
|
set -eu
|
|
3317
3588
|
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
3589
|
+
hud_state="${tmuxLogPath}.hud-state"
|
|
3590
|
+
if [ ! -f "$hud_state" ]; then
|
|
3591
|
+
printf 'absent' > "$hud_state"
|
|
3592
|
+
fi
|
|
3318
3593
|
case "\${1:-}" in
|
|
3319
3594
|
-V)
|
|
3320
3595
|
echo "tmux 3.4"
|
|
@@ -3334,7 +3609,10 @@ case "\${1:-}" in
|
|
|
3334
3609
|
list-panes)
|
|
3335
3610
|
case "$*" in
|
|
3336
3611
|
*"pane_current_command"* )
|
|
3337
|
-
printf "%%1\\tnode\\t'codex'\\n"
|
|
3612
|
+
printf "%%1\\tnode\\t'codex'\\n%%2\\tgemini\\tgemini\\n"
|
|
3613
|
+
if [ "$(cat "$hud_state")" != "absent" ]; then
|
|
3614
|
+
printf "%%3\\tnode\\texec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%%1' node /tmp/bin/omx.js hud --watch\\n"
|
|
3615
|
+
fi
|
|
3338
3616
|
;;
|
|
3339
3617
|
*"#{pane_dead} #{pane_pid}"*)
|
|
3340
3618
|
echo "1 2000999999"
|
|
@@ -3353,13 +3631,38 @@ case "\${1:-}" in
|
|
|
3353
3631
|
*" -h "*)
|
|
3354
3632
|
echo "%2"
|
|
3355
3633
|
;;
|
|
3634
|
+
*" -f "*)
|
|
3635
|
+
printf 'team' > "$hud_state"
|
|
3636
|
+
echo "%3"
|
|
3637
|
+
;;
|
|
3356
3638
|
*)
|
|
3639
|
+
printf 'standalone' > "$hud_state"
|
|
3357
3640
|
echo "%3"
|
|
3358
3641
|
;;
|
|
3359
3642
|
esac
|
|
3360
3643
|
exit 0
|
|
3361
3644
|
;;
|
|
3362
|
-
|
|
3645
|
+
show-option)
|
|
3646
|
+
case "$*" in
|
|
3647
|
+
*"-p -t %1 @omx_team_pane_owner_id"*)
|
|
3648
|
+
echo "team:team-rerun-hud-6aa4d480"
|
|
3649
|
+
;;
|
|
3650
|
+
*"-p -t %3 @omx_team_pane_owner_id"*)
|
|
3651
|
+
echo "team:team-rerun-hud-6aa4d480"
|
|
3652
|
+
;;
|
|
3653
|
+
*)
|
|
3654
|
+
exit 1
|
|
3655
|
+
;;
|
|
3656
|
+
esac
|
|
3657
|
+
exit 0
|
|
3658
|
+
;;
|
|
3659
|
+
kill-pane)
|
|
3660
|
+
case "$*" in
|
|
3661
|
+
*"%3"*) printf 'absent' > "$hud_state" ;;
|
|
3662
|
+
esac
|
|
3663
|
+
exit 0
|
|
3664
|
+
;;
|
|
3665
|
+
set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-session)
|
|
3363
3666
|
exit 0
|
|
3364
3667
|
;;
|
|
3365
3668
|
*)
|
|
@@ -3373,6 +3676,7 @@ esac
|
|
|
3373
3676
|
exit 0
|
|
3374
3677
|
`,
|
|
3375
3678
|
}],
|
|
3679
|
+
env: { OMX_SESSION_ID: 'team-rerun-hud-session' },
|
|
3376
3680
|
}, async ({ tmuxLogPath }) => {
|
|
3377
3681
|
process.env.TMUX = 'leader-session,stub,0';
|
|
3378
3682
|
process.env.TMUX_PANE = '%1';
|
|
@@ -3381,6 +3685,8 @@ exit 0
|
|
|
3381
3685
|
runtime = await withoutTeamWorkerEnv(() => startTeam('team-rerun-hud', 'rerun hud restore', 'explore', 1, [{ subject: 'restore hud', description: 'restore hud', owner: 'worker-1' }], cwd));
|
|
3382
3686
|
assert.equal(runtime.config.hud_pane_id, '%3');
|
|
3383
3687
|
assert.ok(runtime.config.resize_hook_name);
|
|
3688
|
+
const configBeforeShutdown = await readTeamConfig(runtime.teamName, cwd);
|
|
3689
|
+
assert.equal(configBeforeShutdown?.hud_pane_id, '%3');
|
|
3384
3690
|
await shutdownTeam(runtime.teamName, cwd, { force: true });
|
|
3385
3691
|
runtime = null;
|
|
3386
3692
|
runtime = await withoutTeamWorkerEnv(() => startTeam('team-rerun-hud', 'rerun hud restore', 'explore', 1, [{ subject: 'restore hud again', description: 'restore hud again', owner: 'worker-1' }], cwd));
|
|
@@ -3396,7 +3702,7 @@ exit 0
|
|
|
3396
3702
|
assert.equal(tmuxLog.match(/run-shell -b sleep \d+; tmux resize-pane -t %3 -y \d+ >/g)?.length ?? 0, 3);
|
|
3397
3703
|
assert.equal(tmuxLog.match(/run-shell tmux resize-pane -t %3 -y \d+ >/g)?.length ?? 0, 3);
|
|
3398
3704
|
assert.ok((tmuxLog.match(/select-layout -t leader:0 main-vertical/g)?.length ?? 0) >= 2);
|
|
3399
|
-
assert.match(
|
|
3705
|
+
assert.equal(tmuxLog.match(/kill-pane -t %3/g)?.length ?? 0, 2);
|
|
3400
3706
|
});
|
|
3401
3707
|
}
|
|
3402
3708
|
finally {
|
|
@@ -4896,6 +5202,7 @@ esac
|
|
|
4896
5202
|
});
|
|
4897
5203
|
it('shutdownTeam reconciles persisted worker panes with live tmux panes before teardown', async () => {
|
|
4898
5204
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-pane-reconcile-'));
|
|
5205
|
+
const powershellWorkerCommand = Buffer.from("$env:OMX_TEAM_INTERNAL_WORKER = 'team-shutdown-pane-reconcile/worker-6'; & '/opt/node.exe' '/tmp/node_modules/@openai/codex/bin/codex.js'", 'utf16le').toString('base64');
|
|
4899
5206
|
try {
|
|
4900
5207
|
await withMockTmuxFixture({
|
|
4901
5208
|
dirPrefix: 'omx-runtime-shutdown-pane-reconcile-bin-',
|
|
@@ -4922,7 +5229,7 @@ case "$1" in
|
|
|
4922
5229
|
exit 0
|
|
4923
5230
|
;;
|
|
4924
5231
|
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
4925
|
-
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\
|
|
5232
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\texec /bin/sh '/tmp/.omx/state/team/team-shutdown-pane-reconcile/runtime/worker-1-startup.sh'\\n%%14\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-pane-reconcile/worker-2 codex\\n%%15\\tcodex\\tcodex unrelated-not-worker\\n%%16\\tcodex\\tenv OMX_TEAM_WORKER=team-shutdown-pane-reconcile/worker-3 codex\\n%%17\\tcodex\\tworker-wrapper OMX_TEAM_INTERNAL_WORKER='team-shutdown-pane-reconcile/worker-4' codex\\n%%18\\tcodex\\tenv 'OMX_TEAM_INTERNAL_WORKER=team-shutdown-pane-reconcile/worker-5' codex\\n%%19\\tpowershell.exe\\tpowershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${powershellWorkerCommand}\\n%%20\\tzsh\\techo 'OMX_TEAM_INTERNAL_WORKER=team-shutdown-pane-reconcile/worker-7'\\n%%21\\tzsh\\tprintf 'OMX_TEAM_INTERNAL_WORKER=team-shutdown-pane-reconcile/worker-8 codex'\\n%%22\\tzsh\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-pane-reconcile/worker-9 bash -lc 'echo codex'\\n"
|
|
4926
5233
|
if [ -f "$restored_marker" ]; then
|
|
4927
5234
|
printf "%%44\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n"
|
|
4928
5235
|
fi
|
|
@@ -4938,6 +5245,23 @@ case "$1" in
|
|
|
4938
5245
|
printf '%%44\\n'
|
|
4939
5246
|
exit 0
|
|
4940
5247
|
;;
|
|
5248
|
+
show-option)
|
|
5249
|
+
case "$*" in
|
|
5250
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
5251
|
+
echo "team:team-shutdown-pane-reconcile"
|
|
5252
|
+
;;
|
|
5253
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
5254
|
+
echo "team:team-shutdown-pane-reconcile"
|
|
5255
|
+
;;
|
|
5256
|
+
*"-p -t %13 @omx_team_pane_owner_id"|*"-p -t %14 @omx_team_pane_owner_id"|*"-p -t %16 @omx_team_pane_owner_id"|*"-p -t %17 @omx_team_pane_owner_id"|*"-p -t %18 @omx_team_pane_owner_id"|*"-p -t %19 @omx_team_pane_owner_id"*)
|
|
5257
|
+
echo "team:team-shutdown-pane-reconcile"
|
|
5258
|
+
;;
|
|
5259
|
+
*)
|
|
5260
|
+
exit 1
|
|
5261
|
+
;;
|
|
5262
|
+
esac
|
|
5263
|
+
exit 0
|
|
5264
|
+
;;
|
|
4941
5265
|
kill-pane)
|
|
4942
5266
|
if [ "\${3:-}" = "%999" ]; then
|
|
4943
5267
|
echo "missing pane" >&2
|
|
@@ -4953,6 +5277,7 @@ case "$1" in
|
|
|
4953
5277
|
;;
|
|
4954
5278
|
esac
|
|
4955
5279
|
`,
|
|
5280
|
+
env: { OMX_SESSION_ID: 'team-shutdown-pane-reconcile-session' },
|
|
4956
5281
|
}, async ({ tmuxLogPath }) => {
|
|
4957
5282
|
await initTeamState('team-shutdown-pane-reconcile', 'shutdown pane reconcile test', 'executor', 2, cwd);
|
|
4958
5283
|
const config = await readTeamConfig('team-shutdown-pane-reconcile', cwd);
|
|
@@ -4971,7 +5296,15 @@ esac
|
|
|
4971
5296
|
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
4972
5297
|
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
4973
5298
|
assert.match(tmuxLog, /kill-pane -t %14/);
|
|
4974
|
-
assert.match(tmuxLog, /kill-pane -t %
|
|
5299
|
+
assert.match(tmuxLog, /kill-pane -t %16/);
|
|
5300
|
+
assert.match(tmuxLog, /kill-pane -t %17/);
|
|
5301
|
+
assert.match(tmuxLog, /kill-pane -t %18/);
|
|
5302
|
+
assert.match(tmuxLog, /kill-pane -t %19/);
|
|
5303
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %999/);
|
|
5304
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %15/);
|
|
5305
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %20/);
|
|
5306
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %21/);
|
|
5307
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %22/);
|
|
4975
5308
|
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\{pane_id\}`));
|
|
4976
5309
|
assert.doesNotMatch(tmuxLog, /kill-pane -t %44/);
|
|
4977
5310
|
});
|
|
@@ -4980,15 +5313,16 @@ esac
|
|
|
4980
5313
|
await rm(cwd, { recursive: true, force: true });
|
|
4981
5314
|
}
|
|
4982
5315
|
});
|
|
4983
|
-
it('shutdownTeam
|
|
4984
|
-
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-
|
|
5316
|
+
it('shutdownTeam preserves unrelated non-worker panes during shared-session shutdown', async () => {
|
|
5317
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-unrelated-pane-'));
|
|
5318
|
+
const teamName = 'team-shutdown-unrelated-pane';
|
|
4985
5319
|
try {
|
|
4986
|
-
await
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5320
|
+
await withMockTmuxFixture({
|
|
5321
|
+
dirPrefix: 'omx-runtime-shutdown-unrelated-pane-bin-',
|
|
5322
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
4990
5323
|
set -eu
|
|
4991
5324
|
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
5325
|
+
restored_marker="${tmuxLogPath}.restored"
|
|
4992
5326
|
case "$1" in
|
|
4993
5327
|
-V)
|
|
4994
5328
|
echo "tmux 3.4"
|
|
@@ -5000,7 +5334,10 @@ case "$1" in
|
|
|
5000
5334
|
exit 1
|
|
5001
5335
|
;;
|
|
5002
5336
|
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5003
|
-
printf "%%11\\
|
|
5337
|
+
printf "%%10\\tzsh\\tzsh\\n%%11\\tnode\\tnode existing-user-work\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-unrelated-pane/worker-1 codex\\n%%14\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-unrelated-pane/worker-2 codex\\n%%15\\tnode\\tnode /tmp/bin/omx.js sidecar --watch\\n"
|
|
5338
|
+
if [ -f "$restored_marker" ]; then
|
|
5339
|
+
printf "%%44\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n"
|
|
5340
|
+
fi
|
|
5004
5341
|
exit 0
|
|
5005
5342
|
;;
|
|
5006
5343
|
*)
|
|
@@ -5009,10 +5346,25 @@ case "$1" in
|
|
|
5009
5346
|
esac
|
|
5010
5347
|
;;
|
|
5011
5348
|
split-window)
|
|
5349
|
+
: > "$restored_marker"
|
|
5012
5350
|
printf '%%44\\n'
|
|
5013
5351
|
exit 0
|
|
5014
5352
|
;;
|
|
5015
|
-
|
|
5353
|
+
show-option)
|
|
5354
|
+
case "$*" in
|
|
5355
|
+
*"-p -t %10 @omx_team_pane_owner_id"*)
|
|
5356
|
+
echo "team:team-shutdown-unrelated-pane"
|
|
5357
|
+
;;
|
|
5358
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
5359
|
+
echo "team:team-shutdown-unrelated-pane"
|
|
5360
|
+
;;
|
|
5361
|
+
*)
|
|
5362
|
+
exit 1
|
|
5363
|
+
;;
|
|
5364
|
+
esac
|
|
5365
|
+
exit 0
|
|
5366
|
+
;;
|
|
5367
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
5016
5368
|
exit 0
|
|
5017
5369
|
;;
|
|
5018
5370
|
*)
|
|
@@ -5020,48 +5372,47 @@ case "$1" in
|
|
|
5020
5372
|
;;
|
|
5021
5373
|
esac
|
|
5022
5374
|
`,
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5375
|
+
env: { OMX_SESSION_ID: 'team-shutdown-unrelated-pane-session' },
|
|
5376
|
+
}, async ({ tmuxLogPath }) => {
|
|
5377
|
+
await initTeamState(teamName, 'shutdown unrelated pane test', 'executor', 2, cwd);
|
|
5378
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
5379
|
+
assert.ok(config);
|
|
5380
|
+
if (!config)
|
|
5381
|
+
return;
|
|
5382
|
+
config.tmux_session = 'leader:0';
|
|
5383
|
+
config.leader_pane_id = '%10';
|
|
5384
|
+
config.hud_pane_id = '%12';
|
|
5385
|
+
config.workers[0].pane_id = '%13';
|
|
5386
|
+
config.workers[1].pane_id = '%14';
|
|
5387
|
+
await saveTeamConfig(config, cwd);
|
|
5388
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
5389
|
+
const teamRoot = join(cwd, '.omx', 'state', 'team', teamName);
|
|
5390
|
+
assert.equal(existsSync(teamRoot), false);
|
|
5391
|
+
assert.equal(await readMonitorSnapshot(teamName, cwd), null);
|
|
5392
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
5393
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %10/);
|
|
5394
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
5395
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %15/);
|
|
5396
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
5397
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
5398
|
+
assert.match(tmuxLog, /kill-pane -t %14/);
|
|
5399
|
+
assert.doesNotMatch(tmuxLog, /kill-session -t leader:0/);
|
|
5400
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %10 -d -P -F #\\{pane_id\\}`));
|
|
5401
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %44/);
|
|
5402
|
+
assert.match(tmuxLog, /select-pane -t %10/);
|
|
5051
5403
|
});
|
|
5052
5404
|
}
|
|
5053
5405
|
finally {
|
|
5054
5406
|
await rm(cwd, { recursive: true, force: true });
|
|
5055
5407
|
}
|
|
5056
5408
|
});
|
|
5057
|
-
it('shutdownTeam
|
|
5058
|
-
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-
|
|
5059
|
-
const teamName = 'team-
|
|
5409
|
+
it('shutdownTeam ignores stale persisted worker pane ids when a shared-session pane lacks worker evidence', async () => {
|
|
5410
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-stale-worker-pane-id-'));
|
|
5411
|
+
const teamName = 'team-stale-worker-pane-id';
|
|
5060
5412
|
try {
|
|
5061
|
-
await
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5413
|
+
await withMockTmuxFixture({
|
|
5414
|
+
dirPrefix: 'omx-runtime-shutdown-stale-worker-pane-id-bin-',
|
|
5415
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5065
5416
|
set -eu
|
|
5066
5417
|
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
5067
5418
|
case "$1" in
|
|
@@ -5075,7 +5426,7 @@ case "$1" in
|
|
|
5075
5426
|
exit 1
|
|
5076
5427
|
;;
|
|
5077
5428
|
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5078
|
-
printf "%%
|
|
5429
|
+
printf "%%10\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-stale-worker-pane-id/worker-1 codex\\n%%15\\tcodex\\tcodex unrelated-user-pane\\n"
|
|
5079
5430
|
exit 0
|
|
5080
5431
|
;;
|
|
5081
5432
|
*)
|
|
@@ -5087,7 +5438,21 @@ case "$1" in
|
|
|
5087
5438
|
printf '%%44\\n'
|
|
5088
5439
|
exit 0
|
|
5089
5440
|
;;
|
|
5090
|
-
|
|
5441
|
+
show-option)
|
|
5442
|
+
case "$*" in
|
|
5443
|
+
*"-p -t %10 @omx_team_pane_owner_id"*)
|
|
5444
|
+
echo "team:team-stale-worker-pane-id"
|
|
5445
|
+
;;
|
|
5446
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
5447
|
+
echo "team:team-stale-worker-pane-id"
|
|
5448
|
+
;;
|
|
5449
|
+
*)
|
|
5450
|
+
exit 1
|
|
5451
|
+
;;
|
|
5452
|
+
esac
|
|
5453
|
+
exit 0
|
|
5454
|
+
;;
|
|
5455
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
5091
5456
|
exit 0
|
|
5092
5457
|
;;
|
|
5093
5458
|
*)
|
|
@@ -5095,42 +5460,38 @@ case "$1" in
|
|
|
5095
5460
|
;;
|
|
5096
5461
|
esac
|
|
5097
5462
|
`,
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
assert.match(tmuxLog, /kill-pane -t %23/);
|
|
5119
|
-
assert.match(tmuxLog, /kill-pane -t %24/);
|
|
5120
|
-
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %21 -d -P -F #\\{pane_id\\}`));
|
|
5121
|
-
assert.match(tmuxLog, /select-pane -t %21/);
|
|
5122
|
-
});
|
|
5463
|
+
env: { OMX_SESSION_ID: 'team-stale-worker-pane-id-session' },
|
|
5464
|
+
}, async ({ tmuxLogPath }) => {
|
|
5465
|
+
await initTeamState(teamName, 'shutdown stale persisted worker pane id test', 'executor', 2, cwd);
|
|
5466
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
5467
|
+
assert.ok(config);
|
|
5468
|
+
if (!config)
|
|
5469
|
+
return;
|
|
5470
|
+
config.tmux_session = 'leader:0';
|
|
5471
|
+
config.leader_pane_id = '%10';
|
|
5472
|
+
config.hud_pane_id = '%12';
|
|
5473
|
+
config.workers[0].pane_id = '%13';
|
|
5474
|
+
config.workers[1].pane_id = '%15';
|
|
5475
|
+
await saveTeamConfig(config, cwd);
|
|
5476
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
5477
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
5478
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %10/);
|
|
5479
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
5480
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
5481
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %15/);
|
|
5482
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %10 -d -P -F #\\{pane_id\\}`));
|
|
5123
5483
|
});
|
|
5124
5484
|
}
|
|
5125
5485
|
finally {
|
|
5126
5486
|
await rm(cwd, { recursive: true, force: true });
|
|
5127
5487
|
}
|
|
5128
5488
|
});
|
|
5129
|
-
it('shutdownTeam
|
|
5130
|
-
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-
|
|
5489
|
+
it('shutdownTeam preserves worker-looking panes with a mismatched team owner tag', async () => {
|
|
5490
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-worker-owner-mismatch-'));
|
|
5491
|
+
const teamName = 'team-worker-owner-mismatch';
|
|
5131
5492
|
try {
|
|
5132
5493
|
await withMockTmuxFixture({
|
|
5133
|
-
dirPrefix: 'omx-runtime-shutdown-
|
|
5494
|
+
dirPrefix: 'omx-runtime-shutdown-worker-owner-mismatch-bin-',
|
|
5134
5495
|
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5135
5496
|
set -eu
|
|
5136
5497
|
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
@@ -5145,7 +5506,7 @@ case "$1" in
|
|
|
5145
5506
|
exit 1
|
|
5146
5507
|
;;
|
|
5147
5508
|
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5148
|
-
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\
|
|
5509
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-worker-owner-mismatch/worker-1 codex\\n%%14\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-worker-owner-mismatch/worker-2 codex\\n"
|
|
5149
5510
|
exit 0
|
|
5150
5511
|
;;
|
|
5151
5512
|
*)
|
|
@@ -5157,7 +5518,24 @@ case "$1" in
|
|
|
5157
5518
|
printf '%%44\\n'
|
|
5158
5519
|
exit 0
|
|
5159
5520
|
;;
|
|
5160
|
-
|
|
5521
|
+
show-option)
|
|
5522
|
+
case "$*" in
|
|
5523
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
5524
|
+
echo "team:team-worker-owner-mismatch"
|
|
5525
|
+
;;
|
|
5526
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
5527
|
+
echo "team:team-worker-owner-mismatch"
|
|
5528
|
+
;;
|
|
5529
|
+
*"-p -t %14 @omx_team_pane_owner_id"*)
|
|
5530
|
+
echo "team:other-team"
|
|
5531
|
+
;;
|
|
5532
|
+
*)
|
|
5533
|
+
exit 1
|
|
5534
|
+
;;
|
|
5535
|
+
esac
|
|
5536
|
+
exit 0
|
|
5537
|
+
;;
|
|
5538
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
5161
5539
|
exit 0
|
|
5162
5540
|
;;
|
|
5163
5541
|
*)
|
|
@@ -5166,8 +5544,8 @@ case "$1" in
|
|
|
5166
5544
|
esac
|
|
5167
5545
|
`,
|
|
5168
5546
|
}, async ({ tmuxLogPath }) => {
|
|
5169
|
-
await initTeamState(
|
|
5170
|
-
const config = await readTeamConfig(
|
|
5547
|
+
await initTeamState(teamName, 'shutdown worker owner mismatch test', 'executor', 2, cwd);
|
|
5548
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
5171
5549
|
assert.ok(config);
|
|
5172
5550
|
if (!config)
|
|
5173
5551
|
return;
|
|
@@ -5177,47 +5555,69 @@ esac
|
|
|
5177
5555
|
config.workers[0].pane_id = '%13';
|
|
5178
5556
|
config.workers[1].pane_id = '%14';
|
|
5179
5557
|
await saveTeamConfig(config, cwd);
|
|
5180
|
-
await shutdownTeam(
|
|
5181
|
-
const teamRoot = join(cwd, '.omx', 'state', 'team', 'team-shutdown-shared-session');
|
|
5182
|
-
assert.equal(existsSync(teamRoot), false);
|
|
5183
|
-
assert.equal(await readMonitorSnapshot('team-shutdown-shared-session', cwd), null);
|
|
5558
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
5184
5559
|
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
5185
|
-
assert.doesNotMatch(tmuxLog, /list-panes -t %13 -F #\{pane_pid\}/);
|
|
5186
|
-
assert.doesNotMatch(tmuxLog, /list-panes -t %14 -F #\{pane_pid\}/);
|
|
5187
5560
|
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
5188
|
-
assert.doesNotMatch(tmuxLog, /kill-session -t leader:0/);
|
|
5189
5561
|
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
5190
5562
|
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
5191
|
-
assert.
|
|
5563
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %14/);
|
|
5192
5564
|
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\}`));
|
|
5193
|
-
assert.match(tmuxLog, /select-pane -t %11/);
|
|
5194
5565
|
});
|
|
5195
5566
|
}
|
|
5196
5567
|
finally {
|
|
5197
5568
|
await rm(cwd, { recursive: true, force: true });
|
|
5198
5569
|
}
|
|
5199
5570
|
});
|
|
5200
|
-
it('shutdownTeam
|
|
5201
|
-
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-
|
|
5571
|
+
it('shutdownTeam preserves worker-looking panes when the team owner tag cannot be read', async () => {
|
|
5572
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-worker-owner-read-error-'));
|
|
5573
|
+
const teamName = 'team-worker-owner-read-error';
|
|
5202
5574
|
try {
|
|
5203
5575
|
await withMockTmuxFixture({
|
|
5204
|
-
dirPrefix: 'omx-runtime-shutdown-
|
|
5576
|
+
dirPrefix: 'omx-runtime-shutdown-worker-owner-read-error-bin-',
|
|
5205
5577
|
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5206
5578
|
set -eu
|
|
5207
|
-
printf '%s
|
|
5579
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
5208
5580
|
case "$1" in
|
|
5209
5581
|
-V)
|
|
5210
5582
|
echo "tmux 3.4"
|
|
5211
5583
|
exit 0
|
|
5212
5584
|
;;
|
|
5213
5585
|
list-panes)
|
|
5214
|
-
|
|
5586
|
+
case "$*" in
|
|
5587
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
5588
|
+
exit 1
|
|
5589
|
+
;;
|
|
5590
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5591
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-worker-owner-read-error/worker-1 codex\\n%%14\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-worker-owner-read-error/worker-2 codex\\n"
|
|
5592
|
+
exit 0
|
|
5593
|
+
;;
|
|
5594
|
+
*)
|
|
5595
|
+
exit 1
|
|
5596
|
+
;;
|
|
5597
|
+
esac
|
|
5215
5598
|
;;
|
|
5216
5599
|
split-window)
|
|
5217
|
-
printf '%%44
|
|
5600
|
+
printf '%%44\\n'
|
|
5218
5601
|
exit 0
|
|
5219
5602
|
;;
|
|
5220
|
-
|
|
5603
|
+
show-option)
|
|
5604
|
+
case "$*" in
|
|
5605
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
5606
|
+
echo "team:team-worker-owner-read-error"
|
|
5607
|
+
;;
|
|
5608
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
5609
|
+
echo "team:team-worker-owner-read-error"
|
|
5610
|
+
;;
|
|
5611
|
+
*"-p -t %14 @omx_team_pane_owner_id"*)
|
|
5612
|
+
exit 2
|
|
5613
|
+
;;
|
|
5614
|
+
*)
|
|
5615
|
+
exit 1
|
|
5616
|
+
;;
|
|
5617
|
+
esac
|
|
5618
|
+
exit 0
|
|
5619
|
+
;;
|
|
5620
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
5221
5621
|
exit 0
|
|
5222
5622
|
;;
|
|
5223
5623
|
*)
|
|
@@ -5226,31 +5626,989 @@ case "$1" in
|
|
|
5226
5626
|
esac
|
|
5227
5627
|
`,
|
|
5228
5628
|
}, async ({ tmuxLogPath }) => {
|
|
5229
|
-
await initTeamState(
|
|
5230
|
-
const config = await readTeamConfig(
|
|
5629
|
+
await initTeamState(teamName, 'shutdown worker owner read error test', 'executor', 2, cwd);
|
|
5630
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
5231
5631
|
assert.ok(config);
|
|
5232
5632
|
if (!config)
|
|
5233
5633
|
return;
|
|
5234
5634
|
config.tmux_session = 'leader:0';
|
|
5235
5635
|
config.leader_pane_id = '%11';
|
|
5236
5636
|
config.hud_pane_id = '%12';
|
|
5237
|
-
config.workers[0].pane_id = '%
|
|
5238
|
-
config.workers[1].pane_id = '%
|
|
5637
|
+
config.workers[0].pane_id = '%13';
|
|
5638
|
+
config.workers[1].pane_id = '%14';
|
|
5239
5639
|
await saveTeamConfig(config, cwd);
|
|
5240
|
-
await shutdownTeam(
|
|
5640
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
5241
5641
|
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
5242
|
-
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
5243
5642
|
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
5244
5643
|
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
5245
|
-
assert.
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5644
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %14/);
|
|
5645
|
+
});
|
|
5646
|
+
}
|
|
5647
|
+
finally {
|
|
5648
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5649
|
+
}
|
|
5650
|
+
});
|
|
5651
|
+
it('shutdownTeam does not use a detected worker as fallback leader when the shared-session leader is stale', async () => {
|
|
5652
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-stale-leader-worker-'));
|
|
5653
|
+
const teamName = 'team-stale-leader-worker';
|
|
5654
|
+
try {
|
|
5655
|
+
await withMockTmuxFixture({
|
|
5656
|
+
dirPrefix: 'omx-runtime-shutdown-stale-leader-worker-bin-',
|
|
5657
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5658
|
+
set -eu
|
|
5659
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
5660
|
+
case "$1" in
|
|
5661
|
+
-V)
|
|
5662
|
+
echo "tmux 3.4"
|
|
5663
|
+
exit 0
|
|
5664
|
+
;;
|
|
5665
|
+
list-panes)
|
|
5666
|
+
case "$*" in
|
|
5667
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
5668
|
+
exit 1
|
|
5669
|
+
;;
|
|
5670
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5671
|
+
printf "%%11\\tzsh\\tvim notes.md\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv 'OMX_TEAM_INTERNAL_WORKER=team-stale-leader-worker/worker-1' codex\\n"
|
|
5672
|
+
exit 0
|
|
5673
|
+
;;
|
|
5674
|
+
*)
|
|
5675
|
+
exit 1
|
|
5676
|
+
;;
|
|
5677
|
+
esac
|
|
5678
|
+
;;
|
|
5679
|
+
show-option)
|
|
5680
|
+
case "$*" in
|
|
5681
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
5682
|
+
echo "team:team-stale-leader-worker"
|
|
5683
|
+
;;
|
|
5684
|
+
*)
|
|
5685
|
+
exit 1
|
|
5686
|
+
;;
|
|
5687
|
+
esac
|
|
5688
|
+
exit 0
|
|
5689
|
+
;;
|
|
5690
|
+
kill-pane|select-pane|run-shell)
|
|
5691
|
+
exit 0
|
|
5692
|
+
;;
|
|
5693
|
+
*)
|
|
5694
|
+
exit 0
|
|
5695
|
+
;;
|
|
5696
|
+
esac
|
|
5697
|
+
`,
|
|
5698
|
+
}, async ({ tmuxLogPath }) => {
|
|
5699
|
+
await initTeamState(teamName, 'shutdown stale leader worker test', 'executor', 1, cwd);
|
|
5700
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
5701
|
+
assert.ok(config);
|
|
5702
|
+
if (!config)
|
|
5703
|
+
return;
|
|
5704
|
+
config.tmux_session = 'leader:0';
|
|
5705
|
+
config.leader_pane_id = '%10';
|
|
5706
|
+
config.hud_pane_id = '%12';
|
|
5707
|
+
config.workers[0].pane_id = '';
|
|
5708
|
+
await saveTeamConfig(config, cwd);
|
|
5709
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
5710
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
5711
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %10/);
|
|
5712
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
5713
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
5714
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %13/);
|
|
5715
|
+
assert.doesNotMatch(tmuxLog, /split-window/);
|
|
5716
|
+
assert.doesNotMatch(tmuxLog, /select-pane -t %11/);
|
|
5717
|
+
});
|
|
5718
|
+
}
|
|
5719
|
+
finally {
|
|
5720
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5721
|
+
}
|
|
5722
|
+
});
|
|
5723
|
+
it('shutdownTeam does not restore HUD onto a live leader pane without matching ownership tag', async () => {
|
|
5724
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-reused-leader-pane-'));
|
|
5725
|
+
const teamName = 'team-reused-leader-pane';
|
|
5726
|
+
try {
|
|
5727
|
+
await withMockTmuxFixture({
|
|
5728
|
+
dirPrefix: 'omx-runtime-shutdown-reused-leader-pane-bin-',
|
|
5729
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5730
|
+
set -eu
|
|
5731
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
5732
|
+
case "$1" in
|
|
5733
|
+
-V)
|
|
5734
|
+
echo "tmux 3.4"
|
|
5735
|
+
exit 0
|
|
5736
|
+
;;
|
|
5737
|
+
list-panes)
|
|
5738
|
+
case "$*" in
|
|
5739
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
5740
|
+
exit 1
|
|
5741
|
+
;;
|
|
5742
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5743
|
+
printf "%%11\\tzsh\\tvim unrelated-notes.md\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv 'OMX_TEAM_INTERNAL_WORKER=team-reused-leader-pane/worker-1' codex\\n"
|
|
5744
|
+
exit 0
|
|
5745
|
+
;;
|
|
5746
|
+
*)
|
|
5747
|
+
exit 1
|
|
5748
|
+
;;
|
|
5749
|
+
esac
|
|
5750
|
+
;;
|
|
5751
|
+
show-option)
|
|
5752
|
+
case "$*" in
|
|
5753
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
5754
|
+
echo "team:other-team"
|
|
5755
|
+
;;
|
|
5756
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
5757
|
+
echo "team:other-team"
|
|
5758
|
+
;;
|
|
5759
|
+
*"-p -t %13 @omx_team_pane_owner_id"*)
|
|
5760
|
+
echo "team:team-reused-leader-pane"
|
|
5761
|
+
;;
|
|
5762
|
+
*)
|
|
5763
|
+
exit 1
|
|
5764
|
+
;;
|
|
5765
|
+
esac
|
|
5766
|
+
exit 0
|
|
5767
|
+
;;
|
|
5768
|
+
kill-pane|select-pane|run-shell)
|
|
5769
|
+
exit 0
|
|
5770
|
+
;;
|
|
5771
|
+
*)
|
|
5772
|
+
exit 0
|
|
5773
|
+
;;
|
|
5774
|
+
esac
|
|
5775
|
+
`,
|
|
5776
|
+
env: { OMX_SESSION_ID: 'expected-team-session' },
|
|
5777
|
+
}, async ({ tmuxLogPath }) => {
|
|
5778
|
+
await initTeamState(teamName, 'shutdown reused leader pane test', 'executor', 1, cwd);
|
|
5779
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
5780
|
+
assert.ok(config);
|
|
5781
|
+
if (!config)
|
|
5782
|
+
return;
|
|
5783
|
+
config.tmux_session = 'leader:0';
|
|
5784
|
+
config.leader_pane_id = '%11';
|
|
5785
|
+
config.hud_pane_id = '%12';
|
|
5786
|
+
config.workers[0].pane_id = '';
|
|
5787
|
+
await saveTeamConfig(config, cwd);
|
|
5788
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
5789
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
5790
|
+
assert.match(tmuxLog, /show-option -qv -p -t %11 @omx_team_pane_owner_id/);
|
|
5791
|
+
assert.match(tmuxLog, /show-option -qv -p -t %12 @omx_team_pane_owner_id/);
|
|
5792
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
5793
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %12/);
|
|
5794
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
5795
|
+
assert.doesNotMatch(tmuxLog, /split-window/);
|
|
5796
|
+
assert.doesNotMatch(tmuxLog, /select-pane -t %11/);
|
|
5797
|
+
});
|
|
5798
|
+
}
|
|
5799
|
+
finally {
|
|
5800
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5801
|
+
}
|
|
5802
|
+
});
|
|
5803
|
+
it('shutdownTeam skips prekill and keeps the leader pane alive on native Windows split-pane shutdown', async () => {
|
|
5804
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-win32-split-'));
|
|
5805
|
+
try {
|
|
5806
|
+
await withNativeWindowsPlatform(async () => {
|
|
5807
|
+
await withMockTmuxFixture({
|
|
5808
|
+
dirPrefix: 'omx-runtime-shutdown-win32-split-bin-',
|
|
5809
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5810
|
+
set -eu
|
|
5811
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
5812
|
+
case "$1" in
|
|
5813
|
+
-V)
|
|
5814
|
+
echo "tmux 3.4"
|
|
5815
|
+
exit 0
|
|
5816
|
+
;;
|
|
5817
|
+
list-panes)
|
|
5818
|
+
case "$*" in
|
|
5819
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
5820
|
+
exit 1
|
|
5821
|
+
;;
|
|
5822
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5823
|
+
printf "%%11\\tpwsh\\tpwsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-win32-split/worker-1 codex\\n%%14\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-win32-split/worker-2 codex\\n"
|
|
5824
|
+
exit 0
|
|
5825
|
+
;;
|
|
5826
|
+
*)
|
|
5827
|
+
exit 1
|
|
5828
|
+
;;
|
|
5829
|
+
esac
|
|
5830
|
+
;;
|
|
5831
|
+
split-window)
|
|
5832
|
+
printf '%%44\\n'
|
|
5833
|
+
exit 0
|
|
5834
|
+
;;
|
|
5835
|
+
show-option)
|
|
5836
|
+
case "$*" in
|
|
5837
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
5838
|
+
echo "team:team-shutdown-win32-split"
|
|
5839
|
+
;;
|
|
5840
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
5841
|
+
echo "team:team-shutdown-win32-split"
|
|
5842
|
+
;;
|
|
5843
|
+
*)
|
|
5844
|
+
exit 1
|
|
5845
|
+
;;
|
|
5846
|
+
esac
|
|
5847
|
+
exit 0
|
|
5848
|
+
;;
|
|
5849
|
+
kill-pane|resize-pane|select-pane)
|
|
5850
|
+
exit 0
|
|
5851
|
+
;;
|
|
5852
|
+
*)
|
|
5853
|
+
exit 0
|
|
5854
|
+
;;
|
|
5855
|
+
esac
|
|
5856
|
+
`,
|
|
5857
|
+
env: { OMX_SESSION_ID: 'team-shutdown-win32-split-session' },
|
|
5858
|
+
}, async ({ tmuxLogPath }) => {
|
|
5859
|
+
await initTeamState('team-shutdown-win32-split', 'shutdown win32 split test', 'executor', 2, cwd);
|
|
5860
|
+
const config = await readTeamConfig('team-shutdown-win32-split', cwd);
|
|
5861
|
+
assert.ok(config);
|
|
5862
|
+
if (!config)
|
|
5863
|
+
return;
|
|
5864
|
+
config.tmux_session = 'leader:0';
|
|
5865
|
+
config.leader_pane_id = '%11';
|
|
5866
|
+
config.hud_pane_id = '%12';
|
|
5867
|
+
config.workers[0].pane_id = '%13';
|
|
5868
|
+
config.workers[1].pane_id = '%14';
|
|
5869
|
+
await saveTeamConfig(config, cwd);
|
|
5870
|
+
await shutdownTeam('team-shutdown-win32-split', cwd, { force: true });
|
|
5871
|
+
const teamRoot = join(cwd, '.omx', 'state', 'team', 'team-shutdown-win32-split');
|
|
5872
|
+
assert.equal(existsSync(teamRoot), false);
|
|
5873
|
+
assert.equal(await readMonitorSnapshot('team-shutdown-win32-split', cwd), null);
|
|
5874
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
5875
|
+
assert.doesNotMatch(tmuxLog, /list-panes -t %13 -F #\{pane_pid\}/);
|
|
5876
|
+
assert.doesNotMatch(tmuxLog, /list-panes -t %14 -F #\{pane_pid\}/);
|
|
5877
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
5878
|
+
assert.doesNotMatch(tmuxLog, /kill-session -t leader:0/);
|
|
5879
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
5880
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
5881
|
+
assert.match(tmuxLog, /kill-pane -t %14/);
|
|
5882
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\}`));
|
|
5883
|
+
assert.match(tmuxLog, new RegExp(`resize-pane -t %44 -y ${HUD_TMUX_TEAM_HEIGHT_LINES}`));
|
|
5884
|
+
assert.match(tmuxLog, /select-pane -t %11/);
|
|
5885
|
+
});
|
|
5886
|
+
});
|
|
5887
|
+
}
|
|
5888
|
+
finally {
|
|
5889
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5890
|
+
}
|
|
5891
|
+
});
|
|
5892
|
+
it('shutdownTeam preserves an unrelated HUD when the leader is live but persisted shared-session HUD is stale', async () => {
|
|
5893
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-win32-stale-topology-'));
|
|
5894
|
+
const teamName = 'team-win32-stale-topo';
|
|
5895
|
+
try {
|
|
5896
|
+
await withNativeWindowsPlatform(async () => {
|
|
5897
|
+
await withMockTmuxFixture({
|
|
5898
|
+
dirPrefix: 'omx-runtime-shutdown-win32-stale-topology-bin-',
|
|
5899
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5900
|
+
set -eu
|
|
5901
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
5902
|
+
case "$1" in
|
|
5903
|
+
-V)
|
|
5904
|
+
echo "tmux 3.4"
|
|
5905
|
+
exit 0
|
|
5906
|
+
;;
|
|
5907
|
+
list-panes)
|
|
5908
|
+
case "$*" in
|
|
5909
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
5910
|
+
exit 1
|
|
5911
|
+
;;
|
|
5912
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5913
|
+
printf "%%11\\tpwsh\\tpwsh\\n%%22\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%23\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-win32-stale-topo/worker-1 codex\\n%%24\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-win32-stale-topo/worker-2 codex\\n"
|
|
5914
|
+
exit 0
|
|
5915
|
+
;;
|
|
5916
|
+
*)
|
|
5917
|
+
exit 1
|
|
5918
|
+
;;
|
|
5919
|
+
esac
|
|
5920
|
+
;;
|
|
5921
|
+
split-window)
|
|
5922
|
+
printf '%%44\\n'
|
|
5923
|
+
exit 0
|
|
5924
|
+
;;
|
|
5925
|
+
show-option)
|
|
5926
|
+
case "$*" in
|
|
5927
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
5928
|
+
echo "team:team-win32-stale-topo"
|
|
5929
|
+
;;
|
|
5930
|
+
*)
|
|
5931
|
+
exit 1
|
|
5932
|
+
;;
|
|
5933
|
+
esac
|
|
5934
|
+
exit 0
|
|
5935
|
+
;;
|
|
5936
|
+
kill-pane|resize-pane|select-pane)
|
|
5937
|
+
exit 0
|
|
5938
|
+
;;
|
|
5939
|
+
*)
|
|
5940
|
+
exit 0
|
|
5941
|
+
;;
|
|
5942
|
+
esac
|
|
5943
|
+
`,
|
|
5944
|
+
}, async ({ tmuxLogPath }) => {
|
|
5945
|
+
await initTeamState(teamName, 'shutdown win32 stale topology test', 'executor', 2, cwd);
|
|
5946
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
5947
|
+
assert.ok(config);
|
|
5948
|
+
if (!config)
|
|
5949
|
+
return;
|
|
5950
|
+
config.tmux_session = 'leader:0';
|
|
5951
|
+
config.leader_pane_id = '%11';
|
|
5952
|
+
config.hud_pane_id = '%12';
|
|
5953
|
+
config.workers[0].pane_id = '%23';
|
|
5954
|
+
config.workers[1].pane_id = '%24';
|
|
5955
|
+
await saveTeamConfig(config, cwd);
|
|
5956
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
5957
|
+
const teamRoot = join(cwd, '.omx', 'state', 'team', teamName);
|
|
5958
|
+
assert.equal(existsSync(teamRoot), false);
|
|
5959
|
+
assert.equal(await readMonitorSnapshot(teamName, cwd), null);
|
|
5960
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
5961
|
+
assert.doesNotMatch(tmuxLog, /list-panes -t %11 -F #\{pane_pid\}/);
|
|
5962
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
5963
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %22/);
|
|
5964
|
+
assert.match(tmuxLog, /kill-pane -t %23/);
|
|
5965
|
+
assert.match(tmuxLog, /kill-pane -t %24/);
|
|
5966
|
+
assert.doesNotMatch(tmuxLog, /split-window/);
|
|
5967
|
+
assert.doesNotMatch(tmuxLog, /select-pane -t %11/);
|
|
5968
|
+
});
|
|
5969
|
+
});
|
|
5970
|
+
}
|
|
5971
|
+
finally {
|
|
5972
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5973
|
+
}
|
|
5974
|
+
});
|
|
5975
|
+
it('shutdownTeam skips prekill and keeps the leader pane alive on shared-session shutdown', async () => {
|
|
5976
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-shared-session-'));
|
|
5977
|
+
try {
|
|
5978
|
+
await withMockTmuxFixture({
|
|
5979
|
+
dirPrefix: 'omx-runtime-shutdown-shared-session-bin-',
|
|
5980
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
5981
|
+
set -eu
|
|
5982
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
5983
|
+
case "$1" in
|
|
5984
|
+
-V)
|
|
5985
|
+
echo "tmux 3.4"
|
|
5986
|
+
exit 0
|
|
5987
|
+
;;
|
|
5988
|
+
list-panes)
|
|
5989
|
+
case "$*" in
|
|
5990
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
5991
|
+
exit 1
|
|
5992
|
+
;;
|
|
5993
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
5994
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-shared-session/worker-1 codex\\n%%14\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-shared-session/worker-2 codex\\n"
|
|
5995
|
+
exit 0
|
|
5996
|
+
;;
|
|
5997
|
+
*)
|
|
5998
|
+
exit 1
|
|
5999
|
+
;;
|
|
6000
|
+
esac
|
|
6001
|
+
;;
|
|
6002
|
+
split-window)
|
|
6003
|
+
printf '%%44\\n'
|
|
6004
|
+
exit 0
|
|
6005
|
+
;;
|
|
6006
|
+
show-option)
|
|
6007
|
+
case "$*" in
|
|
6008
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
6009
|
+
echo "team:team-shutdown-shared-session"
|
|
6010
|
+
;;
|
|
6011
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
6012
|
+
echo "team:team-shutdown-shared-session"
|
|
6013
|
+
;;
|
|
6014
|
+
*)
|
|
6015
|
+
exit 1
|
|
6016
|
+
;;
|
|
6017
|
+
esac
|
|
6018
|
+
exit 0
|
|
6019
|
+
;;
|
|
6020
|
+
kill-pane|resize-pane|select-pane)
|
|
6021
|
+
exit 0
|
|
6022
|
+
;;
|
|
6023
|
+
*)
|
|
6024
|
+
exit 0
|
|
6025
|
+
;;
|
|
6026
|
+
esac
|
|
6027
|
+
`,
|
|
6028
|
+
env: { OMX_SESSION_ID: 'team-shutdown-shared-session-owner' },
|
|
6029
|
+
}, async ({ tmuxLogPath }) => {
|
|
6030
|
+
await initTeamState('team-shutdown-shared-session', 'shutdown shared session test', 'executor', 2, cwd);
|
|
6031
|
+
const config = await readTeamConfig('team-shutdown-shared-session', cwd);
|
|
6032
|
+
assert.ok(config);
|
|
6033
|
+
if (!config)
|
|
6034
|
+
return;
|
|
6035
|
+
config.tmux_session = 'leader:0';
|
|
6036
|
+
config.leader_pane_id = '%11';
|
|
6037
|
+
config.hud_pane_id = '%12';
|
|
6038
|
+
config.workers[0].pane_id = '%13';
|
|
6039
|
+
config.workers[1].pane_id = '%14';
|
|
6040
|
+
await saveTeamConfig(config, cwd);
|
|
6041
|
+
await shutdownTeam('team-shutdown-shared-session', cwd, { force: true });
|
|
6042
|
+
const teamRoot = join(cwd, '.omx', 'state', 'team', 'team-shutdown-shared-session');
|
|
6043
|
+
assert.equal(existsSync(teamRoot), false);
|
|
6044
|
+
assert.equal(await readMonitorSnapshot('team-shutdown-shared-session', cwd), null);
|
|
6045
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
6046
|
+
assert.doesNotMatch(tmuxLog, /list-panes -t %13 -F #\{pane_pid\}/);
|
|
6047
|
+
assert.doesNotMatch(tmuxLog, /list-panes -t %14 -F #\{pane_pid\}/);
|
|
6048
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
6049
|
+
assert.doesNotMatch(tmuxLog, /kill-session -t leader:0/);
|
|
6050
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
6051
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
6052
|
+
assert.match(tmuxLog, /kill-pane -t %14/);
|
|
6053
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\}`));
|
|
6054
|
+
assert.match(tmuxLog, /select-pane -t %11/);
|
|
6055
|
+
});
|
|
6056
|
+
}
|
|
6057
|
+
finally {
|
|
6058
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6059
|
+
}
|
|
6060
|
+
});
|
|
6061
|
+
it('shutdownTeam restores a standalone HUD pane after tearing down the team HUD', async () => {
|
|
6062
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-restore-hud-'));
|
|
6063
|
+
const leaderPaneCwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-restore-hud-leader-cwd-'));
|
|
6064
|
+
try {
|
|
6065
|
+
await withMockTmuxFixture({
|
|
6066
|
+
dirPrefix: 'omx-runtime-shutdown-restore-hud-bin-',
|
|
6067
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
6068
|
+
set -eu
|
|
6069
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
6070
|
+
case "$1" in
|
|
6071
|
+
-V)
|
|
6072
|
+
echo "tmux 3.4"
|
|
6073
|
+
exit 0
|
|
6074
|
+
;;
|
|
6075
|
+
display-message)
|
|
6076
|
+
case "$*" in
|
|
6077
|
+
*"#{pane_current_path}"*)
|
|
6078
|
+
echo "${leaderPaneCwd}"
|
|
6079
|
+
;;
|
|
6080
|
+
esac
|
|
6081
|
+
exit 0
|
|
6082
|
+
;;
|
|
6083
|
+
list-panes)
|
|
6084
|
+
case "$*" in
|
|
6085
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6086
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\texec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%%11' node /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-restore-hud/worker-2 codex\\n"
|
|
6087
|
+
exit 0
|
|
6088
|
+
;;
|
|
6089
|
+
*)
|
|
6090
|
+
exit 1
|
|
6091
|
+
;;
|
|
6092
|
+
esac
|
|
6093
|
+
;;
|
|
6094
|
+
split-window)
|
|
6095
|
+
printf '%%44\n'
|
|
6096
|
+
exit 0
|
|
6097
|
+
;;
|
|
6098
|
+
show-option)
|
|
6099
|
+
case "$*" in
|
|
6100
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
6101
|
+
echo "team:team-shutdown-restore-hud"
|
|
6102
|
+
;;
|
|
6103
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
6104
|
+
echo "team:team-shutdown-restore-hud"
|
|
6105
|
+
;;
|
|
6106
|
+
*)
|
|
6107
|
+
exit 1
|
|
6108
|
+
;;
|
|
6109
|
+
esac
|
|
6110
|
+
exit 0
|
|
6111
|
+
;;
|
|
6112
|
+
kill-pane|kill-session|select-pane)
|
|
6113
|
+
exit 0
|
|
6114
|
+
;;
|
|
6115
|
+
*)
|
|
6116
|
+
exit 0
|
|
6117
|
+
;;
|
|
6118
|
+
esac
|
|
6119
|
+
`,
|
|
6120
|
+
env: { OMX_SESSION_ID: 'team-shutdown-restore-hud-session' },
|
|
6121
|
+
}, async ({ tmuxLogPath }) => {
|
|
6122
|
+
await initTeamState('team-shutdown-restore-hud', 'shutdown restore hud test', 'executor', 2, cwd);
|
|
6123
|
+
const config = await readTeamConfig('team-shutdown-restore-hud', cwd);
|
|
6124
|
+
assert.ok(config);
|
|
6125
|
+
if (!config)
|
|
6126
|
+
return;
|
|
6127
|
+
config.tmux_session = 'leader:0';
|
|
6128
|
+
config.leader_pane_id = '%11';
|
|
6129
|
+
config.hud_pane_id = '%12';
|
|
6130
|
+
config.workers[0].pane_id = '%12';
|
|
6131
|
+
config.workers[1].pane_id = '%13';
|
|
6132
|
+
await saveTeamConfig(config, cwd);
|
|
6133
|
+
await shutdownTeam('team-shutdown-restore-hud', cwd, { force: true });
|
|
6134
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
6135
|
+
const count = (pattern) => [...tmuxLog.matchAll(pattern)].length;
|
|
6136
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
6137
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
6138
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
6139
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\{pane_id\}`));
|
|
6140
|
+
assert.equal(count(/kill-pane -t %12/g), 1);
|
|
6141
|
+
assert.equal(count(new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\}`, 'g')), 1);
|
|
6142
|
+
assert.match(tmuxLog, /run-shell -b sleep \d+; tmux resize-pane -t %44 -y \d+ >/);
|
|
6143
|
+
assert.match(tmuxLog, /run-shell tmux resize-pane -t %44 -y \d+ >/);
|
|
6144
|
+
assert.match(tmuxLog, /hud --watch/);
|
|
6145
|
+
assert.match(tmuxLog, /OMX_TMUX_HUD_LEADER_PANE='%11'/);
|
|
6146
|
+
assert.match(tmuxLog, /OMX_SESSION_ID='team-shutdown-restore-hud-session'/);
|
|
6147
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\} -c ${escapeRegExp(leaderPaneCwd)} `));
|
|
6148
|
+
assert.doesNotMatch(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\} -c ${escapeRegExp(cwd)} `));
|
|
6149
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %44/);
|
|
6150
|
+
assert.match(tmuxLog, /select-pane -t %11/);
|
|
6151
|
+
});
|
|
6152
|
+
}
|
|
6153
|
+
finally {
|
|
6154
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6155
|
+
await rm(leaderPaneCwd, { recursive: true, force: true });
|
|
6156
|
+
}
|
|
6157
|
+
});
|
|
6158
|
+
it('shutdownTeam preserves unpersisted legacy worker-looking panes without owner tags', async () => {
|
|
6159
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-unpersisted-legacy-worker-'));
|
|
6160
|
+
const teamName = 'team-unpersisted-legacy-worker';
|
|
6161
|
+
try {
|
|
6162
|
+
await withMockTmuxFixture({
|
|
6163
|
+
dirPrefix: 'omx-runtime-shutdown-unpersisted-legacy-worker-bin-',
|
|
6164
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
6165
|
+
set -eu
|
|
6166
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
6167
|
+
case "$1" in
|
|
6168
|
+
-V)
|
|
6169
|
+
echo "tmux 3.4"
|
|
6170
|
+
exit 0
|
|
6171
|
+
;;
|
|
6172
|
+
display-message)
|
|
6173
|
+
case "$*" in
|
|
6174
|
+
*"#{pane_current_path}"*)
|
|
6175
|
+
echo "${cwd}"
|
|
6176
|
+
;;
|
|
6177
|
+
esac
|
|
6178
|
+
exit 0
|
|
6179
|
+
;;
|
|
6180
|
+
list-panes)
|
|
6181
|
+
case "$*" in
|
|
6182
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
6183
|
+
exit 1
|
|
6184
|
+
;;
|
|
6185
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6186
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\texec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%%11' node /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-unpersisted-legacy-worker/worker-1 codex\\n%%14\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-unpersisted-legacy-worker/worker-2 codex\\n"
|
|
6187
|
+
exit 0
|
|
6188
|
+
;;
|
|
6189
|
+
*"-t %11 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6190
|
+
printf "%%11\\tzsh\\tzsh\\n"
|
|
6191
|
+
exit 0
|
|
6192
|
+
;;
|
|
6193
|
+
*)
|
|
6194
|
+
exit 1
|
|
6195
|
+
;;
|
|
6196
|
+
esac
|
|
6197
|
+
;;
|
|
6198
|
+
split-window)
|
|
6199
|
+
printf '%%44\\n'
|
|
6200
|
+
exit 0
|
|
6201
|
+
;;
|
|
6202
|
+
show-option)
|
|
6203
|
+
case "$*" in
|
|
6204
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
6205
|
+
echo "team:team-unpersisted-legacy-worker"
|
|
6206
|
+
;;
|
|
6207
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
6208
|
+
echo "team:team-unpersisted-legacy-worker"
|
|
6209
|
+
;;
|
|
6210
|
+
*)
|
|
6211
|
+
exit 1
|
|
6212
|
+
;;
|
|
6213
|
+
esac
|
|
6214
|
+
exit 0
|
|
6215
|
+
;;
|
|
6216
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
6217
|
+
exit 0
|
|
6218
|
+
;;
|
|
6219
|
+
*)
|
|
6220
|
+
exit 0
|
|
6221
|
+
;;
|
|
6222
|
+
esac
|
|
6223
|
+
`,
|
|
6224
|
+
}, async ({ tmuxLogPath }) => {
|
|
6225
|
+
await initTeamState(teamName, 'shutdown unpersisted legacy worker test', 'executor', 2, cwd);
|
|
6226
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
6227
|
+
assert.ok(config);
|
|
6228
|
+
if (!config)
|
|
6229
|
+
return;
|
|
6230
|
+
config.tmux_session = 'leader:0';
|
|
6231
|
+
config.leader_pane_id = '%11';
|
|
6232
|
+
config.hud_pane_id = '%12';
|
|
6233
|
+
config.workers[0].pane_id = '%13';
|
|
6234
|
+
config.workers[1].pane_id = '%99';
|
|
6235
|
+
await saveTeamConfig(config, cwd);
|
|
6236
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
6237
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
6238
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
6239
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
6240
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %14/);
|
|
6241
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %99/);
|
|
6242
|
+
});
|
|
6243
|
+
}
|
|
6244
|
+
finally {
|
|
6245
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6246
|
+
}
|
|
6247
|
+
});
|
|
6248
|
+
it('shutdownTeam reclaims a matching-owner live HUD when the persisted HUD id is stale', async () => {
|
|
6249
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-stale-hud-live-owner-'));
|
|
6250
|
+
const teamName = 'team-stale-hud-live-owner';
|
|
6251
|
+
try {
|
|
6252
|
+
await withMockTmuxFixture({
|
|
6253
|
+
dirPrefix: 'omx-runtime-shutdown-stale-hud-live-owner-bin-',
|
|
6254
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
6255
|
+
set -eu
|
|
6256
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
6257
|
+
case "$1" in
|
|
6258
|
+
-V)
|
|
6259
|
+
echo "tmux 3.4"
|
|
6260
|
+
exit 0
|
|
6261
|
+
;;
|
|
6262
|
+
display-message)
|
|
6263
|
+
case "$*" in
|
|
6264
|
+
*"#{pane_current_path}"*)
|
|
6265
|
+
echo "${cwd}"
|
|
6266
|
+
;;
|
|
6267
|
+
esac
|
|
6268
|
+
exit 0
|
|
6269
|
+
;;
|
|
6270
|
+
list-panes)
|
|
6271
|
+
case "$*" in
|
|
6272
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
6273
|
+
exit 1
|
|
6274
|
+
;;
|
|
6275
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6276
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\texec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%%11' node /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-stale-hud-live-owner/worker-1 codex\\n"
|
|
6277
|
+
exit 0
|
|
6278
|
+
;;
|
|
6279
|
+
*"-t %11 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6280
|
+
printf "%%11\\tzsh\\tzsh\\n"
|
|
6281
|
+
exit 0
|
|
6282
|
+
;;
|
|
6283
|
+
*)
|
|
6284
|
+
exit 1
|
|
6285
|
+
;;
|
|
6286
|
+
esac
|
|
6287
|
+
;;
|
|
6288
|
+
split-window)
|
|
6289
|
+
printf '%%44\\n'
|
|
6290
|
+
exit 0
|
|
6291
|
+
;;
|
|
6292
|
+
show-option)
|
|
6293
|
+
case "$*" in
|
|
6294
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
6295
|
+
echo "team:team-stale-hud-live-owner"
|
|
6296
|
+
;;
|
|
6297
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
6298
|
+
echo "team:team-stale-hud-live-owner"
|
|
6299
|
+
;;
|
|
6300
|
+
*)
|
|
6301
|
+
exit 1
|
|
6302
|
+
;;
|
|
6303
|
+
esac
|
|
6304
|
+
exit 0
|
|
6305
|
+
;;
|
|
6306
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
6307
|
+
exit 0
|
|
6308
|
+
;;
|
|
6309
|
+
*)
|
|
6310
|
+
exit 0
|
|
6311
|
+
;;
|
|
6312
|
+
esac
|
|
6313
|
+
`,
|
|
6314
|
+
}, async ({ tmuxLogPath }) => {
|
|
6315
|
+
await initTeamState(teamName, 'shutdown stale hud live owner test', 'executor', 1, cwd);
|
|
6316
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
6317
|
+
assert.ok(config);
|
|
6318
|
+
if (!config)
|
|
6319
|
+
return;
|
|
6320
|
+
config.tmux_session = 'leader:0';
|
|
6321
|
+
config.leader_pane_id = '%11';
|
|
6322
|
+
config.hud_pane_id = '%99';
|
|
6323
|
+
config.workers[0].pane_id = '%13';
|
|
6324
|
+
await saveTeamConfig(config, cwd);
|
|
6325
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
6326
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
6327
|
+
const count = (pattern) => [...tmuxLog.matchAll(pattern)].length;
|
|
6328
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
6329
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
6330
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
6331
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %99/);
|
|
6332
|
+
assert.equal(count(/kill-pane -t %12/g), 1);
|
|
6333
|
+
assert.equal(count(new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\}`, 'g')), 1);
|
|
6334
|
+
assert.match(tmuxLog, /select-pane -t %11/);
|
|
6335
|
+
});
|
|
6336
|
+
}
|
|
6337
|
+
finally {
|
|
6338
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6339
|
+
}
|
|
6340
|
+
});
|
|
6341
|
+
it('shutdownTeam reclaims a legacy leader-owned HUD pane without a team owner tag', async () => {
|
|
6342
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-legacy-hud-owner-'));
|
|
6343
|
+
const teamName = 'team-legacy-hud-owner';
|
|
6344
|
+
try {
|
|
6345
|
+
await withMockTmuxFixture({
|
|
6346
|
+
dirPrefix: 'omx-runtime-shutdown-legacy-hud-owner-bin-',
|
|
6347
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
6348
|
+
set -eu
|
|
6349
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
6350
|
+
case "$1" in
|
|
6351
|
+
-V)
|
|
6352
|
+
echo "tmux 3.4"
|
|
6353
|
+
exit 0
|
|
6354
|
+
;;
|
|
6355
|
+
display-message)
|
|
6356
|
+
case "$*" in
|
|
6357
|
+
*"#{pane_current_path}"*)
|
|
6358
|
+
echo "${cwd}"
|
|
6359
|
+
;;
|
|
6360
|
+
esac
|
|
6361
|
+
exit 0
|
|
6362
|
+
;;
|
|
6363
|
+
list-panes)
|
|
6364
|
+
case "$*" in
|
|
6365
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
6366
|
+
exit 1
|
|
6367
|
+
;;
|
|
6368
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6369
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\texec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%%11' node /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-legacy-hud-owner/worker-1 codex\\n"
|
|
6370
|
+
exit 0
|
|
6371
|
+
;;
|
|
6372
|
+
*"-t %11 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6373
|
+
printf "%%11\\tzsh\\tzsh\\n"
|
|
6374
|
+
exit 0
|
|
6375
|
+
;;
|
|
6376
|
+
*)
|
|
6377
|
+
exit 1
|
|
6378
|
+
;;
|
|
6379
|
+
esac
|
|
6380
|
+
;;
|
|
6381
|
+
split-window)
|
|
6382
|
+
printf '%%44\\n'
|
|
6383
|
+
exit 0
|
|
6384
|
+
;;
|
|
6385
|
+
show-option)
|
|
6386
|
+
case "$*" in
|
|
6387
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
6388
|
+
echo "team:team-legacy-hud-owner"
|
|
6389
|
+
;;
|
|
6390
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
6391
|
+
exit 1
|
|
6392
|
+
;;
|
|
6393
|
+
*)
|
|
6394
|
+
exit 1
|
|
6395
|
+
;;
|
|
6396
|
+
esac
|
|
6397
|
+
exit 0
|
|
6398
|
+
;;
|
|
6399
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
6400
|
+
exit 0
|
|
6401
|
+
;;
|
|
6402
|
+
*)
|
|
6403
|
+
exit 0
|
|
6404
|
+
;;
|
|
6405
|
+
esac
|
|
6406
|
+
`,
|
|
6407
|
+
}, async ({ tmuxLogPath }) => {
|
|
6408
|
+
await initTeamState(teamName, 'shutdown legacy hud owner test', 'executor', 1, cwd);
|
|
6409
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
6410
|
+
assert.ok(config);
|
|
6411
|
+
if (!config)
|
|
6412
|
+
return;
|
|
6413
|
+
config.tmux_session = 'leader:0';
|
|
6414
|
+
config.leader_pane_id = '%11';
|
|
6415
|
+
config.hud_pane_id = '%12';
|
|
6416
|
+
config.workers[0].pane_id = '%13';
|
|
6417
|
+
await saveTeamConfig(config, cwd);
|
|
6418
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
6419
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
6420
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
6421
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
6422
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\}`));
|
|
6423
|
+
assert.match(tmuxLog, /select-pane -t %11/);
|
|
6424
|
+
});
|
|
6425
|
+
}
|
|
6426
|
+
finally {
|
|
6427
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6428
|
+
}
|
|
6429
|
+
});
|
|
6430
|
+
it('shutdownTeam restores standalone HUD for legacy leaders with only an instance tag', async () => {
|
|
6431
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-legacy-leader-instance-'));
|
|
6432
|
+
const teamName = 'team-legacy-leader-instance';
|
|
6433
|
+
const legacySessionId = 'legacy-leader-session';
|
|
6434
|
+
try {
|
|
6435
|
+
await withMockTmuxFixture({
|
|
6436
|
+
dirPrefix: 'omx-runtime-shutdown-legacy-leader-instance-bin-',
|
|
6437
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
6438
|
+
set -eu
|
|
6439
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
6440
|
+
case "$1" in
|
|
6441
|
+
-V)
|
|
6442
|
+
echo "tmux 3.4"
|
|
6443
|
+
exit 0
|
|
6444
|
+
;;
|
|
6445
|
+
display-message)
|
|
6446
|
+
case "$*" in
|
|
6447
|
+
*"#{pane_current_path}"*)
|
|
6448
|
+
echo "${cwd}"
|
|
6449
|
+
;;
|
|
6450
|
+
esac
|
|
6451
|
+
exit 0
|
|
6452
|
+
;;
|
|
6453
|
+
list-panes)
|
|
6454
|
+
case "$*" in
|
|
6455
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
6456
|
+
exit 1
|
|
6457
|
+
;;
|
|
6458
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6459
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\texec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%%11' node /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-legacy-leader-instance/worker-1 codex\\n"
|
|
6460
|
+
exit 0
|
|
6461
|
+
;;
|
|
6462
|
+
*"-t %11 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6463
|
+
printf "%%11\\tzsh\\tzsh\\n"
|
|
6464
|
+
exit 0
|
|
6465
|
+
;;
|
|
6466
|
+
*)
|
|
6467
|
+
exit 1
|
|
6468
|
+
;;
|
|
6469
|
+
esac
|
|
6470
|
+
;;
|
|
6471
|
+
split-window)
|
|
6472
|
+
printf '%%44\\n'
|
|
6473
|
+
exit 0
|
|
6474
|
+
;;
|
|
6475
|
+
show-option)
|
|
6476
|
+
case "$*" in
|
|
6477
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
6478
|
+
exit 1
|
|
6479
|
+
;;
|
|
6480
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
6481
|
+
exit 1
|
|
6482
|
+
;;
|
|
6483
|
+
*"-p -t %13 @omx_team_pane_owner_id"*)
|
|
6484
|
+
echo "team:team-legacy-leader-instance"
|
|
6485
|
+
;;
|
|
6486
|
+
*"-p -t %11 @omx_pane_instance_id"*)
|
|
6487
|
+
echo "${legacySessionId}"
|
|
6488
|
+
;;
|
|
6489
|
+
*)
|
|
6490
|
+
exit 1
|
|
6491
|
+
;;
|
|
6492
|
+
esac
|
|
6493
|
+
exit 0
|
|
6494
|
+
;;
|
|
6495
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
6496
|
+
exit 0
|
|
6497
|
+
;;
|
|
6498
|
+
*)
|
|
6499
|
+
exit 0
|
|
6500
|
+
;;
|
|
6501
|
+
esac
|
|
6502
|
+
`,
|
|
6503
|
+
env: { OMX_SESSION_ID: legacySessionId },
|
|
6504
|
+
}, async ({ tmuxLogPath }) => {
|
|
6505
|
+
await initTeamState(teamName, 'shutdown legacy leader instance test', 'executor', 1, cwd);
|
|
6506
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
6507
|
+
assert.ok(config);
|
|
6508
|
+
if (!config)
|
|
6509
|
+
return;
|
|
6510
|
+
config.tmux_session = 'leader:0';
|
|
6511
|
+
config.leader_pane_id = '%11';
|
|
6512
|
+
config.hud_pane_id = '%12';
|
|
6513
|
+
config.workers[0].pane_id = '%13';
|
|
6514
|
+
await saveTeamConfig(config, cwd);
|
|
6515
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
6516
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
6517
|
+
assert.match(tmuxLog, /show-option -qv -p -t %11 @omx_team_pane_owner_id/);
|
|
6518
|
+
assert.match(tmuxLog, /show-option -qv -p -t %11 @omx_pane_instance_id/);
|
|
6519
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
6520
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
6521
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\}`));
|
|
6522
|
+
assert.match(tmuxLog, /select-pane -t %11/);
|
|
6523
|
+
});
|
|
6524
|
+
}
|
|
6525
|
+
finally {
|
|
6526
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6527
|
+
}
|
|
6528
|
+
});
|
|
6529
|
+
it('shutdownTeam preserves a persisted HUD pane when the team owner tag cannot be read', async () => {
|
|
6530
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-hud-owner-read-error-'));
|
|
6531
|
+
const teamName = 'team-hud-owner-read-error';
|
|
6532
|
+
const originalWarn = console.warn;
|
|
6533
|
+
const warnings = [];
|
|
6534
|
+
try {
|
|
6535
|
+
console.warn = (message, ...optionalParams) => {
|
|
6536
|
+
warnings.push([message, ...optionalParams].map(String).join(' '));
|
|
6537
|
+
};
|
|
6538
|
+
await withMockTmuxFixture({
|
|
6539
|
+
dirPrefix: 'omx-runtime-shutdown-hud-owner-read-error-bin-',
|
|
6540
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
6541
|
+
set -eu
|
|
6542
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
6543
|
+
case "$1" in
|
|
6544
|
+
-V)
|
|
6545
|
+
echo "tmux 3.4"
|
|
6546
|
+
exit 0
|
|
6547
|
+
;;
|
|
6548
|
+
list-panes)
|
|
6549
|
+
case "$*" in
|
|
6550
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
6551
|
+
exit 1
|
|
6552
|
+
;;
|
|
6553
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
6554
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\texec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%%11' node /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-hud-owner-read-error/worker-1 codex\\n"
|
|
6555
|
+
exit 0
|
|
6556
|
+
;;
|
|
6557
|
+
*)
|
|
6558
|
+
exit 1
|
|
6559
|
+
;;
|
|
6560
|
+
esac
|
|
6561
|
+
;;
|
|
6562
|
+
split-window)
|
|
6563
|
+
printf '%%44\\n'
|
|
6564
|
+
exit 0
|
|
6565
|
+
;;
|
|
6566
|
+
show-option)
|
|
6567
|
+
case "$*" in
|
|
6568
|
+
*"-p -t %11 @omx_team_pane_owner_id"*)
|
|
6569
|
+
echo "team:team-hud-owner-read-error"
|
|
6570
|
+
;;
|
|
6571
|
+
*"-p -t %12 @omx_team_pane_owner_id"*)
|
|
6572
|
+
exit 2
|
|
6573
|
+
;;
|
|
6574
|
+
*"-p -t %13 @omx_team_pane_owner_id"*)
|
|
6575
|
+
echo "team:team-hud-owner-read-error"
|
|
6576
|
+
;;
|
|
6577
|
+
*)
|
|
6578
|
+
exit 1
|
|
6579
|
+
;;
|
|
6580
|
+
esac
|
|
6581
|
+
exit 0
|
|
6582
|
+
;;
|
|
6583
|
+
kill-pane|resize-pane|select-pane|run-shell)
|
|
6584
|
+
exit 0
|
|
6585
|
+
;;
|
|
6586
|
+
*)
|
|
6587
|
+
exit 0
|
|
6588
|
+
;;
|
|
6589
|
+
esac
|
|
6590
|
+
`,
|
|
6591
|
+
}, async ({ tmuxLogPath }) => {
|
|
6592
|
+
await initTeamState(teamName, 'shutdown hud owner read error test', 'executor', 1, cwd);
|
|
6593
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
6594
|
+
assert.ok(config);
|
|
6595
|
+
if (!config)
|
|
6596
|
+
return;
|
|
6597
|
+
config.tmux_session = 'leader:0';
|
|
6598
|
+
config.leader_pane_id = '%11';
|
|
6599
|
+
config.hud_pane_id = '%12';
|
|
6600
|
+
config.workers[0].pane_id = '%13';
|
|
6601
|
+
await saveTeamConfig(config, cwd);
|
|
6602
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
6603
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
6604
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %12/);
|
|
6605
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
6606
|
+
assert.doesNotMatch(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\\{pane_id\\}`));
|
|
6607
|
+
assert.match(warnings.join('\n'), /skipped shared-session HUD pane %12 because team owner tag could not be read: tmux show-option exited 2/);
|
|
5251
6608
|
});
|
|
5252
6609
|
}
|
|
5253
6610
|
finally {
|
|
6611
|
+
console.warn = originalWarn;
|
|
5254
6612
|
await rm(cwd, { recursive: true, force: true });
|
|
5255
6613
|
}
|
|
5256
6614
|
});
|