oh-my-codex 0.20.2 → 0.20.5
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 +36 -18
- package/Cargo.toml +1 -1
- package/README.md +28 -2
- package/crates/omx-runtime/Cargo.toml +4 -0
- package/crates/omx-runtime/src/main.rs +415 -2
- package/crates/omx-runtime/tests/execution.rs +483 -0
- package/crates/omx-runtime-core/src/dispatch.rs +96 -10
- package/crates/omx-runtime-core/src/engine.rs +839 -29
- package/crates/omx-runtime-core/src/lib.rs +8 -0
- package/dist/adapt/__tests__/herdr-target.test.d.ts +2 -0
- package/dist/adapt/__tests__/herdr-target.test.d.ts.map +1 -0
- package/dist/adapt/__tests__/herdr-target.test.js +72 -0
- package/dist/adapt/__tests__/herdr-target.test.js.map +1 -0
- package/dist/adapt/contracts.d.ts +1 -1
- package/dist/adapt/contracts.d.ts.map +1 -1
- package/dist/adapt/contracts.js +1 -1
- package/dist/adapt/contracts.js.map +1 -1
- package/dist/adapt/herdr/__tests__/authority.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/authority.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/authority.test.js +49 -0
- package/dist/adapt/herdr/__tests__/authority.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/bridge.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/bridge.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/bridge.test.js +176 -0
- package/dist/adapt/herdr/__tests__/bridge.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/sanitize.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/sanitize.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/sanitize.test.js +45 -0
- package/dist/adapt/herdr/__tests__/sanitize.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/semantic.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/semantic.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/semantic.test.js +130 -0
- package/dist/adapt/herdr/__tests__/semantic.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/seq-store.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/seq-store.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/seq-store.test.js +54 -0
- package/dist/adapt/herdr/__tests__/seq-store.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/socket-live.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/socket-live.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/socket-live.test.js +104 -0
- package/dist/adapt/herdr/__tests__/socket-live.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/team-map.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/team-map.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/team-map.test.js +46 -0
- package/dist/adapt/herdr/__tests__/team-map.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/transport.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/transport.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/transport.test.js +160 -0
- package/dist/adapt/herdr/__tests__/transport.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/wiring.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/wiring.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/wiring.test.js +118 -0
- package/dist/adapt/herdr/__tests__/wiring.test.js.map +1 -0
- package/dist/adapt/herdr/authority.d.ts +34 -0
- package/dist/adapt/herdr/authority.d.ts.map +1 -0
- package/dist/adapt/herdr/authority.js +71 -0
- package/dist/adapt/herdr/authority.js.map +1 -0
- package/dist/adapt/herdr/bridge.d.ts +84 -0
- package/dist/adapt/herdr/bridge.d.ts.map +1 -0
- package/dist/adapt/herdr/bridge.js +222 -0
- package/dist/adapt/herdr/bridge.js.map +1 -0
- package/dist/adapt/herdr/sanitize.d.ts +20 -0
- package/dist/adapt/herdr/sanitize.d.ts.map +1 -0
- package/dist/adapt/herdr/sanitize.js +70 -0
- package/dist/adapt/herdr/sanitize.js.map +1 -0
- package/dist/adapt/herdr/semantic.d.ts +67 -0
- package/dist/adapt/herdr/semantic.d.ts.map +1 -0
- package/dist/adapt/herdr/semantic.js +170 -0
- package/dist/adapt/herdr/semantic.js.map +1 -0
- package/dist/adapt/herdr/seq-store.d.ts +32 -0
- package/dist/adapt/herdr/seq-store.d.ts.map +1 -0
- package/dist/adapt/herdr/seq-store.js +126 -0
- package/dist/adapt/herdr/seq-store.js.map +1 -0
- package/dist/adapt/herdr/team-map.d.ts +30 -0
- package/dist/adapt/herdr/team-map.d.ts.map +1 -0
- package/dist/adapt/herdr/team-map.js +37 -0
- package/dist/adapt/herdr/team-map.js.map +1 -0
- package/dist/adapt/herdr/transport.d.ts +115 -0
- package/dist/adapt/herdr/transport.d.ts.map +1 -0
- package/dist/adapt/herdr/transport.js +260 -0
- package/dist/adapt/herdr/transport.js.map +1 -0
- package/dist/adapt/herdr/wiring.d.ts +24 -0
- package/dist/adapt/herdr/wiring.d.ts.map +1 -0
- package/dist/adapt/herdr/wiring.js +62 -0
- package/dist/adapt/herdr/wiring.js.map +1 -0
- package/dist/adapt/herdr.d.ts +19 -0
- package/dist/adapt/herdr.d.ts.map +1 -0
- package/dist/adapt/herdr.js +115 -0
- package/dist/adapt/herdr.js.map +1 -0
- package/dist/adapt/index.d.ts +4 -0
- package/dist/adapt/index.d.ts.map +1 -1
- package/dist/adapt/index.js +48 -0
- package/dist/adapt/index.js.map +1 -1
- package/dist/adapt/registry.d.ts.map +1 -1
- package/dist/adapt/registry.js +11 -0
- package/dist/adapt/registry.js.map +1 -1
- package/dist/agents/__tests__/definitions.test.js +81 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +25 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/auth/__tests__/hotswap.test.js +60 -1
- package/dist/auth/__tests__/hotswap.test.js.map +1 -1
- package/dist/auth/__tests__/storage.test.js +33 -1
- package/dist/auth/__tests__/storage.test.js.map +1 -1
- package/dist/auth/hotswap.d.ts +22 -2
- package/dist/auth/hotswap.d.ts.map +1 -1
- package/dist/auth/hotswap.js +107 -22
- package/dist/auth/hotswap.js.map +1 -1
- package/dist/auth/storage.js +1 -1
- package/dist/auth/storage.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +86 -1333
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +7 -6
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/cli/__tests__/agents-init.test.js +4 -23
- package/dist/cli/__tests__/agents-init.test.js.map +1 -1
- package/dist/cli/__tests__/api.test.js +61 -24
- package/dist/cli/__tests__/api.test.js.map +1 -1
- package/dist/cli/__tests__/auth.test.js +63 -7
- package/dist/cli/__tests__/auth.test.js.map +1 -1
- package/dist/cli/__tests__/codex-feature-probe.test.js +66 -1
- package/dist/cli/__tests__/codex-feature-probe.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +150 -2
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/detached-leader-teardown.test.d.ts +2 -0
- package/dist/cli/__tests__/detached-leader-teardown.test.d.ts.map +1 -0
- package/dist/cli/__tests__/detached-leader-teardown.test.js +473 -0
- package/dist/cli/__tests__/detached-leader-teardown.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-process-identity-readiness.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-process-identity-readiness.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-process-identity-readiness.test.js +103 -0
- package/dist/cli/__tests__/doctor-process-identity-readiness.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-state-root-binding.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-state-root-binding.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-state-root-binding.test.js +139 -0
- package/dist/cli/__tests__/doctor-state-root-binding.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js +135 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +211 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +878 -463
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +1290 -127
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +8 -4
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/native-assets.test.js +398 -16
- package/dist/cli/__tests__/native-assets.test.js.map +1 -1
- package/dist/cli/__tests__/native-hook-claim-journal.test.js +132 -6
- package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +3 -3
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +49 -164
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-child-binding-3212.test.d.ts +2 -0
- package/dist/cli/__tests__/ralplan-child-binding-3212.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ralplan-child-binding-3212.test.js +56 -0
- package/dist/cli/__tests__/ralplan-child-binding-3212.test.js.map +1 -0
- package/dist/cli/__tests__/ralplan.test.js +107 -109
- package/dist/cli/__tests__/ralplan.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +11 -10
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +1118 -43
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/session-search-help.test.js +25 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +8 -55
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +224 -7
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +93 -41
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +150 -8
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +117 -2
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +95 -4
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +669 -177
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +33 -2
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/api.d.ts.map +1 -1
- package/dist/cli/api.js +9 -10
- package/dist/cli/api.js.map +1 -1
- package/dist/cli/codex-feature-probe.d.ts +16 -0
- package/dist/cli/codex-feature-probe.d.ts.map +1 -1
- package/dist/cli/codex-feature-probe.js +44 -0
- package/dist/cli/codex-feature-probe.js.map +1 -1
- package/dist/cli/codex-home.d.ts.map +1 -1
- package/dist/cli/codex-home.js +7 -4
- package/dist/cli/codex-home.js.map +1 -1
- package/dist/cli/doctor.d.ts +20 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +290 -3
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +234 -12
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2409 -746
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.js +2 -2
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/native-assets.d.ts +25 -0
- package/dist/cli/native-assets.d.ts.map +1 -1
- package/dist/cli/native-assets.js +448 -51
- package/dist/cli/native-assets.js.map +1 -1
- package/dist/cli/native-hook-claim-journal.d.ts +20 -5
- package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
- package/dist/cli/native-hook-claim-journal.js +67 -32
- package/dist/cli/native-hook-claim-journal.js.map +1 -1
- package/dist/cli/package-manager-ownership.d.ts +63 -0
- package/dist/cli/package-manager-ownership.d.ts.map +1 -0
- package/dist/cli/package-manager-ownership.js +323 -0
- package/dist/cli/package-manager-ownership.js.map +1 -0
- package/dist/cli/ralph.js +2 -2
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/ralplan.d.ts +5 -7
- package/dist/cli/ralplan.d.ts.map +1 -1
- package/dist/cli/ralplan.js +83 -134
- package/dist/cli/ralplan.js.map +1 -1
- package/dist/cli/session-search.d.ts +5 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +87 -0
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +15 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +27 -1
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +119 -47
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +52 -9
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +2 -2
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/uninstall.d.ts +9 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +71 -30
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/cli/update-worker.d.ts +2 -0
- package/dist/cli/update-worker.d.ts.map +1 -0
- package/dist/cli/update-worker.js +142 -0
- package/dist/cli/update-worker.js.map +1 -0
- package/dist/cli/update.d.ts +14 -22
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +192 -241
- package/dist/cli/update.js.map +1 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +503 -30
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +5 -3
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +67 -3
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +27 -21
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +610 -152
- package/dist/config/generator.js.map +1 -1
- package/dist/config/models.d.ts +12 -2
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +15 -1
- package/dist/config/models.js.map +1 -1
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +25 -0
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.js +2 -0
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +5 -3
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +27 -4
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.d.ts +2 -0
- package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.js +172 -0
- package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +176 -37
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +402 -545
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +39 -4
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +161 -26
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +27 -0
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +10 -118
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js +7 -0
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +533 -14
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +159 -24
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +2 -1
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +44 -4
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js +9 -0
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +3130 -365
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/agents-overlay.js +1 -1
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +108 -2
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/sdk.test.js +151 -1
- package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js +20 -3
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner.js +1 -0
- package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
- package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
- package/dist/hooks/extensibility/runtime.js.map +1 -1
- package/dist/hooks/extensibility/sdk/paths.d.ts +1 -1
- package/dist/hooks/extensibility/sdk/paths.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk/paths.js +2 -2
- package/dist/hooks/extensibility/sdk/paths.js.map +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.d.ts +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.js +55 -15
- package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
- package/dist/hooks/extensibility/sdk.d.ts +1 -0
- package/dist/hooks/extensibility/sdk.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk.js +1 -1
- package/dist/hooks/extensibility/sdk.js.map +1 -1
- package/dist/hooks/extensibility/types.d.ts +7 -0
- package/dist/hooks/extensibility/types.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +10 -3
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +116 -20
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/session.d.ts +204 -14
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +1500 -223
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +6 -6
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +26 -16
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +83 -78
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +36 -31
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +155 -198
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/tmux.d.ts +42 -4
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +1017 -139
- package/dist/hud/tmux.js.map +1 -1
- package/dist/leader/__tests__/contract.test.js +124 -37
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +17 -6
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +102 -26
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
- package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
- package/dist/mcp/__tests__/hermes-bridge.test.js +17 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
- package/dist/mcp/__tests__/server-lifecycle.test.js +4 -4
- package/dist/mcp/__tests__/state-paths.test.js +735 -4
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +1 -1
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/__tests__/trace-server.test.js +2 -2
- package/dist/mcp/__tests__/trace-server.test.js.map +1 -1
- package/dist/mcp/__tests__/wiki-server.test.js +18 -0
- package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
- package/dist/mcp/code-intel-server.d.ts.map +1 -1
- package/dist/mcp/code-intel-server.js +13 -3
- package/dist/mcp/code-intel-server.js.map +1 -1
- package/dist/mcp/hermes-bridge.d.ts.map +1 -1
- package/dist/mcp/hermes-bridge.js +2 -2
- package/dist/mcp/hermes-bridge.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +78 -4
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +543 -40
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/wiki-server.d.ts +11 -0
- package/dist/mcp/wiki-server.d.ts.map +1 -1
- package/dist/mcp/wiki-server.js +9 -2
- package/dist/mcp/wiki-server.js.map +1 -1
- package/dist/modes/__tests__/base-autopilot-gates.test.js +36 -7
- package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
- package/dist/modes/__tests__/base-multi-state-compat.test.js +1 -1
- package/dist/modes/__tests__/base-multi-state-compat.test.js.map +1 -1
- package/dist/modes/__tests__/base-session-scope.test.js +17 -8
- package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/modes/base.d.ts +5 -5
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +76 -21
- package/dist/modes/base.js.map +1 -1
- package/dist/native-assets/archive.d.ts +17 -0
- package/dist/native-assets/archive.d.ts.map +1 -0
- package/dist/native-assets/archive.js +238 -0
- package/dist/native-assets/archive.js.map +1 -0
- package/dist/native-assets/policy.d.ts +42 -0
- package/dist/native-assets/policy.d.ts.map +1 -0
- package/dist/native-assets/policy.js +155 -0
- package/dist/native-assets/policy.js.map +1 -0
- package/dist/notifications/temp-contract.d.ts.map +1 -1
- package/dist/notifications/temp-contract.js +4 -0
- package/dist/notifications/temp-contract.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +164 -42
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +72 -155
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +124 -18
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +36 -8
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +73 -1316
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js +335 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
- package/dist/ralplan/__tests__/runtime.test.js +85 -101
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +9 -1
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +82 -338
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/documented-leader-preflight.d.ts +41 -0
- package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
- package/dist/ralplan/documented-leader-preflight.js +360 -0
- package/dist/ralplan/documented-leader-preflight.js.map +1 -0
- package/dist/ralplan/runtime.d.ts +2 -1
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +67 -168
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/__tests__/bridge.test.js +42 -1
- package/dist/runtime/__tests__/bridge.test.js.map +1 -1
- package/dist/runtime/bridge.d.ts +12 -0
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +52 -1
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/runtime/run-state.d.ts +5 -1
- package/dist/runtime/run-state.d.ts.map +1 -1
- package/dist/runtime/run-state.js +15 -8
- package/dist/runtime/run-state.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +16323 -8978
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/issue-3293-callsite-parity.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3293-callsite-parity.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3293-callsite-parity.test.js +225 -0
- package/dist/scripts/__tests__/issue-3293-callsite-parity.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.js +188 -0
- package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.js +259 -0
- package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.js +491 -0
- package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.js +708 -0
- package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.js +208 -0
- package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.js.map +1 -0
- package/dist/scripts/__tests__/notify-state-io.test.js +26 -0
- package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.js +23 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -1
- package/dist/scripts/__tests__/postinstall.test.js +19 -1
- package/dist/scripts/__tests__/postinstall.test.js.map +1 -1
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +63 -173
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +74 -159
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +404 -13
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-release-assets.test.d.ts +2 -0
- package/dist/scripts/__tests__/verify-native-release-assets.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/verify-native-release-assets.test.js +235 -0
- package/dist/scripts/__tests__/verify-native-release-assets.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +53 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +14448 -1480
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts +9 -0
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +35 -12
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/generate-native-release-manifest.js +80 -51
- package/dist/scripts/generate-native-release-manifest.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +78 -115
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +74 -3
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +5 -0
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +234 -79
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +71 -16
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +42 -21
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +82 -33
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +1 -0
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +32 -3
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +79 -82
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/postinstall-advisory.d.ts +14 -0
- package/dist/scripts/postinstall-advisory.d.ts.map +1 -0
- package/dist/scripts/postinstall-advisory.js +49 -0
- package/dist/scripts/postinstall-advisory.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +1 -1
- package/dist/scripts/postinstall.d.ts.map +1 -1
- package/dist/scripts/postinstall.js +1 -1
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +80 -4
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +3162 -26
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +16 -1
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-release-assets.js +37 -23
- package/dist/scripts/verify-native-release-assets.js.map +1 -1
- package/dist/state/__tests__/mode-state-context.test.js +61 -0
- package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +462 -850
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +400 -2
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +8 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +73 -0
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/state/operations.d.ts +3 -0
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +144 -40
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +18 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +420 -23
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts +3 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +10 -4
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +3 -1
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +53 -247
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
- package/dist/subagents/__tests__/tracker.test.js +837 -1048
- package/dist/subagents/__tests__/tracker.test.js.map +1 -1
- package/dist/subagents/tracker.d.ts +58 -135
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +811 -632
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +91 -1
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +58 -17
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/idle-nudge.test.js +45 -15
- package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
- package/dist/team/__tests__/mcp-comm.test.js +197 -30
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +110 -3
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/notice-ledger.test.d.ts +2 -0
- package/dist/team/__tests__/notice-ledger.test.d.ts.map +1 -0
- package/dist/team/__tests__/notice-ledger.test.js +131 -0
- package/dist/team/__tests__/notice-ledger.test.js.map +1 -0
- package/dist/team/__tests__/pane-teardown-residue-e2e.test.d.ts +2 -0
- package/dist/team/__tests__/pane-teardown-residue-e2e.test.d.ts.map +1 -0
- package/dist/team/__tests__/pane-teardown-residue-e2e.test.js +410 -0
- package/dist/team/__tests__/pane-teardown-residue-e2e.test.js.map +1 -0
- package/dist/team/__tests__/phase-controller.test.js +20 -0
- package/dist/team/__tests__/phase-controller.test.js.map +1 -1
- package/dist/team/__tests__/runtime-cli.test.js +9 -0
- package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +3963 -428
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +2405 -203
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state-root.test.js +117 -2
- package/dist/team/__tests__/state-root.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +804 -36
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/team-ops-contract.test.js +6 -0
- package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +4099 -1039
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/tmux-source-authority-realtmux.test.d.ts +2 -0
- package/dist/team/__tests__/tmux-source-authority-realtmux.test.d.ts.map +1 -0
- package/dist/team/__tests__/tmux-source-authority-realtmux.test.js +149 -0
- package/dist/team/__tests__/tmux-source-authority-realtmux.test.js.map +1 -0
- package/dist/team/__tests__/tmux-test-fixture.d.ts +69 -0
- package/dist/team/__tests__/tmux-test-fixture.d.ts.map +1 -1
- package/dist/team/__tests__/tmux-test-fixture.js +242 -15
- package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
- package/dist/team/__tests__/tmux-test-fixture.test.js +177 -1
- package/dist/team/__tests__/tmux-test-fixture.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +53 -44
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/__tests__/worktree.test.js +6 -0
- package/dist/team/__tests__/worktree.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +15 -9
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/exact-pane.d.ts +23 -0
- package/dist/team/exact-pane.d.ts.map +1 -0
- package/dist/team/exact-pane.js +133 -0
- package/dist/team/exact-pane.js.map +1 -0
- package/dist/team/idle-nudge.d.ts +12 -1
- package/dist/team/idle-nudge.d.ts.map +1 -1
- package/dist/team/idle-nudge.js +41 -14
- package/dist/team/idle-nudge.js.map +1 -1
- package/dist/team/model-contract.d.ts +2 -5
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +59 -17
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/notice-ledger.d.ts +54 -0
- package/dist/team/notice-ledger.d.ts.map +1 -0
- package/dist/team/notice-ledger.js +259 -0
- package/dist/team/notice-ledger.js.map +1 -0
- package/dist/team/phase-controller.d.ts.map +1 -1
- package/dist/team/phase-controller.js +2 -0
- package/dist/team/phase-controller.js.map +1 -1
- package/dist/team/runtime.d.ts +4 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +1527 -291
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts +13 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +1503 -387
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +26 -8
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/locks.d.ts.map +1 -1
- package/dist/team/state/locks.js +61 -13
- package/dist/team/state/locks.js.map +1 -1
- package/dist/team/state/monitor.d.ts +10 -0
- package/dist/team/state/monitor.d.ts.map +1 -1
- package/dist/team/state/monitor.js +13 -0
- package/dist/team/state/monitor.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +4 -3
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state/types.d.ts +15 -0
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state-root.d.ts +12 -0
- package/dist/team/state-root.d.ts.map +1 -1
- package/dist/team/state-root.js +177 -0
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts +85 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +672 -129
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +5 -0
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +4 -0
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +163 -27
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +1682 -376
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +4 -0
- package/dist/team/worktree.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +950 -2
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +1 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +35 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +232 -46
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
- package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
- package/dist/utils/__tests__/file-durability.test.js +114 -0
- package/dist/utils/__tests__/file-durability.test.js.map +1 -0
- package/dist/utils/__tests__/paths.test.js +21 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/file-durability.d.ts +25 -0
- package/dist/utils/file-durability.d.ts.map +1 -0
- package/dist/utils/file-durability.js +67 -0
- package/dist/utils/file-durability.js.map +1 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -6
- package/dist/utils/paths.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +49 -4
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +60 -0
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
- package/dist/verification/__tests__/native-release-manifest.test.js +157 -4
- package/dist/verification/__tests__/native-release-manifest.test.js.map +1 -1
- package/dist/wiki/lint.d.ts +4 -1
- package/dist/wiki/lint.d.ts.map +1 -1
- package/dist/wiki/lint.js +3 -2
- package/dist/wiki/lint.js.map +1 -1
- package/package.json +13 -5
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +105 -17
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -5
- package/plugins/oh-my-codex/skills/cancel/SKILL.md +30 -85
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +16 -17
- package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +3 -2
- package/skills/autopilot/SKILL.md +13 -5
- package/skills/cancel/SKILL.md +30 -85
- package/skills/ralph/SKILL.md +6 -4
- package/skills/ralplan/SKILL.md +16 -17
- package/skills/team/SKILL.md +13 -9
- package/skills/ultragoal/SKILL.md +3 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +8831 -951
- package/src/scripts/__tests__/issue-3293-callsite-parity.test.ts +187 -0
- package/src/scripts/__tests__/issue-3293-cancel-transaction.test.ts +189 -0
- package/src/scripts/__tests__/issue-3293-hook-owned-cancel.test.ts +187 -0
- package/src/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.ts +591 -0
- package/src/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.ts +701 -0
- package/src/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.ts +211 -0
- package/src/scripts/__tests__/notify-state-io.test.ts +29 -0
- package/src/scripts/__tests__/notify-tmux-injection.test.ts +26 -1
- package/src/scripts/__tests__/postinstall.test.ts +23 -1
- package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
- package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +67 -185
- package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +79 -178
- package/src/scripts/__tests__/smoke-packed-install.test.ts +525 -16
- package/src/scripts/__tests__/verify-native-release-assets.test.ts +240 -0
- package/src/scripts/codex-native-hook.ts +14238 -1429
- package/src/scripts/codex-native-pre-post.ts +43 -12
- package/src/scripts/generate-native-release-manifest.ts +83 -79
- package/src/scripts/notify-fallback-watcher.ts +91 -122
- package/src/scripts/notify-hook/auto-nudge.ts +71 -3
- package/src/scripts/notify-hook/state-io.ts +11 -0
- package/src/scripts/notify-hook/team-dispatch.ts +242 -75
- package/src/scripts/notify-hook/team-leader-nudge.ts +72 -22
- package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
- package/src/scripts/notify-hook/team-worker-stop.ts +43 -20
- package/src/scripts/notify-hook/team-worker.ts +82 -31
- package/src/scripts/notify-hook/tmux-injection.ts +33 -3
- package/src/scripts/notify-hook.ts +84 -82
- package/src/scripts/postinstall-advisory.ts +67 -0
- package/src/scripts/postinstall.ts +1 -1
- package/src/scripts/smoke-packed-install.ts +3608 -39
- package/src/scripts/sync-plugin-mirror.ts +17 -1
- package/src/scripts/verify-native-release-assets.ts +39 -31
- package/templates/AGENTS.md +1 -0
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
- package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
- package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
- package/dist/subagents/adapted-role-binding.d.ts +0 -17
- package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
- package/dist/subagents/adapted-role-binding.js +0 -75
- package/dist/subagents/adapted-role-binding.js.map +0 -1
package/dist/hud/tmux.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { execFileSync } from 'child_process';
|
|
2
3
|
import { existsSync } from 'fs';
|
|
3
4
|
import { HUD_RESIZE_RECONCILE_DELAY_SECONDS, HUD_TMUX_HEIGHT_LINES } from './constants.js';
|
|
@@ -9,6 +10,70 @@ export const TMUX_PANE_FIELD_SEPARATOR = '\x1f';
|
|
|
9
10
|
export const TMUX_PANE_FIELD_SEPARATOR_OCTAL_ESCAPE = '\\037';
|
|
10
11
|
/** Upper bound for tmux hook indices (signed 32-bit max). */
|
|
11
12
|
const TMUX_HOOK_INDEX_MAX = 2147483647;
|
|
13
|
+
const PSMUX_PANE_ID_MAX = 0xffffffffn;
|
|
14
|
+
const PSMUX_PANE_ID_MAX_DIGITS = PSMUX_PANE_ID_MAX.toString().length;
|
|
15
|
+
export function parseCanonicalTmuxPaneId(value) {
|
|
16
|
+
if (typeof value !== 'string')
|
|
17
|
+
return null;
|
|
18
|
+
const match = /^%(0|[1-9]\d*)$/.exec(value);
|
|
19
|
+
if (!match || match[1].length > PSMUX_PANE_ID_MAX_DIGITS)
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
return BigInt(match[1]) <= PSMUX_PANE_ID_MAX ? value : null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parses tmux output used as authority. Authority frames are deliberately
|
|
30
|
+
* transport-strict: a nonempty body followed by exactly one LF or CRLF.
|
|
31
|
+
*/
|
|
32
|
+
export function parseExactTmuxAuthorityLines(output) {
|
|
33
|
+
if (typeof output !== 'string' || output.length < 2)
|
|
34
|
+
return null;
|
|
35
|
+
const terminator = output.endsWith('\r\n') ? '\r\n' : output.endsWith('\n') ? '\n' : null;
|
|
36
|
+
if (!terminator)
|
|
37
|
+
return null;
|
|
38
|
+
const body = output.slice(0, -terminator.length);
|
|
39
|
+
if (body === '' || body.endsWith('\n') || body.includes('\r'))
|
|
40
|
+
return null;
|
|
41
|
+
return body.split('\n');
|
|
42
|
+
}
|
|
43
|
+
/** Parses one exact, nonempty tmux authority scalar. */
|
|
44
|
+
export function parseExactTmuxAuthorityScalar(output) {
|
|
45
|
+
const lines = parseExactTmuxAuthorityLines(output);
|
|
46
|
+
return lines?.length === 1 && lines[0] !== '' ? lines[0] : null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Tolerant pane parsing is for display metadata only. Callers using its output
|
|
50
|
+
* as authority MUST first validate framing with parseExactTmuxAuthorityLines.
|
|
51
|
+
*/
|
|
52
|
+
function canonicalizePaneSnapshotBatch(panes) {
|
|
53
|
+
const paneIds = new Set();
|
|
54
|
+
const canonicalPanes = [];
|
|
55
|
+
for (const pane of panes) {
|
|
56
|
+
const paneId = parseCanonicalTmuxPaneId(pane.paneId);
|
|
57
|
+
if (!paneId || paneIds.has(paneId))
|
|
58
|
+
return null;
|
|
59
|
+
paneIds.add(paneId);
|
|
60
|
+
canonicalPanes.push({ ...pane, paneId });
|
|
61
|
+
}
|
|
62
|
+
return canonicalPanes;
|
|
63
|
+
}
|
|
64
|
+
function parseCanonicalPaneIdSnapshot(output) {
|
|
65
|
+
const lines = parseExactTmuxAuthorityLines(output);
|
|
66
|
+
if (!lines)
|
|
67
|
+
return null;
|
|
68
|
+
const paneIds = new Set();
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
const paneId = parseCanonicalTmuxPaneId(line);
|
|
71
|
+
if (!paneId || paneIds.has(paneId))
|
|
72
|
+
return null;
|
|
73
|
+
paneIds.add(paneId);
|
|
74
|
+
}
|
|
75
|
+
return paneIds;
|
|
76
|
+
}
|
|
12
77
|
function defaultExecTmuxSync(args) {
|
|
13
78
|
try {
|
|
14
79
|
return execFileSync(resolveTmuxBinaryForPlatform() || 'tmux', args, {
|
|
@@ -25,28 +90,36 @@ function defaultExecTmuxSync(args) {
|
|
|
25
90
|
}
|
|
26
91
|
}
|
|
27
92
|
function parseNonNegativeInteger(value) {
|
|
28
|
-
if (typeof value !== 'string' ||
|
|
93
|
+
if (typeof value !== 'string' || !/^(?:0|[1-9][0-9]*)$/.test(value))
|
|
29
94
|
return null;
|
|
30
|
-
const parsed = Number
|
|
31
|
-
return Number.
|
|
95
|
+
const parsed = Number(value);
|
|
96
|
+
return Number.isSafeInteger(parsed) && parsed <= 4_294_967_295 ? parsed : null;
|
|
32
97
|
}
|
|
33
98
|
function parsePositiveInteger(value) {
|
|
34
99
|
const parsed = parseNonNegativeInteger(value);
|
|
35
100
|
return parsed !== null && parsed > 0 ? parsed : null;
|
|
36
101
|
}
|
|
37
|
-
export function parseTmuxPaneSnapshot(output) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
102
|
+
export function parseTmuxPaneSnapshot(output, authoritativePaneIds) {
|
|
103
|
+
const lines = output.split('\n');
|
|
104
|
+
if (lines.at(-1) === '')
|
|
105
|
+
lines.pop();
|
|
106
|
+
if (lines.length === 0)
|
|
107
|
+
return [];
|
|
108
|
+
const panes = [];
|
|
109
|
+
for (const line of lines) {
|
|
110
|
+
if (line === '')
|
|
111
|
+
return [];
|
|
43
112
|
const fieldSeparator = line.includes(TMUX_PANE_FIELD_SEPARATOR)
|
|
44
113
|
? TMUX_PANE_FIELD_SEPARATOR
|
|
45
114
|
: line.includes(TMUX_PANE_FIELD_SEPARATOR_OCTAL_ESCAPE)
|
|
46
115
|
? TMUX_PANE_FIELD_SEPARATOR_OCTAL_ESCAPE
|
|
47
116
|
: '\t';
|
|
48
117
|
const parts = line.split(fieldSeparator);
|
|
49
|
-
const [
|
|
118
|
+
const [rawPaneId = '', currentCommand = ''] = parts;
|
|
119
|
+
const paneId = parseCanonicalTmuxPaneId(rawPaneId);
|
|
120
|
+
if (!paneId)
|
|
121
|
+
return [];
|
|
122
|
+
const hasGeometryFields = parts.length >= 9;
|
|
50
123
|
const paneLeft = parseNonNegativeInteger(parts[2]);
|
|
51
124
|
const paneTop = parseNonNegativeInteger(parts[3]);
|
|
52
125
|
const paneWidth = parsePositiveInteger(parts[4]);
|
|
@@ -61,16 +134,25 @@ export function parseTmuxPaneSnapshot(output) {
|
|
|
61
134
|
&& paneBottom !== null
|
|
62
135
|
&& windowWidth !== null
|
|
63
136
|
&& windowHeight !== null);
|
|
137
|
+
if (hasGeometryFields && !hasGeometry)
|
|
138
|
+
return [];
|
|
64
139
|
const payloadParts = hasGeometry ? parts.slice(9) : parts.slice(2);
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
140
|
+
const hasIncarnationFields = hasGeometry
|
|
141
|
+
&& payloadParts.length >= 4
|
|
142
|
+
&& /^(?:0|1)$/.test(payloadParts.at(-2) ?? '')
|
|
143
|
+
&& parsePositiveInteger(payloadParts.at(-1)) !== null;
|
|
144
|
+
const incarnationParts = hasIncarnationFields ? payloadParts.slice(-2) : [];
|
|
145
|
+
const commandParts = hasIncarnationFields ? payloadParts.slice(0, -2) : payloadParts;
|
|
146
|
+
const hasCurrentPathColumn = commandParts.length >= 2;
|
|
147
|
+
const currentPath = hasCurrentPathColumn ? (commandParts.at(-1) ?? '') : '';
|
|
148
|
+
const startCommandParts = hasCurrentPathColumn ? commandParts.slice(0, -1) : commandParts;
|
|
68
149
|
const trimmedCurrentPath = currentPath.trim();
|
|
69
|
-
|
|
70
|
-
paneId
|
|
150
|
+
panes.push({
|
|
151
|
+
paneId,
|
|
71
152
|
currentCommand: currentCommand.trim(),
|
|
72
153
|
startCommand: startCommandParts.join('\t').trim(),
|
|
73
154
|
...(trimmedCurrentPath ? { currentPath: trimmedCurrentPath } : {}),
|
|
155
|
+
...(hasIncarnationFields ? { paneDead: incarnationParts[0] === '1', panePid: incarnationParts[1] } : {}),
|
|
74
156
|
...(hasGeometry
|
|
75
157
|
? {
|
|
76
158
|
paneLeft,
|
|
@@ -82,9 +164,16 @@ export function parseTmuxPaneSnapshot(output) {
|
|
|
82
164
|
windowHeight,
|
|
83
165
|
}
|
|
84
166
|
: {}),
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const canonicalPanes = canonicalizePaneSnapshotBatch(panes);
|
|
170
|
+
if (!canonicalPanes)
|
|
171
|
+
return [];
|
|
172
|
+
if (authoritativePaneIds
|
|
173
|
+
&& (canonicalPanes.length !== authoritativePaneIds.size
|
|
174
|
+
|| canonicalPanes.some((pane) => !authoritativePaneIds.has(pane.paneId))))
|
|
175
|
+
return [];
|
|
176
|
+
return canonicalPanes;
|
|
88
177
|
}
|
|
89
178
|
export function isHudWatchPane(pane) {
|
|
90
179
|
const command = `${pane.startCommand} ${pane.currentCommand}`.toLowerCase();
|
|
@@ -92,53 +181,302 @@ export function isHudWatchPane(pane) {
|
|
|
92
181
|
&& /--watch\b/.test(command)
|
|
93
182
|
&& (/\bomx(?:\.js)?\b/.test(command) || /\bnode\b/.test(command)));
|
|
94
183
|
}
|
|
184
|
+
const HUD_OWNER_ENV_KEYS = [
|
|
185
|
+
OMX_TMUX_HUD_OWNER_ENV,
|
|
186
|
+
'OMX_SESSION_ID',
|
|
187
|
+
OMX_TMUX_HUD_LEADER_PANE_ENV,
|
|
188
|
+
];
|
|
189
|
+
function isHudOwnerEnvKey(key) {
|
|
190
|
+
return HUD_OWNER_ENV_KEYS.some((ownerKey) => ownerKey === key);
|
|
191
|
+
}
|
|
192
|
+
function isPowerShellHudOwnerEnvKey(key) {
|
|
193
|
+
return HUD_OWNER_ENV_KEYS.some((ownerKey) => ownerKey === key.toUpperCase());
|
|
194
|
+
}
|
|
195
|
+
function lexPosixWords(command) {
|
|
196
|
+
const words = [];
|
|
197
|
+
let index = 0;
|
|
198
|
+
while (index < command.length) {
|
|
199
|
+
while (/\s/.test(command[index] ?? ''))
|
|
200
|
+
index += 1;
|
|
201
|
+
if (index >= command.length)
|
|
202
|
+
break;
|
|
203
|
+
if (command[index] === '#')
|
|
204
|
+
break;
|
|
205
|
+
let word = '';
|
|
206
|
+
let quoted = false;
|
|
207
|
+
while (index < command.length && !/\s/.test(command[index] ?? '')) {
|
|
208
|
+
const char = command[index];
|
|
209
|
+
if (';|&()<>`'.includes(char) || char === '\n' || char === '\r')
|
|
210
|
+
return null;
|
|
211
|
+
if (char === '\\') {
|
|
212
|
+
index += 1;
|
|
213
|
+
if (index >= command.length)
|
|
214
|
+
return null;
|
|
215
|
+
word += command[index];
|
|
216
|
+
index += 1;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (char === "'") {
|
|
220
|
+
quoted = true;
|
|
221
|
+
index += 1;
|
|
222
|
+
while (index < command.length && command[index] !== "'") {
|
|
223
|
+
word += command[index];
|
|
224
|
+
index += 1;
|
|
225
|
+
}
|
|
226
|
+
if (index >= command.length)
|
|
227
|
+
return null;
|
|
228
|
+
index += 1;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (char === '"') {
|
|
232
|
+
quoted = true;
|
|
233
|
+
index += 1;
|
|
234
|
+
while (index < command.length && command[index] !== '"') {
|
|
235
|
+
if (command[index] === '\\') {
|
|
236
|
+
index += 1;
|
|
237
|
+
if (index >= command.length)
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
word += command[index];
|
|
241
|
+
index += 1;
|
|
242
|
+
}
|
|
243
|
+
if (index >= command.length)
|
|
244
|
+
return null;
|
|
245
|
+
index += 1;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
word += char;
|
|
249
|
+
index += 1;
|
|
250
|
+
}
|
|
251
|
+
if (!word && !quoted)
|
|
252
|
+
return null;
|
|
253
|
+
words.push(word);
|
|
254
|
+
}
|
|
255
|
+
return words;
|
|
256
|
+
}
|
|
257
|
+
function parsePosixHudEnvAssignments(command) {
|
|
258
|
+
const values = new Map();
|
|
259
|
+
const attempted = HUD_OWNER_ENV_KEYS.some((key) => command.includes(key));
|
|
260
|
+
const words = lexPosixWords(command);
|
|
261
|
+
if (!words)
|
|
262
|
+
return { attempted, valid: false, values };
|
|
263
|
+
const inspect = (tokens) => {
|
|
264
|
+
let commandStarted = false;
|
|
265
|
+
let acceptsEnvAssignments = false;
|
|
266
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
267
|
+
const token = tokens[index];
|
|
268
|
+
const assignment = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(token);
|
|
269
|
+
if (assignment && isHudOwnerEnvKey(assignment[1]) && (!commandStarted || acceptsEnvAssignments)) {
|
|
270
|
+
const key = assignment[1];
|
|
271
|
+
const existing = values.get(key) ?? [];
|
|
272
|
+
existing.push(assignment[2]);
|
|
273
|
+
values.set(key, existing);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (token === '-c' && index + 1 < tokens.length) {
|
|
277
|
+
const nested = lexPosixWords(tokens[index + 1]);
|
|
278
|
+
if (!nested || !inspect(nested))
|
|
279
|
+
return false;
|
|
280
|
+
commandStarted = true;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (token === 'exec' && !commandStarted)
|
|
284
|
+
continue;
|
|
285
|
+
if (!commandStarted && token === 'env') {
|
|
286
|
+
commandStarted = true;
|
|
287
|
+
acceptsEnvAssignments = true;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
commandStarted = true;
|
|
291
|
+
acceptsEnvAssignments = false;
|
|
292
|
+
}
|
|
293
|
+
return true;
|
|
294
|
+
};
|
|
295
|
+
return { attempted, valid: inspect(words), values };
|
|
296
|
+
}
|
|
297
|
+
function parseShellEnvAssignments(command, key) {
|
|
298
|
+
const parsed = parsePosixHudEnvAssignments(command);
|
|
299
|
+
return parsed.valid ? (parsed.values.get(key) ?? []) : [];
|
|
300
|
+
}
|
|
95
301
|
function parseShellEnvAssignment(command, key) {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
302
|
+
const values = parseShellEnvAssignments(command, key);
|
|
303
|
+
return values.length === 1 && values[0] !== '' ? values[0] : undefined;
|
|
304
|
+
}
|
|
305
|
+
function containsPowerShellHudOwnerReferenceOutsideQuotes(command) {
|
|
306
|
+
let quote = null;
|
|
307
|
+
let blockComment = false;
|
|
308
|
+
let lineComment = false;
|
|
309
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
310
|
+
const char = command[index];
|
|
311
|
+
const next = command[index + 1];
|
|
312
|
+
if (blockComment) {
|
|
313
|
+
if (char === '#' && next === '>') {
|
|
314
|
+
blockComment = false;
|
|
315
|
+
index += 1;
|
|
316
|
+
}
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (lineComment) {
|
|
320
|
+
if (char === '\n' || char === '\r')
|
|
321
|
+
lineComment = false;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (quote === 'single') {
|
|
325
|
+
if (char === "'" && next === "'")
|
|
326
|
+
index += 1;
|
|
327
|
+
else if (char === "'")
|
|
328
|
+
quote = null;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (quote === 'double') {
|
|
332
|
+
if (char === '`')
|
|
333
|
+
index += 1;
|
|
334
|
+
else if (char === '"')
|
|
335
|
+
quote = null;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (char === '<' && next === '#') {
|
|
339
|
+
blockComment = true;
|
|
340
|
+
index += 1;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (char === '#') {
|
|
344
|
+
lineComment = true;
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (char === "'") {
|
|
348
|
+
quote = 'single';
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (char === '"') {
|
|
352
|
+
quote = 'double';
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (char === '`') {
|
|
356
|
+
index += 1;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (command.slice(index, index + 5).toLowerCase() === '$env:') {
|
|
360
|
+
const match = /^\$env:([A-Za-z_][A-Za-z0-9_]*)/i.exec(command.slice(index));
|
|
361
|
+
if (match && isPowerShellHudOwnerEnvKey(match[1]))
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
function unwrapKnownPowerShellCommand(command) {
|
|
368
|
+
const match = /^\s*powershell(?:\.exe)?\s+-NoLogo\s+-NoExit\s+-Command\s+'((?:''|[^'])*)'(.*)$/i.exec(command);
|
|
369
|
+
if (!match)
|
|
370
|
+
return null;
|
|
371
|
+
return { command: match[1].replace(/''/g, "'"), remainder: match[2] };
|
|
372
|
+
}
|
|
373
|
+
function parsePowerShellHudEnvPrefix(command) {
|
|
374
|
+
const unwrapped = unwrapKnownPowerShellCommand(command);
|
|
375
|
+
const source = unwrapped?.command ?? command;
|
|
376
|
+
const present = containsPowerShellHudOwnerReferenceOutsideQuotes(source);
|
|
377
|
+
const values = new Map();
|
|
378
|
+
if (!present)
|
|
379
|
+
return { present: false, valid: false, values, remainder: command };
|
|
380
|
+
const prefixMatch = /^\s*((?:\$env:[A-Za-z_][A-Za-z0-9_]*\s*=\s*'(?:''|[^'])*'\s*;\s*)+)&(?:\s|$)/i.exec(source);
|
|
381
|
+
if (!prefixMatch)
|
|
382
|
+
return { present: true, valid: false, values, remainder: command };
|
|
383
|
+
const assignmentPattern = /\$env:([A-Za-z_][A-Za-z0-9_]*)\s*=\s*'((?:''|[^'])*)'\s*;/gi;
|
|
384
|
+
for (const match of prefixMatch[1].matchAll(assignmentPattern)) {
|
|
385
|
+
const key = match[1].toUpperCase();
|
|
386
|
+
const existing = values.get(key) ?? [];
|
|
387
|
+
existing.push(match[2].replace(/''/g, "'"));
|
|
388
|
+
values.set(key, existing);
|
|
389
|
+
}
|
|
390
|
+
const remainder = `${source.slice(prefixMatch[0].length)}${unwrapped?.remainder ?? ''}`;
|
|
391
|
+
return { present: true, valid: true, values, remainder };
|
|
392
|
+
}
|
|
393
|
+
function hasHudOwnerMetadataAttempt(command) {
|
|
394
|
+
return parsePowerShellHudEnvPrefix(command).present || parsePosixHudEnvAssignments(command).attempted;
|
|
395
|
+
}
|
|
396
|
+
function isInvalidHudOwnerValue(key, value) {
|
|
397
|
+
return value === '' || (key === OMX_TMUX_HUD_OWNER_ENV && value !== '1');
|
|
398
|
+
}
|
|
399
|
+
function parseHudEnvAssignment(command, key) {
|
|
400
|
+
const posix = parsePosixHudEnvAssignments(command);
|
|
401
|
+
const powerShellPrefix = parsePowerShellHudEnvPrefix(command);
|
|
402
|
+
if (powerShellPrefix.present)
|
|
403
|
+
return powerShellPrefix.valid ? (powerShellPrefix.values.get(key.toUpperCase())?.length === 1 ? powerShellPrefix.values.get(key.toUpperCase())[0] : undefined) : undefined;
|
|
404
|
+
return posix.valid ? parseShellEnvAssignment(command, key) : undefined;
|
|
405
|
+
}
|
|
406
|
+
function hasAmbiguousHudOwnerMetadata(command) {
|
|
407
|
+
const posix = parsePosixHudEnvAssignments(command);
|
|
408
|
+
const powerShellPrefix = parsePowerShellHudEnvPrefix(command);
|
|
409
|
+
if (powerShellPrefix.present) {
|
|
410
|
+
if (!powerShellPrefix.valid
|
|
411
|
+
|| posix.values.size > 0
|
|
412
|
+
|| containsPowerShellHudOwnerReferenceOutsideQuotes(powerShellPrefix.remainder)
|
|
413
|
+
|| parsePosixHudEnvAssignments(powerShellPrefix.remainder).attempted)
|
|
414
|
+
return true;
|
|
415
|
+
return HUD_OWNER_ENV_KEYS.some((key) => {
|
|
416
|
+
const values = powerShellPrefix.values.get(key) ?? [];
|
|
417
|
+
return values.length > 1 || (values.length === 1 && isInvalidHudOwnerValue(key, values[0]));
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
if (posix.attempted && (!posix.valid || posix.values.size === 0))
|
|
421
|
+
return true;
|
|
422
|
+
return HUD_OWNER_ENV_KEYS.some((key) => {
|
|
423
|
+
const values = posix.values.get(key) ?? [];
|
|
424
|
+
return values.length > 1 || (values.length === 1 && isInvalidHudOwnerValue(key, values[0]));
|
|
425
|
+
});
|
|
106
426
|
}
|
|
107
427
|
export function readHudPaneOwner(pane) {
|
|
108
428
|
const command = `${pane.startCommand} ${pane.currentCommand}`;
|
|
429
|
+
const rawLeaderPaneId = parseHudEnvAssignment(command, OMX_TMUX_HUD_LEADER_PANE_ENV);
|
|
430
|
+
if (hasAmbiguousHudOwnerMetadata(command)
|
|
431
|
+
|| (rawLeaderPaneId !== undefined && !parseCanonicalTmuxPaneId(rawLeaderPaneId))) {
|
|
432
|
+
return { sessionId: undefined, leaderPaneId: undefined };
|
|
433
|
+
}
|
|
109
434
|
return {
|
|
110
|
-
sessionId:
|
|
111
|
-
leaderPaneId:
|
|
435
|
+
sessionId: parseHudEnvAssignment(command, 'OMX_SESSION_ID'),
|
|
436
|
+
leaderPaneId: rawLeaderPaneId ? parseCanonicalTmuxPaneId(rawLeaderPaneId) ?? undefined : undefined,
|
|
112
437
|
};
|
|
113
438
|
}
|
|
114
439
|
function hasHudPaneOwnerMetadata(pane) {
|
|
115
440
|
const command = `${pane.startCommand} ${pane.currentCommand}`;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
441
|
+
return hasHudOwnerMetadataAttempt(command) && !hasAmbiguousHudOwnerMetadata(command);
|
|
442
|
+
}
|
|
443
|
+
export function hasValidHudOwnerMarker(pane) {
|
|
444
|
+
const command = `${pane.startCommand} ${pane.currentCommand}`;
|
|
445
|
+
if (hasAmbiguousHudOwnerMetadata(command))
|
|
446
|
+
return false;
|
|
447
|
+
return parseHudEnvAssignment(command, OMX_TMUX_HUD_OWNER_ENV) === '1';
|
|
119
448
|
}
|
|
120
449
|
function hasOmxCliToken(command) {
|
|
121
450
|
return /(?:^|[\s'"])(?:[^\s'"]*\/)?omx(?:\.js)?(?=$|[\s'"])/.test(command);
|
|
122
451
|
}
|
|
452
|
+
function hasPowerShellEnvironmentAssignmentPrefix(command) {
|
|
453
|
+
return /^\s*(?:\$env:[A-Za-z_][A-Za-z0-9_]*\s*=\s*(?:'(?:''|[^'])*'|"(?:`.|[^"])*")\s*;\s*)+&(?:\s|$)/i.test(command);
|
|
454
|
+
}
|
|
123
455
|
function isLegacyFocusedHudWatchPane(pane) {
|
|
124
456
|
// Migration-only heuristic for prompt-submit auto-HUD reconciliation: older
|
|
125
457
|
// focused auto-HUD panes lacked owner metadata, so keep this deliberately
|
|
126
458
|
// narrower than general HUD ownership/reaping.
|
|
127
|
-
if (!isHudWatchPane(pane) || hasHudPaneOwnerMetadata(pane))
|
|
128
|
-
return false;
|
|
129
459
|
const command = `${pane.startCommand} ${pane.currentCommand}`;
|
|
460
|
+
if (!isHudWatchPane(pane)
|
|
461
|
+
|| hasHudOwnerMetadataAttempt(command)
|
|
462
|
+
|| hasPowerShellEnvironmentAssignmentPrefix(command))
|
|
463
|
+
return false;
|
|
130
464
|
return hasOmxCliToken(command)
|
|
131
465
|
&& !/(?:^|[\s'"])--tmux(?:[\s'"]|$)/.test(command)
|
|
132
466
|
&& /(?:^|[\s'"])--preset=focused(?:[\s'"]|$)/.test(command);
|
|
133
467
|
}
|
|
134
468
|
export function findLegacyFocusedHudWatchPaneIds(panes, currentPaneId) {
|
|
135
|
-
|
|
136
|
-
|
|
469
|
+
const canonicalPanes = canonicalizePaneSnapshotBatch(panes);
|
|
470
|
+
const canonicalCurrentPaneId = currentPaneId ? parseCanonicalTmuxPaneId(currentPaneId) : null;
|
|
471
|
+
if (!canonicalPanes || (currentPaneId && !canonicalCurrentPaneId))
|
|
472
|
+
return [];
|
|
473
|
+
return canonicalPanes
|
|
474
|
+
.filter((pane) => pane.paneId !== canonicalCurrentPaneId)
|
|
137
475
|
.filter((pane) => isLegacyFocusedHudWatchPane(pane))
|
|
138
476
|
.map((pane) => pane.paneId);
|
|
139
477
|
}
|
|
140
478
|
export function hudPaneMatchesOwner(pane, owner = {}) {
|
|
141
|
-
if (!isHudWatchPane(pane))
|
|
479
|
+
if (!parseCanonicalTmuxPaneId(pane.paneId) || !isHudWatchPane(pane))
|
|
142
480
|
return false;
|
|
143
481
|
const wantedSessionIds = [
|
|
144
482
|
typeof owner.sessionId === 'string' ? owner.sessionId.trim() : '',
|
|
@@ -146,11 +484,16 @@ export function hudPaneMatchesOwner(pane, owner = {}) {
|
|
|
146
484
|
]
|
|
147
485
|
.map((sessionId) => sessionId.trim())
|
|
148
486
|
.filter((sessionId, index, sessionIds) => sessionId !== '' && sessionIds.indexOf(sessionId) === index);
|
|
149
|
-
const
|
|
487
|
+
const rawWantedLeaderPaneId = typeof owner.leaderPaneId === 'string' ? owner.leaderPaneId : '';
|
|
488
|
+
const wantedLeaderPaneId = rawWantedLeaderPaneId ? parseCanonicalTmuxPaneId(rawWantedLeaderPaneId) : null;
|
|
489
|
+
if (rawWantedLeaderPaneId && !wantedLeaderPaneId)
|
|
490
|
+
return false;
|
|
150
491
|
const wantsSession = wantedSessionIds.length > 0;
|
|
151
|
-
const wantsLeaderPane = wantedLeaderPaneId !==
|
|
492
|
+
const wantsLeaderPane = wantedLeaderPaneId !== null;
|
|
152
493
|
if (!wantsSession && !wantsLeaderPane)
|
|
153
494
|
return true;
|
|
495
|
+
if (hasAmbiguousHudOwnerMetadata(`${pane.startCommand} ${pane.currentCommand}`))
|
|
496
|
+
return false;
|
|
154
497
|
const paneOwner = readHudPaneOwner(pane);
|
|
155
498
|
const sessionMatches = wantsSession && wantedSessionIds.includes(paneOwner.sessionId ?? '');
|
|
156
499
|
const leaderPaneMatches = wantsLeaderPane && paneOwner.leaderPaneId === wantedLeaderPaneId;
|
|
@@ -165,8 +508,12 @@ export function hudPaneMatchesOwner(pane, owner = {}) {
|
|
|
165
508
|
return leaderPaneMatches;
|
|
166
509
|
}
|
|
167
510
|
export function findHudWatchPaneIds(panes, currentPaneId, owner = {}) {
|
|
168
|
-
|
|
169
|
-
|
|
511
|
+
const canonicalPanes = canonicalizePaneSnapshotBatch(panes);
|
|
512
|
+
const canonicalCurrentPaneId = currentPaneId ? parseCanonicalTmuxPaneId(currentPaneId) : null;
|
|
513
|
+
if (!canonicalPanes || (currentPaneId && !canonicalCurrentPaneId))
|
|
514
|
+
return [];
|
|
515
|
+
return canonicalPanes
|
|
516
|
+
.filter((pane) => pane.paneId !== canonicalCurrentPaneId)
|
|
170
517
|
.filter((pane) => hudPaneMatchesOwner(pane, owner))
|
|
171
518
|
.map((pane) => pane.paneId);
|
|
172
519
|
}
|
|
@@ -184,8 +531,13 @@ function isDoctorSmokeSessionId(sessionId) {
|
|
|
184
531
|
function shouldReapDeletedCwdHudPane(pane, isLivePane) {
|
|
185
532
|
// A deleted tmux launch cwd is not enough to prove a HUD is dead: watch mode can
|
|
186
533
|
// keep serving from a resolved live cwd. Reap only explicit doctor smoke panes or
|
|
187
|
-
// owner-tagged panes whose leader is gone.
|
|
188
|
-
|
|
534
|
+
// owner-tagged panes whose canonical leader is gone.
|
|
535
|
+
const command = `${pane.startCommand} ${pane.currentCommand}`;
|
|
536
|
+
const rawLeaderPaneId = parseHudEnvAssignment(command, OMX_TMUX_HUD_LEADER_PANE_ENV);
|
|
537
|
+
if (!hasHudPaneOwnerMetadata(pane)
|
|
538
|
+
|| !hasDeletedTmuxPaneMarker(pane.currentPath)
|
|
539
|
+
|| hasAmbiguousHudOwnerMetadata(command)
|
|
540
|
+
|| (rawLeaderPaneId !== undefined && !parseCanonicalTmuxPaneId(rawLeaderPaneId)))
|
|
189
541
|
return false;
|
|
190
542
|
const owner = readHudPaneOwner(pane);
|
|
191
543
|
if (isDoctorSmokeSessionId(owner.sessionId))
|
|
@@ -195,18 +547,27 @@ function shouldReapDeletedCwdHudPane(pane, isLivePane) {
|
|
|
195
547
|
return !owner.leaderPaneId || !isLivePane(owner.leaderPaneId);
|
|
196
548
|
}
|
|
197
549
|
export function reapDeadHudPanes(panes, opts = {}) {
|
|
198
|
-
const
|
|
550
|
+
const canonicalPanes = canonicalizePaneSnapshotBatch(panes);
|
|
551
|
+
if (!canonicalPanes)
|
|
552
|
+
return { reaped: [], preserved: panes.map((pane) => pane.paneId) };
|
|
553
|
+
const livePaneIds = new Set(canonicalPanes.map((pane) => pane.paneId));
|
|
199
554
|
const isLivePane = opts.isLivePane ?? ((paneId) => livePaneIds.has(paneId));
|
|
200
555
|
const killPane = opts.killPane ?? ((paneId) => killTmuxPane(paneId));
|
|
201
556
|
const reaped = [];
|
|
202
557
|
const preserved = [];
|
|
203
|
-
for (const pane of
|
|
558
|
+
for (const pane of canonicalPanes) {
|
|
204
559
|
if (!isHudWatchPane(pane))
|
|
205
560
|
continue;
|
|
206
561
|
if (shouldReapDeletedCwdHudPane(pane, isLivePane) && killPane(pane.paneId)) {
|
|
207
562
|
reaped.push(pane.paneId);
|
|
208
563
|
continue;
|
|
209
564
|
}
|
|
565
|
+
// Never let HUD-looking command text authorize a destructive pane mutation.
|
|
566
|
+
// Only metadata accepted by the strict owner parsers may do so.
|
|
567
|
+
if (!hasHudPaneOwnerMetadata(pane)) {
|
|
568
|
+
preserved.push(pane.paneId);
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
210
571
|
const leaderPaneId = readHudPaneOwner(pane).leaderPaneId;
|
|
211
572
|
if (!leaderPaneId) {
|
|
212
573
|
preserved.push(pane.paneId);
|
|
@@ -226,8 +587,10 @@ export function reapDeadHudPanes(panes, opts = {}) {
|
|
|
226
587
|
return { reaped, preserved };
|
|
227
588
|
}
|
|
228
589
|
export function parsePaneIdFromTmuxOutput(rawOutput) {
|
|
229
|
-
const
|
|
230
|
-
|
|
590
|
+
const lines = rawOutput.split('\n');
|
|
591
|
+
if (lines.at(-1) === '')
|
|
592
|
+
lines.pop();
|
|
593
|
+
return lines.length === 1 ? parseCanonicalTmuxPaneId(lines[0]) : null;
|
|
231
594
|
}
|
|
232
595
|
export function shellEscapeSingle(value) {
|
|
233
596
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -242,9 +605,6 @@ function isTmuxSessionId(value) {
|
|
|
242
605
|
function isTmuxWindowId(value) {
|
|
243
606
|
return /^@\d+$/.test(value);
|
|
244
607
|
}
|
|
245
|
-
function isTmuxPaneId(value) {
|
|
246
|
-
return /^%\d+$/.test(value);
|
|
247
|
-
}
|
|
248
608
|
export function buildHudResizeHookName(sessionId, windowId, leaderPaneId) {
|
|
249
609
|
return [
|
|
250
610
|
'omx_hud_resize',
|
|
@@ -253,13 +613,6 @@ export function buildHudResizeHookName(sessionId, windowId, leaderPaneId) {
|
|
|
253
613
|
normalizeTmuxHookToken(leaderPaneId),
|
|
254
614
|
].join('_');
|
|
255
615
|
}
|
|
256
|
-
function buildLegacyHudResizeHookName(sessionId, windowId) {
|
|
257
|
-
return [
|
|
258
|
-
'omx_hud_resize',
|
|
259
|
-
normalizeTmuxHookToken(sessionId),
|
|
260
|
-
normalizeTmuxHookToken(windowId),
|
|
261
|
-
].join('_');
|
|
262
|
-
}
|
|
263
616
|
export function buildHudResizeHookSlot(hookName) {
|
|
264
617
|
let hash = 0;
|
|
265
618
|
for (let i = 0; i < hookName.length; i++) {
|
|
@@ -274,35 +627,101 @@ export function buildHudLayoutHookSlot(hookName) {
|
|
|
274
627
|
}
|
|
275
628
|
return `window-layout-changed[${Math.abs(hash) % TMUX_HOOK_INDEX_MAX}]`;
|
|
276
629
|
}
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
630
|
+
function hudHookIdentityOption(hookSlot) {
|
|
631
|
+
const match = /^(client-resized|window-layout-changed)\[([0-9]+)\]$/.exec(hookSlot);
|
|
632
|
+
if (!match)
|
|
633
|
+
throw new Error('invalid_tmux_hook_slot');
|
|
634
|
+
return `@omx_hook_identity_${match[1].replaceAll('-', '_')}_${match[2]}`;
|
|
635
|
+
}
|
|
636
|
+
function hudHookIdentityToken(hookName, hookSlot) {
|
|
637
|
+
let hash = 2166136261;
|
|
638
|
+
for (const char of `${hookName}:${hookSlot}`)
|
|
639
|
+
hash = Math.imul(hash ^ char.charCodeAt(0), 16777619);
|
|
640
|
+
return `omx-${(hash >>> 0).toString(16)}`;
|
|
641
|
+
}
|
|
642
|
+
function buildHudHookRegistrationSuffix(context, hookSlot) {
|
|
643
|
+
return [
|
|
644
|
+
'\\;', 'set-option', '-t', context.sessionId,
|
|
645
|
+
hudHookIdentityOption(hookSlot), hudHookIdentityToken(context.hookName, hookSlot),
|
|
646
|
+
];
|
|
647
|
+
}
|
|
648
|
+
function buildGuardedHudHookUnregisterArgs(context, hookSlot) {
|
|
649
|
+
const identityOption = hudHookIdentityOption(hookSlot);
|
|
650
|
+
return [
|
|
651
|
+
'if-shell', '-F', '-t', context.sessionId,
|
|
652
|
+
`#{==:${identityOption},${hudHookIdentityToken(context.hookName, hookSlot)}}`,
|
|
653
|
+
`set-hook -u -t ${context.sessionId} ${hookSlot} \\; set-option -u -t ${context.sessionId} ${identityOption}`,
|
|
654
|
+
'',
|
|
655
|
+
];
|
|
656
|
+
}
|
|
657
|
+
function readHudResizeHookPaneIncarnations(hudPaneId, leaderPaneId, execTmuxSync) {
|
|
658
|
+
try {
|
|
659
|
+
const lines = parseExactTmuxAuthorityLines(execTmuxSync(['list-panes', '-a', '-F', '#{pane_id} #{pane_dead} #{pane_pid}']));
|
|
660
|
+
if (!lines)
|
|
661
|
+
return null;
|
|
662
|
+
const seen = new Set();
|
|
663
|
+
let leaderPanePid = null;
|
|
664
|
+
let hudPanePid = null;
|
|
665
|
+
for (const line of lines) {
|
|
666
|
+
const match = /^(%\S+) ([01]) ([1-9][0-9]*)$/.exec(line);
|
|
667
|
+
const paneId = match ? parseCanonicalTmuxPaneId(match[1]) : null;
|
|
668
|
+
if (!match || !paneId || paneId !== match[1] || seen.has(paneId))
|
|
669
|
+
return null;
|
|
670
|
+
seen.add(paneId);
|
|
671
|
+
if (match[2] === '1')
|
|
672
|
+
continue;
|
|
673
|
+
if (paneId === leaderPaneId)
|
|
674
|
+
leaderPanePid = match[3];
|
|
675
|
+
if (paneId === hudPaneId)
|
|
676
|
+
hudPanePid = match[3];
|
|
677
|
+
}
|
|
678
|
+
return leaderPanePid && hudPanePid ? { leaderPanePid, hudPanePid } : null;
|
|
679
|
+
}
|
|
680
|
+
catch {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
export function parseHudResizeHookContext(output, leaderPaneId, hudPaneId = leaderPaneId, incarnations = { leaderPanePid: '1', hudPanePid: '1' }) {
|
|
685
|
+
const line = parseExactTmuxAuthorityScalar(output);
|
|
686
|
+
if (line === null)
|
|
687
|
+
return null;
|
|
688
|
+
const parts = line.split('\t');
|
|
689
|
+
if (parts.length !== 2 || parts.some((part) => part.trim() !== part || part === ''))
|
|
690
|
+
return null;
|
|
691
|
+
const [sessionId = '', windowId = ''] = parts;
|
|
692
|
+
const normalizedLeaderPaneId = parseCanonicalTmuxPaneId(leaderPaneId);
|
|
693
|
+
const normalizedHudPaneId = parseCanonicalTmuxPaneId(hudPaneId);
|
|
694
|
+
if (!isTmuxSessionId(sessionId) || !isTmuxWindowId(windowId) || !normalizedLeaderPaneId || !normalizedHudPaneId)
|
|
284
695
|
return null;
|
|
285
696
|
const hookName = buildHudResizeHookName(sessionId, windowId, normalizedLeaderPaneId);
|
|
286
697
|
return {
|
|
287
698
|
sessionId,
|
|
288
699
|
windowId,
|
|
289
700
|
leaderPaneId: normalizedLeaderPaneId,
|
|
701
|
+
leaderPanePid: incarnations.leaderPanePid,
|
|
702
|
+
hudPaneId: normalizedHudPaneId,
|
|
703
|
+
hudPanePid: incarnations.hudPanePid,
|
|
290
704
|
hookName,
|
|
291
705
|
hookSlot: buildHudResizeHookSlot(hookName),
|
|
292
706
|
layoutHookSlot: buildHudLayoutHookSlot(hookName),
|
|
293
707
|
};
|
|
294
708
|
}
|
|
295
|
-
export function readHudResizeHookContext(leaderPaneId, execTmuxSync = defaultExecTmuxSync) {
|
|
296
|
-
|
|
709
|
+
export function readHudResizeHookContext(hudPaneId, leaderPaneId, execTmuxSync = defaultExecTmuxSync) {
|
|
710
|
+
const canonicalLeaderPaneId = parseCanonicalTmuxPaneId(leaderPaneId);
|
|
711
|
+
const canonicalHudPaneId = parseCanonicalTmuxPaneId(hudPaneId);
|
|
712
|
+
if (!canonicalLeaderPaneId || !canonicalHudPaneId)
|
|
713
|
+
return null;
|
|
714
|
+
const incarnations = readHudResizeHookPaneIncarnations(canonicalHudPaneId, canonicalLeaderPaneId, execTmuxSync);
|
|
715
|
+
if (!incarnations)
|
|
297
716
|
return null;
|
|
298
717
|
try {
|
|
299
718
|
return parseHudResizeHookContext(execTmuxSync([
|
|
300
719
|
'display-message',
|
|
301
720
|
'-p',
|
|
302
721
|
'-t',
|
|
303
|
-
|
|
722
|
+
canonicalLeaderPaneId,
|
|
304
723
|
'#{session_id}\t#{window_id}',
|
|
305
|
-
]),
|
|
724
|
+
]), canonicalLeaderPaneId, canonicalHudPaneId, incarnations);
|
|
306
725
|
}
|
|
307
726
|
catch {
|
|
308
727
|
return null;
|
|
@@ -312,22 +731,54 @@ function buildNestedTmuxCommand(tmuxBin, args, tmuxEnv) {
|
|
|
312
731
|
const command = [tmuxBin, ...args].map((part) => shellEscapeSingle(part)).join(' ');
|
|
313
732
|
return `${buildEnvPrefix({ TMUX: tmuxEnv })}${command}`;
|
|
314
733
|
}
|
|
315
|
-
function
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
734
|
+
function quoteHudHookShellArgument(value) {
|
|
735
|
+
return process.platform === 'win32'
|
|
736
|
+
? `'${value.replace(/'/g, "''")}'`
|
|
737
|
+
: shellEscapeSingle(value);
|
|
738
|
+
}
|
|
739
|
+
function buildHudHookIncarnationCondition(paneId, panePid) {
|
|
740
|
+
return `#{&&:#{==:#{pane_id},${paneId}},#{&&:#{==:#{pane_dead},0},#{==:#{pane_pid},${panePid}}}}`;
|
|
741
|
+
}
|
|
742
|
+
function buildHudHookSelfUnregister(context) {
|
|
743
|
+
const identityOption = hudHookIdentityOption(context.hookSlot);
|
|
744
|
+
return `if-shell -F -t ${context.sessionId} ${quoteHudHookShellArgument(`#{==:${identityOption},${hudHookIdentityToken(context.hookName, context.hookSlot)}}`)} ${quoteHudHookShellArgument(`set-hook -u -t ${context.sessionId} ${context.hookSlot} \\; set-option -u -t ${context.sessionId} ${identityOption}`)} ''`;
|
|
320
745
|
}
|
|
321
|
-
function
|
|
322
|
-
const
|
|
323
|
-
const
|
|
324
|
-
|
|
746
|
+
function buildAtomicHudHookCommand(tmuxBin, context, success, tmuxEnv) {
|
|
747
|
+
const unregister = buildHudHookSelfUnregister(context);
|
|
748
|
+
const hudConditional = [
|
|
749
|
+
'if-shell', '-F', '-t', context.hudPaneId,
|
|
750
|
+
quoteHudHookShellArgument(buildHudHookIncarnationCondition(context.hudPaneId, context.hudPanePid)),
|
|
751
|
+
quoteHudHookShellArgument(success),
|
|
752
|
+
quoteHudHookShellArgument(unregister),
|
|
753
|
+
].join(' ');
|
|
754
|
+
const args = [
|
|
755
|
+
'if-shell', '-F', '-t', context.leaderPaneId,
|
|
756
|
+
buildHudHookIncarnationCondition(context.leaderPaneId, context.leaderPanePid),
|
|
757
|
+
hudConditional,
|
|
758
|
+
unregister,
|
|
759
|
+
];
|
|
760
|
+
if (process.platform === 'win32') {
|
|
761
|
+
const quotePowerShell = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
762
|
+
const invoke = `& ${quotePowerShell(tmuxBin)} ${args.map(quotePowerShell).join(' ')}`;
|
|
763
|
+
return tmuxEnv
|
|
764
|
+
? `& { $env:TMUX = ${quotePowerShell(tmuxEnv)}; ${invoke} } | Out-Null`
|
|
765
|
+
: `${invoke} | Out-Null`;
|
|
766
|
+
}
|
|
767
|
+
const command = buildNestedTmuxCommand(tmuxBin, args, tmuxEnv);
|
|
768
|
+
return `${command} >/dev/null 2>&1 || true`;
|
|
769
|
+
}
|
|
770
|
+
function buildAtomicHudResizeCommand(tmuxBin, hudPaneId, height, context, tmuxEnv) {
|
|
771
|
+
return buildAtomicHudHookCommand(tmuxBin, context, `resize-pane -t ${hudPaneId} -y ${height}`, tmuxEnv);
|
|
772
|
+
}
|
|
773
|
+
function buildHudResizeHookCommand(tmuxBin, hudPaneId, height, context, tmuxEnv) {
|
|
774
|
+
const atomicResize = buildAtomicHudResizeCommand(tmuxBin, hudPaneId, height, context, tmuxEnv);
|
|
775
|
+
return process.platform === 'win32'
|
|
776
|
+
? `${atomicResize}; Start-Sleep -Seconds ${HUD_RESIZE_RECONCILE_DELAY_SECONDS}; ${atomicResize}`
|
|
777
|
+
: `${atomicResize}; sleep ${HUD_RESIZE_RECONCILE_DELAY_SECONDS}; ${atomicResize}`;
|
|
325
778
|
}
|
|
326
779
|
function buildHudLayoutReconcileHookCommand(tmuxBin, omxBin, leaderPaneId, context, options = {}) {
|
|
327
780
|
const env = options.env ?? process.env;
|
|
328
781
|
const cwd = options.cwd?.trim() || process.cwd();
|
|
329
|
-
const leaderAlive = buildNestedTmuxCommand(tmuxBin, ['display-message', '-p', '-t', leaderPaneId, '#{pane_id}'], env.TMUX);
|
|
330
|
-
const unregister = buildHudHookUnregisterCommand(tmuxBin, context, env.TMUX);
|
|
331
782
|
const reconcileEnv = buildEnvPrefix({
|
|
332
783
|
TMUX: env.TMUX,
|
|
333
784
|
TMUX_PANE: leaderPaneId,
|
|
@@ -346,19 +797,20 @@ function buildHudLayoutReconcileHookCommand(tmuxBin, omxBin, leaderPaneId, conte
|
|
|
346
797
|
'hud',
|
|
347
798
|
'--reconcile-tmux',
|
|
348
799
|
].join(' ');
|
|
349
|
-
|
|
800
|
+
const layoutContext = { ...context, hookSlot: context.layoutHookSlot };
|
|
801
|
+
if (process.platform === 'win32') {
|
|
802
|
+
const nativeReconcile = `Set-Location -LiteralPath '${cwd.replace(/'/g, "''")}'; & '${process.execPath.replace(/'/g, "''")}' '${omxBin.replace(/'/g, "''")}' hud --reconcile-tmux | Out-Null`;
|
|
803
|
+
const success = `${buildHudHookSelfUnregister(layoutContext)} \\; run-shell -b ${quoteHudHookShellArgument(nativeReconcile)}`;
|
|
804
|
+
return buildAtomicHudHookCommand(tmuxBin, layoutContext, success, env.TMUX);
|
|
805
|
+
}
|
|
806
|
+
const success = `${buildHudHookSelfUnregister(layoutContext)} \\; run-shell -b ${quoteHudHookShellArgument(`${reconcile} >/dev/null 2>&1`)}`;
|
|
807
|
+
return buildAtomicHudHookCommand(tmuxBin, layoutContext, success, env.TMUX);
|
|
350
808
|
}
|
|
351
809
|
function unregisterLegacyHudResizeHook(context, execTmuxSync) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
execTmuxSync(['set-hook', '-u', '-t', context.sessionId, legacyHookSlot]);
|
|
357
|
-
}
|
|
358
|
-
catch {
|
|
359
|
-
// Best-effort migration cleanup: failure should not prevent the new
|
|
360
|
-
// leader-scoped hook from being registered or unregistered.
|
|
361
|
-
}
|
|
810
|
+
// Legacy registrations predate identity metadata. Leaving them untouched is
|
|
811
|
+
// safer than clearing a slot that may now belong to another registration.
|
|
812
|
+
void context;
|
|
813
|
+
void execTmuxSync;
|
|
362
814
|
}
|
|
363
815
|
function buildEnvPrefix(env) {
|
|
364
816
|
const assignments = Object.entries(env)
|
|
@@ -367,9 +819,34 @@ function buildEnvPrefix(env) {
|
|
|
367
819
|
.map(([key, value]) => `${key}=${shellEscapeSingle(value)}`);
|
|
368
820
|
return assignments.length > 0 ? `env ${assignments.join(' ')} ` : '';
|
|
369
821
|
}
|
|
822
|
+
/** The sole platform-aware serializer for tmux HUD watch commands. */
|
|
823
|
+
export function writeHudWatchCommand(options) {
|
|
824
|
+
const safePreset = options.preset === 'minimal' || options.preset === 'focused' || options.preset === 'full'
|
|
825
|
+
? `--preset=${options.preset}`
|
|
826
|
+
: undefined;
|
|
827
|
+
const platform = options.platform ?? process.platform;
|
|
828
|
+
const presetArgument = safePreset ? ` ${shellEscapeSingle(safePreset)}` : '';
|
|
829
|
+
if (platform === 'win32') {
|
|
830
|
+
const quotePowerShell = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
831
|
+
const assignments = Object.entries(options.runtimeEnv)
|
|
832
|
+
.map(([key, value]) => `$env:${key} = ${quotePowerShell(value)}`)
|
|
833
|
+
.join('; ');
|
|
834
|
+
const invocation = ['&', quotePowerShell(options.nodeCommand), quotePowerShell(options.omxEntry), 'hud', '--watch', ...(safePreset ? [safePreset] : [])].join(' ');
|
|
835
|
+
const script = `${assignments}; ${invocation}`;
|
|
836
|
+
return options.powerShellEnvelope
|
|
837
|
+
? `powershell.exe -NoLogo -NoExit -Command ${quotePowerShell(script)}`
|
|
838
|
+
: script;
|
|
839
|
+
}
|
|
840
|
+
const assignments = Object.entries(options.runtimeEnv)
|
|
841
|
+
.map(([key, value]) => `${key}=${key === OMX_TMUX_HUD_OWNER_ENV && !options.quoteOwnerValue ? value : shellEscapeSingle(value)}`)
|
|
842
|
+
.join(' ');
|
|
843
|
+
const nodeCommand = options.nodeCommand === 'node' ? 'node' : shellEscapeSingle(options.nodeCommand);
|
|
844
|
+
return `exec ${assignments ? `env ${assignments} ` : ''}${nodeCommand} ${shellEscapeSingle(options.omxEntry)} hud --watch${presetArgument}`;
|
|
845
|
+
}
|
|
370
846
|
export function buildHudRuntimeEnv(input = {}) {
|
|
371
847
|
const sessionId = typeof input.sessionId === 'string' ? input.sessionId.trim() : '';
|
|
372
|
-
const
|
|
848
|
+
const rawLeaderPaneId = typeof input.leaderPaneId === 'string' ? input.leaderPaneId : '';
|
|
849
|
+
const leaderPaneId = rawLeaderPaneId ? parseCanonicalTmuxPaneId(rawLeaderPaneId) : null;
|
|
373
850
|
const env = {};
|
|
374
851
|
if (sessionId)
|
|
375
852
|
env.OMX_SESSION_ID = sessionId;
|
|
@@ -393,23 +870,40 @@ export function buildHudRuntimeEnv(input = {}) {
|
|
|
393
870
|
},
|
|
394
871
|
};
|
|
395
872
|
}
|
|
396
|
-
export function buildHudWatchCommand(omxBin, preset, sessionId, omxRoot, leaderPaneId, rootEnv) {
|
|
397
|
-
const
|
|
398
|
-
? ` --preset=${preset}`
|
|
399
|
-
: '';
|
|
400
|
-
const envPrefix = buildEnvPrefix(buildHudRuntimeEnv({
|
|
873
|
+
export function buildHudWatchCommand(omxBin, preset, sessionId, omxRoot, leaderPaneId, rootEnv, platform = process.platform) {
|
|
874
|
+
const runtimeEnv = buildHudRuntimeEnv({
|
|
401
875
|
sessionId,
|
|
402
876
|
leaderPaneId,
|
|
403
877
|
omxRoot,
|
|
404
878
|
...(rootEnv ?? { rootSource: 'omx-root-env' }),
|
|
405
|
-
}).env
|
|
406
|
-
return
|
|
879
|
+
}).env;
|
|
880
|
+
return writeHudWatchCommand({
|
|
881
|
+
omxEntry: omxBin,
|
|
882
|
+
runtimeEnv,
|
|
883
|
+
nodeCommand: process.execPath,
|
|
884
|
+
preset,
|
|
885
|
+
platform,
|
|
886
|
+
powerShellEnvelope: platform === 'win32',
|
|
887
|
+
quoteOwnerValue: true,
|
|
888
|
+
});
|
|
407
889
|
}
|
|
408
890
|
export function listCurrentWindowPanes(execTmuxSync = defaultExecTmuxSync, currentPaneId) {
|
|
891
|
+
const canonicalCurrentPaneId = currentPaneId ? parseCanonicalTmuxPaneId(currentPaneId) : null;
|
|
892
|
+
if (currentPaneId && !canonicalCurrentPaneId)
|
|
893
|
+
return [];
|
|
894
|
+
const targetArgs = canonicalCurrentPaneId ? ['-t', canonicalCurrentPaneId] : [];
|
|
409
895
|
try {
|
|
410
|
-
|
|
896
|
+
const paneIds = parseCanonicalPaneIdSnapshot(execTmuxSync([
|
|
897
|
+
'list-panes',
|
|
898
|
+
...targetArgs,
|
|
899
|
+
'-F',
|
|
900
|
+
'#{pane_id}',
|
|
901
|
+
]));
|
|
902
|
+
if (!paneIds)
|
|
903
|
+
return [];
|
|
904
|
+
const paneSnapshotOutput = execTmuxSync([
|
|
411
905
|
'list-panes',
|
|
412
|
-
...
|
|
906
|
+
...targetArgs,
|
|
413
907
|
'-F',
|
|
414
908
|
[
|
|
415
909
|
'#{pane_id}',
|
|
@@ -423,8 +917,13 @@ export function listCurrentWindowPanes(execTmuxSync = defaultExecTmuxSync, curre
|
|
|
423
917
|
'#{window_height}',
|
|
424
918
|
'#{pane_start_command}',
|
|
425
919
|
'#{pane_current_path}',
|
|
920
|
+
'#{pane_dead}',
|
|
921
|
+
'#{pane_pid}',
|
|
426
922
|
].join(TMUX_PANE_FIELD_SEPARATOR),
|
|
427
|
-
])
|
|
923
|
+
]);
|
|
924
|
+
if (!parseExactTmuxAuthorityLines(paneSnapshotOutput))
|
|
925
|
+
return [];
|
|
926
|
+
return parseTmuxPaneSnapshot(paneSnapshotOutput, paneIds);
|
|
428
927
|
}
|
|
429
928
|
catch {
|
|
430
929
|
return [];
|
|
@@ -432,7 +931,8 @@ export function listCurrentWindowPanes(execTmuxSync = defaultExecTmuxSync, curre
|
|
|
432
931
|
}
|
|
433
932
|
export function readActiveTmuxPaneId(execTmuxSync = defaultExecTmuxSync) {
|
|
434
933
|
try {
|
|
435
|
-
|
|
934
|
+
const paneId = parseExactTmuxAuthorityScalar(execTmuxSync(['display-message', '-p', '#{pane_id}']));
|
|
935
|
+
return parseCanonicalTmuxPaneId(paneId);
|
|
436
936
|
}
|
|
437
937
|
catch {
|
|
438
938
|
return null;
|
|
@@ -442,56 +942,421 @@ export function listCurrentWindowHudPaneIds(currentPaneId, execTmuxSync = defaul
|
|
|
442
942
|
return findHudWatchPaneIds(listCurrentWindowPanes(execTmuxSync, currentPaneId), currentPaneId, owner);
|
|
443
943
|
}
|
|
444
944
|
export function readCurrentWindowSize(execTmuxSync = defaultExecTmuxSync, currentPaneId) {
|
|
945
|
+
const canonicalCurrentPaneId = currentPaneId ? parseCanonicalTmuxPaneId(currentPaneId) : null;
|
|
946
|
+
if (currentPaneId && !canonicalCurrentPaneId)
|
|
947
|
+
return { width: null, height: null };
|
|
445
948
|
try {
|
|
446
949
|
const raw = execTmuxSync([
|
|
447
950
|
'display-message',
|
|
448
951
|
'-p',
|
|
449
|
-
...(
|
|
952
|
+
...(canonicalCurrentPaneId ? ['-t', canonicalCurrentPaneId] : []),
|
|
450
953
|
'#{window_width}\t#{window_height}',
|
|
451
954
|
]);
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
955
|
+
const framed = parseExactTmuxAuthorityScalar(raw);
|
|
956
|
+
if (framed === null)
|
|
957
|
+
return { width: null, height: null };
|
|
958
|
+
const fields = framed.split('\t');
|
|
959
|
+
if (fields.length !== 2)
|
|
960
|
+
return { width: null, height: null };
|
|
961
|
+
const width = parsePositiveInteger(fields[0]);
|
|
962
|
+
const height = parsePositiveInteger(fields[1]);
|
|
963
|
+
return { width, height };
|
|
459
964
|
}
|
|
460
965
|
catch {
|
|
461
966
|
return { width: null, height: null };
|
|
462
967
|
}
|
|
463
968
|
}
|
|
969
|
+
function readHudSplitSourceAuthority(targetPaneId, execTmuxSync) {
|
|
970
|
+
try {
|
|
971
|
+
const source = parseExactTmuxAuthorityScalar(execTmuxSync([
|
|
972
|
+
'display-message',
|
|
973
|
+
'-p',
|
|
974
|
+
...(targetPaneId ? ['-t', targetPaneId] : []),
|
|
975
|
+
'#{pane_id}\t#{pane_dead}\t#{pane_pid}\t#{session_id}\t#{window_id}',
|
|
976
|
+
]));
|
|
977
|
+
if (!source)
|
|
978
|
+
return null;
|
|
979
|
+
const fields = source.split('\t');
|
|
980
|
+
if (fields.length !== 5)
|
|
981
|
+
return null;
|
|
982
|
+
const paneId = parseCanonicalTmuxPaneId(fields[0]);
|
|
983
|
+
const paneDead = fields[1];
|
|
984
|
+
const panePid = fields[2];
|
|
985
|
+
const sessionId = fields[3];
|
|
986
|
+
const windowId = fields[4];
|
|
987
|
+
if (!paneId
|
|
988
|
+
|| (targetPaneId && paneId !== targetPaneId)
|
|
989
|
+
|| paneDead !== '0'
|
|
990
|
+
|| !/^[1-9][0-9]*$/.test(panePid ?? '')
|
|
991
|
+
|| !isTmuxSessionId(sessionId ?? '')
|
|
992
|
+
|| !isTmuxWindowId(windowId ?? ''))
|
|
993
|
+
return null;
|
|
994
|
+
return { paneId, panePid: panePid, sessionId: sessionId, windowId: windowId };
|
|
995
|
+
}
|
|
996
|
+
catch {
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
function buildHudSplitSourceCondition(source) {
|
|
1001
|
+
return `#{&&:#{==:#{pane_id},${source.paneId}},#{&&:#{==:#{pane_dead},0},#{&&:#{==:#{pane_pid},${source.panePid}},#{&&:#{==:#{session_id},${source.sessionId}},#{==:#{window_id},${source.windowId}}}}}}`;
|
|
1002
|
+
}
|
|
1003
|
+
const hudSplitAuthorities = new Map();
|
|
1004
|
+
function readCanonicalPaneIdSnapshot(execTmuxSync, targetPaneId, global = false) {
|
|
1005
|
+
try {
|
|
1006
|
+
return parseCanonicalPaneIdSnapshot(execTmuxSync([
|
|
1007
|
+
'list-panes',
|
|
1008
|
+
...(global ? ['-a'] : targetPaneId ? ['-t', targetPaneId] : []),
|
|
1009
|
+
'-F',
|
|
1010
|
+
'#{pane_id}',
|
|
1011
|
+
]));
|
|
1012
|
+
}
|
|
1013
|
+
catch {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
function deriveSingleNewPane(before, after) {
|
|
1018
|
+
if (after.size !== before.size + 1 || ![...before].every((paneId) => after.has(paneId)))
|
|
1019
|
+
return null;
|
|
1020
|
+
const created = [...after].filter((paneId) => !before.has(paneId));
|
|
1021
|
+
return created.length === 1 ? created[0] ?? null : null;
|
|
1022
|
+
}
|
|
1023
|
+
const OMX_TMUX_SPLIT_OPERATION_MARKER_ENV = 'OMX_TMUX_SPLIT_OPERATION_MARKER';
|
|
1024
|
+
function writeHudSplitOperationMarkedCommand(command, marker) {
|
|
1025
|
+
if (process.platform === 'win32')
|
|
1026
|
+
return `$env:${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV} = '${marker}'; ${command}`;
|
|
1027
|
+
return `${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV}='${marker}'; export ${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV}; ${command}`;
|
|
1028
|
+
}
|
|
1029
|
+
function hasHudSplitOperationMarker(startCommand, marker) {
|
|
1030
|
+
const posixMarker = `${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV}='${marker}'`;
|
|
1031
|
+
const powerShellMarker = `$env:${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV} = '${marker}'`;
|
|
1032
|
+
return startCommand === posixMarker
|
|
1033
|
+
|| startCommand.startsWith(`${posixMarker};`)
|
|
1034
|
+
|| startCommand === powerShellMarker
|
|
1035
|
+
|| startCommand.startsWith(`${powerShellMarker};`);
|
|
1036
|
+
}
|
|
1037
|
+
function findHudSplitOperationMarkerPaneId(marker, execTmuxSync) {
|
|
1038
|
+
try {
|
|
1039
|
+
const lines = parseExactTmuxAuthorityLines(execTmuxSync(['list-panes', '-a', '-F', '#{pane_id}\t#{pane_start_command}']));
|
|
1040
|
+
if (!lines)
|
|
1041
|
+
return null;
|
|
1042
|
+
let candidate = null;
|
|
1043
|
+
const seen = new Set();
|
|
1044
|
+
for (const rawLine of lines) {
|
|
1045
|
+
const fields = rawLine.split('\t');
|
|
1046
|
+
if (fields.length !== 2)
|
|
1047
|
+
return null;
|
|
1048
|
+
const paneId = parseCanonicalTmuxPaneId(fields[0]);
|
|
1049
|
+
if (!paneId || paneId !== fields[0] || seen.has(paneId))
|
|
1050
|
+
return null;
|
|
1051
|
+
seen.add(paneId);
|
|
1052
|
+
const command = fields[1] ?? '';
|
|
1053
|
+
if (!hasHudSplitOperationMarker(command, marker))
|
|
1054
|
+
continue;
|
|
1055
|
+
if (candidate)
|
|
1056
|
+
return null;
|
|
1057
|
+
candidate = paneId;
|
|
1058
|
+
}
|
|
1059
|
+
return candidate;
|
|
1060
|
+
}
|
|
1061
|
+
catch {
|
|
1062
|
+
return null;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
function recoverHudSplitPaneId(globalBefore, targetBefore, targetPaneId, marker, execTmuxSync) {
|
|
1066
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1067
|
+
const globalAfter = readCanonicalPaneIdSnapshot(execTmuxSync, undefined, true);
|
|
1068
|
+
const targetAfter = targetPaneId ? readCanonicalPaneIdSnapshot(execTmuxSync, targetPaneId) : null;
|
|
1069
|
+
const globalCandidate = globalAfter ? deriveSingleNewPane(globalBefore, globalAfter) : null;
|
|
1070
|
+
const targetCandidate = targetPaneId ? (targetAfter ? deriveSingleNewPane(targetBefore, targetAfter) : null) : globalCandidate;
|
|
1071
|
+
const markerCandidate = findHudSplitOperationMarkerPaneId(marker, execTmuxSync);
|
|
1072
|
+
if (globalCandidate && globalCandidate === targetCandidate && markerCandidate === globalCandidate)
|
|
1073
|
+
return globalCandidate;
|
|
1074
|
+
if (markerCandidate && !globalBefore.has(markerCandidate))
|
|
1075
|
+
return markerCandidate;
|
|
1076
|
+
if (attempt < 2)
|
|
1077
|
+
sleepSync(50);
|
|
1078
|
+
}
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
function sleepSync(milliseconds) {
|
|
1082
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
1083
|
+
}
|
|
1084
|
+
function readTmuxOptionExactly(execTmuxSync, option) {
|
|
1085
|
+
try {
|
|
1086
|
+
return parseExactTmuxAuthorityScalar(execTmuxSync(['show-options', '-g', '-v', option]));
|
|
1087
|
+
}
|
|
1088
|
+
catch {
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
function hasHudSplitAuthority(paneId, execTmuxSync = defaultExecTmuxSync) {
|
|
1093
|
+
const authority = hudSplitAuthorities.get(paneId);
|
|
1094
|
+
if (!authority)
|
|
1095
|
+
return false;
|
|
1096
|
+
const globalPanes = readCanonicalPaneIdSnapshot(execTmuxSync, undefined, true);
|
|
1097
|
+
if (!globalPanes?.has(paneId))
|
|
1098
|
+
return false;
|
|
1099
|
+
if (authority.targetPaneId) {
|
|
1100
|
+
const targetPanes = readCanonicalPaneIdSnapshot(execTmuxSync, authority.targetPaneId);
|
|
1101
|
+
if (!targetPanes?.has(paneId))
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
const location = readHudPaneSessionAndWindow(paneId, execTmuxSync);
|
|
1105
|
+
return (findHudSplitOperationMarkerPaneId(authority.operationMarker, execTmuxSync) === paneId
|
|
1106
|
+
&& readHudPaneIncarnation(paneId, execTmuxSync)?.panePid === authority.panePid
|
|
1107
|
+
&& location?.sessionId === authority.sessionId
|
|
1108
|
+
&& location?.windowId === authority.windowId
|
|
1109
|
+
&& readTmuxOptionExactly(execTmuxSync, authority.proofOption) === authority.proofValue);
|
|
1110
|
+
}
|
|
1111
|
+
function readHudPaneIncarnation(paneId, execTmuxSync) {
|
|
1112
|
+
try {
|
|
1113
|
+
const lines = parseExactTmuxAuthorityLines(execTmuxSync(['list-panes', '-a', '-F', '#{pane_id} #{pane_dead} #{pane_pid}']));
|
|
1114
|
+
if (!lines)
|
|
1115
|
+
return null;
|
|
1116
|
+
const seenPaneIds = new Set();
|
|
1117
|
+
let incarnation = null;
|
|
1118
|
+
for (const line of lines) {
|
|
1119
|
+
const match = /^(%\S+) ([01]) ([0-9]+)$/.exec(line);
|
|
1120
|
+
const observedPaneId = match ? parseCanonicalTmuxPaneId(match[1]) : null;
|
|
1121
|
+
if (!match || !observedPaneId || observedPaneId !== match[1] || seenPaneIds.has(observedPaneId))
|
|
1122
|
+
return null;
|
|
1123
|
+
seenPaneIds.add(observedPaneId);
|
|
1124
|
+
if (match[2] === '1')
|
|
1125
|
+
continue;
|
|
1126
|
+
if (!/^[1-9][0-9]*$/.test(match[3]))
|
|
1127
|
+
return null;
|
|
1128
|
+
if (observedPaneId === paneId)
|
|
1129
|
+
incarnation = { paneDead: false, panePid: match[3] };
|
|
1130
|
+
}
|
|
1131
|
+
return incarnation;
|
|
1132
|
+
}
|
|
1133
|
+
catch {
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
function isPaneLiveInStrictGlobalProbe(paneId, expectedPid, execTmuxSync) {
|
|
1138
|
+
const incarnation = readHudPaneIncarnation(paneId, execTmuxSync);
|
|
1139
|
+
return Boolean(incarnation && !incarnation.paneDead && (!expectedPid || incarnation.panePid === expectedPid));
|
|
1140
|
+
}
|
|
1141
|
+
/** Freshly validates retained split proof and sustained global liveness before a HUD mutation sink. */
|
|
1142
|
+
export function verifyHudWatchPaneAuthority(paneId, execTmuxSync = defaultExecTmuxSync) {
|
|
1143
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1144
|
+
if (!canonicalPaneId || !hasHudSplitAuthority(canonicalPaneId, execTmuxSync))
|
|
1145
|
+
return false;
|
|
1146
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1147
|
+
if (attempt > 0)
|
|
1148
|
+
sleepSync(100);
|
|
1149
|
+
if (!hasHudSplitAuthority(canonicalPaneId, execTmuxSync)
|
|
1150
|
+
|| !isPaneLiveInStrictGlobalProbe(canonicalPaneId, hudSplitAuthorities.get(canonicalPaneId)?.panePid, execTmuxSync))
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
return true;
|
|
1154
|
+
}
|
|
1155
|
+
/** Kills only a pane still bound to this process's exact split proof. */
|
|
1156
|
+
export function rollbackHudWatchPaneAuthority(paneId, execTmuxSync = defaultExecTmuxSync) {
|
|
1157
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1158
|
+
const authority = canonicalPaneId ? hudSplitAuthorities.get(canonicalPaneId) : undefined;
|
|
1159
|
+
if (!canonicalPaneId || !authority)
|
|
1160
|
+
return false;
|
|
1161
|
+
const killed = mutateHudWatchPaneIfCurrent(canonicalPaneId, authority.panePid, `kill-pane -t ${canonicalPaneId}`, execTmuxSync);
|
|
1162
|
+
if (killed)
|
|
1163
|
+
hudSplitAuthorities.delete(canonicalPaneId);
|
|
1164
|
+
return killed;
|
|
1165
|
+
}
|
|
1166
|
+
function rollbackRecoveredHudSplitPane(paneId, proofOption, proofValue, operationMarker, panePid, sessionId, windowId, execTmuxSync = defaultExecTmuxSync) {
|
|
1167
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1168
|
+
if (!canonicalPaneId || canonicalPaneId !== paneId)
|
|
1169
|
+
return false;
|
|
1170
|
+
const pidCondition = panePid && /^[1-9][0-9]*$/.test(panePid) ? `#{&&:#{==:#{pane_pid},${panePid}},` : '';
|
|
1171
|
+
const sessionCondition = sessionId && /^\$[0-9]+$/.test(sessionId) ? `#{&&:#{==:#{session_id},${sessionId}},` : '';
|
|
1172
|
+
const windowCondition = windowId && /^@[0-9]+$/.test(windowId) ? `#{&&:#{==:#{window_id},${windowId}},` : '';
|
|
1173
|
+
const closes = `${pidCondition ? '}' : ''}${sessionCondition ? '}' : ''}${windowCondition ? '}' : ''}`;
|
|
1174
|
+
const receipt = `__omx_hud_rollback_${randomUUID()}`;
|
|
1175
|
+
const condition = `#{&&:#{==:#{pane_id},${paneId}},#{&&:#{==:#{pane_dead},0},${pidCondition}${sessionCondition}${windowCondition}#{&&:#{==:#{${proofOption}},${proofValue}},#{m:*${operationMarker}*,#{pane_start_command}}}${closes}}`;
|
|
1176
|
+
try {
|
|
1177
|
+
return parseExactTmuxAuthorityScalar(execTmuxSync([
|
|
1178
|
+
'if-shell', '-F', '-t', paneId,
|
|
1179
|
+
condition,
|
|
1180
|
+
`kill-pane -t ${paneId} \\; display-message -p ${receipt}`,
|
|
1181
|
+
`display-message -p __omx_hud_rollback_rejected_${receipt}`,
|
|
1182
|
+
])) === receipt;
|
|
1183
|
+
}
|
|
1184
|
+
catch {
|
|
1185
|
+
return false;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
464
1188
|
export function createHudWatchPane(cwd, hudCmd, options = {}, execTmuxSync = defaultExecTmuxSync) {
|
|
1189
|
+
const canonicalTargetPaneId = options.targetPaneId ? parseCanonicalTmuxPaneId(options.targetPaneId) : null;
|
|
1190
|
+
if (options.targetPaneId && !canonicalTargetPaneId)
|
|
1191
|
+
return null;
|
|
1192
|
+
const sourceAuthority = readHudSplitSourceAuthority(canonicalTargetPaneId, execTmuxSync);
|
|
1193
|
+
if (!sourceAuthority)
|
|
1194
|
+
return null;
|
|
1195
|
+
const sourcePaneId = sourceAuthority.paneId;
|
|
1196
|
+
const globalBefore = readCanonicalPaneIdSnapshot(execTmuxSync, undefined, true);
|
|
1197
|
+
const targetBefore = readCanonicalPaneIdSnapshot(execTmuxSync, sourcePaneId);
|
|
1198
|
+
if (!globalBefore || !targetBefore)
|
|
1199
|
+
return null;
|
|
1200
|
+
const operationMarker = randomUUID();
|
|
465
1201
|
const heightLines = Number.isFinite(options.heightLines) && (options.heightLines ?? 0) > 0
|
|
466
1202
|
? Math.floor(options.heightLines ?? HUD_TMUX_HEIGHT_LINES)
|
|
467
1203
|
: HUD_TMUX_HEIGHT_LINES;
|
|
468
1204
|
const args = [
|
|
469
|
-
'split-window',
|
|
470
|
-
'-
|
|
471
|
-
...(options.fullWidth ? ['-f'] : []),
|
|
472
|
-
'-l',
|
|
473
|
-
String(heightLines),
|
|
474
|
-
'-d',
|
|
475
|
-
...(options.targetPaneId ? ['-t', options.targetPaneId] : []),
|
|
476
|
-
'-c',
|
|
477
|
-
cwd,
|
|
478
|
-
'-P',
|
|
479
|
-
'-F',
|
|
480
|
-
'#{pane_id}',
|
|
481
|
-
hudCmd,
|
|
1205
|
+
'split-window', '-v', ...(options.fullWidth ? ['-f'] : []), '-l', String(heightLines), '-d',
|
|
1206
|
+
'-t', sourcePaneId, '-c', shellEscapeSingle(cwd), writeHudSplitOperationMarkedCommand(hudCmd, operationMarker),
|
|
482
1207
|
];
|
|
1208
|
+
let paneId = null;
|
|
1209
|
+
const nonce = randomUUID();
|
|
1210
|
+
const proofOption = `@omx_hud_split_owner_nonce_${nonce}`;
|
|
1211
|
+
const provisionalProof = `pending:${nonce}`;
|
|
1212
|
+
const receipt = `__omx_hud_split_${randomUUID()}`;
|
|
1213
|
+
const splitCommand = args.join(' ');
|
|
1214
|
+
try {
|
|
1215
|
+
execTmuxSync(['set-option', '-g', proofOption, provisionalProof]);
|
|
1216
|
+
if (readTmuxOptionExactly(execTmuxSync, proofOption) !== provisionalProof)
|
|
1217
|
+
return null;
|
|
1218
|
+
const splitOutput = execTmuxSync([
|
|
1219
|
+
'if-shell', '-F', '-t', sourcePaneId, buildHudSplitSourceCondition(sourceAuthority),
|
|
1220
|
+
`${splitCommand} \\; display-message -p ${receipt}`,
|
|
1221
|
+
`display-message -p __omx_hud_split_rejected_${receipt}`,
|
|
1222
|
+
]);
|
|
1223
|
+
if (parseExactTmuxAuthorityScalar(splitOutput) !== receipt)
|
|
1224
|
+
return null;
|
|
1225
|
+
paneId = recoverHudSplitPaneId(globalBefore, targetBefore, sourcePaneId, operationMarker, execTmuxSync);
|
|
1226
|
+
const incarnation = paneId ? readHudPaneIncarnation(paneId, execTmuxSync) : null;
|
|
1227
|
+
const location = paneId ? readHudPaneSessionAndWindow(paneId, execTmuxSync) : null;
|
|
1228
|
+
if (!paneId
|
|
1229
|
+
|| !incarnation
|
|
1230
|
+
|| !location
|
|
1231
|
+
|| location.sessionId !== sourceAuthority.sessionId
|
|
1232
|
+
|| location.windowId !== sourceAuthority.windowId) {
|
|
1233
|
+
if (paneId) {
|
|
1234
|
+
rollbackRecoveredHudSplitPane(paneId, proofOption, provisionalProof, operationMarker, incarnation?.panePid, location?.sessionId ?? sourceAuthority.sessionId, location?.windowId ?? sourceAuthority.windowId, execTmuxSync);
|
|
1235
|
+
}
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
hudSplitAuthorities.set(paneId, {
|
|
1239
|
+
proofOption,
|
|
1240
|
+
proofValue: provisionalProof,
|
|
1241
|
+
operationMarker,
|
|
1242
|
+
panePid: incarnation.panePid,
|
|
1243
|
+
sessionId: location.sessionId,
|
|
1244
|
+
windowId: location.windowId,
|
|
1245
|
+
targetPaneId: sourcePaneId,
|
|
1246
|
+
});
|
|
1247
|
+
if (!hasHudSplitAuthority(paneId, execTmuxSync)) {
|
|
1248
|
+
rollbackHudWatchPaneAuthority(paneId, execTmuxSync);
|
|
1249
|
+
return null;
|
|
1250
|
+
}
|
|
1251
|
+
const proofValue = `${paneId}:split:${nonce}`;
|
|
1252
|
+
execTmuxSync(['set-option', '-g', proofOption, proofValue]);
|
|
1253
|
+
hudSplitAuthorities.set(paneId, { proofOption, proofValue, operationMarker, panePid: incarnation.panePid, sessionId: location.sessionId, windowId: location.windowId, targetPaneId: sourcePaneId });
|
|
1254
|
+
if (readTmuxOptionExactly(execTmuxSync, proofOption) !== proofValue) {
|
|
1255
|
+
rollbackHudWatchPaneAuthority(paneId, execTmuxSync);
|
|
1256
|
+
return null;
|
|
1257
|
+
}
|
|
1258
|
+
if (!verifyHudWatchPaneAuthority(paneId, execTmuxSync)) {
|
|
1259
|
+
rollbackHudWatchPaneAuthority(paneId, execTmuxSync);
|
|
1260
|
+
return null;
|
|
1261
|
+
}
|
|
1262
|
+
return paneId;
|
|
1263
|
+
}
|
|
1264
|
+
catch {
|
|
1265
|
+
if (paneId) {
|
|
1266
|
+
rollbackHudWatchPaneAuthority(paneId, execTmuxSync);
|
|
1267
|
+
}
|
|
1268
|
+
else {
|
|
1269
|
+
const recoveredPaneId = recoverHudSplitPaneId(globalBefore, targetBefore, sourcePaneId, operationMarker, execTmuxSync);
|
|
1270
|
+
if (recoveredPaneId) {
|
|
1271
|
+
rollbackRecoveredHudSplitPane(recoveredPaneId, proofOption, provisionalProof, operationMarker, undefined, sourceAuthority.sessionId, sourceAuthority.windowId, execTmuxSync);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
function readHudPaneSessionAndWindow(paneId, execTmuxSync) {
|
|
483
1278
|
try {
|
|
484
|
-
|
|
1279
|
+
const value = parseExactTmuxAuthorityScalar(execTmuxSync([
|
|
1280
|
+
'display-message', '-p', '-t', paneId, '#{session_id}\t#{window_id}',
|
|
1281
|
+
]));
|
|
1282
|
+
if (!value)
|
|
1283
|
+
return null;
|
|
1284
|
+
const [sessionId, windowId, ...extra] = value.split('\t');
|
|
1285
|
+
return !extra.length && isTmuxSessionId(sessionId ?? '') && isTmuxWindowId(windowId ?? '')
|
|
1286
|
+
? { sessionId: sessionId, windowId: windowId }
|
|
1287
|
+
: null;
|
|
485
1288
|
}
|
|
486
1289
|
catch {
|
|
487
1290
|
return null;
|
|
488
1291
|
}
|
|
489
1292
|
}
|
|
1293
|
+
/** Executes a split-owned pane mutation only while its immutable owner proof and exact incarnation remain current. */
|
|
1294
|
+
export function mutateHudWatchPaneIfCurrent(paneId, expectedPanePid, mutation, execTmuxSync = defaultExecTmuxSync) {
|
|
1295
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1296
|
+
const authority = canonicalPaneId ? hudSplitAuthorities.get(canonicalPaneId) : undefined;
|
|
1297
|
+
if (!canonicalPaneId
|
|
1298
|
+
|| !authority
|
|
1299
|
+
|| authority.panePid !== expectedPanePid
|
|
1300
|
+
|| !/^[1-9][0-9]*$/.test(expectedPanePid)
|
|
1301
|
+
|| !/^(?:kill-pane|resize-pane) -t %(?:0|[1-9][0-9]*)(?: -y [1-9][0-9]*)?$/.test(mutation))
|
|
1302
|
+
return false;
|
|
1303
|
+
const marker = `__omx_hud_mutation_${randomUUID()}`;
|
|
1304
|
+
const condition = `#{&&:#{==:#{pane_id},${canonicalPaneId}},#{&&:#{==:#{pane_dead},0},#{&&:#{==:#{pane_pid},${expectedPanePid}},#{&&:#{==:#{session_id},${authority.sessionId}},#{&&:#{==:#{window_id},${authority.windowId}},#{&&:#{==:#{${authority.proofOption}},${authority.proofValue}},#{m:*${authority.operationMarker}*,#{pane_start_command}}}}}}}`;
|
|
1305
|
+
try {
|
|
1306
|
+
const output = execTmuxSync([
|
|
1307
|
+
'if-shell', '-F', '-t', canonicalPaneId,
|
|
1308
|
+
condition,
|
|
1309
|
+
`${mutation} \\; display-message -p ${marker}`,
|
|
1310
|
+
`display-message -p __omx_hud_mutation_failed_${marker}`,
|
|
1311
|
+
]);
|
|
1312
|
+
return parseExactTmuxAuthorityScalar(output) === marker;
|
|
1313
|
+
}
|
|
1314
|
+
catch {
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
/** Executes a retained pane mutation only while the exact target incarnation remains live. */
|
|
1319
|
+
function mutateTmuxPaneIfCurrent(paneId, expectedPanePid, mutation, execTmuxSync = defaultExecTmuxSync) {
|
|
1320
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1321
|
+
if (!canonicalPaneId || !/^[1-9][0-9]*$/.test(expectedPanePid))
|
|
1322
|
+
return false;
|
|
1323
|
+
const marker = `__omx_hud_mutation_${randomUUID()}`;
|
|
1324
|
+
try {
|
|
1325
|
+
const output = execTmuxSync([
|
|
1326
|
+
'if-shell', '-F', '-t', canonicalPaneId,
|
|
1327
|
+
buildHudHookIncarnationCondition(canonicalPaneId, expectedPanePid),
|
|
1328
|
+
`${mutation} \\; display-message -p ${marker}`,
|
|
1329
|
+
`display-message -p __omx_hud_mutation_failed_${marker}`,
|
|
1330
|
+
]);
|
|
1331
|
+
return parseExactTmuxAuthorityScalar(output) === marker;
|
|
1332
|
+
}
|
|
1333
|
+
catch {
|
|
1334
|
+
return false;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
/** Kills a pane only while its exact live incarnation remains the target. */
|
|
1338
|
+
export function killTmuxPaneIfCurrent(paneId, expectedPanePid, execTmuxSync = defaultExecTmuxSync) {
|
|
1339
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1340
|
+
return canonicalPaneId
|
|
1341
|
+
? mutateTmuxPaneIfCurrent(canonicalPaneId, expectedPanePid, `kill-pane -t ${canonicalPaneId}`, execTmuxSync)
|
|
1342
|
+
: false;
|
|
1343
|
+
}
|
|
1344
|
+
/** Resizes a pane only while its exact live incarnation remains the target. */
|
|
1345
|
+
export function resizeTmuxPaneIfCurrent(paneId, expectedPanePid, heightLines, execTmuxSync = defaultExecTmuxSync) {
|
|
1346
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1347
|
+
if (!canonicalPaneId)
|
|
1348
|
+
return false;
|
|
1349
|
+
const height = Number.isFinite(heightLines) && heightLines > 0
|
|
1350
|
+
? Math.floor(heightLines)
|
|
1351
|
+
: HUD_TMUX_HEIGHT_LINES;
|
|
1352
|
+
return mutateTmuxPaneIfCurrent(canonicalPaneId, expectedPanePid, `resize-pane -t ${canonicalPaneId} -y ${height}`, execTmuxSync);
|
|
1353
|
+
}
|
|
490
1354
|
export function killTmuxPane(paneId, execTmuxSync = defaultExecTmuxSync) {
|
|
491
|
-
|
|
1355
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1356
|
+
if (!canonicalPaneId)
|
|
492
1357
|
return false;
|
|
493
1358
|
try {
|
|
494
|
-
execTmuxSync(['kill-pane', '-t',
|
|
1359
|
+
execTmuxSync(['kill-pane', '-t', canonicalPaneId]);
|
|
495
1360
|
return true;
|
|
496
1361
|
}
|
|
497
1362
|
catch {
|
|
@@ -499,13 +1364,14 @@ export function killTmuxPane(paneId, execTmuxSync = defaultExecTmuxSync) {
|
|
|
499
1364
|
}
|
|
500
1365
|
}
|
|
501
1366
|
export function resizeTmuxPane(paneId, heightLines, execTmuxSync = defaultExecTmuxSync) {
|
|
502
|
-
|
|
1367
|
+
const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
|
|
1368
|
+
if (!canonicalPaneId)
|
|
503
1369
|
return false;
|
|
504
1370
|
const height = Number.isFinite(heightLines) && heightLines > 0
|
|
505
1371
|
? Math.floor(heightLines)
|
|
506
1372
|
: HUD_TMUX_HEIGHT_LINES;
|
|
507
1373
|
try {
|
|
508
|
-
execTmuxSync(['resize-pane', '-t',
|
|
1374
|
+
execTmuxSync(['resize-pane', '-t', canonicalPaneId, '-y', String(height)]);
|
|
509
1375
|
return true;
|
|
510
1376
|
}
|
|
511
1377
|
catch {
|
|
@@ -517,26 +1383,28 @@ export function registerHudResizeHook(hudPaneId, leaderPaneId, heightLines, opti
|
|
|
517
1383
|
const execTmuxSync = typeof optionsOrExecTmuxSync === 'function'
|
|
518
1384
|
? optionsOrExecTmuxSync
|
|
519
1385
|
: (maybeExecTmuxSync ?? defaultExecTmuxSync);
|
|
520
|
-
|
|
1386
|
+
const canonicalHudPaneId = parseCanonicalTmuxPaneId(hudPaneId);
|
|
1387
|
+
const canonicalLeaderPaneId = parseCanonicalTmuxPaneId(leaderPaneId);
|
|
1388
|
+
if (!canonicalHudPaneId || !canonicalLeaderPaneId)
|
|
521
1389
|
return false;
|
|
522
|
-
const context = readHudResizeHookContext(
|
|
1390
|
+
const context = readHudResizeHookContext(canonicalHudPaneId, canonicalLeaderPaneId, execTmuxSync);
|
|
523
1391
|
if (!context)
|
|
524
1392
|
return false;
|
|
525
1393
|
const tmuxBin = resolveTmuxBinaryForPlatform() || 'tmux';
|
|
526
1394
|
const height = String(Math.max(1, Math.floor(heightLines)));
|
|
527
|
-
const resizeCmd = shellEscapeSingle(buildHudResizeHookCommand(tmuxBin,
|
|
1395
|
+
const resizeCmd = shellEscapeSingle(buildHudResizeHookCommand(tmuxBin, canonicalHudPaneId, height, context, options.env?.TMUX));
|
|
528
1396
|
const omxBin = resolveOmxCliEntryPath({ cwd: options.cwd, env: options.env });
|
|
529
1397
|
try {
|
|
530
|
-
execTmuxSync(['set-hook', '-t', context.sessionId, context.hookSlot, `run-shell -b ${resizeCmd}
|
|
1398
|
+
execTmuxSync(['set-hook', '-t', context.sessionId, context.hookSlot, `run-shell -b ${resizeCmd}`, ...buildHudHookRegistrationSuffix(context, context.hookSlot)]);
|
|
531
1399
|
unregisterLegacyHudResizeHook(context, execTmuxSync);
|
|
532
1400
|
}
|
|
533
1401
|
catch {
|
|
534
1402
|
return false;
|
|
535
1403
|
}
|
|
536
|
-
if (omxBin
|
|
1404
|
+
if (omxBin) {
|
|
537
1405
|
try {
|
|
538
|
-
const reconcileCmd = shellEscapeSingle(buildHudLayoutReconcileHookCommand(tmuxBin, omxBin,
|
|
539
|
-
execTmuxSync(['set-hook', '-t', context.sessionId, context.layoutHookSlot, `run-shell -b ${reconcileCmd}
|
|
1406
|
+
const reconcileCmd = shellEscapeSingle(buildHudLayoutReconcileHookCommand(tmuxBin, omxBin, canonicalLeaderPaneId, context, options));
|
|
1407
|
+
execTmuxSync(['set-hook', '-t', context.sessionId, context.layoutHookSlot, `run-shell -b ${reconcileCmd}`, ...buildHudHookRegistrationSuffix(context, context.layoutHookSlot)]);
|
|
540
1408
|
}
|
|
541
1409
|
catch {
|
|
542
1410
|
// Keep the resize hook installed so older tmux builds still recover on
|
|
@@ -547,19 +1415,29 @@ export function registerHudResizeHook(hudPaneId, leaderPaneId, heightLines, opti
|
|
|
547
1415
|
return true;
|
|
548
1416
|
}
|
|
549
1417
|
export function unregisterHudResizeHook(leaderPaneId, execTmuxSync = defaultExecTmuxSync) {
|
|
550
|
-
const
|
|
1418
|
+
const canonicalLeaderPaneId = parseCanonicalTmuxPaneId(leaderPaneId);
|
|
1419
|
+
if (!canonicalLeaderPaneId)
|
|
1420
|
+
return false;
|
|
1421
|
+
let contextOutput;
|
|
1422
|
+
try {
|
|
1423
|
+
contextOutput = execTmuxSync(['display-message', '-p', '-t', canonicalLeaderPaneId, '#{session_id}\t#{window_id}']);
|
|
1424
|
+
}
|
|
1425
|
+
catch {
|
|
1426
|
+
return false;
|
|
1427
|
+
}
|
|
1428
|
+
const context = parseHudResizeHookContext(contextOutput, canonicalLeaderPaneId, canonicalLeaderPaneId, { leaderPanePid: '1', hudPanePid: '1' });
|
|
551
1429
|
if (!context)
|
|
552
1430
|
return false;
|
|
553
1431
|
let ok = true;
|
|
554
1432
|
try {
|
|
555
1433
|
unregisterLegacyHudResizeHook(context, execTmuxSync);
|
|
556
|
-
execTmuxSync(
|
|
1434
|
+
execTmuxSync(buildGuardedHudHookUnregisterArgs(context, context.hookSlot));
|
|
557
1435
|
}
|
|
558
1436
|
catch {
|
|
559
1437
|
ok = false;
|
|
560
1438
|
}
|
|
561
1439
|
try {
|
|
562
|
-
execTmuxSync(
|
|
1440
|
+
execTmuxSync(buildGuardedHudHookUnregisterArgs(context, context.layoutHookSlot));
|
|
563
1441
|
}
|
|
564
1442
|
catch {
|
|
565
1443
|
ok = false;
|