@polderlabs/bizar-omp 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -2
- package/dist/cli/agents-ink.d.ts.map +1 -1
- package/dist/cli/agents-ink.js +11 -10
- package/dist/cli/agents-ink.js.map +1 -1
- package/dist/cli/agents.d.ts +1 -0
- package/dist/cli/agents.d.ts.map +1 -1
- package/dist/cli/agents.js +11 -0
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/directory-selector.d.ts +36 -0
- package/dist/cli/directory-selector.d.ts.map +1 -1
- package/dist/cli/directory-selector.js +25 -0
- package/dist/cli/directory-selector.js.map +1 -1
- package/dist/cli/main.js +0 -0
- package/dist/cli/omb-commands.d.ts +30 -0
- package/dist/cli/omb-commands.d.ts.map +1 -0
- package/dist/cli/omb-commands.js +109 -0
- package/dist/cli/omb-commands.js.map +1 -0
- package/dist/cli/omb-daemon.d.ts +1 -0
- package/dist/cli/omb-daemon.d.ts.map +1 -1
- package/dist/cli/omb-daemon.js +15 -1
- package/dist/cli/omb-daemon.js.map +1 -1
- package/dist/cli/omb-dashboard.d.ts +1 -1
- package/dist/cli/omb-dashboard.d.ts.map +1 -1
- package/dist/cli/omb-dashboard.js +2 -1
- package/dist/cli/omb-dashboard.js.map +1 -1
- package/dist/cli/omb-runtime.d.ts +42 -0
- package/dist/cli/omb-runtime.d.ts.map +1 -0
- package/dist/cli/omb-runtime.js +507 -0
- package/dist/cli/omb-runtime.js.map +1 -0
- package/dist/cli/omb-tray.d.ts +3 -0
- package/dist/cli/omb-tray.d.ts.map +1 -0
- package/dist/cli/omb-tray.js +44 -0
- package/dist/cli/omb-tray.js.map +1 -0
- package/dist/cli/omb.js +8 -33
- package/dist/cli/omb.js.map +1 -1
- package/dist/extension.d.ts +2 -1
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +160 -33
- package/dist/extension.js.map +1 -1
- package/dist/omp/daemon-service.d.ts.map +1 -1
- package/dist/omp/daemon-service.js +6 -3
- package/dist/omp/daemon-service.js.map +1 -1
- package/dist/omp/daemon.d.ts +1 -0
- package/dist/omp/daemon.d.ts.map +1 -1
- package/dist/omp/daemon.js +1 -1
- package/dist/omp/daemon.js.map +1 -1
- package/dist/omp/dashboard-html.d.ts.map +1 -1
- package/dist/omp/dashboard-html.js +360 -38
- package/dist/omp/dashboard-html.js.map +1 -1
- package/dist/omp/model-role-assignment.d.ts +166 -1
- package/dist/omp/model-role-assignment.d.ts.map +1 -1
- package/dist/omp/model-role-assignment.js +393 -4
- package/dist/omp/model-role-assignment.js.map +1 -1
- package/dist/omp/omb-sessions.d.ts +44 -0
- package/dist/omp/omb-sessions.d.ts.map +1 -1
- package/dist/omp/omb-sessions.js +160 -52
- package/dist/omp/omb-sessions.js.map +1 -1
- package/dist/omp/omb-settings.d.ts +16 -0
- package/dist/omp/omb-settings.d.ts.map +1 -1
- package/dist/omp/omb-settings.js +55 -3
- package/dist/omp/omb-settings.js.map +1 -1
- package/dist/omp/run-log.d.ts +26 -0
- package/dist/omp/run-log.d.ts.map +1 -0
- package/dist/omp/run-log.js +24 -0
- package/dist/omp/run-log.js.map +1 -0
- package/dist/omp/tmux.d.ts +38 -2
- package/dist/omp/tmux.d.ts.map +1 -1
- package/dist/omp/tmux.js +231 -46
- package/dist/omp/tmux.js.map +1 -1
- package/dist/omp/tray.d.ts +2 -0
- package/dist/omp/tray.d.ts.map +1 -1
- package/dist/omp/tray.js +31 -5
- package/dist/omp/tray.js.map +1 -1
- package/dist/src/cli/agents-ink.d.ts +25 -0
- package/dist/src/cli/agents-ink.js +517 -0
- package/dist/src/cli/agents-ink.js.map +1 -0
- package/dist/src/cli/agents.d.ts +14 -0
- package/dist/src/cli/agents.js +197 -0
- package/dist/src/cli/agents.js.map +1 -0
- package/dist/src/cli/directory-selector.d.ts +60 -0
- package/dist/src/cli/directory-selector.js +208 -0
- package/dist/src/cli/directory-selector.js.map +1 -0
- package/dist/src/cli/doctor.d.ts +41 -0
- package/dist/src/cli/doctor.js +31 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/install.d.ts +129 -0
- package/dist/src/cli/install.js +164 -0
- package/dist/src/cli/install.js.map +1 -0
- package/dist/src/cli/main.d.ts +2 -0
- package/dist/src/cli/main.js +117 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/migrate.d.ts +18 -0
- package/dist/src/cli/migrate.js +14 -0
- package/dist/src/cli/migrate.js.map +1 -0
- package/dist/src/cli/omb-commands.d.ts +29 -0
- package/dist/src/cli/omb-commands.js +107 -0
- package/dist/src/cli/omb-commands.js.map +1 -0
- package/dist/src/cli/omb-daemon.d.ts +2 -0
- package/dist/src/cli/omb-daemon.js +135 -0
- package/dist/src/cli/omb-daemon.js.map +1 -0
- package/dist/src/cli/omb-dashboard.d.ts +12 -0
- package/dist/src/cli/omb-dashboard.js +113 -0
- package/dist/src/cli/omb-dashboard.js.map +1 -0
- package/dist/src/cli/omb-invocation.d.ts +8 -0
- package/dist/src/cli/omb-invocation.js +41 -0
- package/dist/src/cli/omb-invocation.js.map +1 -0
- package/dist/src/cli/omb-runtime.d.ts +41 -0
- package/dist/src/cli/omb-runtime.js +371 -0
- package/dist/src/cli/omb-runtime.js.map +1 -0
- package/dist/src/cli/omb-tray.d.ts +2 -0
- package/dist/src/cli/omb-tray.js +38 -0
- package/dist/src/cli/omb-tray.js.map +1 -0
- package/dist/src/cli/omb.d.ts +2 -0
- package/dist/src/cli/omb.js +31 -0
- package/dist/src/cli/omb.js.map +1 -0
- package/dist/src/cli/omp-theme.d.ts +17 -0
- package/dist/src/cli/omp-theme.js +22 -0
- package/dist/src/cli/omp-theme.js.map +1 -0
- package/dist/src/cli/process.d.ts +6 -0
- package/dist/src/cli/process.js +54 -0
- package/dist/src/cli/process.js.map +1 -0
- package/dist/src/cli/run.d.ts +2 -0
- package/dist/src/cli/run.js +5 -0
- package/dist/src/cli/run.js.map +1 -0
- package/dist/src/cli/setup-ui.d.ts +37 -0
- package/dist/src/cli/setup-ui.js +90 -0
- package/dist/src/cli/setup-ui.js.map +1 -0
- package/dist/src/core/acceptance.d.ts +32 -0
- package/dist/src/core/acceptance.js +87 -0
- package/dist/src/core/acceptance.js.map +1 -0
- package/dist/src/core/errors.d.ts +20 -0
- package/dist/src/core/errors.js +30 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/evidence.d.ts +10 -0
- package/dist/src/core/evidence.js +35 -0
- package/dist/src/core/evidence.js.map +1 -0
- package/dist/src/core/policies.d.ts +10 -0
- package/dist/src/core/policies.js +60 -0
- package/dist/src/core/policies.js.map +1 -0
- package/dist/src/core/recovery.d.ts +6 -0
- package/dist/src/core/recovery.js +56 -0
- package/dist/src/core/recovery.js.map +1 -0
- package/dist/src/core/transitions.d.ts +13 -0
- package/dist/src/core/transitions.js +59 -0
- package/dist/src/core/transitions.js.map +1 -0
- package/dist/src/core/types.d.ts +256 -0
- package/dist/src/core/types.js +2 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/extension.d.ts +60 -0
- package/dist/src/extension.js +538 -0
- package/dist/src/extension.js.map +1 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.js +30 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/omp/autonomous-settings.d.ts +14 -0
- package/dist/src/omp/autonomous-settings.js +65 -0
- package/dist/src/omp/autonomous-settings.js.map +1 -0
- package/dist/src/omp/capabilities.d.ts +23 -0
- package/dist/src/omp/capabilities.js +6 -0
- package/dist/src/omp/capabilities.js.map +1 -0
- package/dist/src/omp/compatibility.d.ts +30 -0
- package/dist/src/omp/compatibility.js +34 -0
- package/dist/src/omp/compatibility.js.map +1 -0
- package/dist/src/omp/completion.d.ts +7 -0
- package/dist/src/omp/completion.js +10 -0
- package/dist/src/omp/completion.js.map +1 -0
- package/dist/src/omp/config.d.ts +10 -0
- package/dist/src/omp/config.js +14 -0
- package/dist/src/omp/config.js.map +1 -0
- package/dist/src/omp/context.d.ts +15 -0
- package/dist/src/omp/context.js +3 -0
- package/dist/src/omp/context.js.map +1 -0
- package/dist/src/omp/daemon-service.d.ts +25 -0
- package/dist/src/omp/daemon-service.js +483 -0
- package/dist/src/omp/daemon-service.js.map +1 -0
- package/dist/src/omp/daemon.d.ts +48 -0
- package/dist/src/omp/daemon.js +194 -0
- package/dist/src/omp/daemon.js.map +1 -0
- package/dist/src/omp/dashboard-html.d.ts +3 -0
- package/dist/src/omp/dashboard-html.js +419 -0
- package/dist/src/omp/dashboard-html.js.map +1 -0
- package/dist/src/omp/dispatch-admission.d.ts +2 -0
- package/dist/src/omp/dispatch-admission.js +12 -0
- package/dist/src/omp/dispatch-admission.js.map +1 -0
- package/dist/src/omp/integration-recovery.d.ts +10 -0
- package/dist/src/omp/integration-recovery.js +18 -0
- package/dist/src/omp/integration-recovery.js.map +1 -0
- package/dist/src/omp/isolation-policy.d.ts +14 -0
- package/dist/src/omp/isolation-policy.js +9 -0
- package/dist/src/omp/isolation-policy.js.map +1 -0
- package/dist/src/omp/lifecycle.d.ts +14 -0
- package/dist/src/omp/lifecycle.js +14 -0
- package/dist/src/omp/lifecycle.js.map +1 -0
- package/dist/src/omp/memory.d.ts +16 -0
- package/dist/src/omp/memory.js +4 -0
- package/dist/src/omp/memory.js.map +1 -0
- package/dist/src/omp/model-role-assignment.d.ts +296 -0
- package/dist/src/omp/model-role-assignment.js +559 -0
- package/dist/src/omp/model-role-assignment.js.map +1 -0
- package/dist/src/omp/model-roles.d.ts +19 -0
- package/dist/src/omp/model-roles.js +6 -0
- package/dist/src/omp/model-roles.js.map +1 -0
- package/dist/src/omp/omb-sessions.d.ts +122 -0
- package/dist/src/omp/omb-sessions.js +512 -0
- package/dist/src/omp/omb-sessions.js.map +1 -0
- package/dist/src/omp/omb-settings.d.ts +35 -0
- package/dist/src/omp/omb-settings.js +104 -0
- package/dist/src/omp/omb-settings.js.map +1 -0
- package/dist/src/omp/result-reconciliation.d.ts +2 -0
- package/dist/src/omp/result-reconciliation.js +9 -0
- package/dist/src/omp/result-reconciliation.js.map +1 -0
- package/dist/src/omp/rpc.d.ts +67 -0
- package/dist/src/omp/rpc.js +4 -0
- package/dist/src/omp/rpc.js.map +1 -0
- package/dist/src/omp/run-log.d.ts +25 -0
- package/dist/src/omp/run-log.js +24 -0
- package/dist/src/omp/run-log.js.map +1 -0
- package/dist/src/omp/session-activity.d.ts +70 -0
- package/dist/src/omp/session-activity.js +361 -0
- package/dist/src/omp/session-activity.js.map +1 -0
- package/dist/src/omp/session-controls.d.ts +17 -0
- package/dist/src/omp/session-controls.js +14 -0
- package/dist/src/omp/session-controls.js.map +1 -0
- package/dist/src/omp/tmux.d.ts +89 -0
- package/dist/src/omp/tmux.js +445 -0
- package/dist/src/omp/tmux.js.map +1 -0
- package/dist/src/omp/tray-icons.d.ts +5 -0
- package/dist/src/omp/tray-icons.js +78 -0
- package/dist/src/omp/tray-icons.js.map +1 -0
- package/dist/src/omp/tray.d.ts +23 -0
- package/dist/src/omp/tray.js +167 -0
- package/dist/src/omp/tray.js.map +1 -0
- package/dist/src/services/budget.d.ts +19 -0
- package/dist/src/services/budget.js +10 -0
- package/dist/src/services/budget.js.map +1 -0
- package/dist/src/services/candidate-local.d.ts +13 -0
- package/dist/src/services/candidate-local.js +15 -0
- package/dist/src/services/candidate-local.js.map +1 -0
- package/dist/src/services/candidates.d.ts +20 -0
- package/dist/src/services/candidates.js +100 -0
- package/dist/src/services/candidates.js.map +1 -0
- package/dist/src/services/evidence-bundle.d.ts +17 -0
- package/dist/src/services/evidence-bundle.js +21 -0
- package/dist/src/services/evidence-bundle.js.map +1 -0
- package/dist/src/services/inspector.d.ts +11 -0
- package/dist/src/services/inspector.js +6 -0
- package/dist/src/services/inspector.js.map +1 -0
- package/dist/src/services/lessons.d.ts +13 -0
- package/dist/src/services/lessons.js +11 -0
- package/dist/src/services/lessons.js.map +1 -0
- package/dist/src/services/projection.d.ts +21 -0
- package/dist/src/services/projection.js +10 -0
- package/dist/src/services/projection.js.map +1 -0
- package/dist/src/services/readiness.d.ts +4 -0
- package/dist/src/services/readiness.js +35 -0
- package/dist/src/services/readiness.js.map +1 -0
- package/dist/src/services/recipes.d.ts +15 -0
- package/dist/src/services/recipes.js +42 -0
- package/dist/src/services/recipes.js.map +1 -0
- package/dist/src/services/recovery.d.ts +19 -0
- package/dist/src/services/recovery.js +9 -0
- package/dist/src/services/recovery.js.map +1 -0
- package/dist/src/services/review.d.ts +15 -0
- package/dist/src/services/review.js +9 -0
- package/dist/src/services/review.js.map +1 -0
- package/dist/src/storage/journal-recovery.d.ts +10 -0
- package/dist/src/storage/journal-recovery.js +33 -0
- package/dist/src/storage/journal-recovery.js.map +1 -0
- package/dist/src/storage/journal.d.ts +12 -0
- package/dist/src/storage/journal.js +82 -0
- package/dist/src/storage/journal.js.map +1 -0
- package/dist/src/storage/migrations.d.ts +22 -0
- package/dist/src/storage/migrations.js +107 -0
- package/dist/src/storage/migrations.js.map +1 -0
- package/dist/src/storage/repository-identity.d.ts +11 -0
- package/dist/src/storage/repository-identity.js +67 -0
- package/dist/src/storage/repository-identity.js.map +1 -0
- package/dist/src/storage/resources.d.ts +66 -0
- package/dist/src/storage/resources.js +148 -0
- package/dist/src/storage/resources.js.map +1 -0
- package/dist/src/tools/evidence.d.ts +8 -0
- package/dist/src/tools/evidence.js +9 -0
- package/dist/src/tools/evidence.js.map +1 -0
- package/dist/src/tools/integrate.d.ts +13 -0
- package/dist/src/tools/integrate.js +90 -0
- package/dist/src/tools/integrate.js.map +1 -0
- package/dist/src/tools/result.d.ts +9 -0
- package/dist/src/tools/result.js +2 -0
- package/dist/src/tools/result.js.map +1 -0
- package/dist/src/tools/verify.d.ts +13 -0
- package/dist/src/tools/verify.js +61 -0
- package/dist/src/tools/verify.js.map +1 -0
- package/dist/src/tools/workflow.d.ts +8 -0
- package/dist/src/tools/workflow.js +65 -0
- package/dist/src/tools/workflow.js.map +1 -0
- package/dist/src/workflows/direct.d.ts +9 -0
- package/dist/src/workflows/direct.js +2 -0
- package/dist/src/workflows/direct.js.map +1 -0
- package/dist/src/workflows/implement.d.ts +2 -0
- package/dist/src/workflows/implement.js +4 -0
- package/dist/src/workflows/implement.js.map +1 -0
- package/dist/src/workflows/plan.d.ts +17 -0
- package/dist/src/workflows/plan.js +6 -0
- package/dist/src/workflows/plan.js.map +1 -0
- package/dist/src/workflows/research.d.ts +15 -0
- package/dist/src/workflows/research.js +2 -0
- package/dist/src/workflows/research.js.map +1 -0
- package/dist/src/workflows/review.d.ts +15 -0
- package/dist/src/workflows/review.js +2 -0
- package/dist/src/workflows/review.js.map +1 -0
- package/dist/src/workflows/verify.d.ts +9 -0
- package/dist/src/workflows/verify.js +2 -0
- package/dist/src/workflows/verify.js.map +1 -0
- package/dist/tests/acceptance.test.d.ts +1 -0
- package/dist/tests/acceptance.test.js +27 -0
- package/dist/tests/acceptance.test.js.map +1 -0
- package/dist/tests/agents.test.d.ts +1 -0
- package/dist/tests/agents.test.js +369 -0
- package/dist/tests/agents.test.js.map +1 -0
- package/dist/tests/candidates.test.d.ts +1 -0
- package/dist/tests/candidates.test.js +60 -0
- package/dist/tests/candidates.test.js.map +1 -0
- package/dist/tests/core.test.d.ts +1 -0
- package/dist/tests/core.test.js +39 -0
- package/dist/tests/core.test.js.map +1 -0
- package/dist/tests/daemon.test.d.ts +1 -0
- package/dist/tests/daemon.test.js +157 -0
- package/dist/tests/daemon.test.js.map +1 -0
- package/dist/tests/dashboard.test.d.ts +1 -0
- package/dist/tests/dashboard.test.js +414 -0
- package/dist/tests/dashboard.test.js.map +1 -0
- package/dist/tests/directory-selector.test.d.ts +1 -0
- package/dist/tests/directory-selector.test.js +93 -0
- package/dist/tests/directory-selector.test.js.map +1 -0
- package/dist/tests/doctor.test.d.ts +1 -0
- package/dist/tests/doctor.test.js +32 -0
- package/dist/tests/doctor.test.js.map +1 -0
- package/dist/tests/install.test.d.ts +1 -0
- package/dist/tests/install.test.js +101 -0
- package/dist/tests/install.test.js.map +1 -0
- package/dist/tests/integration.test.d.ts +1 -0
- package/dist/tests/integration.test.js +49 -0
- package/dist/tests/integration.test.js.map +1 -0
- package/dist/tests/journal.test.d.ts +1 -0
- package/dist/tests/journal.test.js +50 -0
- package/dist/tests/journal.test.js.map +1 -0
- package/dist/tests/model-role-assignment.test.d.ts +1 -0
- package/dist/tests/model-role-assignment.test.js +294 -0
- package/dist/tests/model-role-assignment.test.js.map +1 -0
- package/dist/tests/omb-runtime.test.d.ts +1 -0
- package/dist/tests/omb-runtime.test.js +554 -0
- package/dist/tests/omb-runtime.test.js.map +1 -0
- package/dist/tests/omb-session-actions.test.d.ts +1 -0
- package/dist/tests/omb-session-actions.test.js +191 -0
- package/dist/tests/omb-session-actions.test.js.map +1 -0
- package/dist/tests/omb-settings.test.d.ts +1 -0
- package/dist/tests/omb-settings.test.js +82 -0
- package/dist/tests/omb-settings.test.js.map +1 -0
- package/dist/tests/omp-contract.test.d.ts +1 -0
- package/dist/tests/omp-contract.test.js +362 -0
- package/dist/tests/omp-contract.test.js.map +1 -0
- package/dist/tests/process.test.d.ts +1 -0
- package/dist/tests/process.test.js +12 -0
- package/dist/tests/process.test.js.map +1 -0
- package/dist/tests/psmux-windows.test.d.ts +1 -0
- package/dist/tests/psmux-windows.test.js +105 -0
- package/dist/tests/psmux-windows.test.js.map +1 -0
- package/dist/tests/recovery.test.d.ts +1 -0
- package/dist/tests/recovery.test.js +9 -0
- package/dist/tests/recovery.test.js.map +1 -0
- package/dist/tests/resources.test.d.ts +1 -0
- package/dist/tests/resources.test.js +55 -0
- package/dist/tests/resources.test.js.map +1 -0
- package/dist/tests/session-activity.test.d.ts +1 -0
- package/dist/tests/session-activity.test.js +159 -0
- package/dist/tests/session-activity.test.js.map +1 -0
- package/dist/tests/setup-ui.test.d.ts +1 -0
- package/dist/tests/setup-ui.test.js +37 -0
- package/dist/tests/setup-ui.test.js.map +1 -0
- package/dist/tests/storage-integrity.test.d.ts +1 -0
- package/dist/tests/storage-integrity.test.js +88 -0
- package/dist/tests/storage-integrity.test.js.map +1 -0
- package/dist/tests/tmux.test.d.ts +1 -0
- package/dist/tests/tmux.test.js +239 -0
- package/dist/tests/tmux.test.js.map +1 -0
- package/dist/tests/tray.test.d.ts +1 -0
- package/dist/tests/tray.test.js +37 -0
- package/dist/tests/tray.test.js.map +1 -0
- package/dist/tests/verification.test.d.ts +1 -0
- package/dist/tests/verification.test.js +61 -0
- package/dist/tests/verification.test.js.map +1 -0
- package/dist/tests/workflow-features.test.d.ts +1 -0
- package/dist/tests/workflow-features.test.js +82 -0
- package/dist/tests/workflow-features.test.js.map +1 -0
- package/dist/tests/workflow-safety.test.d.ts +1 -0
- package/dist/tests/workflow-safety.test.js +57 -0
- package/dist/tests/workflow-safety.test.js.map +1 -0
- package/docs/compatibility/baseline.json +1 -1
- package/docs/decisions/console-handoff.md +5 -0
- package/docs/releases/0.6.2.md +32 -0
- package/docs/releases/0.6.3.md +27 -0
- package/package.json +1 -1
- package/skills/bizar-omp/SKILL.md +2 -1
package/dist/omp/omb-settings.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { parseCatalogSnapshot } from "./model-role-assignment.js";
|
|
4
5
|
export const OMB_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max", "auto"];
|
|
5
6
|
export const OMB_APPROVAL_MODES = ["always-ask", "write", "yolo"];
|
|
6
7
|
export const DEFAULT_OMB_SETTINGS = {
|
|
@@ -29,7 +30,7 @@ function validChoice(value, choices, fallback) {
|
|
|
29
30
|
export function loadOmbSettings(env = process.env) {
|
|
30
31
|
try {
|
|
31
32
|
const value = JSON.parse(readFileSync(ombSettingsPath(env), "utf8"));
|
|
32
|
-
|
|
33
|
+
const settings = {
|
|
33
34
|
autoAttachLatest: validBoolean(value.autoAttachLatest, DEFAULT_OMB_SETTINGS.autoAttachLatest),
|
|
34
35
|
mouseScrolling: validBoolean(value.mouseScrolling, DEFAULT_OMB_SETTINGS.mouseScrolling),
|
|
35
36
|
showStoppedSessions: validBoolean(value.showStoppedSessions, DEFAULT_OMB_SETTINGS.showStoppedSessions),
|
|
@@ -39,6 +40,10 @@ export function loadOmbSettings(env = process.env) {
|
|
|
39
40
|
defaultThinking: validChoice(value.defaultThinking, OMB_THINKING_LEVELS, DEFAULT_OMB_SETTINGS.defaultThinking),
|
|
40
41
|
defaultApprovalMode: validChoice(value.defaultApprovalMode, OMB_APPROVAL_MODES, DEFAULT_OMB_SETTINGS.defaultApprovalMode),
|
|
41
42
|
};
|
|
43
|
+
const catalog = parseCatalogSnapshot(value.modelCatalog);
|
|
44
|
+
if (catalog)
|
|
45
|
+
settings.modelCatalog = catalog;
|
|
46
|
+
return settings;
|
|
42
47
|
}
|
|
43
48
|
catch {
|
|
44
49
|
return { ...DEFAULT_OMB_SETTINGS };
|
|
@@ -47,6 +52,53 @@ export function loadOmbSettings(env = process.env) {
|
|
|
47
52
|
export function saveOmbSettings(settings, env = process.env) {
|
|
48
53
|
const path = ombSettingsPath(env);
|
|
49
54
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
50
|
-
|
|
55
|
+
// Temp file plus rename: a reader or a crash never observes a half-written file.
|
|
56
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
57
|
+
writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}\n`, { mode: 0o600 });
|
|
58
|
+
renameSync(temporary, path);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Record the observed model catalog without ever replacing settings that could
|
|
62
|
+
* not be read. An existing but unparseable `omb.json` is left byte-for-byte
|
|
63
|
+
* untouched and reported, because that file may hold launcher preferences an
|
|
64
|
+
* interrupted write or a hand edit corrupted; rewriting it as defaults would
|
|
65
|
+
* silently discard the operator's configuration.
|
|
66
|
+
*/
|
|
67
|
+
export function recordCatalogObservation(catalog, env = process.env) {
|
|
68
|
+
const path = ombSettingsPath(env);
|
|
69
|
+
let raw;
|
|
70
|
+
try {
|
|
71
|
+
raw = readFileSync(path, "utf8");
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error.code !== "ENOENT")
|
|
75
|
+
return { ok: false, reason: describeWriteError(error) };
|
|
76
|
+
}
|
|
77
|
+
let existing = {};
|
|
78
|
+
if (raw !== undefined) {
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(raw);
|
|
81
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
82
|
+
throw new Error("settings root is not an object");
|
|
83
|
+
existing = parsed;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return { ok: false, reason: `${path} is not readable JSON (${describeWriteError(error)}); left untouched` };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const merged = { ...existing, modelCatalog: catalog };
|
|
91
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
92
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
93
|
+
writeFileSync(temporary, `${JSON.stringify(merged, null, 2)}\n`, { mode: 0o600 });
|
|
94
|
+
renameSync(temporary, path);
|
|
95
|
+
return { ok: true };
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return { ok: false, reason: describeWriteError(error) };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function describeWriteError(error) {
|
|
102
|
+
return error instanceof Error ? error.message : String(error);
|
|
51
103
|
}
|
|
52
104
|
//# sourceMappingURL=omb-settings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"omb-settings.js","sourceRoot":"","sources":["../../src/omp/omb-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"omb-settings.js","sourceRoot":"","sources":["../../src/omp/omb-settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator report that no longer fits OMP's status line.
|
|
3
|
+
*
|
|
4
|
+
* `ctx.ui.notify(message, "info")` renders through the host status line, which
|
|
5
|
+
* folds a single text component onto about two screen lines. A report longer
|
|
6
|
+
* than that arrives clipped, so the command that produced it writes the
|
|
7
|
+
* complete text here and points the operator at it. Only the notice is fitted;
|
|
8
|
+
* this log always holds the report verbatim.
|
|
9
|
+
*/
|
|
10
|
+
export interface RunLogEntry {
|
|
11
|
+
/** Command or subsystem that produced the report. */
|
|
12
|
+
event: string;
|
|
13
|
+
/** Complete report text, exactly as the command returned it. */
|
|
14
|
+
detail: string;
|
|
15
|
+
/** Last session entry seen when the report was produced, when the host exposes one. */
|
|
16
|
+
sessionEntry?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Run logs live beside the project journal, so a project keeps one directory. */
|
|
19
|
+
export declare function runLogPath(cwd: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Append one entry as a single JSON line. A run log is operator bookkeeping: a
|
|
22
|
+
* directory that cannot be created or written must never turn a completed
|
|
23
|
+
* command into a failed one, so the caller sees false instead of an exception.
|
|
24
|
+
*/
|
|
25
|
+
export declare function appendRunLog(cwd: string, entry: RunLogEntry): boolean;
|
|
26
|
+
//# sourceMappingURL=run-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-log.d.ts","sourceRoot":"","sources":["../../src/omp/run-log.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAUrE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
/** Run logs live beside the project journal, so a project keeps one directory. */
|
|
4
|
+
export function runLogPath(cwd) {
|
|
5
|
+
return join(cwd, ".bizar-omp", "run.log");
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Append one entry as a single JSON line. A run log is operator bookkeeping: a
|
|
9
|
+
* directory that cannot be created or written must never turn a completed
|
|
10
|
+
* command into a failed one, so the caller sees false instead of an exception.
|
|
11
|
+
*/
|
|
12
|
+
export function appendRunLog(cwd, entry) {
|
|
13
|
+
const line = `${JSON.stringify({ at: new Date().toISOString(), ...entry })}\n`;
|
|
14
|
+
try {
|
|
15
|
+
const path = runLogPath(cwd);
|
|
16
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
17
|
+
appendFileSync(path, line, { mode: 0o600 });
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=run-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-log.js","sourceRoot":"","sources":["../../src/omp/run-log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAoB1C,kFAAkF;AAClF,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,KAAkB;IAC1D,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC;IAC/E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC7B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/omp/tmux.d.ts
CHANGED
|
@@ -23,6 +23,22 @@ export interface OmbSessionConsole {
|
|
|
23
23
|
code: number;
|
|
24
24
|
text: string;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Lines of history kept for the window an OMB session runs in.
|
|
28
|
+
*
|
|
29
|
+
* tmux defaults to 2000 lines, which a long OMP chat scrolls past in one
|
|
30
|
+
* sitting: the pane then drops the messages the operator needs to review and
|
|
31
|
+
* neither the wheel binding nor copy-mode can bring them back. `history-limit`
|
|
32
|
+
* is a window option, so it is set on the session's own window — every other
|
|
33
|
+
* tmux session keeps the limit its owner configured.
|
|
34
|
+
*/
|
|
35
|
+
export declare const OMB_SESSION_HISTORY_LIMIT = 100000;
|
|
36
|
+
/**
|
|
37
|
+
* Raise one Bizar-owned session's window history limit. Idempotent, so attach
|
|
38
|
+
* paths run it too: a session created before the limit was raised must not stay
|
|
39
|
+
* capped at 2000 lines for the rest of its life.
|
|
40
|
+
*/
|
|
41
|
+
export declare function raiseOmbSessionHistory(session: string, cwd?: string, env?: NodeJS.ProcessEnv): Promise<number>;
|
|
26
42
|
/** Capture a bounded, plain-text view of one Bizar-owned session's active pane. */
|
|
27
43
|
export declare function captureOmbSessionConsole(session: string, cwd?: string, env?: NodeJS.ProcessEnv, lines?: number): Promise<OmbSessionConsole>;
|
|
28
44
|
/** Send literal composer text followed by Enter to one Bizar-owned session. */
|
|
@@ -31,15 +47,35 @@ export declare function sendOmbSessionInput(session: string, input: string, cwd?
|
|
|
31
47
|
* Open one Bizar-owned session in a new terminal emulator window. The daemon
|
|
32
48
|
* and hub run without a controlling TTY, so attaching means handing the attach
|
|
33
49
|
* command to a terminal we spawn; when no emulator exists the caller still
|
|
34
|
-
* receives the exact command to run by hand.
|
|
50
|
+
* receives the exact command to run by hand. Attaching is also where a session
|
|
51
|
+
* created before the history limit was raised is repaired, because an operator
|
|
52
|
+
* attaching to a long-running chat is the one who needs its scrollback.
|
|
35
53
|
*/
|
|
36
|
-
export declare function openOmbSessionInTerminal(session: string, env?: NodeJS.ProcessEnv): OmbAttachLaunch
|
|
54
|
+
export declare function openOmbSessionInTerminal(session: string, env?: NodeJS.ProcessEnv): Promise<OmbAttachLaunch>;
|
|
37
55
|
/** Open a fixed Bizar CLI command in a new terminal emulator window. */
|
|
38
56
|
export declare function openCommandInTerminal(command: string, env?: NodeJS.ProcessEnv): OmbAttachLaunch;
|
|
39
57
|
/** Apply the launcher mouse preference to one exact Bizar-owned session. */
|
|
40
58
|
export declare function setOmbSessionMouse(session: string, enabled: boolean, cwd?: string, env?: NodeJS.ProcessEnv): Promise<number>;
|
|
41
59
|
/** Rename one exact Bizar-owned tmux session. */
|
|
42
60
|
export declare function renameOmbSession(session: string, nextName: string, cwd?: string, env?: NodeJS.ProcessEnv): Promise<number>;
|
|
61
|
+
export interface TmuxSessionRecord {
|
|
62
|
+
name: string;
|
|
63
|
+
createdAt?: number;
|
|
64
|
+
lastAttachedAt?: number;
|
|
65
|
+
attached: boolean;
|
|
66
|
+
}
|
|
67
|
+
/** Parse one `list-sessions -F "#{session_name}\t#{session_created}\t#{session_last_attached}\t#{session_attached}"` result. */
|
|
68
|
+
export declare function parseTmuxSessionRecords(output: string): TmuxSessionRecord[];
|
|
69
|
+
/** Native records for the sessions the multiplexer currently hosts. */
|
|
70
|
+
export declare function listTmuxSessions(cwd?: string, env?: NodeJS.ProcessEnv): Promise<TmuxSessionRecord[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Whether the multiplexer still hosts this exact session.
|
|
73
|
+
*
|
|
74
|
+
* `unknown` means the multiplexer could not be run at all, so a missing row
|
|
75
|
+
* must never be read as a session that is gone: a caller that reports a kill
|
|
76
|
+
* has to say which of the two it proved.
|
|
77
|
+
*/
|
|
78
|
+
export declare function ombSessionLiveness(session: string, cwd?: string, env?: NodeJS.ProcessEnv): Promise<"live" | "gone" | "unknown">;
|
|
43
79
|
/** Kill one exact Bizar-owned tmux session after the caller has confirmed it. */
|
|
44
80
|
export declare function deleteOmbSession(session: string, cwd?: string, env?: NodeJS.ProcessEnv): Promise<number>;
|
|
45
81
|
/** Hide an unhosted journal record that has no tmux session left to kill. */
|
package/dist/omp/tmux.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tmux.d.ts","sourceRoot":"","sources":["../../src/omp/tmux.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAYD,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAOlF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAMtE;
|
|
1
|
+
{"version":3,"file":"tmux.d.ts","sourceRoot":"","sources":["../../src/omp/tmux.ts"],"names":[],"mappings":"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"}
|
package/dist/omp/tmux.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import { statSync } from "node:fs";
|
|
3
|
+
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
import { basename, join } from "node:path";
|
|
5
|
-
import { clearOmbSessionDeleted, findLastOmbSession, markOmbSessionDeleted, ombSessionDir, resolveMultiplexerBin } from "./omb-sessions.js";
|
|
5
|
+
import { clearOmbSessionDeleted, findLastOmbSession, isOmbSessionName, markOmbSessionDeleted, ombSessionBoundNames, ombSessionDir, resolveMultiplexerBin } from "./omb-sessions.js";
|
|
6
6
|
import { loadOmbSettings } from "./omb-settings.js";
|
|
7
7
|
const NON_INTERACTIVE_FLAGS = new Set(["-p", "--print", "--export"]);
|
|
8
8
|
const NON_INTERACTIVE_MODES = new Set(["json", "rpc", "rpc-ui", "acp"]);
|
|
@@ -54,21 +54,36 @@ function runTmux(command, args, cwd, env) {
|
|
|
54
54
|
return closed.promise;
|
|
55
55
|
}
|
|
56
56
|
function readTmux(command, args, cwd, env) {
|
|
57
|
-
const child = spawn(command, args, { cwd, env, stdio: ["ignore", "pipe", "
|
|
57
|
+
const child = spawn(command, args, { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
58
58
|
let output = "";
|
|
59
|
+
let error = "";
|
|
59
60
|
child.stdout?.setEncoding("utf8");
|
|
60
61
|
child.stdout?.on("data", chunk => { output += chunk; });
|
|
62
|
+
child.stderr?.setEncoding("utf8");
|
|
63
|
+
child.stderr?.on("data", chunk => { error += chunk; });
|
|
61
64
|
const closed = Promise.withResolvers();
|
|
62
65
|
child.once("error", closed.reject);
|
|
63
|
-
child.once("close", (code, signal) => closed.resolve({ code: exitCode(code, signal), output }));
|
|
66
|
+
child.once("close", (code, signal) => closed.resolve({ code: exitCode(code, signal), output, error }));
|
|
64
67
|
return closed.promise;
|
|
65
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Stop the parent's stdin read loop before handing the TTY to an inherit
|
|
71
|
+
* child. On Windows a flowing stdin with no data listener (the state an ink
|
|
72
|
+
* unmount can leave behind) consumes console input records, so a psmux
|
|
73
|
+
* attach sharing the console never sees keystrokes. Pausing only stops this
|
|
74
|
+
* process from reading; the inherit child keeps full access. The attached
|
|
75
|
+
* client restores terminal state itself, so nothing is restored here.
|
|
76
|
+
*/
|
|
77
|
+
function quiesceStdinForHandoff() {
|
|
78
|
+
if (process.stdin.isTTY === true && !process.stdin.isPaused())
|
|
79
|
+
process.stdin.pause();
|
|
80
|
+
}
|
|
66
81
|
/**
|
|
67
82
|
* Apply the standard Bizar options to a Bizar-owned session: respawn dead
|
|
68
|
-
* panes, remain-on-exit off, mouse preference, and the
|
|
69
|
-
* Ctrl-D/C detach the client without sending exit or
|
|
70
|
-
* OMP. The tmux host remains alive for background work,
|
|
71
|
-
* prefix still enters tmux's standard prefix table.
|
|
83
|
+
* panes, remain-on-exit off, window history limit, mouse preference, and the
|
|
84
|
+
* private root table so Ctrl-D/C detach the client without sending exit or
|
|
85
|
+
* interrupt input into OMP. The tmux host remains alive for background work,
|
|
86
|
+
* and the normal Ctrl-B prefix still enters tmux's standard prefix table.
|
|
72
87
|
*/
|
|
73
88
|
async function prepareOmbSession(tmux, omp, session, cwd, childEnv, mouseScrolling) {
|
|
74
89
|
// Keep the respawn opportunity below for stale sessions, but do not leave
|
|
@@ -87,31 +102,43 @@ async function prepareOmbSession(tmux, omp, session, cwd, childEnv, mouseScrolli
|
|
|
87
102
|
const remainOnExitOff = await runTmux(tmux, ["set-option", "-t", session, "remain-on-exit", "off"], cwd, childEnv);
|
|
88
103
|
if (remainOnExitOff !== 0)
|
|
89
104
|
return remainOnExitOff;
|
|
105
|
+
// Keep the conversation reviewable: the pane's history is the only scrollback
|
|
106
|
+
// an OMP session has, and the tmux default holds minutes of a live chat.
|
|
107
|
+
const historyRaised = await runTmux(tmux, sessionHistoryArgs(session), cwd, childEnv);
|
|
108
|
+
if (historyRaised !== 0)
|
|
109
|
+
return historyRaised;
|
|
90
110
|
// tmux must own wheel events so the terminal scrolls the pane instead of
|
|
91
111
|
// translating them into up/down input for OMP's prompt-history editor.
|
|
92
112
|
const mouseEnabled = await runTmux(tmux, ["set-option", "-t", session, "mouse", mouseScrolling ? "on" : "off"], cwd, childEnv);
|
|
93
113
|
if (mouseEnabled !== 0)
|
|
94
114
|
return mouseEnabled;
|
|
95
|
-
const keyTable = `${session}-bizar-root`;
|
|
96
115
|
// Keep pointer events inside the Bizar-owned key table. Without these
|
|
97
116
|
// explicit bindings, tmux consumes wheel/click events while an OMP
|
|
98
117
|
// alternate-screen overlay is active, so Advisor never receives them; when
|
|
99
118
|
// the main OMP screen is active, the wheel should scroll tmux's pane.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
// psmux ignores persistent key-table assignment (and its #583 `set-option`
|
|
120
|
+
// quirk eats dash-leading values), so skip the binds there; real tmux keeps
|
|
121
|
+
// them.
|
|
122
|
+
const psmuxMode = process.platform === "win32" && tmux === resolveMultiplexerBin();
|
|
123
|
+
if (!psmuxMode) {
|
|
124
|
+
const keyTable = `${session}-bizar-root`;
|
|
125
|
+
const bindWheelUp = await runTmux(tmux, ["bind-key", "-T", keyTable, "WheelUpPane", "if-shell", "-F", "#{alternate_on}", "send-keys -M", "copy-mode -e; send-keys -M"], cwd, childEnv);
|
|
126
|
+
const bindWheelDown = await runTmux(tmux, ["bind-key", "-T", keyTable, "WheelDownPane", "if-shell", "-F", "#{alternate_on}", "send-keys -M", "send-keys -M"], cwd, childEnv);
|
|
127
|
+
const bindMouseDown = await runTmux(tmux, ["bind-key", "-T", keyTable, "MouseDown1Pane", "if-shell", "-F", "#{alternate_on}", "send-keys -M", "select-pane"], cwd, childEnv);
|
|
128
|
+
const bindMouseDrag = await runTmux(tmux, ["bind-key", "-T", keyTable, "MouseDrag1Pane", "if-shell", "-F", "#{alternate_on}", "send-keys -M", "copy-mode -e; send-keys -M"], cwd, childEnv);
|
|
129
|
+
const bindMouseUp = await runTmux(tmux, ["bind-key", "-T", keyTable, "MouseUp1Pane", "if-shell", "-F", "#{alternate_on}", "send-keys -M", ""], cwd, childEnv);
|
|
130
|
+
const bindDetach = await runTmux(tmux, ["bind-key", "-T", keyTable, "C-d", "detach-client"], cwd, childEnv);
|
|
131
|
+
if (bindDetach !== 0)
|
|
132
|
+
return bindDetach;
|
|
133
|
+
const bindInterruptDetach = await runTmux(tmux, ["bind-key", "-T", keyTable, "C-c", "detach-client"], cwd, childEnv);
|
|
134
|
+
if (bindInterruptDetach !== 0)
|
|
135
|
+
return bindInterruptDetach;
|
|
136
|
+
const bindPrefix = await runTmux(tmux, ["bind-key", "-T", keyTable, "C-b", "switch-client", "-T", "prefix"], cwd, childEnv);
|
|
137
|
+
if (bindPrefix !== 0)
|
|
138
|
+
return bindPrefix;
|
|
139
|
+
return runTmux(tmux, ["set-option", "-t", session, "key-table", keyTable], cwd, childEnv);
|
|
140
|
+
}
|
|
141
|
+
return 0;
|
|
115
142
|
}
|
|
116
143
|
async function launchInTmux(tmux, omp, session, cwd, args, env, mouseScrolling, sessionDir) {
|
|
117
144
|
const childEnv = { ...env, BIZAR_OMP_NO_TMUX: "1" };
|
|
@@ -129,15 +156,13 @@ async function launchInTmux(tmux, omp, session, cwd, args, env, mouseScrolling,
|
|
|
129
156
|
const prepared = await prepareOmbSession(tmux, omp, session, cwd, childEnv, mouseScrolling);
|
|
130
157
|
if (prepared !== 0)
|
|
131
158
|
return prepared;
|
|
159
|
+
quiesceStdinForHandoff();
|
|
132
160
|
const client = spawn(tmux, ["attach-session", "-t", session], { cwd, env: childEnv, stdio: "inherit" });
|
|
133
161
|
const closed = Promise.withResolvers();
|
|
134
162
|
client.once("error", closed.reject);
|
|
135
163
|
client.once("close", (code, signal) => closed.resolve(exitCode(code, signal)));
|
|
136
164
|
return closed.promise;
|
|
137
165
|
}
|
|
138
|
-
function ownedSession(session) {
|
|
139
|
-
return /^bizar-[A-Za-z0-9_.-]+$/.test(session);
|
|
140
|
-
}
|
|
141
166
|
/**
|
|
142
167
|
* Terminal emulators that can host an interactive attach, in preference order.
|
|
143
168
|
* `$BIZAR_TERMINAL` overrides the list; `xfce4-terminal` takes the command as
|
|
@@ -179,6 +204,29 @@ export function findOnPath(bin, env = process.env) {
|
|
|
179
204
|
const SESSION_CONSOLE_MAX_LINES = 1_000;
|
|
180
205
|
const SESSION_CONSOLE_MAX_CHARS = 128 * 1024;
|
|
181
206
|
const SESSION_INPUT_MAX_BYTES = 16 * 1024;
|
|
207
|
+
/**
|
|
208
|
+
* Lines of history kept for the window an OMB session runs in.
|
|
209
|
+
*
|
|
210
|
+
* tmux defaults to 2000 lines, which a long OMP chat scrolls past in one
|
|
211
|
+
* sitting: the pane then drops the messages the operator needs to review and
|
|
212
|
+
* neither the wheel binding nor copy-mode can bring them back. `history-limit`
|
|
213
|
+
* is a window option, so it is set on the session's own window — every other
|
|
214
|
+
* tmux session keeps the limit its owner configured.
|
|
215
|
+
*/
|
|
216
|
+
export const OMB_SESSION_HISTORY_LIMIT = 100_000;
|
|
217
|
+
function sessionHistoryArgs(session) {
|
|
218
|
+
return ["set-option", "-w", "-t", `${session}:0`, "history-limit", String(OMB_SESSION_HISTORY_LIMIT)];
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Raise one Bizar-owned session's window history limit. Idempotent, so attach
|
|
222
|
+
* paths run it too: a session created before the limit was raised must not stay
|
|
223
|
+
* capped at 2000 lines for the rest of its life.
|
|
224
|
+
*/
|
|
225
|
+
export async function raiseOmbSessionHistory(session, cwd = process.cwd(), env = process.env) {
|
|
226
|
+
if (!isOmbSessionName(session))
|
|
227
|
+
return 2;
|
|
228
|
+
return runTmux(resolveMultiplexerBin(env), sessionHistoryArgs(session), cwd, { ...env, BIZAR_OMP_NO_TMUX: "1" });
|
|
229
|
+
}
|
|
182
230
|
function cleanTerminalText(value) {
|
|
183
231
|
return value
|
|
184
232
|
.replace(/\u001B\][^\u0007]*(?:\u0007|\u001B\\)/g, "")
|
|
@@ -190,7 +238,7 @@ function cleanTerminalText(value) {
|
|
|
190
238
|
}
|
|
191
239
|
/** Capture a bounded, plain-text view of one Bizar-owned session's active pane. */
|
|
192
240
|
export async function captureOmbSessionConsole(session, cwd = process.cwd(), env = process.env, lines = 500) {
|
|
193
|
-
if (!
|
|
241
|
+
if (!isOmbSessionName(session))
|
|
194
242
|
return { code: 2, text: "" };
|
|
195
243
|
const requestedLines = Number.isFinite(lines) ? Math.floor(lines) : 500;
|
|
196
244
|
const boundedLines = Math.max(1, Math.min(SESSION_CONSOLE_MAX_LINES, requestedLines));
|
|
@@ -200,7 +248,7 @@ export async function captureOmbSessionConsole(session, cwd = process.cwd(), env
|
|
|
200
248
|
}
|
|
201
249
|
/** Send literal composer text followed by Enter to one Bizar-owned session. */
|
|
202
250
|
export async function sendOmbSessionInput(session, input, cwd = process.cwd(), env = process.env) {
|
|
203
|
-
if (!
|
|
251
|
+
if (!isOmbSessionName(session) || !input.trim() || input.includes("\u0000") || Buffer.byteLength(input) > SESSION_INPUT_MAX_BYTES)
|
|
204
252
|
return 2;
|
|
205
253
|
const tmux = resolveMultiplexerBin(env);
|
|
206
254
|
const childEnv = { ...env, BIZAR_OMP_NO_TMUX: "1" };
|
|
@@ -213,13 +261,21 @@ export async function sendOmbSessionInput(session, input, cwd = process.cwd(), e
|
|
|
213
261
|
* Open one Bizar-owned session in a new terminal emulator window. The daemon
|
|
214
262
|
* and hub run without a controlling TTY, so attaching means handing the attach
|
|
215
263
|
* command to a terminal we spawn; when no emulator exists the caller still
|
|
216
|
-
* receives the exact command to run by hand.
|
|
264
|
+
* receives the exact command to run by hand. Attaching is also where a session
|
|
265
|
+
* created before the history limit was raised is repaired, because an operator
|
|
266
|
+
* attaching to a long-running chat is the one who needs its scrollback.
|
|
217
267
|
*/
|
|
218
|
-
export function openOmbSessionInTerminal(session, env = process.env) {
|
|
268
|
+
export async function openOmbSessionInTerminal(session, env = process.env) {
|
|
219
269
|
// Names come from tmux; keep the shell command single-quoted and refuse
|
|
220
270
|
// anything outside the character set Bizar ever generates.
|
|
221
|
-
if (!
|
|
271
|
+
if (!isOmbSessionName(session) || !/^[A-Za-z0-9_.-]+$/.test(session))
|
|
222
272
|
return { spawned: false, command: "" };
|
|
273
|
+
// Best effort: a session that cannot take the option is attached to anyway so
|
|
274
|
+
// the operator still sees why the attach failed.
|
|
275
|
+
try {
|
|
276
|
+
await raiseOmbSessionHistory(session, process.cwd(), env);
|
|
277
|
+
}
|
|
278
|
+
catch { /* attach proceeds */ }
|
|
223
279
|
const multiplexer = resolveMultiplexerBin(env).replace(/'/g, "'\\''");
|
|
224
280
|
const command = `'${multiplexer}' attach-session -t '${session}'`;
|
|
225
281
|
return openCommandInTerminal(command, env);
|
|
@@ -249,7 +305,7 @@ export function openCommandInTerminal(command, env = process.env) {
|
|
|
249
305
|
}
|
|
250
306
|
/** Apply the launcher mouse preference to one exact Bizar-owned session. */
|
|
251
307
|
export function setOmbSessionMouse(session, enabled, cwd = process.cwd(), env = process.env) {
|
|
252
|
-
if (!
|
|
308
|
+
if (!isOmbSessionName(session))
|
|
253
309
|
return Promise.resolve(2);
|
|
254
310
|
return runTmux(resolveMultiplexerBin(env), ["set-option", "-t", session, "mouse", enabled ? "on" : "off"], cwd, {
|
|
255
311
|
...env,
|
|
@@ -258,29 +314,156 @@ export function setOmbSessionMouse(session, enabled, cwd = process.cwd(), env =
|
|
|
258
314
|
}
|
|
259
315
|
/** Rename one exact Bizar-owned tmux session. */
|
|
260
316
|
export function renameOmbSession(session, nextName, cwd = process.cwd(), env = process.env) {
|
|
261
|
-
if (!
|
|
317
|
+
if (!isOmbSessionName(session) || !isOmbSessionName(nextName))
|
|
262
318
|
return Promise.resolve(2);
|
|
263
319
|
return runTmux(resolveMultiplexerBin(env), ["rename-session", "-t", session, nextName], cwd, {
|
|
264
320
|
...env,
|
|
265
321
|
BIZAR_OMP_NO_TMUX: "1",
|
|
266
322
|
});
|
|
267
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* One `-F` record: `name\tcreated\tlast-attached\tattached`.
|
|
326
|
+
*
|
|
327
|
+
* The two timestamps are optional because the multiplexer prints an empty
|
|
328
|
+
* field, not a zero, for a session nothing has ever attached to; `attached`
|
|
329
|
+
* is the client *count*, so a session with two clients prints `2`. Treating
|
|
330
|
+
* either as a malformed record would drop a live session out of discovery and
|
|
331
|
+
* out of every "is it gone?" answer.
|
|
332
|
+
*/
|
|
333
|
+
const SESSION_RECORD = /^([^\t]+)\t(\d*)\t(\d*)\t(\d+)$/;
|
|
334
|
+
/** A count tmux left empty means "nothing to count", never zero seconds. */
|
|
335
|
+
function optionalSeconds(field) {
|
|
336
|
+
if (!field)
|
|
337
|
+
return undefined;
|
|
338
|
+
const value = Number(field);
|
|
339
|
+
return Number.isFinite(value) ? value : undefined;
|
|
340
|
+
}
|
|
341
|
+
/** Parse one `list-sessions -F "#{session_name}\t#{session_created}\t#{session_last_attached}\t#{session_attached}"` result. */
|
|
342
|
+
export function parseTmuxSessionRecords(output) {
|
|
343
|
+
const records = [];
|
|
344
|
+
for (const line of output.split("\n")) {
|
|
345
|
+
if (!line)
|
|
346
|
+
continue;
|
|
347
|
+
const match = SESSION_RECORD.exec(line.trim());
|
|
348
|
+
if (!match)
|
|
349
|
+
continue;
|
|
350
|
+
records.push({
|
|
351
|
+
name: match[1],
|
|
352
|
+
createdAt: optionalSeconds(match[2]),
|
|
353
|
+
lastAttachedAt: optionalSeconds(match[3]),
|
|
354
|
+
attached: match[4] !== "0",
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
return records;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* The socket a multiplexer command will use, when that can be derived.
|
|
361
|
+
*
|
|
362
|
+
* A client inside a session names its own server first (`TMUX` is
|
|
363
|
+
* `<socket>,<pid>,<session>`), and every other command goes to that same
|
|
364
|
+
* server. Without one, tmux uses `<TMUX_TMPDIR|/tmp>/tmux-<uid>/default`.
|
|
365
|
+
*/
|
|
366
|
+
function multiplexerSocket(env) {
|
|
367
|
+
const current = env.TMUX?.split(",")[0];
|
|
368
|
+
if (current)
|
|
369
|
+
return current;
|
|
370
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
371
|
+
if (uid === undefined)
|
|
372
|
+
return undefined;
|
|
373
|
+
return join(env.TMUX_TMPDIR || "/tmp", `tmux-${uid}`, "default");
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Whether a failed listing means "no server is running".
|
|
377
|
+
*
|
|
378
|
+
* This is the one failure that proves nothing is hosted: tmux answers a
|
|
379
|
+
* request it cannot serve with a nonzero exit, and only an absent socket (or
|
|
380
|
+
* tmux's own "no server running" wording) says the server itself is gone. A
|
|
381
|
+
* socket that exists but refuses — a permission error, a request the server
|
|
382
|
+
* rejected — answered nothing, so a caller that must prove a session is gone
|
|
383
|
+
* cannot read it as one.
|
|
384
|
+
*/
|
|
385
|
+
function isNoServerFailure(env, error) {
|
|
386
|
+
if (/no server running/i.test(error))
|
|
387
|
+
return true;
|
|
388
|
+
const socket = multiplexerSocket(env);
|
|
389
|
+
return socket !== undefined && !existsSync(socket);
|
|
390
|
+
}
|
|
391
|
+
const SESSION_LIST_FORMAT = "#{session_name}\t#{session_created}\t#{session_last_attached}\t#{session_attached}";
|
|
392
|
+
/**
|
|
393
|
+
* One listing of the multiplexer, or `undefined` when it could not be read.
|
|
394
|
+
*
|
|
395
|
+
* `undefined` is not an empty listing: a caller that must prove a session is
|
|
396
|
+
* gone cannot do it with a multiplexer that never answered.
|
|
397
|
+
*/
|
|
398
|
+
async function readSessionRecords(cwd, env) {
|
|
399
|
+
let result;
|
|
400
|
+
try {
|
|
401
|
+
result = await readTmux(resolveMultiplexerBin(env), ["list-sessions", "-F", SESSION_LIST_FORMAT], cwd, { ...env, BIZAR_OMP_NO_TMUX: "1" });
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// The multiplexer could not be run at all, so nothing was ever asked.
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
if (result.code === 0)
|
|
408
|
+
return parseTmuxSessionRecords(result.output);
|
|
409
|
+
return isNoServerFailure(env, result.error) ? [] : undefined;
|
|
410
|
+
}
|
|
411
|
+
/** Native records for the sessions the multiplexer currently hosts. */
|
|
412
|
+
export async function listTmuxSessions(cwd = process.cwd(), env = process.env) {
|
|
413
|
+
return await readSessionRecords(cwd, env) ?? [];
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Whether the multiplexer still hosts this exact session.
|
|
417
|
+
*
|
|
418
|
+
* `unknown` means the multiplexer could not be run at all, so a missing row
|
|
419
|
+
* must never be read as a session that is gone: a caller that reports a kill
|
|
420
|
+
* has to say which of the two it proved.
|
|
421
|
+
*/
|
|
422
|
+
export async function ombSessionLiveness(session, cwd = process.cwd(), env = process.env) {
|
|
423
|
+
const records = await readSessionRecords(cwd, env);
|
|
424
|
+
if (!records)
|
|
425
|
+
return "unknown";
|
|
426
|
+
// The listing is parsed, never matched as raw text: a multiplexer may answer
|
|
427
|
+
// the format request with the records it has, and reading a name out of that
|
|
428
|
+
// text would call a hosted session gone.
|
|
429
|
+
return records.some(record => record.name === session) ? "live" : "gone";
|
|
430
|
+
}
|
|
268
431
|
/** Kill one exact Bizar-owned tmux session after the caller has confirmed it. */
|
|
269
|
-
export function deleteOmbSession(session, cwd = process.cwd(), env = process.env) {
|
|
270
|
-
if (!
|
|
271
|
-
return
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
432
|
+
export async function deleteOmbSession(session, cwd = process.cwd(), env = process.env) {
|
|
433
|
+
if (!isOmbSessionName(session))
|
|
434
|
+
return 2;
|
|
435
|
+
const childEnv = { ...env, BIZAR_OMP_NO_TMUX: "1" };
|
|
436
|
+
// A renamed session journals into the directory it was started with, so read
|
|
437
|
+
// the pane binding while the session still exists.
|
|
438
|
+
const bound = await ombSessionBoundNames(session, cwd, childEnv);
|
|
439
|
+
const code = await runTmux(resolveMultiplexerBin(env), ["kill-session", "-t", session], cwd, childEnv);
|
|
440
|
+
// One listing decides, whichever way the kill answered. A multiplexer can
|
|
441
|
+
// refuse a kill for a session a concurrent caller already removed - which is
|
|
442
|
+
// a success here - and it can report success while another session has
|
|
443
|
+
// already taken the name, which is not.
|
|
444
|
+
const liveness = await ombSessionLiveness(session, cwd, childEnv);
|
|
445
|
+
// A session the multiplexer still hosts is not deleted, and its journal must
|
|
446
|
+
// not be tombstoned: hiding a live session's journal would drop it from the
|
|
447
|
+
// hub while it keeps running, with no row left to open it again.
|
|
448
|
+
if (liveness === "live")
|
|
449
|
+
return code || 1;
|
|
450
|
+
// A kill the multiplexer accepted removed the session it was pointed at, so
|
|
451
|
+
// absence holds even when the listing cannot be taken again: the server may
|
|
452
|
+
// have exited with its last session. A kill it refused proves nothing on its
|
|
453
|
+
// own, and an unreadable listing proves nothing either, so neither may hide
|
|
454
|
+
// a journal. `omb forget` stays the explicit way to hide one by hand.
|
|
455
|
+
if (code !== 0 && liveness !== "gone")
|
|
456
|
+
return code || 1;
|
|
457
|
+
// Deletion outlives the tmux binding. The row the user removed came from one
|
|
458
|
+
// of these journals, so every directory behind this session is tombstoned:
|
|
459
|
+
// when tmux has already lost the session, its journal must not come back as a
|
|
460
|
+
// row that cannot be opened.
|
|
461
|
+
const tombstoned = [session, ...bound].every(name => markOmbSessionDeleted(name, env));
|
|
462
|
+
return tombstoned ? 0 : code || 1;
|
|
280
463
|
}
|
|
281
464
|
/** Hide an unhosted journal record that has no tmux session left to kill. */
|
|
282
465
|
export function forgetOmbSession(session, env = process.env) {
|
|
283
|
-
if (!
|
|
466
|
+
if (!isOmbSessionName(session))
|
|
284
467
|
return 2;
|
|
285
468
|
return markOmbSessionDeleted(session, env) ? 0 : 1;
|
|
286
469
|
}
|
|
@@ -290,6 +473,7 @@ export function attachOmbSession(session, cwd = process.cwd(), env = process.env
|
|
|
290
473
|
const args = env.TMUX || env.TMUX_PANE
|
|
291
474
|
? ["switch-client", "-t", session]
|
|
292
475
|
: ["attach-session", "-t", session];
|
|
476
|
+
quiesceStdinForHandoff();
|
|
293
477
|
const child = spawn(tmux, args, { cwd, env: { ...env, BIZAR_OMP_NO_TMUX: "1" }, stdio: "inherit" });
|
|
294
478
|
return new Promise((resolve, reject) => {
|
|
295
479
|
// A missing multiplexer degrades to a nonzero exit like a failed attach;
|
|
@@ -332,6 +516,7 @@ export async function launchOmb(cwd, args, env = process.env, sessionId) {
|
|
|
332
516
|
throw error;
|
|
333
517
|
}
|
|
334
518
|
}
|
|
519
|
+
quiesceStdinForHandoff();
|
|
335
520
|
const child = spawn(omp, args, { cwd, env, stdio: "inherit" });
|
|
336
521
|
const closed = Promise.withResolvers();
|
|
337
522
|
child.once("error", closed.reject);
|