tmux-ide 2.9.2 → 2.9.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 +1 -1
- package/bin/cli.js +2071 -625
- package/package.json +14 -5
- package/packages/contracts/src/desktop-host.ts +3 -0
- package/packages/contracts/src/saved-machines.ts +19 -0
- package/packages/contracts/src/terminal-replica.ts +1 -1
- package/packages/core/package.json +3 -2
- package/packages/core/src/terminal-delivery.ts +51 -3
- package/packages/core/src/terminal-fnv64-wasm.ts +4 -3
- package/packages/core/src/terminal-replica-hash-cache.ts +129 -31
- package/packages/core/src/terminal-replica-owned-row.ts +27 -0
- package/packages/core/src/terminal-replica.ts +13 -28
- package/packages/daemon/dist/command-center/daemon-fleet-facts-observer.js +96 -10
- package/packages/daemon/dist/command-center/resources/saved-machine-route.js +40 -1
- package/packages/daemon/dist/command-center/ws-events.js +5 -24
- package/packages/daemon/dist/lib/canonical-daemon-bootstrap.js +8 -0
- package/packages/daemon/dist/lib/canonical-daemon.js +28 -2
- package/packages/daemon/dist/lib/daemon-embed.js +1 -1
- package/packages/daemon/dist/lib/daemon-service-manager.js +122 -0
- package/packages/daemon/dist/lib/daemon-service-plan.js +127 -0
- package/packages/daemon/dist/lib/daemon-service.js +259 -0
- package/packages/daemon/dist/lib/install-origin.js +2 -0
- package/packages/daemon/dist/lib/local-fleet-request.js +9 -3
- package/packages/daemon/dist/lib/log.js +6 -10
- package/packages/daemon/dist/lib/managed-installer-update.js +67 -0
- package/packages/daemon/dist/lib/{tui-download-lock.js → private-operation-lock.js} +11 -11
- package/packages/daemon/dist/lib/remote-daemon-info.js +3 -2
- package/packages/daemon/dist/lib/remote-daemon-lifecycle.js +4 -4
- package/packages/daemon/dist/lib/remote-tmux-command.js +10 -0
- package/packages/daemon/dist/lib/runtime-namespace.js +28 -16
- package/packages/daemon/dist/lib/saved-machines.js +40 -2
- package/packages/daemon/dist/lib/ssh-daemon-transport.js +24 -9
- package/packages/daemon/dist/lib/tmux-server-owner.js +1 -1
- package/packages/daemon/dist/lib/tmux-server-registration.js +21 -3
- package/packages/daemon/dist/lib/tui-binary.js +2 -2
- package/packages/daemon/dist/lib/update.js +18 -0
- package/packages/daemon/dist/lib/workspace-multiplexer-verbs.js +24 -1
- package/packages/daemon/dist/machines.js +40 -4
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +2 -2
- package/packages/daemon/dist/terminal/attachments/direct-websocket.js +4 -1
- package/packages/daemon/dist/terminal/attachments/native-runtime.js +22 -13
- package/packages/daemon/dist/terminal/mirror/mirror-service.js +101 -3
- package/packages/daemon/dist/terminal/mirror/registered-window-guard.js +62 -0
- package/packages/daemon/dist/terminal/mirror/session-channel.js +75 -4
- package/packages/daemon/dist/terminal/mirror/shared-window-index.js +45 -0
- package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +11 -1
- package/packages/daemon/dist/terminal/session-runtime/multiplexer-backend.js +1 -0
- package/packages/daemon/dist/terminal/session-runtime/registry.js +56 -7
- package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-executor.js +3 -1
- package/packages/daemon/dist/tui/mirror/dialog-model.js +2 -35
- package/packages/daemon/dist/tui/mirror/runtime/application-daemon-authority-owner.js +8 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +20 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-agents.js +7 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-authority.js +59 -9
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +27 -19
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +23 -18
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-startup.js +31 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-palette-search-owner.js +7 -3
- package/packages/daemon/dist/tui/mirror/runtime/application-performance-log.js +3 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-input-ingress.js +146 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +201 -50
- package/packages/daemon/dist/tui/mirror/runtime/bounded-performance-record-writer.js +50 -10
- package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +6 -5
- package/packages/daemon/dist/tui/mirror/runtime/terminal-pane-input-router.js +3 -0
- package/packages/daemon/dist/tui/mirror/runtime/workspace-terminal-fast-lane.js +8 -2
- package/packages/daemon/dist/tui/mirror/terminal-text.js +35 -0
- package/packages/daemon/dist/tui/mirror/workspace/connection-feedback.js +5 -3
- package/packages/daemon/src/command-center/daemon-fleet-facts-observer.ts +100 -10
- package/packages/daemon/src/command-center/resources/saved-machine-route.ts +48 -1
- package/packages/daemon/src/command-center/ws-events.ts +5 -25
- package/packages/daemon/src/lib/canonical-daemon-bootstrap.ts +12 -0
- package/packages/daemon/src/lib/canonical-daemon.ts +44 -2
- package/packages/daemon/src/lib/daemon-embed.ts +1 -0
- package/packages/daemon/src/lib/daemon-service-manager.ts +152 -0
- package/packages/daemon/src/lib/daemon-service-plan.ts +153 -0
- package/packages/daemon/src/lib/daemon-service.ts +301 -0
- package/packages/daemon/src/lib/install-origin.ts +5 -0
- package/packages/daemon/src/lib/local-fleet-request.ts +19 -3
- package/packages/daemon/src/lib/log.ts +13 -9
- package/packages/daemon/src/lib/managed-installer-update.ts +79 -0
- package/packages/daemon/src/lib/{tui-download-lock.ts → private-operation-lock.ts} +15 -11
- package/packages/daemon/src/lib/remote-daemon-info.ts +3 -3
- package/packages/daemon/src/lib/remote-daemon-lifecycle.ts +4 -4
- package/packages/daemon/src/lib/remote-tmux-command.ts +12 -0
- package/packages/daemon/src/lib/runtime-namespace.ts +27 -18
- package/packages/daemon/src/lib/saved-machines.ts +40 -1
- package/packages/daemon/src/lib/ssh-daemon-transport.ts +25 -7
- package/packages/daemon/src/lib/tmux-server-owner.ts +1 -0
- package/packages/daemon/src/lib/tmux-server-registration.ts +21 -3
- package/packages/daemon/src/lib/tui-binary.ts +2 -2
- package/packages/daemon/src/lib/update.ts +24 -0
- package/packages/daemon/src/lib/workspace-multiplexer-verbs.ts +22 -0
- package/packages/daemon/src/machines.ts +53 -5
- package/packages/daemon/src/terminal/attachments/direct-websocket.ts +8 -2
- package/packages/daemon/src/terminal/attachments/native-runtime.ts +28 -13
- package/packages/daemon/src/terminal/mirror/mirror-service.ts +105 -3
- package/packages/daemon/src/terminal/mirror/registered-window-guard.ts +66 -0
- package/packages/daemon/src/terminal/mirror/session-channel.ts +92 -4
- package/packages/daemon/src/terminal/mirror/shared-window-index.ts +42 -0
- package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +12 -1
- package/packages/daemon/src/terminal/session-runtime/multiplexer-backend.ts +2 -0
- package/packages/daemon/src/terminal/session-runtime/registry.ts +70 -8
- package/packages/daemon/src/terminal/session-runtime/semantic-mutation-executor.ts +3 -1
- package/packages/daemon/src/tui/mirror/dialog-model.ts +2 -32
- package/packages/daemon/src/tui/mirror/runtime/application-daemon-authority-owner.ts +8 -0
- package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +19 -2
- package/packages/daemon/src/tui/mirror/runtime/application-machine-agents.ts +6 -1
- package/packages/daemon/src/tui/mirror/runtime/application-machine-authority.ts +60 -11
- package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +39 -33
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +31 -23
- package/packages/daemon/src/tui/mirror/runtime/application-machine-startup.ts +38 -2
- package/packages/daemon/src/tui/mirror/runtime/application-palette-search-owner.ts +7 -4
- package/packages/daemon/src/tui/mirror/runtime/application-performance-log.ts +3 -0
- package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +1 -1
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +47 -46
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-input-ingress.ts +165 -2
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +207 -53
- package/packages/daemon/src/tui/mirror/runtime/bounded-performance-record-writer.ts +49 -9
- package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +6 -5
- package/packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.ts +4 -0
- package/packages/daemon/src/tui/mirror/runtime/workspace-terminal-fast-lane.ts +8 -3
- package/packages/daemon/src/tui/mirror/terminal-text.ts +32 -0
- package/packages/daemon/src/tui/mirror/workspace/connection-feedback.ts +6 -3
- package/packages/daemon-client/src/fleet-connection-status.ts +10 -1
- package/packages/daemon-client/src/workspace-client.ts +4 -1
- package/packages/contracts/src/__tests__/actions-contract.test.ts +0 -119
- package/packages/contracts/src/__tests__/agent-graph-overlay.test.ts +0 -428
- package/packages/contracts/src/__tests__/app-window-mutation.test.ts +0 -51
- package/packages/contracts/src/__tests__/app-window-state.test.ts +0 -34
- package/packages/contracts/src/__tests__/application-shell.test.ts +0 -645
- package/packages/contracts/src/__tests__/automation-operations.test.ts +0 -78
- package/packages/contracts/src/__tests__/causal-cell.test.ts +0 -47
- package/packages/contracts/src/__tests__/client-view-state.test.ts +0 -115
- package/packages/contracts/src/__tests__/cohesion-fixture.test.ts +0 -106
- package/packages/contracts/src/__tests__/commands.test.ts +0 -86
- package/packages/contracts/src/__tests__/control.test.ts +0 -154
- package/packages/contracts/src/__tests__/daemon-events.test.ts +0 -405
- package/packages/contracts/src/__tests__/daemon-resources.test.ts +0 -143
- package/packages/contracts/src/__tests__/daemon-wire.test.ts +0 -149
- package/packages/contracts/src/__tests__/desktop-host.test.ts +0 -269
- package/packages/contracts/src/__tests__/desktop-missions.test.ts +0 -215
- package/packages/contracts/src/__tests__/experience-conformance.test.ts +0 -153
- package/packages/contracts/src/__tests__/experience-kernel-boundary.test.ts +0 -113
- package/packages/contracts/src/__tests__/experience-shell.test.ts +0 -65
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v2.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v3.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v4.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/scoped-receipt.ts +0 -62
- package/packages/contracts/src/__tests__/fleet-agent-graph.test.ts +0 -231
- package/packages/contracts/src/__tests__/fleet-catalog.test.ts +0 -270
- package/packages/contracts/src/__tests__/fleet-lifecycle.test.ts +0 -62
- package/packages/contracts/src/__tests__/focus-overlay.test.ts +0 -110
- package/packages/contracts/src/__tests__/ide-config.test.ts +0 -64
- package/packages/contracts/src/__tests__/interaction-evidence.test.ts +0 -288
- package/packages/contracts/src/__tests__/interaction-receipt-v2.test.ts +0 -72
- package/packages/contracts/src/__tests__/issue-error.test.ts +0 -78
- package/packages/contracts/src/__tests__/mission-projections.test.ts +0 -186
- package/packages/contracts/src/__tests__/missions.test.ts +0 -272
- package/packages/contracts/src/__tests__/multiplexer-verbs.test.ts +0 -242
- package/packages/contracts/src/__tests__/pane-appearance.test.ts +0 -146
- package/packages/contracts/src/__tests__/pane-read-result.test.ts +0 -55
- package/packages/contracts/src/__tests__/pane-stream.test.ts +0 -536
- package/packages/contracts/src/__tests__/pane-widget-marker.test.ts +0 -327
- package/packages/contracts/src/__tests__/performance-metrics.test.ts +0 -73
- package/packages/contracts/src/__tests__/performance-qualification.test.ts +0 -137
- package/packages/contracts/src/__tests__/rich-card-widget.test.ts +0 -29
- package/packages/contracts/src/__tests__/session-runtime.test.ts +0 -512
- package/packages/contracts/src/__tests__/startup-readiness.test.ts +0 -409
- package/packages/contracts/src/__tests__/terminal-attachment-stream.test.ts +0 -72
- package/packages/contracts/src/__tests__/terminal-attachments.test.ts +0 -329
- package/packages/contracts/src/__tests__/terminal-delivery.test.ts +0 -88
- package/packages/contracts/src/__tests__/terminal-runtime-inventory.test.ts +0 -68
- package/packages/contracts/src/__tests__/terminals.test.ts +0 -87
- package/packages/contracts/src/__tests__/tmux-server-scope.test.ts +0 -166
- package/packages/contracts/src/__tests__/transport-contract-loading.test.ts +0 -91
- package/packages/contracts/src/__tests__/visual-tokens.test.ts +0 -224
- package/packages/contracts/src/__tests__/window-links.test.ts +0 -243
- package/packages/contracts/src/__tests__/workspace-changes-resource.test.ts +0 -317
- package/packages/contracts/src/__tests__/workspace-changes-view.test.ts +0 -122
- package/packages/contracts/src/__tests__/workspace-config.test.ts +0 -294
- package/packages/contracts/src/__tests__/workspace-files-resource.test.ts +0 -261
- package/packages/contracts/src/__tests__/workspace-files-tree.test.ts +0 -147
- package/packages/contracts/src/__tests__/workspace-pane-creation.test.ts +0 -249
- package/packages/contracts/src/__tests__/workspace-promotion.test.ts +0 -94
- package/packages/contracts/src/__tests__/workspace-resource-identity.test.ts +0 -90
- package/packages/contracts/src/daemon-resource-request.test.ts +0 -192
- package/packages/contracts/src/native-operation-identity.test.ts +0 -23
- package/packages/contracts/src/workspace-admission.test.ts +0 -21
- package/packages/contracts/src/workspace-catalog-resource.test.ts +0 -146
- package/packages/core/src/agent-provisioning.test.ts +0 -42
- package/packages/core/src/application-shell-session.test.ts +0 -105
- package/packages/core/src/interaction-evidence.test.ts +0 -134
- package/packages/core/src/interaction-flow.test.ts +0 -66
- package/packages/core/src/interaction-receipts.test.ts +0 -671
- package/packages/core/src/navigator.test.ts +0 -34
- package/packages/core/src/optimistic-projection-conformance.test.ts +0 -75
- package/packages/core/src/optimistic-projection.test.ts +0 -118
- package/packages/core/src/performance-metrics.test.ts +0 -121
- package/packages/core/src/performance-qualification.test.ts +0 -283
- package/packages/core/src/saved-machines.test.ts +0 -80
- package/packages/core/src/terminal-compact-cold.test.ts +0 -87
- package/packages/core/src/terminal-conformance.test.ts +0 -48
- package/packages/core/src/terminal-delivery-cache-lifetime.test.ts +0 -167
- package/packages/core/src/terminal-delivery-encode-cooperative.test.ts +0 -140
- package/packages/core/src/terminal-delivery-representation-hash.test.ts +0 -77
- package/packages/core/src/terminal-delivery.test.ts +0 -1471
- package/packages/core/src/terminal-fnv64-wasm.test.ts +0 -61
- package/packages/core/src/terminal-replica-cold.test.ts +0 -31
- package/packages/core/src/terminal-replica-cooperative.test.ts +0 -170
- package/packages/core/src/terminal-replica-hash-cache.test.ts +0 -283
- package/packages/core/src/terminal-replica.test.ts +0 -347
- package/packages/core/src/workspace-selection.test.ts +0 -63
- package/packages/daemon/dist/__tests__/support.js +0 -33
- package/packages/daemon/dist/lib/__tests__/fixtures/workspace-state-writer.js +0 -58
- package/packages/daemon/dist/lib/__tests__/installed-recovery-fixture.js +0 -400
- package/packages/daemon/dist/terminal/__tests__/MockPtyAdapter.js +0 -182
- package/packages/daemon/dist/terminal/mirror/__tests__/scripted-channel.js +0 -98
- package/packages/daemon/dist/terminal/mirror/__tests__/simulated-channel.js +0 -156
- package/packages/daemon/src/tui/mirror/__snapshots__/activity-surface-renderer.test.tsx.snap +0 -42
- package/packages/daemon/src/tui/mirror/__snapshots__/changes-terminal-surface-renderer.test.tsx.snap +0 -267
- package/packages/daemon/src/tui/mirror/__snapshots__/home-files-surface-renderer.test.tsx.snap +0 -267
- package/packages/daemon/src/tui/mirror/__snapshots__/missions-surface-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/__snapshots__/recipes-gallery-renderer.test.tsx.snap +0 -141
- package/packages/daemon/src/tui/mirror/__snapshots__/shell-chrome-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/runtime/__snapshots__/application-shell-view-renderer.test.tsx.snap +0 -87
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/agent-terminal-canvas-renderer.test.tsx.snap +0 -348
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/application-shell-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/command-palette-surface-renderer.test.tsx.snap +0 -255
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/pane-frame-renderer.test.tsx.snap +0 -525
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/terminal-pane-header-polish-renderer.test.tsx.snap +0 -31
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/workbench-shell-renderer.test.tsx.snap +0 -134
- package/packages/daemon-client/src/application-shell-session.test.ts +0 -196
- package/packages/daemon-client/src/automation-client.test.ts +0 -214
- package/packages/daemon-client/src/bootstrap-coordinator.test.ts +0 -145
- package/packages/daemon-client/src/connection-supervisor.test.ts +0 -109
- package/packages/daemon-client/src/direct-application-shell-transport.test.ts +0 -941
- package/packages/daemon-client/src/first-latest-coordinator.test.ts +0 -175
- package/packages/daemon-client/src/fleet-dial-scheduler.test.ts +0 -114
- package/packages/daemon-client/src/fleet-environments.test.ts +0 -40
- package/packages/daemon-client/src/generation-bound-store-timers.test.ts +0 -74
- package/packages/daemon-client/src/owner-action-client.test.ts +0 -245
- package/packages/daemon-client/src/pane-stream-client.test.ts +0 -2841
- package/packages/daemon-client/src/pane-stream-clock-calibration.test.ts +0 -207
- package/packages/daemon-client/src/pane-stream-loopback-benchmark.test.ts +0 -51
- package/packages/daemon-client/src/push-resource-session.test.ts +0 -509
- package/packages/daemon-client/src/resource-replica.test.ts +0 -186
- package/packages/daemon-client/src/runtime-resource-ledger.test.ts +0 -18
- package/packages/daemon-client/src/scoped-tmux-server-transport.test.ts +0 -114
- package/packages/daemon-client/src/terminal-fast-lane.test.ts +0 -835
- package/packages/daemon-client/src/tmux-server-client.test.ts +0 -279
- package/packages/daemon-client/src/tmux-server-interaction-events.test.ts +0 -230
- package/packages/daemon-client/src/workspace-catalog-v2.test.ts +0 -109
- package/packages/daemon-client/src/workspace-client-boundaries.test.ts +0 -59
- package/packages/daemon-client/src/workspace-client-operations.test.ts +0 -96
- package/packages/daemon-client/src/workspace-client.test.ts +0 -2912
- package/packages/daemon-client/src/workspace-event-supervisor.test.ts +0 -1086
- package/packages/daemon-client/src/workspace-pane-client.test.ts +0 -80
- package/packages/presentation/src/presentation-contract.test.ts +0 -63
- package/packages/tmux-bridge/src/runner.test.ts +0 -614
- package/packages/tmux-bridge/src/targeting.test.ts +0 -107
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runtimeOwnedPath } from "./runtime-namespace.js";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { closeSync, fstatSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { closeSync, fstatSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, watch, } from "node:fs";
|
|
4
|
+
import { basename, dirname, join } from "node:path";
|
|
5
5
|
import { SavedMachineRegistrySchema } from "@tmux-ide/contracts";
|
|
6
6
|
import { changeSavedMachines } from "@tmux-ide/core";
|
|
7
7
|
import { resolveRuntimeNamespace } from "./runtime-namespace.js";
|
|
@@ -29,6 +29,44 @@ export function loadSavedMachines(path = savedMachinesPath()) {
|
|
|
29
29
|
closeSync(fd);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
/** Watch the directory so atomic registry replacement does not detach the watcher. */
|
|
33
|
+
export function watchSavedMachines(onChange, onError, path = savedMachinesPath()) {
|
|
34
|
+
let stopped = false;
|
|
35
|
+
let pending = null;
|
|
36
|
+
const refresh = () => {
|
|
37
|
+
pending = null;
|
|
38
|
+
if (stopped)
|
|
39
|
+
return;
|
|
40
|
+
try {
|
|
41
|
+
onChange(loadSavedMachines(path));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Keep the last valid routes while an editor is midway through a write.
|
|
45
|
+
onError();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
// A first-run app can subscribe before the daemon creates its registry.
|
|
49
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
50
|
+
const watcher = watch(dirname(path), (_event, file) => {
|
|
51
|
+
if (stopped || (file !== null && file.toString() !== basename(path)))
|
|
52
|
+
return;
|
|
53
|
+
if (pending)
|
|
54
|
+
clearTimeout(pending);
|
|
55
|
+
pending = setTimeout(refresh, 50);
|
|
56
|
+
pending.unref();
|
|
57
|
+
});
|
|
58
|
+
watcher.unref();
|
|
59
|
+
watcher.on("error", onError);
|
|
60
|
+
// Close the read-before-subscribe race at startup.
|
|
61
|
+
pending = setTimeout(refresh, 0);
|
|
62
|
+
pending.unref();
|
|
63
|
+
return () => {
|
|
64
|
+
stopped = true;
|
|
65
|
+
if (pending)
|
|
66
|
+
clearTimeout(pending);
|
|
67
|
+
watcher.close();
|
|
68
|
+
};
|
|
69
|
+
}
|
|
32
70
|
/** The daemon owns writes. Synchronous read/reduce/rename cannot interleave within it. */
|
|
33
71
|
export function updateSavedMachines(change, path = savedMachinesPath()) {
|
|
34
72
|
const registry = changeSavedMachines(loadSavedMachines(path), change);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { createServer } from "node:net";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { remoteTmuxIdeCommand } from "./remote-tmux-command.js";
|
|
4
5
|
import { createSshDaemonRelay } from "./ssh-daemon-relay.js";
|
|
5
6
|
import { CanonicalDaemonInfoSchema, DAEMON_WIRE_PROTOCOL_VERSION, DaemonIdentitySchema, SavedMachineSchema, DesktopDaemonCapabilitiesResultSchemaZ, } from "@tmux-ide/contracts";
|
|
6
7
|
/** The remote CLI emits this over SSH stdout; it must never be logged or persisted. */
|
|
@@ -24,12 +25,29 @@ export class SshConnectionError extends Error {
|
|
|
24
25
|
this.code = code;
|
|
25
26
|
}
|
|
26
27
|
get retryable() {
|
|
27
|
-
|
|
28
|
+
// A supervisor may temporarily remove the record while replacing its daemon.
|
|
29
|
+
return this.code === "unavailable" || this.code === "daemon-missing";
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
function failure(message, code = "unavailable") {
|
|
31
33
|
return new SshConnectionError(message, code);
|
|
32
34
|
}
|
|
35
|
+
/** Classify known OpenSSH errors without publishing remote stderr or credentials. */
|
|
36
|
+
function observeSshFailure(child) {
|
|
37
|
+
let tail = "";
|
|
38
|
+
let code = "unavailable";
|
|
39
|
+
child.stderr?.on("data", (chunk) => {
|
|
40
|
+
tail = (tail + chunk.toString("utf8")).slice(-4096);
|
|
41
|
+
if (/Host key verification failed|REMOTE HOST IDENTIFICATION HAS CHANGED/i.test(tail))
|
|
42
|
+
code = "ssh-host-key";
|
|
43
|
+
else if (/Permission denied \((?:publickey|password|keyboard-interactive)[^)]*\)/i.test(tail))
|
|
44
|
+
code = "ssh-authentication";
|
|
45
|
+
});
|
|
46
|
+
child.once("close", () => {
|
|
47
|
+
tail = "";
|
|
48
|
+
});
|
|
49
|
+
return () => code;
|
|
50
|
+
}
|
|
33
51
|
const stoppedChildren = new WeakSet();
|
|
34
52
|
function stop(child) {
|
|
35
53
|
if (stoppedChildren.has(child) || child.exitCode !== null || child.signalCode !== null)
|
|
@@ -119,6 +137,7 @@ const defaults = {
|
|
|
119
137
|
probe: probeSshDaemonIdentity,
|
|
120
138
|
};
|
|
121
139
|
function discover(child, signal) {
|
|
140
|
+
const sshFailure = observeSshFailure(child);
|
|
122
141
|
return new Promise((resolve, reject) => {
|
|
123
142
|
const chunks = [];
|
|
124
143
|
let bytes = 0;
|
|
@@ -147,14 +166,12 @@ function discover(child, signal) {
|
|
|
147
166
|
}
|
|
148
167
|
chunks.push(Buffer.from(chunk));
|
|
149
168
|
});
|
|
150
|
-
// Drain without capturing: SSH banners/errors may contain confidential remote information.
|
|
151
|
-
child.stderr?.resume();
|
|
152
169
|
child.once("error", () => finish(failure("could not start OpenSSH")));
|
|
153
170
|
child.once("close", (code) => {
|
|
154
171
|
if (settled)
|
|
155
172
|
return;
|
|
156
173
|
if (code !== 0) {
|
|
157
|
-
finish(failure("discovery failed; check SSH authentication, host trust, and remote tmux-ide installation"));
|
|
174
|
+
finish(failure("discovery failed; check SSH authentication, host trust, and remote tmux-ide installation", code === 127 ? "remote-cli-missing" : sshFailure()));
|
|
158
175
|
return;
|
|
159
176
|
}
|
|
160
177
|
try {
|
|
@@ -234,9 +251,7 @@ export async function openSshDaemonTransport(options, dependencies = defaults) {
|
|
|
234
251
|
"ForkAfterAuthentication=no",
|
|
235
252
|
"--",
|
|
236
253
|
options.alias,
|
|
237
|
-
"
|
|
238
|
-
"remote-daemon-info",
|
|
239
|
-
"--json",
|
|
254
|
+
remoteTmuxIdeCommand("discover"),
|
|
240
255
|
]);
|
|
241
256
|
const daemon = await discover(child, signal);
|
|
242
257
|
const port = await cancellable(dependencies.allocatePort(), signal);
|
|
@@ -276,7 +291,7 @@ export async function openSshDaemonTransport(options, dependencies = defaults) {
|
|
|
276
291
|
options.alias,
|
|
277
292
|
]);
|
|
278
293
|
child.stdout?.resume();
|
|
279
|
-
child
|
|
294
|
+
const sshFailure = observeSshFailure(child);
|
|
280
295
|
child.once("exit", dispose);
|
|
281
296
|
const tunnelClosed = new Promise((resolve) => {
|
|
282
297
|
child.once("close", () => {
|
|
@@ -312,7 +327,7 @@ export async function openSshDaemonTransport(options, dependencies = defaults) {
|
|
|
312
327
|
}
|
|
313
328
|
await delay(signal);
|
|
314
329
|
}
|
|
315
|
-
throw failure("tunnel could not authenticate the expected daemon before cancellation or timeout");
|
|
330
|
+
throw failure("tunnel could not authenticate the expected daemon before cancellation or timeout", sshFailure());
|
|
316
331
|
}
|
|
317
332
|
catch (error) {
|
|
318
333
|
dispose();
|
|
@@ -198,7 +198,7 @@ export async function createNativeTmuxServerOwner(options) {
|
|
|
198
198
|
if (intent.verb === "workspace.window.link.select" ||
|
|
199
199
|
intent.verb === "workspace.window.link.unlink" ||
|
|
200
200
|
intent.verb === "workspace.pane.select") {
|
|
201
|
-
return multiplexer.mutateWindowLink({ operationId, expectedDaemonInstanceId: generation, intent }, (session, action) => sessionRuntimeRegistry.executeWindowLinkAction(session, action));
|
|
201
|
+
return multiplexer.mutateWindowLink({ operationId, expectedDaemonInstanceId: generation, intent }, (session, action) => sessionRuntimeRegistry.executeWindowLinkAction(session, action), timing);
|
|
202
202
|
}
|
|
203
203
|
if (intent.verb === "workspace.pane.resize") {
|
|
204
204
|
return multiplexer.mutateResize({ operationId, expectedDaemonInstanceId: generation, intent }, (session) => {
|
|
@@ -53,12 +53,30 @@ export function createTmuxServerProbe(executable) {
|
|
|
53
53
|
const args = selector.kind === "path" ? ["-S", selector.path] : ["-L", selector.name];
|
|
54
54
|
const read = async (selection) => (await exec(executablePath, [...selection, "-N", "display-message", "-p", "#{socket_path}|#{pid}|#{start_time}"], { env, encoding: "utf8", timeout: 500, maxBuffer: 8192 })).stdout.trimEnd();
|
|
55
55
|
try {
|
|
56
|
-
|
|
56
|
+
// A direct socket can be pinned before contacting tmux. Named servers
|
|
57
|
+
// and aliases still need discovery followed by an explicit-path proof.
|
|
58
|
+
let pinned = null;
|
|
59
|
+
if (selector.kind === "path") {
|
|
60
|
+
try {
|
|
61
|
+
pinned = captureUnixSocketIdentity(selector.path);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Preserve discovery for aliases which are not direct socket paths.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const first = await read(pinned ? ["-S", pinned.path] : args);
|
|
57
68
|
const match = /^(.*)\|([1-9][0-9]*)\|([1-9][0-9]*)$/u.exec(first);
|
|
58
69
|
if (!match)
|
|
59
70
|
return null;
|
|
60
|
-
const
|
|
61
|
-
|
|
71
|
+
const reportedSocket = captureUnixSocketIdentity(match[1]);
|
|
72
|
+
const socket = pinned ?? reportedSocket;
|
|
73
|
+
if (pinned) {
|
|
74
|
+
if (reportedSocket.path !== pinned.path ||
|
|
75
|
+
reportedSocket.dev !== pinned.dev ||
|
|
76
|
+
reportedSocket.ino !== pinned.ino)
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
else if ((await read(["-S", socket.path])) !== first)
|
|
62
80
|
return null;
|
|
63
81
|
revalidateUnixSocketIdentity(socket);
|
|
64
82
|
return {
|
|
@@ -17,7 +17,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
17
17
|
import { accessSync, chmodSync, constants as fsConstants, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
18
18
|
import { dirname, join } from "node:path";
|
|
19
19
|
import { gunzipSync } from "node:zlib";
|
|
20
|
-
import {
|
|
20
|
+
import { acquirePrivateOperationLock } from "./private-operation-lock.js";
|
|
21
21
|
import { getCurrentVersion } from "./update-check.js";
|
|
22
22
|
/** The GitHub repo the release assets live under. */
|
|
23
23
|
export const RELEASE_REPO = "wavyrai/tmux-ide";
|
|
@@ -274,7 +274,7 @@ export async function downloadTuiBinary(opts = {}) {
|
|
|
274
274
|
timeoutMs: opts.timeoutMs ?? TUI_DOWNLOAD_TIMEOUT_MS,
|
|
275
275
|
};
|
|
276
276
|
mkdirSync(dirname(dest), { recursive: true });
|
|
277
|
-
const releaseLock = await
|
|
277
|
+
const releaseLock = await acquirePrivateOperationLock(downloadLockPath(dest), limits.timeoutMs * 2 + 5_000);
|
|
278
278
|
const suffix = `${process.pid}.${randomUUID()}.tmp`;
|
|
279
279
|
const binaryTmp = `${dest}.${suffix}`;
|
|
280
280
|
const checksumTmp = `${checksumPath(dest)}.${suffix}`;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { managedInstallerPrefix, runManagedInstallerUpdate, MANAGED_INSTALLER_URL, } from "./managed-installer-update.js";
|
|
1
2
|
import { resolveRuntimeNamespace } from "./runtime-namespace.js";
|
|
2
3
|
import { parseStrictSemver } from "./semver.js";
|
|
3
4
|
/** Origin-aware update planning; unsupported origins never fall back to npm. */
|
|
@@ -35,7 +36,20 @@ export function planUpdate(input) {
|
|
|
35
36
|
reason: `global ${method} layout (${input.cliPath})`,
|
|
36
37
|
};
|
|
37
38
|
}
|
|
39
|
+
if (method === "installer") {
|
|
40
|
+
const prefix = managedInstallerPrefix(input.cliPath);
|
|
41
|
+
if (prefix) {
|
|
42
|
+
return {
|
|
43
|
+
method,
|
|
44
|
+
channel,
|
|
45
|
+
installerPrefix: prefix,
|
|
46
|
+
command: `installer ${MANAGED_INSTALLER_URL} --prefix ${JSON.stringify(prefix)} --version ${channel}`,
|
|
47
|
+
reason: `verified active managed installation (${prefix})`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
38
51
|
const guidance = {
|
|
52
|
+
installer: "Rerun https://tmux-ide.com/install.sh with the same --prefix to update atomically and retain rollback. This managed release must not be modified by npm in place.",
|
|
39
53
|
dev: "Update this checkout with git pull, then follow its build instructions.",
|
|
40
54
|
homebrew: "Update with brew upgrade tmux-ide (using the tap/formula you installed).",
|
|
41
55
|
yarn: `Update this Yarn global installation with yarn global add tmux-ide@${channel}.`,
|
|
@@ -123,6 +137,10 @@ export function runUpdate({ cliDir, dryRun, json = false }, dependencies = {}) {
|
|
|
123
137
|
});
|
|
124
138
|
executed = true;
|
|
125
139
|
}
|
|
140
|
+
if (!dryRun && plan.installerPrefix && plan.channel) {
|
|
141
|
+
runManagedInstallerUpdate(plan.installerPrefix, plan.channel, json, dependencies.execute);
|
|
142
|
+
executed = true;
|
|
143
|
+
}
|
|
126
144
|
if (json)
|
|
127
145
|
output(JSON.stringify({ ...plan, current, latest, dryRun, executed }));
|
|
128
146
|
return plan;
|
|
@@ -286,7 +286,7 @@ export class WorkspaceMultiplexerAuthority {
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
/** Runs only inside the existing semantic mutation lane; never rediscovers a stale link. */
|
|
289
|
-
async mutateWindowLink(raw, execute) {
|
|
289
|
+
async mutateWindowLink(raw, execute, timing) {
|
|
290
290
|
if (this.#disposed)
|
|
291
291
|
throw new WorkspaceMultiplexerError("workspace_unavailable");
|
|
292
292
|
const request = WorkspaceMultiplexerMutationRequestSchemaZ.parse(raw);
|
|
@@ -297,7 +297,28 @@ export class WorkspaceMultiplexerAuthority {
|
|
|
297
297
|
intent.verb !== "workspace.window.link.unlink" &&
|
|
298
298
|
intent.verb !== "workspace.pane.select")
|
|
299
299
|
throw new TypeError("Expected a window-link selection or unlink intent");
|
|
300
|
+
const now = () => {
|
|
301
|
+
try {
|
|
302
|
+
return timing?.nowMicros() ?? null;
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const record = (operation, start) => {
|
|
309
|
+
const end = now();
|
|
310
|
+
if (start !== null && end !== null) {
|
|
311
|
+
try {
|
|
312
|
+
timing?.record(operation, start, end);
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
/* Diagnostics cannot own selection. */
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
const lookupStarted = now();
|
|
300
320
|
const workspace = this.#registry.get(intent.workspaceName);
|
|
321
|
+
record("semantic-workspace-lookup", lookupStarted);
|
|
301
322
|
if (!workspace)
|
|
302
323
|
throw new WorkspaceMultiplexerError("workspace_not_found");
|
|
303
324
|
const envelope = {
|
|
@@ -306,6 +327,7 @@ export class WorkspaceMultiplexerAuthority {
|
|
|
306
327
|
workspaceName: intent.workspaceName,
|
|
307
328
|
};
|
|
308
329
|
try {
|
|
330
|
+
const effectStarted = now();
|
|
309
331
|
const result = await execute(workspace.sessionName, intent.verb === "workspace.pane.select"
|
|
310
332
|
? {
|
|
311
333
|
action: "select",
|
|
@@ -316,6 +338,7 @@ export class WorkspaceMultiplexerAuthority {
|
|
|
316
338
|
action: intent.verb === "workspace.window.link.unlink" ? "unlink" : "select",
|
|
317
339
|
target: intent.target,
|
|
318
340
|
});
|
|
341
|
+
record("window-link-effect-proof", effectStarted);
|
|
319
342
|
if (result.outcome !== "applied") {
|
|
320
343
|
throw new WorkspaceMultiplexerError(result.outcome === "stale"
|
|
321
344
|
? "window_link_stale"
|
|
@@ -1,17 +1,53 @@
|
|
|
1
1
|
import { readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { SavedMachineRegistrySchema, SavedMachineSchema } from "@tmux-ide/contracts/saved-machines";
|
|
3
|
+
import { SavedMachineRegistrySchema, SavedMachineSchema, SavedMachineMutationSchema, } from "@tmux-ide/contracts/saved-machines";
|
|
4
4
|
import { loadSavedMachines, planSavedMachineMerge } from "./lib/saved-machines.js";
|
|
5
5
|
import { loadFleetClientState } from "./lib/fleet-client-state.js";
|
|
6
|
-
import {
|
|
6
|
+
import { changeSavedMachines } from "@tmux-ide/core";
|
|
7
|
+
import { saveMachineProfiles, mutateMachineProfile } from "./lib/local-fleet-request.js";
|
|
8
|
+
import { remoteTmuxIdeCommand } from "./lib/remote-tmux-command.js";
|
|
7
9
|
export async function machines(command, argument, options) {
|
|
8
10
|
const current = loadSavedMachines();
|
|
11
|
+
if ((command === "enable" || command === "disable" || command === "remove" || command === "edit") &&
|
|
12
|
+
argument) {
|
|
13
|
+
const key = argument.normalize("NFKC").toLowerCase();
|
|
14
|
+
const match = current.machines.find((machine) => machine.id === key) ??
|
|
15
|
+
current.machines.find((machine) => machine.label.normalize("NFKC").toLowerCase() === key);
|
|
16
|
+
if (!match)
|
|
17
|
+
throw new Error("Saved machine not found; use machines ls to select its ID or label.");
|
|
18
|
+
const change = SavedMachineMutationSchema.parse(command === "edit"
|
|
19
|
+
? {
|
|
20
|
+
id: match.id,
|
|
21
|
+
operation: command,
|
|
22
|
+
patch: {
|
|
23
|
+
...(options.label !== undefined ? { label: options.label } : {}),
|
|
24
|
+
...(options.sshTarget !== undefined ? { sshTarget: options.sshTarget } : {}),
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
: { id: match.id, operation: command });
|
|
28
|
+
const preview = changeSavedMachines(current, command === "remove"
|
|
29
|
+
? { type: "remove", id: match.id }
|
|
30
|
+
: {
|
|
31
|
+
type: "update",
|
|
32
|
+
id: match.id,
|
|
33
|
+
patch: change.operation === "edit" ? change.patch : { enabled: command === "enable" },
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
written: options.write === true,
|
|
37
|
+
registry: options.write ? await mutateMachineProfile(change) : preview,
|
|
38
|
+
...(!options.write
|
|
39
|
+
? {
|
|
40
|
+
note: "Repeat with --write to change this saved profile. Remote sessions and daemons are preserved.",
|
|
41
|
+
}
|
|
42
|
+
: {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
9
45
|
if (command === "start" && argument) {
|
|
10
46
|
SavedMachineSchema.shape.sshTarget.parse(argument);
|
|
11
47
|
if (!options.write)
|
|
12
48
|
return {
|
|
13
49
|
written: false,
|
|
14
|
-
command: ["ssh", "--", argument, "
|
|
50
|
+
command: ["ssh", "--", argument, remoteTmuxIdeCommand("start")],
|
|
15
51
|
note: "Repeat with --write to start the installed daemon. No package installation occurs.",
|
|
16
52
|
};
|
|
17
53
|
await (await import("./lib/remote-daemon-lifecycle.js")).startInstalledRemoteDaemon(argument);
|
|
@@ -69,7 +105,7 @@ export async function machines(command, argument, options) {
|
|
|
69
105
|
};
|
|
70
106
|
}
|
|
71
107
|
else
|
|
72
|
-
throw new Error("Usage: tmux-ide machines ls|export|import <file>|add <alias> [--write] [--json]");
|
|
108
|
+
throw new Error("Usage: tmux-ide machines ls|export|import <file>|add <alias>|edit|enable|disable|remove <id-or-label> [--write] [--json]");
|
|
73
109
|
const preview = planSavedMachineMerge(current, incoming);
|
|
74
110
|
if (!options.write)
|
|
75
111
|
return {
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
<key>CFBundlePackageType</key>
|
|
22
22
|
<string>APPL</string>
|
|
23
23
|
<key>CFBundleShortVersionString</key>
|
|
24
|
-
<string>2.9.
|
|
24
|
+
<string>2.9.3</string>
|
|
25
25
|
<key>CFBundleVersion</key>
|
|
26
|
-
<string>2.9.
|
|
26
|
+
<string>2.9.3</string>
|
|
27
27
|
<key>LSMinimumSystemVersion</key>
|
|
28
28
|
<string>11.0</string>
|
|
29
29
|
<key>LSUIElement</key>
|
|
Binary file
|
|
Binary file
|
package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
</data>
|
|
11
11
|
<key>Resources/Assets.car</key>
|
|
12
12
|
<data>
|
|
13
|
-
|
|
13
|
+
Z5KHrNHvuP0dz4KeTalLvP3ooGE=
|
|
14
14
|
</data>
|
|
15
15
|
</dict>
|
|
16
16
|
<key>files2</key>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<dict>
|
|
27
27
|
<key>hash2</key>
|
|
28
28
|
<data>
|
|
29
|
-
|
|
29
|
+
tjo0L6QuhP9jDVHACgzDgf4ZWE5eMPM/btcI9elmdEQ=
|
|
30
30
|
</data>
|
|
31
31
|
</dict>
|
|
32
32
|
</dict>
|
|
@@ -434,7 +434,10 @@ export class TerminalAttachmentAdmissionCoordinator {
|
|
|
434
434
|
let sessionRuntimeBinding = null;
|
|
435
435
|
let live;
|
|
436
436
|
try {
|
|
437
|
-
sessionRuntimeBinding = this.#bindSessionRuntime?.(activeDescriptor) ?? null;
|
|
437
|
+
sessionRuntimeBinding = (await this.#bindSessionRuntime?.(activeDescriptor)) ?? null;
|
|
438
|
+
if (!admission.isOpen()) {
|
|
439
|
+
throw new TerminalAttachmentAdmissionError("redemption-rejected", "Terminal attachment redemption was rejected.");
|
|
440
|
+
}
|
|
438
441
|
live = new TerminalAttachmentLiveConnection({
|
|
439
442
|
onRetire: (connection) => this.#trackRetiringRelease(connection),
|
|
440
443
|
socket,
|
|
@@ -821,6 +821,7 @@ export class WorkspaceTerminalInventoryRuntime {
|
|
|
821
821
|
#inventoryRead = null;
|
|
822
822
|
#applicationReads = new Map();
|
|
823
823
|
#lifecycle = "initializing";
|
|
824
|
+
#registeredWindows;
|
|
824
825
|
#disposed = false;
|
|
825
826
|
constructor(options) {
|
|
826
827
|
const authority = canonicalAuthority(options.tmuxAuthority);
|
|
@@ -831,6 +832,10 @@ export class WorkspaceTerminalInventoryRuntime {
|
|
|
831
832
|
});
|
|
832
833
|
this.readRunner = pinnedReadRunner(authority, executeRead);
|
|
833
834
|
this.#registry = options.registry;
|
|
835
|
+
this.#registeredWindows = options.sessionRuntimeRegistry?.setRegisteredWindowReader?.(async (signal) => {
|
|
836
|
+
const snapshot = await discoverWorkspaceRegistryTerminalInventory(this.#registry, this.readRunner, signal);
|
|
837
|
+
return snapshot.panes.map((pane) => ({ session: pane.sessionName, window: pane.windowId }));
|
|
838
|
+
});
|
|
834
839
|
this.#observability = options.observability ?? DISABLED_SESSION_RUNTIME_OBSERVABILITY;
|
|
835
840
|
this.#resolveInteractionEndpoint = options.resolveInteractionEndpoint;
|
|
836
841
|
this.#nativeServerEpoch = options.nativeServerEpoch;
|
|
@@ -929,10 +934,12 @@ export class WorkspaceTerminalInventoryRuntime {
|
|
|
929
934
|
this.#observeWorkspaceSession = null;
|
|
930
935
|
}
|
|
931
936
|
this.#stopWorkspaceAddedObserver = options.registry.on("workspace.added", (workspace) => {
|
|
937
|
+
this.#registeredWindows?.invalidate();
|
|
932
938
|
this.invalidate();
|
|
933
939
|
this.#observeWorkspaceSession?.(workspace.name, workspace.sessionName);
|
|
934
940
|
});
|
|
935
941
|
this.#stopWorkspaceRemovedObserver = options.registry.on("workspace.removed", (name) => {
|
|
942
|
+
this.#registeredWindows?.invalidate();
|
|
936
943
|
this.invalidate();
|
|
937
944
|
if (!options.sessionRuntimeRegistry)
|
|
938
945
|
return;
|
|
@@ -1200,12 +1207,12 @@ export class WorkspaceTerminalInventoryRuntime {
|
|
|
1200
1207
|
let catalogIssue = inventory.catalog
|
|
1201
1208
|
.invalidRuntimeProof
|
|
1202
1209
|
? "invalid-runtime-proof"
|
|
1203
|
-
: inventory.catalog.
|
|
1204
|
-
? "
|
|
1205
|
-
: inventory.catalog.
|
|
1206
|
-
? "
|
|
1207
|
-
: inventory.catalog.
|
|
1208
|
-
? "duplicate-
|
|
1210
|
+
: inventory.catalog.duplicateRuntimePaneBinding
|
|
1211
|
+
? "duplicate-runtime-pane-binding"
|
|
1212
|
+
: inventory.catalog.missingSemanticStamp
|
|
1213
|
+
? "missing-semantic-stamp"
|
|
1214
|
+
: inventory.catalog.duplicateSemanticStamp
|
|
1215
|
+
? "duplicate-semantic-stamp"
|
|
1209
1216
|
: null;
|
|
1210
1217
|
if (shouldPrewarm &&
|
|
1211
1218
|
(trustedInventory || allowColdQualification) &&
|
|
@@ -1348,6 +1355,7 @@ export class WorkspaceTerminalInventoryRuntime {
|
|
|
1348
1355
|
return;
|
|
1349
1356
|
this.#disposed = true;
|
|
1350
1357
|
this.#lifecycle = "disposed";
|
|
1358
|
+
this.#registeredWindows?.dispose();
|
|
1351
1359
|
this.invalidate();
|
|
1352
1360
|
this.#stopWorkspaceAddedObserver?.();
|
|
1353
1361
|
this.#stopWorkspaceRemovedObserver?.();
|
|
@@ -1460,13 +1468,14 @@ export class NativeTerminalAttachmentRuntime {
|
|
|
1460
1468
|
resolveGeometry: (descriptor, client) => geometry.resolve(descriptor, client),
|
|
1461
1469
|
...(options.sessionRuntimeRegistry
|
|
1462
1470
|
? {
|
|
1463
|
-
bindSessionRuntime: (descriptor) => {
|
|
1471
|
+
bindSessionRuntime: async (descriptor) => {
|
|
1464
1472
|
const workspace = options.registry.get(descriptor.target.workspaceName);
|
|
1465
1473
|
if (!workspace)
|
|
1466
1474
|
throw new Error("Terminal attachment workspace is unavailable");
|
|
1467
1475
|
if (!descriptor.hostClientId) {
|
|
1468
1476
|
throw new Error("Interactive terminal attachment lacks trusted host identity");
|
|
1469
1477
|
}
|
|
1478
|
+
await options.sessionRuntimeRegistry.verifyWindowOwnership();
|
|
1470
1479
|
return new SessionRuntimeTransportBinder(options.sessionRuntimeRegistry).bind({
|
|
1471
1480
|
transport: "terminal-attachment",
|
|
1472
1481
|
transportLeaseId: descriptor.leaseId,
|
|
@@ -1656,12 +1665,12 @@ export class NativeTerminalAttachmentRuntime {
|
|
|
1656
1665
|
const catalogIssue = inventory.catalog
|
|
1657
1666
|
.invalidRuntimeProof
|
|
1658
1667
|
? "invalid-runtime-proof"
|
|
1659
|
-
: inventory.catalog.
|
|
1660
|
-
? "
|
|
1661
|
-
: inventory.catalog.
|
|
1662
|
-
? "
|
|
1663
|
-
: inventory.catalog.
|
|
1664
|
-
? "duplicate-
|
|
1668
|
+
: inventory.catalog.duplicateRuntimePaneBinding
|
|
1669
|
+
? "duplicate-runtime-pane-binding"
|
|
1670
|
+
: inventory.catalog.missingSemanticStamp
|
|
1671
|
+
? "missing-semantic-stamp"
|
|
1672
|
+
: inventory.catalog.duplicateSemanticStamp
|
|
1673
|
+
? "duplicate-semantic-stamp"
|
|
1665
1674
|
: null;
|
|
1666
1675
|
// Ground-truth agent facts (authority + scrape fallback). All IO stays here;
|
|
1667
1676
|
// the resource projector composes them purely. Absent probe → no facts →
|