@openrig/cli 0.3.4 → 0.4.1
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/daemon/assets/plugins/openrig-core/hooks/claude.json +11 -0
- package/daemon/assets/plugins/openrig-core/hooks/codex.json +14 -3
- package/daemon/assets/plugins/openrig-core/hooks/scripts/activity-relay.cjs +34 -0
- package/daemon/assets/plugins/openrig-core/hooks/scripts/compaction-restore-bridge.cjs +28 -24
- package/daemon/assets/plugins/openrig-core/skills/claude-compaction-restore/scripts/precompact-hook.mjs +72 -11
- package/daemon/assets/plugins/openrig-core/skills/queue-handoff/SKILL.md +62 -29
- package/daemon/dist/adapters/codex-resume.d.ts +2 -1
- package/daemon/dist/adapters/codex-resume.d.ts.map +1 -1
- package/daemon/dist/adapters/codex-resume.js +18 -6
- package/daemon/dist/adapters/codex-resume.js.map +1 -1
- package/daemon/dist/adapters/codex-runtime-adapter.d.ts +37 -1
- package/daemon/dist/adapters/codex-runtime-adapter.d.ts.map +1 -1
- package/daemon/dist/adapters/codex-runtime-adapter.js +212 -6
- package/daemon/dist/adapters/codex-runtime-adapter.js.map +1 -1
- package/daemon/dist/adapters/tmux.d.ts +28 -14
- package/daemon/dist/adapters/tmux.d.ts.map +1 -1
- package/daemon/dist/adapters/tmux.js +65 -30
- package/daemon/dist/adapters/tmux.js.map +1 -1
- package/daemon/dist/db/migrations/043_resume_provenance.d.ts +3 -0
- package/daemon/dist/db/migrations/043_resume_provenance.d.ts.map +1 -0
- package/daemon/dist/db/migrations/043_resume_provenance.js +5 -0
- package/daemon/dist/db/migrations/043_resume_provenance.js.map +1 -0
- package/daemon/dist/db/migrations/044_queue_item_summary.d.ts +21 -0
- package/daemon/dist/db/migrations/044_queue_item_summary.d.ts.map +1 -0
- package/daemon/dist/db/migrations/044_queue_item_summary.js +25 -0
- package/daemon/dist/db/migrations/044_queue_item_summary.js.map +1 -0
- package/daemon/dist/domain/agent-activity-store.d.ts +11 -1
- package/daemon/dist/domain/agent-activity-store.d.ts.map +1 -1
- package/daemon/dist/domain/agent-activity-store.js +22 -2
- package/daemon/dist/domain/agent-activity-store.js.map +1 -1
- package/daemon/dist/domain/claude-compaction-enforcer.d.ts.map +1 -1
- package/daemon/dist/domain/claude-compaction-enforcer.js +69 -1
- package/daemon/dist/domain/claude-compaction-enforcer.js.map +1 -1
- package/daemon/dist/domain/daemon-version.d.ts +15 -0
- package/daemon/dist/domain/daemon-version.d.ts.map +1 -0
- package/daemon/dist/domain/daemon-version.js +28 -0
- package/daemon/dist/domain/daemon-version.js.map +1 -0
- package/daemon/dist/domain/native-resume-probe.d.ts +2 -1
- package/daemon/dist/domain/native-resume-probe.d.ts.map +1 -1
- package/daemon/dist/domain/native-resume-probe.js +21 -2
- package/daemon/dist/domain/native-resume-probe.js.map +1 -1
- package/daemon/dist/domain/node-cmux-service.d.ts +1 -0
- package/daemon/dist/domain/node-cmux-service.d.ts.map +1 -1
- package/daemon/dist/domain/node-cmux-service.js +29 -2
- package/daemon/dist/domain/node-cmux-service.js.map +1 -1
- package/daemon/dist/domain/node-inventory.d.ts.map +1 -1
- package/daemon/dist/domain/node-inventory.js +70 -46
- package/daemon/dist/domain/node-inventory.js.map +1 -1
- package/daemon/dist/domain/node-launcher.d.ts +7 -7
- package/daemon/dist/domain/node-launcher.d.ts.map +1 -1
- package/daemon/dist/domain/node-launcher.js +0 -22
- package/daemon/dist/domain/node-launcher.js.map +1 -1
- package/daemon/dist/domain/profile-resolver.d.ts +4 -5
- package/daemon/dist/domain/profile-resolver.d.ts.map +1 -1
- package/daemon/dist/domain/profile-resolver.js.map +1 -1
- package/daemon/dist/domain/queue-repository.d.ts +22 -0
- package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
- package/daemon/dist/domain/queue-repository.js +39 -2
- package/daemon/dist/domain/queue-repository.js.map +1 -1
- package/daemon/dist/domain/restore-check-service.d.ts +14 -0
- package/daemon/dist/domain/restore-check-service.d.ts.map +1 -1
- package/daemon/dist/domain/restore-check-service.js +78 -8
- package/daemon/dist/domain/restore-check-service.js.map +1 -1
- package/daemon/dist/domain/restore-orchestrator.d.ts.map +1 -1
- package/daemon/dist/domain/restore-orchestrator.js +3 -3
- package/daemon/dist/domain/restore-orchestrator.js.map +1 -1
- package/daemon/dist/domain/resume-metadata-refresher.js +1 -1
- package/daemon/dist/domain/resume-metadata-refresher.js.map +1 -1
- package/daemon/dist/domain/resume-token-validation.d.ts +17 -0
- package/daemon/dist/domain/resume-token-validation.d.ts.map +1 -0
- package/daemon/dist/domain/resume-token-validation.js +46 -0
- package/daemon/dist/domain/resume-token-validation.js.map +1 -0
- package/daemon/dist/domain/rigspec-instantiator.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-instantiator.js +3 -6
- package/daemon/dist/domain/rigspec-instantiator.js.map +1 -1
- package/daemon/dist/domain/runtime-adapter.d.ts.map +1 -1
- package/daemon/dist/domain/runtime-adapter.js +1 -0
- package/daemon/dist/domain/runtime-adapter.js.map +1 -1
- package/daemon/dist/domain/scope/dot-id.d.ts +43 -0
- package/daemon/dist/domain/scope/dot-id.d.ts.map +1 -0
- package/daemon/dist/domain/scope/dot-id.js +165 -0
- package/daemon/dist/domain/scope/dot-id.js.map +1 -0
- package/daemon/dist/domain/scope/scope-audit.d.ts +38 -0
- package/daemon/dist/domain/scope/scope-audit.d.ts.map +1 -0
- package/daemon/dist/domain/scope/scope-audit.js +191 -0
- package/daemon/dist/domain/scope/scope-audit.js.map +1 -0
- package/daemon/dist/domain/scope/types.d.ts +67 -0
- package/daemon/dist/domain/scope/types.d.ts.map +1 -0
- package/daemon/dist/domain/scope/types.js +46 -0
- package/daemon/dist/domain/scope/types.js.map +1 -0
- package/daemon/dist/domain/seat-attention-reconciler.d.ts +12 -0
- package/daemon/dist/domain/seat-attention-reconciler.d.ts.map +1 -1
- package/daemon/dist/domain/seat-attention-reconciler.js +116 -88
- package/daemon/dist/domain/seat-attention-reconciler.js.map +1 -1
- package/daemon/dist/domain/session-registry.d.ts +15 -1
- package/daemon/dist/domain/session-registry.d.ts.map +1 -1
- package/daemon/dist/domain/session-registry.js +46 -4
- package/daemon/dist/domain/session-registry.js.map +1 -1
- package/daemon/dist/domain/session-transport.d.ts +10 -0
- package/daemon/dist/domain/session-transport.d.ts.map +1 -1
- package/daemon/dist/domain/session-transport.js +153 -19
- package/daemon/dist/domain/session-transport.js.map +1 -1
- package/daemon/dist/domain/skill-audit.d.ts +47 -0
- package/daemon/dist/domain/skill-audit.d.ts.map +1 -0
- package/daemon/dist/domain/skill-audit.js +218 -0
- package/daemon/dist/domain/skill-audit.js.map +1 -0
- package/daemon/dist/domain/skill-discovery.d.ts +15 -0
- package/daemon/dist/domain/skill-discovery.d.ts.map +1 -1
- package/daemon/dist/domain/skill-discovery.js +70 -0
- package/daemon/dist/domain/skill-discovery.js.map +1 -1
- package/daemon/dist/domain/skill-mirror-drift.d.ts +10 -0
- package/daemon/dist/domain/skill-mirror-drift.d.ts.map +1 -0
- package/daemon/dist/domain/skill-mirror-drift.js +24 -0
- package/daemon/dist/domain/skill-mirror-drift.js.map +1 -0
- package/daemon/dist/domain/slices/slice-detail-projector.d.ts.map +1 -1
- package/daemon/dist/domain/slices/slice-detail-projector.js +10 -3
- package/daemon/dist/domain/slices/slice-detail-projector.js.map +1 -1
- package/daemon/dist/domain/startup-orchestrator.js +1 -1
- package/daemon/dist/domain/startup-orchestrator.js.map +1 -1
- package/daemon/dist/domain/types.d.ts +48 -14
- package/daemon/dist/domain/types.d.ts.map +1 -1
- package/daemon/dist/domain/types.js.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.d.ts +1 -1
- package/daemon/dist/domain/user-settings/settings-store.d.ts.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.js +4 -0
- package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
- package/daemon/dist/domain/whoami-service.d.ts +1 -0
- package/daemon/dist/domain/whoami-service.d.ts.map +1 -1
- package/daemon/dist/domain/whoami-service.js +8 -4
- package/daemon/dist/domain/whoami-service.js.map +1 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.d.ts +1 -0
- package/daemon/dist/domain/workspace/default-workspace-scaffold.d.ts.map +1 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.js +25 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.js.map +1 -1
- package/daemon/dist/index.d.ts.map +1 -1
- package/daemon/dist/index.js +25 -3
- package/daemon/dist/index.js.map +1 -1
- package/daemon/dist/routes/activity.d.ts.map +1 -1
- package/daemon/dist/routes/activity.js +29 -0
- package/daemon/dist/routes/activity.js.map +1 -1
- package/daemon/dist/routes/bundles.d.ts.map +1 -1
- package/daemon/dist/routes/bundles.js +1 -18
- package/daemon/dist/routes/bundles.js.map +1 -1
- package/daemon/dist/routes/health-summary.d.ts.map +1 -1
- package/daemon/dist/routes/health-summary.js +13 -2
- package/daemon/dist/routes/health-summary.js.map +1 -1
- package/daemon/dist/routes/queue.d.ts.map +1 -1
- package/daemon/dist/routes/queue.js +11 -0
- package/daemon/dist/routes/queue.js.map +1 -1
- package/daemon/dist/routes/restore-check.js +33 -2
- package/daemon/dist/routes/restore-check.js.map +1 -1
- package/daemon/dist/routes/scope-audit.d.ts +3 -0
- package/daemon/dist/routes/scope-audit.d.ts.map +1 -0
- package/daemon/dist/routes/scope-audit.js +175 -0
- package/daemon/dist/routes/scope-audit.js.map +1 -0
- package/daemon/dist/routes/sessions.d.ts.map +1 -1
- package/daemon/dist/routes/sessions.js +67 -3
- package/daemon/dist/routes/sessions.js.map +1 -1
- package/daemon/dist/routes/skills.d.ts.map +1 -1
- package/daemon/dist/routes/skills.js +43 -0
- package/daemon/dist/routes/skills.js.map +1 -1
- package/daemon/dist/routes/terminal-ws.d.ts +7 -0
- package/daemon/dist/routes/terminal-ws.d.ts.map +1 -0
- package/daemon/dist/routes/terminal-ws.js +132 -0
- package/daemon/dist/routes/terminal-ws.js.map +1 -0
- package/daemon/dist/routes/transport.d.ts +3 -1
- package/daemon/dist/routes/transport.d.ts.map +1 -1
- package/daemon/dist/routes/transport.js +26 -1
- package/daemon/dist/routes/transport.js.map +1 -1
- package/daemon/dist/routes/whoami.d.ts.map +1 -1
- package/daemon/dist/routes/whoami.js +5 -0
- package/daemon/dist/routes/whoami.js.map +1 -1
- package/daemon/dist/server.d.ts +6 -0
- package/daemon/dist/server.d.ts.map +1 -1
- package/daemon/dist/server.js +29 -2
- package/daemon/dist/server.js.map +1 -1
- package/daemon/dist/startup.d.ts +8 -0
- package/daemon/dist/startup.d.ts.map +1 -1
- package/daemon/dist/startup.js +41 -10
- package/daemon/dist/startup.js.map +1 -1
- package/daemon/dist/terminal/TerminalSessionBroker.d.ts +173 -0
- package/daemon/dist/terminal/TerminalSessionBroker.d.ts.map +1 -0
- package/daemon/dist/terminal/TerminalSessionBroker.js +570 -0
- package/daemon/dist/terminal/TerminalSessionBroker.js.map +1 -0
- package/daemon/specs/agents/shared/skills/core/openrig-user/SKILL.md +131 -2
- package/daemon/specs/agents/shared/skills/pods/orchestration-team/SKILL.md +29 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +23 -0
- package/dist/client.js.map +1 -1
- package/dist/commands/auth.d.ts +11 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +145 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +6 -6
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/config-init-workspace.d.ts.map +1 -1
- package/dist/commands/config-init-workspace.js +7 -4
- package/dist/commands/config-init-workspace.js.map +1 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +41 -5
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +27 -0
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +34 -0
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +361 -53
- package/dist/commands/ps.js.map +1 -1
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +83 -3
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/restore-check.d.ts.map +1 -1
- package/dist/commands/restore-check.js +80 -2
- package/dist/commands/restore-check.js.map +1 -1
- package/dist/commands/scope.d.ts.map +1 -1
- package/dist/commands/scope.js +747 -5
- package/dist/commands/scope.js.map +1 -1
- package/dist/commands/seat.d.ts +3 -1
- package/dist/commands/seat.d.ts.map +1 -1
- package/dist/commands/seat.js +61 -1
- package/dist/commands/seat.js.map +1 -1
- package/dist/commands/send.d.ts.map +1 -1
- package/dist/commands/send.js +46 -13
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/skill.d.ts +4 -0
- package/dist/commands/skill.d.ts.map +1 -0
- package/dist/commands/skill.js +77 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +27 -0
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +208 -20
- package/dist/commands/whoami.js.map +1 -1
- package/dist/cross-host-executor.d.ts +1 -1
- package/dist/cross-host-executor.d.ts.map +1 -1
- package/dist/cross-host-executor.js +3 -2
- package/dist/cross-host-executor.js.map +1 -1
- package/dist/cross-host-types.d.ts +2 -0
- package/dist/cross-host-types.d.ts.map +1 -0
- package/dist/cross-host-types.js +2 -0
- package/dist/cross-host-types.js.map +1 -0
- package/dist/host-registry.d.ts +27 -5
- package/dist/host-registry.d.ts.map +1 -1
- package/dist/host-registry.js +86 -19
- package/dist/host-registry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/codex-auth.d.ts +124 -0
- package/dist/lib/codex-auth.d.ts.map +1 -0
- package/dist/lib/codex-auth.js +418 -0
- package/dist/lib/codex-auth.js.map +1 -0
- package/dist/lib/scope/progress-edit.d.ts +42 -0
- package/dist/lib/scope/progress-edit.d.ts.map +1 -0
- package/dist/lib/scope/progress-edit.js +171 -0
- package/dist/lib/scope/progress-edit.js.map +1 -0
- package/dist/lib/scope/scope-audit.d.ts +38 -0
- package/dist/lib/scope/scope-audit.d.ts.map +1 -0
- package/dist/lib/scope/scope-audit.js +191 -0
- package/dist/lib/scope/scope-audit.js.map +1 -0
- package/dist/lib/scope/templates.d.ts +11 -0
- package/dist/lib/scope/templates.d.ts.map +1 -1
- package/dist/lib/scope/templates.js +22 -1
- package/dist/lib/scope/templates.js.map +1 -1
- package/dist/lib/scope/trust.d.ts +30 -0
- package/dist/lib/scope/trust.d.ts.map +1 -0
- package/dist/lib/scope/trust.js +80 -0
- package/dist/lib/scope/trust.js.map +1 -0
- package/dist/lib/scope/types.d.ts +6 -0
- package/dist/lib/scope/types.d.ts.map +1 -1
- package/dist/lib/scope/types.js +8 -0
- package/dist/lib/scope/types.js.map +1 -1
- package/dist/lib/scope-templates/backlog-deprecation.md +1 -0
- package/dist/lib/scope-templates/backlog-tech-debt.md +1 -0
- package/dist/lib/scope-templates/bug-fix.md +1 -0
- package/dist/lib/scope-templates/mission-brief.md +20 -0
- package/dist/lib/scope-templates/mission-placeholder.md +1 -0
- package/dist/lib/scope-templates/mission-progress.md +12 -0
- package/dist/lib/scope-templates/mission-release.md +1 -0
- package/dist/lib/scope-templates/placeholder.md +9 -0
- package/dist/lib/scope-templates/proof.md +17 -0
- package/dist/lib/scope-templates/release-feature.md +1 -0
- package/dist/lib/scope-templates/research.md +1 -0
- package/dist/lib/scope-templates/slice-progress.md +11 -0
- package/dist/remote-host-ops.d.ts +24 -0
- package/dist/remote-host-ops.d.ts.map +1 -0
- package/dist/remote-host-ops.js +53 -0
- package/dist/remote-host-ops.js.map +1 -0
- package/package.json +2 -1
- package/ui/dist/assets/index-CnqRX7A3.css +32 -0
- package/ui/dist/assets/index-CtL9jXFt.js +582 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-C_Q5FczU.css +0 -1
- package/ui/dist/assets/index-De6vv0yO.js +0 -552
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
// OPR.0.4.0.38 - real-terminal session broker.
|
|
5
|
+
//
|
|
6
|
+
// The product invariant from the founder is: NO LIVE TERMINAL LIES. A live
|
|
7
|
+
// terminal surface must show the true session state and report honestly when a
|
|
8
|
+
// session dies - never a silently stale "live" pane.
|
|
9
|
+
//
|
|
10
|
+
// Before this slice, every WebSocket connection opened its OWN tmux pipe-pane
|
|
11
|
+
// for the same session (the per-connection bug): a second viewer of one seat
|
|
12
|
+
// fought the first for output. The broker fixes that: ONE tmux pipe per
|
|
13
|
+
// session, MANY subscribers, output fanned out to all, each subscriber seeded
|
|
14
|
+
// with a cursor-safe snapshot of the current screen on attach, fixed geometry
|
|
15
|
+
// (no client-driven resize), honest session-death reporting to ALL subscribers,
|
|
16
|
+
// and full cleanup when the last subscriber leaves.
|
|
17
|
+
const PIPE_PANE_POLL_MS = 50;
|
|
18
|
+
const MAX_OUTPUT_BUFFER = 64 * 1024;
|
|
19
|
+
const DEFAULT_LIVENESS_MS = 2000;
|
|
20
|
+
/**
|
|
21
|
+
* Bounded size of the broker-owned recent-output history ring (AC-5 / FR-4).
|
|
22
|
+
* Mirrors the 64KB per-read tail sizing: a session-level window of recent
|
|
23
|
+
* output, replayed to late subscribers so they share the scrollback the
|
|
24
|
+
* earlier subscribers have - NOT per-xterm local. Bounded so a long-lived
|
|
25
|
+
* session never accumulates unbounded memory.
|
|
26
|
+
*/
|
|
27
|
+
const MAX_HISTORY_BYTES = 64 * 1024;
|
|
28
|
+
/**
|
|
29
|
+
* Canonical fixed terminal geometry (FR-7). 90 cols (OPR.0.4.0.39, founder-directed):
|
|
30
|
+
* Claude Code (Ink) + Codex CLI are RESPONSIVE TUIs that reflow to whatever width they
|
|
31
|
+
* are given - they have no required width; 80 is the legacy fallback that users find
|
|
32
|
+
* too narrow, so 90 sits comfortably above the 80 floor while being narrower than 120 so
|
|
33
|
+
* the scaled static/live mirror reads bigger (more legible) in the topology grid cells.
|
|
34
|
+
* 27 rows gives the classic-terminal 1.72:1 landscape shape (90x27 = ~650x378px, matching the canonical 80x24 aspect) (founder: classic terminal rectangle) while staying a workable agent-TUI height; subscribers fit/scroll/pan their
|
|
35
|
+
* viewport but never resize the pane - so multiple viewers cannot shrink the session to
|
|
36
|
+
* the smallest one. MUST stay in sync with the client mirror LIVE_TERMINAL_COLS
|
|
37
|
+
* (packages/ui/.../terminal/terminal-geometry.ts) - the xterm grid must match the pane.
|
|
38
|
+
*/
|
|
39
|
+
export const CANONICAL_COLS = 90;
|
|
40
|
+
export const CANONICAL_ROWS = 27;
|
|
41
|
+
/** ANSI 1-based absolute cursor move (terminal coords are 1-based; tmux is 0-based). */
|
|
42
|
+
export function cursorPositionEscape(x, y) {
|
|
43
|
+
return `\x1b[${y + 1};${x + 1}H`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build the cursor-safe seed escape sequence for a captured screen. Each row is
|
|
47
|
+
* painted with an ABSOLUTE cursor move so the client renders the screen at the
|
|
48
|
+
* exact same rows tmux has it - never a relative append that drifts as content
|
|
49
|
+
* scrolls. Normalizes CRLF, drops one trailing print newline, and (when a pane
|
|
50
|
+
* height is known and the capture is taller) keeps only the last `height` rows.
|
|
51
|
+
* Lifted from the FR-4 seed work.
|
|
52
|
+
*/
|
|
53
|
+
export function screenSnapshotEscape(snapshot, cursor) {
|
|
54
|
+
const normalized = snapshot.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
55
|
+
const withoutTrailingPrintNewline = normalized.endsWith("\n")
|
|
56
|
+
? normalized.slice(0, -1)
|
|
57
|
+
: normalized;
|
|
58
|
+
const rows = withoutTrailingPrintNewline.split("\n");
|
|
59
|
+
const visibleRows = cursor?.height && rows.length > cursor.height
|
|
60
|
+
? rows.slice(rows.length - cursor.height)
|
|
61
|
+
: rows;
|
|
62
|
+
const paintedRows = visibleRows
|
|
63
|
+
.map((row, index) => `\x1b[${index + 1};1H${row}`)
|
|
64
|
+
.join("");
|
|
65
|
+
return `\x1b[2J${paintedRows}${cursor ? cursorPositionEscape(cursor.x, cursor.y) : "\x1b[H"}`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Raw pipe output from a full-screen TUI is a repaint stream, not durable
|
|
69
|
+
* scrollback. Replaying cursor-addressed history into a fresh xterm before the
|
|
70
|
+
* current snapshot causes stale prompt/status rows to appear above or under the
|
|
71
|
+
* real screen. Preserve the AC-5 shared-history behavior for plain line output
|
|
72
|
+
* (and simple SGR color), but skip history that contains cursor movement,
|
|
73
|
+
* erase, alternate-screen, OSC, or carriage-return repaint semantics.
|
|
74
|
+
*/
|
|
75
|
+
export function isSafeHistoryReplay(data) {
|
|
76
|
+
if (!data)
|
|
77
|
+
return false;
|
|
78
|
+
if (/\r(?!\n)/.test(data))
|
|
79
|
+
return false;
|
|
80
|
+
const escapePattern = /\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1b\\)|[\(\)][ -~]|[@-Z\\-_])/g;
|
|
81
|
+
let match;
|
|
82
|
+
while ((match = escapePattern.exec(data)) !== null) {
|
|
83
|
+
const sequence = match[0];
|
|
84
|
+
const final = sequence.at(-1);
|
|
85
|
+
if (final !== "m")
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* One broker per live tmux session. Owns a single pipe-pane and fans its output
|
|
92
|
+
* out to every attached subscriber.
|
|
93
|
+
*/
|
|
94
|
+
export class TerminalSessionBroker {
|
|
95
|
+
sessionName;
|
|
96
|
+
tmux;
|
|
97
|
+
pollMs;
|
|
98
|
+
livenessMs;
|
|
99
|
+
cols;
|
|
100
|
+
rows;
|
|
101
|
+
maxHistoryBytes;
|
|
102
|
+
onEmpty;
|
|
103
|
+
subscribers = new Set();
|
|
104
|
+
// OPR.0.4.0.39: per-subscriber scroll-back offset (lines above the live bottom).
|
|
105
|
+
// 0/absent = live. A scrolled subscriber is PAINTED a tmux history window and is
|
|
106
|
+
// SKIPPED by the live fanout (so live output doesn't yank it back); read-only on
|
|
107
|
+
// the pane (capture-pane), so every viewer scrolls independently and nobody else's
|
|
108
|
+
// live view is disturbed (multi-subscriber-safe scrollback - vs pane-global copy-mode).
|
|
109
|
+
scrollOffsets = new Map();
|
|
110
|
+
// Broker-owned recent-output ring (AC-5): raw fanned-out bytes, bounded,
|
|
111
|
+
// replayed to late subscribers so their scrollback matches the earlier ones.
|
|
112
|
+
history = [];
|
|
113
|
+
historyBytes = 0;
|
|
114
|
+
outputPath = null;
|
|
115
|
+
pipeActive = false;
|
|
116
|
+
tailInterval = null;
|
|
117
|
+
livenessInterval = null;
|
|
118
|
+
lastSize = 0;
|
|
119
|
+
inputQueue = Promise.resolve();
|
|
120
|
+
// Singleflight the pipe-open as a shared promise so EVERY concurrent attach
|
|
121
|
+
// awaits the SAME open result before it seeds/adds (a bare boolean would let a
|
|
122
|
+
// later attach add itself before the open result is known, then never be
|
|
123
|
+
// closed if the open fails). Null until the first attach starts the open.
|
|
124
|
+
openPromise = null;
|
|
125
|
+
tailStarted = false;
|
|
126
|
+
torndown = false;
|
|
127
|
+
// The honest close reason a subscriber should get if it resumes (after an
|
|
128
|
+
// async open/seed) to find the broker already torn down. Set on every
|
|
129
|
+
// teardown path so a late/racing attach never goes silently live.
|
|
130
|
+
lastClose = null;
|
|
131
|
+
constructor(sessionName, tmux, opts = {}) {
|
|
132
|
+
this.sessionName = sessionName;
|
|
133
|
+
this.tmux = tmux;
|
|
134
|
+
this.pollMs = opts.pollMs ?? PIPE_PANE_POLL_MS;
|
|
135
|
+
this.livenessMs = opts.livenessMs ?? DEFAULT_LIVENESS_MS;
|
|
136
|
+
this.cols = opts.cols ?? CANONICAL_COLS;
|
|
137
|
+
this.rows = opts.rows ?? CANONICAL_ROWS;
|
|
138
|
+
this.maxHistoryBytes = opts.maxHistoryBytes ?? MAX_HISTORY_BYTES;
|
|
139
|
+
this.onEmpty = opts.onEmpty;
|
|
140
|
+
}
|
|
141
|
+
get subscriberCount() {
|
|
142
|
+
return this.subscribers.size;
|
|
143
|
+
}
|
|
144
|
+
/** Current size of the broker-owned history ring in bytes (bounded). */
|
|
145
|
+
get historyByteLength() {
|
|
146
|
+
return this.historyBytes;
|
|
147
|
+
}
|
|
148
|
+
/** The session-scoped pipe output file (one per session). Null until open. */
|
|
149
|
+
get pipeOutputPath() {
|
|
150
|
+
return this.outputPath;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Attach a subscriber. The FIRST subscriber stands up the single pipe-pane
|
|
154
|
+
* (fixed geometry, one outputPath, tail + liveness). Every subscriber - first
|
|
155
|
+
* or later - is seeded with the current screen BEFORE it joins the fanout, so
|
|
156
|
+
* it sees coherent state immediately and does not depend on a resize message.
|
|
157
|
+
*/
|
|
158
|
+
async attach(sub) {
|
|
159
|
+
if (this.torndown) {
|
|
160
|
+
this.closeTorndown(sub);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Start the single pipe-open exactly once; every concurrent attach awaits
|
|
164
|
+
// the SAME result before it seeds/adds.
|
|
165
|
+
if (!this.openPromise) {
|
|
166
|
+
this.openPromise = this.openPipe();
|
|
167
|
+
}
|
|
168
|
+
const open = await this.openPromise;
|
|
169
|
+
// The broker may have been torn down while we awaited - a co-waiter's open
|
|
170
|
+
// failed, or the session died. Close this subscriber HONESTLY; never leave a
|
|
171
|
+
// live-looking subscriber on a dead broker (the no-live-terminal-lies rule).
|
|
172
|
+
if (this.torndown) {
|
|
173
|
+
this.closeTorndown(sub);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (!open.ok) {
|
|
177
|
+
// Open failed: remember the reason and tear down ONCE, then close THIS
|
|
178
|
+
// subscriber. Every co-waiter takes a torndown branch and closes with the
|
|
179
|
+
// same remembered reason - none is left live.
|
|
180
|
+
this.lastClose = { code: open.code, reason: open.reason };
|
|
181
|
+
this.torndown = true;
|
|
182
|
+
this.teardownResources();
|
|
183
|
+
this.onEmpty?.(this.sessionName);
|
|
184
|
+
sub.close(open.code, open.reason);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// Open succeeded: seed this subscriber (ring replay + current screen) BEFORE
|
|
188
|
+
// it joins the fanout.
|
|
189
|
+
await this.seed(sub);
|
|
190
|
+
// RECHECK after the async seed: liveness/dispose may have torn the broker
|
|
191
|
+
// down while the capture was pending. Never add a subscriber to a dead
|
|
192
|
+
// broker - close it honestly with the remembered teardown reason.
|
|
193
|
+
if (this.torndown) {
|
|
194
|
+
this.closeTorndown(sub);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
this.subscribers.add(sub);
|
|
198
|
+
if (!this.tailStarted) {
|
|
199
|
+
this.tailStarted = true;
|
|
200
|
+
this.startTail();
|
|
201
|
+
this.startLiveness();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/** Close a subscriber that resumed after teardown, with the remembered reason. */
|
|
205
|
+
closeTorndown(sub) {
|
|
206
|
+
const c = this.lastClose ?? { code: 1011, reason: "terminal broker unavailable" };
|
|
207
|
+
try {
|
|
208
|
+
sub.close(c.code, c.reason);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// already-closed subscriber is fine
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** Forward client input to tmux, serialized so rapid input keeps order (FR-3). */
|
|
215
|
+
async input(msg) {
|
|
216
|
+
if (this.torndown)
|
|
217
|
+
return;
|
|
218
|
+
await this.enqueueInput(async () => {
|
|
219
|
+
if (msg.type === "keys") {
|
|
220
|
+
await this.tmux.sendKeys(this.sessionName, msg.keys);
|
|
221
|
+
}
|
|
222
|
+
else if (msg.type === "text") {
|
|
223
|
+
await this.tmux.sendText(this.sessionName, msg.text);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* OPR.0.4.0.39: per-subscriber scroll-back. `offset` = lines above the live
|
|
229
|
+
* bottom (0 = live). Reads tmux scrollback via capture-pane (READ-ONLY on the
|
|
230
|
+
* pane, so it never disturbs the live view of OTHER subscribers) and paints the
|
|
231
|
+
* windowed history to THIS subscriber. At offset 0 it repaints the current screen
|
|
232
|
+
* and the subscriber rejoins the live fanout.
|
|
233
|
+
*/
|
|
234
|
+
async scroll(sub, offset) {
|
|
235
|
+
if (this.torndown || !this.subscribers.has(sub))
|
|
236
|
+
return;
|
|
237
|
+
const clamped = Math.max(0, Math.floor(offset));
|
|
238
|
+
if (clamped === 0) {
|
|
239
|
+
this.scrollOffsets.delete(sub);
|
|
240
|
+
await this.repaintScreen(sub);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
this.scrollOffsets.set(sub, clamped);
|
|
244
|
+
// tmux `capture-pane -p -S -(offset+rows)` returns a buffer that ENDS at the live
|
|
245
|
+
// bottom (verified against real tmux: `-S -N` returns ~N history lines above the
|
|
246
|
+
// visible screen PLUS the screen, ending at the bottom row). To show a window
|
|
247
|
+
// `offset` lines ABOVE the live bottom, the slice must be BOTTOM-anchored: drop
|
|
248
|
+
// the last `offset` lines (toward live) and take the `rows` above them. Slicing
|
|
249
|
+
// the TOP `rows` would jump a whole extra screen up on the first wheel notch.
|
|
250
|
+
let content = null;
|
|
251
|
+
try {
|
|
252
|
+
content = await this.tmux.capturePaneContent(this.sessionName, clamped + this.rows);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
content = null;
|
|
256
|
+
}
|
|
257
|
+
if (content === null)
|
|
258
|
+
return;
|
|
259
|
+
const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
260
|
+
// Drop a single trailing empty line (capture-pane's trailing newline) so the last
|
|
261
|
+
// element is the true live-bottom row and the offset stays honest.
|
|
262
|
+
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
263
|
+
lines.pop();
|
|
264
|
+
const bottom = lines.length - clamped; // exclusive end: the window sits `offset` up
|
|
265
|
+
const window = bottom <= this.rows
|
|
266
|
+
? lines.slice(0, this.rows) // scrolled at/past the top of history: oldest screenful
|
|
267
|
+
: lines.slice(bottom - this.rows, bottom);
|
|
268
|
+
try {
|
|
269
|
+
sub.send(screenSnapshotEscape(window.join("\n"), null));
|
|
270
|
+
}
|
|
271
|
+
catch { /* dead subscriber */ }
|
|
272
|
+
}
|
|
273
|
+
/** Repaint the current visible screen to ONE subscriber (scroll-back to live). */
|
|
274
|
+
async repaintScreen(sub) {
|
|
275
|
+
let snapshot = null;
|
|
276
|
+
let cursor = null;
|
|
277
|
+
try {
|
|
278
|
+
snapshot = await this.tmux.capturePaneScreen(this.sessionName);
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
snapshot = null;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
cursor = await this.tmux.getPaneCursorPosition(this.sessionName);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
cursor = null;
|
|
288
|
+
}
|
|
289
|
+
if (snapshot !== null) {
|
|
290
|
+
try {
|
|
291
|
+
sub.send(screenSnapshotEscape(snapshot, cursor));
|
|
292
|
+
}
|
|
293
|
+
catch { /* dead */ }
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Detach a subscriber. The broker SURVIVES while other subscribers remain
|
|
298
|
+
* (FR-6); the LAST detach tears the pipe down and deletes the temp file.
|
|
299
|
+
*/
|
|
300
|
+
detach(sub) {
|
|
301
|
+
if (!this.subscribers.delete(sub))
|
|
302
|
+
return;
|
|
303
|
+
this.scrollOffsets.delete(sub);
|
|
304
|
+
if (this.subscribers.size === 0) {
|
|
305
|
+
void this.teardown();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/** Force teardown (used by the registry/route on shutdown and by tests). */
|
|
309
|
+
dispose() {
|
|
310
|
+
if (this.torndown)
|
|
311
|
+
return;
|
|
312
|
+
this.lastClose = { code: 1011, reason: "terminal broker unavailable" };
|
|
313
|
+
this.torndown = true;
|
|
314
|
+
this.subscribers.clear();
|
|
315
|
+
this.scrollOffsets.clear();
|
|
316
|
+
if (this.pipeActive) {
|
|
317
|
+
this.pipeActive = false;
|
|
318
|
+
void this.tmux.stopPipePane(this.sessionName).catch(() => { });
|
|
319
|
+
}
|
|
320
|
+
this.teardownResources();
|
|
321
|
+
this.onEmpty?.(this.sessionName);
|
|
322
|
+
}
|
|
323
|
+
async openPipe() {
|
|
324
|
+
// Close codes mirror the pre-broker route so the UI keeps its semantics:
|
|
325
|
+
// 1008 (policy) = the session genuinely does not exist; 1011 (server error)
|
|
326
|
+
// = the pipe/temp-file machinery failed. Both are honest; neither is a lie.
|
|
327
|
+
const alive = await this.tmux.hasSession(this.sessionName);
|
|
328
|
+
if (!alive)
|
|
329
|
+
return { ok: false, code: 1008, reason: `session not found: ${this.sessionName}` };
|
|
330
|
+
// FR-7 fixed geometry: window-size manual so tmux will NOT auto-shrink the
|
|
331
|
+
// window to the smallest attached client; then the canonical width/height
|
|
332
|
+
// ONCE. Deliberately NOT aggressive-resize, which does the opposite.
|
|
333
|
+
await this.tmux.setWindowOption(this.sessionName, "window-size", "manual").catch(() => { });
|
|
334
|
+
await this.tmux.resizeWindow(this.sessionName, this.cols, this.rows).catch(() => { });
|
|
335
|
+
const outputPath = path.join(os.tmpdir(), `openrig-term-${this.sessionName.replace(/[^a-zA-Z0-9@-]/g, "_")}-${Date.now()}.log`);
|
|
336
|
+
try {
|
|
337
|
+
fs.writeFileSync(outputPath, "", "utf-8");
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
return { ok: false, code: 1011, reason: `pipe output file failed: ${String(err)}` };
|
|
341
|
+
}
|
|
342
|
+
this.outputPath = outputPath;
|
|
343
|
+
const pipe = await this.tmux.startPipePane(this.sessionName, outputPath);
|
|
344
|
+
if (!pipe.ok) {
|
|
345
|
+
return { ok: false, code: 1011, reason: `pipe-pane failed: ${pipe.message}` };
|
|
346
|
+
}
|
|
347
|
+
this.pipeActive = true;
|
|
348
|
+
// Nudge a redraw so the freshly attached pipe captures current pane content
|
|
349
|
+
// (parity with the prior single-connection behavior; FR-9 no regression).
|
|
350
|
+
await this.tmux.sendKeys(this.sessionName, ["", ""]).catch(() => { });
|
|
351
|
+
return { ok: true };
|
|
352
|
+
}
|
|
353
|
+
async seed(sub) {
|
|
354
|
+
// AC-5: replay the broker-owned recent-output ring FIRST so the late
|
|
355
|
+
// subscriber's xterm builds the same scrollback the earlier subscribers
|
|
356
|
+
// have for plain terminal streams. Cursor-addressed TUI repaint history is
|
|
357
|
+
// not scrollback; replaying it corrupts late subscribers, so those sessions
|
|
358
|
+
// seed from the current visible-screen snapshot only.
|
|
359
|
+
if (this.historyBytes > 0) {
|
|
360
|
+
const history = this.history.join("");
|
|
361
|
+
if (isSafeHistoryReplay(history)) {
|
|
362
|
+
try {
|
|
363
|
+
sub.send(history);
|
|
364
|
+
}
|
|
365
|
+
catch { /* dead subscriber */ }
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// Best-effort: a failed capture (or an adapter without the seed methods)
|
|
369
|
+
// must never break the attach - the tail still streams live output.
|
|
370
|
+
let snapshot = null;
|
|
371
|
+
let cursor = null;
|
|
372
|
+
try {
|
|
373
|
+
snapshot = await this.tmux.capturePaneScreen(this.sessionName);
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
snapshot = null;
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
cursor = await this.tmux.getPaneCursorPosition(this.sessionName);
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
cursor = null;
|
|
383
|
+
}
|
|
384
|
+
if (snapshot != null) {
|
|
385
|
+
try {
|
|
386
|
+
sub.send(screenSnapshotEscape(snapshot, cursor));
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
// a dead subscriber is harmless here; the route handles its own close
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
startTail() {
|
|
394
|
+
if (this.tailInterval)
|
|
395
|
+
return;
|
|
396
|
+
this.tailInterval = setInterval(() => {
|
|
397
|
+
const p = this.outputPath;
|
|
398
|
+
if (!p)
|
|
399
|
+
return;
|
|
400
|
+
try {
|
|
401
|
+
const stat = fs.statSync(p);
|
|
402
|
+
if (stat.size > this.lastSize) {
|
|
403
|
+
const fd = fs.openSync(p, "r");
|
|
404
|
+
const buf = Buffer.alloc(Math.min(stat.size - this.lastSize, MAX_OUTPUT_BUFFER));
|
|
405
|
+
fs.readSync(fd, buf, 0, buf.length, this.lastSize);
|
|
406
|
+
fs.closeSync(fd);
|
|
407
|
+
this.lastSize += buf.length;
|
|
408
|
+
this.fanout(buf.toString("utf-8"));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
// transient stat/read failures are tolerated; liveness owns death
|
|
413
|
+
}
|
|
414
|
+
}, this.pollMs);
|
|
415
|
+
}
|
|
416
|
+
fanout(data) {
|
|
417
|
+
// Feed the broker-owned ring from the SAME single tail that fans out, so
|
|
418
|
+
// late subscribers can replay the recent window before going live (AC-5).
|
|
419
|
+
this.appendHistory(data);
|
|
420
|
+
// One subscriber whose send throws must not break delivery to the others,
|
|
421
|
+
// and it should be detached cleanly (a throwing send means a dead socket).
|
|
422
|
+
let dead = null;
|
|
423
|
+
for (const sub of this.subscribers) {
|
|
424
|
+
// OPR.0.4.0.39: a subscriber scrolled back into history is viewing a static
|
|
425
|
+
// tmux capture window; skip the live fanout so output does not overwrite it.
|
|
426
|
+
// It rejoins live when it scrolls back to the bottom (offset 0).
|
|
427
|
+
if ((this.scrollOffsets.get(sub) ?? 0) > 0)
|
|
428
|
+
continue;
|
|
429
|
+
try {
|
|
430
|
+
sub.send(data);
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
(dead ??= []).push(sub);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// Detach AFTER the loop so we never mutate the set mid-iteration.
|
|
437
|
+
if (dead) {
|
|
438
|
+
for (const sub of dead)
|
|
439
|
+
this.detach(sub);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/** Append to the bounded ring, dropping oldest chunks past the byte cap. */
|
|
443
|
+
appendHistory(data) {
|
|
444
|
+
if (!data)
|
|
445
|
+
return;
|
|
446
|
+
this.history.push(data);
|
|
447
|
+
this.historyBytes += Buffer.byteLength(data, "utf-8");
|
|
448
|
+
// Keep at least the most recent chunk so a single large burst is never
|
|
449
|
+
// fully discarded; otherwise drop oldest until within the cap.
|
|
450
|
+
while (this.historyBytes > this.maxHistoryBytes && this.history.length > 1) {
|
|
451
|
+
const dropped = this.history.shift();
|
|
452
|
+
this.historyBytes -= Buffer.byteLength(dropped, "utf-8");
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
startLiveness() {
|
|
456
|
+
if (this.livenessInterval)
|
|
457
|
+
return;
|
|
458
|
+
this.livenessInterval = setInterval(() => {
|
|
459
|
+
this.tmux
|
|
460
|
+
.hasSession(this.sessionName)
|
|
461
|
+
.then((alive) => {
|
|
462
|
+
if (!alive)
|
|
463
|
+
this.handleSessionDeath();
|
|
464
|
+
})
|
|
465
|
+
.catch(() => {
|
|
466
|
+
this.handleSessionDeath();
|
|
467
|
+
});
|
|
468
|
+
}, this.livenessMs);
|
|
469
|
+
}
|
|
470
|
+
/** FR-5: a dead session closes ALL subscribers honestly - never silent stale-live. */
|
|
471
|
+
handleSessionDeath() {
|
|
472
|
+
if (this.torndown)
|
|
473
|
+
return;
|
|
474
|
+
this.lastClose = { code: 1001, reason: "tmux session terminated" };
|
|
475
|
+
this.torndown = true;
|
|
476
|
+
const subs = [...this.subscribers];
|
|
477
|
+
this.subscribers.clear();
|
|
478
|
+
if (this.pipeActive) {
|
|
479
|
+
this.pipeActive = false;
|
|
480
|
+
void this.tmux.stopPipePane(this.sessionName).catch(() => { });
|
|
481
|
+
}
|
|
482
|
+
this.teardownResources();
|
|
483
|
+
for (const sub of subs) {
|
|
484
|
+
try {
|
|
485
|
+
sub.close(1001, "tmux session terminated");
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
// already-closed subscriber is fine
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
this.onEmpty?.(this.sessionName);
|
|
492
|
+
}
|
|
493
|
+
async teardown() {
|
|
494
|
+
if (this.torndown)
|
|
495
|
+
return;
|
|
496
|
+
this.lastClose = { code: 1011, reason: "terminal broker unavailable" };
|
|
497
|
+
this.torndown = true;
|
|
498
|
+
if (this.pipeActive) {
|
|
499
|
+
this.pipeActive = false;
|
|
500
|
+
await this.tmux.stopPipePane(this.sessionName).catch(() => { });
|
|
501
|
+
}
|
|
502
|
+
this.teardownResources();
|
|
503
|
+
this.onEmpty?.(this.sessionName);
|
|
504
|
+
}
|
|
505
|
+
teardownResources() {
|
|
506
|
+
if (this.tailInterval) {
|
|
507
|
+
clearInterval(this.tailInterval);
|
|
508
|
+
this.tailInterval = null;
|
|
509
|
+
}
|
|
510
|
+
if (this.livenessInterval) {
|
|
511
|
+
clearInterval(this.livenessInterval);
|
|
512
|
+
this.livenessInterval = null;
|
|
513
|
+
}
|
|
514
|
+
if (this.outputPath) {
|
|
515
|
+
try {
|
|
516
|
+
fs.unlinkSync(this.outputPath);
|
|
517
|
+
}
|
|
518
|
+
catch {
|
|
519
|
+
// temp file may already be gone
|
|
520
|
+
}
|
|
521
|
+
this.outputPath = null;
|
|
522
|
+
}
|
|
523
|
+
this.lastSize = 0;
|
|
524
|
+
// Clear the history ring so a torn-down broker leaks no retained output.
|
|
525
|
+
this.history = [];
|
|
526
|
+
this.historyBytes = 0;
|
|
527
|
+
}
|
|
528
|
+
enqueueInput(op) {
|
|
529
|
+
const run = this.inputQueue.then(op, op);
|
|
530
|
+
this.inputQueue = run.catch(() => { });
|
|
531
|
+
return run;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Daemon-owned registry of brokers keyed by canonical session name. Create the
|
|
536
|
+
* broker on the first subscriber for a session; reuse it for later subscribers
|
|
537
|
+
* (so only one pipe-pane exists per session); evict it when it empties.
|
|
538
|
+
*/
|
|
539
|
+
export class TerminalBrokerRegistry {
|
|
540
|
+
tmux;
|
|
541
|
+
opts;
|
|
542
|
+
brokers = new Map();
|
|
543
|
+
constructor(tmux, opts = {}) {
|
|
544
|
+
this.tmux = tmux;
|
|
545
|
+
this.opts = opts;
|
|
546
|
+
}
|
|
547
|
+
get size() {
|
|
548
|
+
return this.brokers.size;
|
|
549
|
+
}
|
|
550
|
+
get(sessionName) {
|
|
551
|
+
return this.brokers.get(sessionName);
|
|
552
|
+
}
|
|
553
|
+
/** Get-or-create the broker for a session, attach the subscriber, return the broker. */
|
|
554
|
+
async attach(sessionName, sub) {
|
|
555
|
+
let broker = this.brokers.get(sessionName);
|
|
556
|
+
if (!broker) {
|
|
557
|
+
broker = new TerminalSessionBroker(sessionName, this.tmux, {
|
|
558
|
+
...this.opts,
|
|
559
|
+
onEmpty: (name) => {
|
|
560
|
+
this.brokers.delete(name);
|
|
561
|
+
this.opts.onEmpty?.(name);
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
this.brokers.set(sessionName, broker);
|
|
565
|
+
}
|
|
566
|
+
await broker.attach(sub);
|
|
567
|
+
return broker;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
//# sourceMappingURL=TerminalSessionBroker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TerminalSessionBroker.js","sourceRoot":"","sources":["../../src/terminal/TerminalSessionBroker.ts"],"names":[],"mappings":"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"}
|