tmux-ide 2.9.12 → 2.9.13
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/bin/cli.js +3438 -2062
- package/package.json +1 -1
- package/packages/contracts/src/index.ts +2 -0
- package/packages/contracts/src/pane-stream.ts +17 -0
- package/packages/contracts/src/terminal-delivery.ts +2 -1
- package/packages/contracts/src/terminal-graphics-compressed.ts +66 -0
- package/packages/contracts/src/terminal-graphics.ts +5 -0
- package/packages/contracts/src/terminal-surfaces.ts +137 -0
- package/packages/core/src/terminal-delivery.ts +8 -2
- package/packages/core/src/terminal-replica.ts +33 -0
- package/packages/daemon/dist/command-center/resources/saved-machine-route.js +2 -0
- package/packages/daemon/dist/command-center/resources/saved-machine-watch.js +117 -0
- package/packages/daemon/dist/command-center/server.js +13 -0
- package/packages/daemon/dist/command-center/tmux-servers.js +9 -0
- package/packages/daemon/dist/lib/daemon-embed.js +32 -1
- package/packages/daemon/dist/lib/native-surface-context.js +96 -0
- package/packages/daemon/dist/lib/tmux-server-owner.js +9 -0
- 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/server/native-surface-provider-upgrade.js +88 -0
- package/packages/daemon/dist/terminal/graphics/compress-chunk.js +40 -0
- package/packages/daemon/dist/terminal/graphics/native-surface-demand.js +99 -0
- package/packages/daemon/dist/terminal/graphics/native-surface-provider-channel.js +162 -0
- package/packages/daemon/dist/terminal/graphics/native-surface-provider.js +275 -0
- package/packages/daemon/dist/terminal/graphics/native-surface-viewer.js +146 -0
- package/packages/daemon/dist/terminal/mirror/mirror-service.js +3 -0
- package/packages/daemon/dist/terminal/mirror/session-channel.js +32 -9
- package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +196 -3
- package/packages/daemon/dist/terminal/pane-stream/runtime.js +9 -0
- package/packages/daemon/src/command-center/resources/saved-machine-route.ts +4 -0
- package/packages/daemon/src/command-center/resources/saved-machine-watch.ts +132 -0
- package/packages/daemon/src/command-center/server.ts +18 -0
- package/packages/daemon/src/command-center/tmux-servers.ts +14 -0
- package/packages/daemon/src/lib/daemon-embed.ts +43 -1
- package/packages/daemon/src/lib/native-surface-context.ts +123 -0
- package/packages/daemon/src/lib/tmux-server-owner.ts +9 -0
- package/packages/daemon/src/server/native-surface-provider-upgrade.ts +103 -0
- package/packages/daemon/src/terminal/graphics/compress-chunk.ts +49 -0
- package/packages/daemon/src/terminal/graphics/native-surface-demand.ts +88 -0
- package/packages/daemon/src/terminal/graphics/native-surface-provider-channel.ts +161 -0
- package/packages/daemon/src/terminal/graphics/native-surface-provider.ts +308 -0
- package/packages/daemon/src/terminal/graphics/native-surface-viewer.ts +154 -0
- package/packages/daemon/src/terminal/mirror/mirror-service.ts +5 -0
- package/packages/daemon/src/terminal/mirror/session-channel.ts +31 -10
- package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +229 -2
- package/packages/daemon/src/terminal/pane-stream/runtime.ts +11 -1
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
import type { SurfaceViewerDemand } from "../graphics/native-surface-demand.ts";
|
|
1
2
|
import {
|
|
3
|
+
TERMINAL_SURFACE_MAX_CONTROL_BYTES,
|
|
4
|
+
TERMINAL_SURFACE_CAPABILITY_VERSION,
|
|
5
|
+
type TerminalSurfaceReceiver,
|
|
6
|
+
} from "@tmux-ide/contracts";
|
|
7
|
+
import type {
|
|
8
|
+
NativeSurfaceProviderRegistry,
|
|
9
|
+
NativeSurfaceSession,
|
|
10
|
+
} from "../graphics/native-surface-provider.ts";
|
|
11
|
+
import { NativeSurfaceViewer } from "../graphics/native-surface-viewer.ts";
|
|
12
|
+
import { compressGraphicsChunk } from "../graphics/compress-chunk.ts";
|
|
13
|
+
import {
|
|
14
|
+
TERMINAL_GRAPHICS_COMPRESSED_CAPABILITY_VERSION,
|
|
15
|
+
TERMINAL_GRAPHICS_COMPRESSED_LOCAL_CAPABILITY_VERSION,
|
|
2
16
|
TERMINAL_PROJECTION_CAPABILITY_VERSION,
|
|
3
17
|
TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION,
|
|
4
18
|
type TerminalProjectionReceipt,
|
|
@@ -259,6 +273,7 @@ export interface PaneStreamAdmissionSnapshot {
|
|
|
259
273
|
}
|
|
260
274
|
|
|
261
275
|
export interface PaneStreamAdmissionCoordinatorOptions {
|
|
276
|
+
readonly nativeSurfaces?: NativeSurfaceProviderRegistry;
|
|
262
277
|
readonly localImages?: LocalImageOwner;
|
|
263
278
|
readonly daemonInstanceId: string;
|
|
264
279
|
readonly webSocketUrl: string;
|
|
@@ -405,6 +420,7 @@ function errorFrameCode(error: unknown): PaneStreamErrorFrameCode {
|
|
|
405
420
|
* ticket digests; a daemon restart makes every prior descriptor inert.
|
|
406
421
|
*/
|
|
407
422
|
export class PaneStreamAdmissionCoordinator {
|
|
423
|
+
readonly #nativeSurfaces: NativeSurfaceProviderRegistry | undefined;
|
|
408
424
|
readonly #localImages: LocalImageOwner | undefined;
|
|
409
425
|
readonly #instanceId: string;
|
|
410
426
|
readonly #webSocketUrl: string;
|
|
@@ -439,6 +455,9 @@ export class PaneStreamAdmissionCoordinator {
|
|
|
439
455
|
#shutdownPromise: Promise<void> | null = null;
|
|
440
456
|
|
|
441
457
|
constructor(options: PaneStreamAdmissionCoordinatorOptions) {
|
|
458
|
+
this.#nativeSurfaces = options.nativeSurfaces;
|
|
459
|
+
if (this.#nativeSurfaces && this.#nativeSurfaces.generation !== options.daemonInstanceId)
|
|
460
|
+
throw new Error("Native surface provider generation mismatch");
|
|
442
461
|
this.#localImages = options.localImages;
|
|
443
462
|
this.#instanceId = BindingIdSchemaZ.parse(options.daemonInstanceId);
|
|
444
463
|
this.#webSocketUrl = PaneStreamLoopbackWebSocketUrlSchemaZ.parse(options.webSocketUrl);
|
|
@@ -811,6 +830,7 @@ export class PaneStreamAdmissionCoordinator {
|
|
|
811
830
|
let live: PaneStreamLiveConnection;
|
|
812
831
|
try {
|
|
813
832
|
live = new PaneStreamLiveConnection({
|
|
833
|
+
nativeSurfaces: this.#nativeSurfaces,
|
|
814
834
|
localImages: this.#localImages,
|
|
815
835
|
clientId: `psc-${this.#clientCounter}`,
|
|
816
836
|
socket,
|
|
@@ -1030,6 +1050,7 @@ class PreAuthAdmission implements PaneStreamPreAuthAdmission {
|
|
|
1030
1050
|
}
|
|
1031
1051
|
|
|
1032
1052
|
interface LiveConnectionOptions {
|
|
1053
|
+
readonly nativeSurfaces?: NativeSurfaceProviderRegistry;
|
|
1033
1054
|
readonly localImages?: LocalImageOwner;
|
|
1034
1055
|
readonly clientId: string;
|
|
1035
1056
|
readonly socket: DirectTerminalSocket;
|
|
@@ -1088,6 +1109,7 @@ interface PaneChannel {
|
|
|
1088
1109
|
graphicsState: TerminalGraphicsState | null;
|
|
1089
1110
|
graphicsZlib?: boolean;
|
|
1090
1111
|
graphicsBinary?: boolean;
|
|
1112
|
+
graphicsCompressed?: boolean;
|
|
1091
1113
|
binaryCells?: boolean;
|
|
1092
1114
|
graphicsLocal?: boolean;
|
|
1093
1115
|
graphicsInline?: Set<string>;
|
|
@@ -1122,6 +1144,13 @@ export function rawGraphicsState(state: TerminalGraphicsState): TerminalGraphics
|
|
|
1122
1144
|
const GRAPHICS_SEND_WINDOW_BYTES = 2 * 1024 * 1024;
|
|
1123
1145
|
|
|
1124
1146
|
export class PaneStreamLiveConnection {
|
|
1147
|
+
// One bounded native negotiation per redeemed socket; never reuse on reconnect.
|
|
1148
|
+
readonly #nativeSurfaces: NativeSurfaceProviderRegistry | undefined;
|
|
1149
|
+
#surfaceViewer: NativeSurfaceViewer | null = null;
|
|
1150
|
+
#surfaceDemands = new Map<string, SurfaceViewerDemand>();
|
|
1151
|
+
#surfaceNative = false;
|
|
1152
|
+
#surfaceSession: NativeSurfaceSession | null = null;
|
|
1153
|
+
#surfaceReceiver: TerminalSurfaceReceiver | null = null;
|
|
1125
1154
|
readonly #localImages: LocalImageOwner | undefined;
|
|
1126
1155
|
readonly #localLeases = new Map<string, { pane: string; expiresAt: number }>();
|
|
1127
1156
|
readonly #clientId: string;
|
|
@@ -1248,6 +1277,7 @@ export class PaneStreamLiveConnection {
|
|
|
1248
1277
|
}
|
|
1249
1278
|
|
|
1250
1279
|
constructor(options: LiveConnectionOptions) {
|
|
1280
|
+
this.#nativeSurfaces = options.nativeSurfaces;
|
|
1251
1281
|
this.#localImages = options.localImages;
|
|
1252
1282
|
this.#clientId = options.clientId;
|
|
1253
1283
|
this.#socket = options.socket;
|
|
@@ -1349,6 +1379,7 @@ export class PaneStreamLiveConnection {
|
|
|
1349
1379
|
this.#sessionRuntimeBinding?.onAuthoritySnapshot?.((snapshot) =>
|
|
1350
1380
|
this.#usesExplicitAuthority ? this.#sendAuthoritySnapshot(snapshot) : undefined,
|
|
1351
1381
|
) ?? null;
|
|
1382
|
+
if (!this.#descriptor.terminalDelivery) this.#observeSurfaceDemand(this.#descriptor.panes);
|
|
1352
1383
|
void this.#subscribeAll();
|
|
1353
1384
|
}
|
|
1354
1385
|
|
|
@@ -1356,6 +1387,12 @@ export class PaneStreamLiveConnection {
|
|
|
1356
1387
|
if (this.#closed) return;
|
|
1357
1388
|
this.#recordDiagnosticLifecycle("pane-stream-terminal", code, reason);
|
|
1358
1389
|
this.#closed = true;
|
|
1390
|
+
for (const demand of this.#surfaceDemands.values()) demand.close();
|
|
1391
|
+
this.#surfaceDemands.clear();
|
|
1392
|
+
this.#surfaceViewer?.close();
|
|
1393
|
+
this.#surfaceViewer = null;
|
|
1394
|
+
this.#surfaceSession?.close();
|
|
1395
|
+
this.#surfaceSession = null;
|
|
1359
1396
|
this.#cancelDrainTick?.();
|
|
1360
1397
|
this.#cancelDrainTick = null;
|
|
1361
1398
|
// Departure force-returns every ticket within this same tick.
|
|
@@ -1387,6 +1424,8 @@ export class PaneStreamLiveConnection {
|
|
|
1387
1424
|
const delivery = channel.delivery;
|
|
1388
1425
|
channel.delivery = null;
|
|
1389
1426
|
channel.closed = true;
|
|
1427
|
+
this.#surfaceDemands.get(channel.semanticPaneId)?.close();
|
|
1428
|
+
this.#surfaceDemands.delete(channel.semanticPaneId);
|
|
1390
1429
|
if (sub) closures.push(sub.close().catch(() => undefined));
|
|
1391
1430
|
if (delivery) closures.push(delivery.close().catch(() => undefined));
|
|
1392
1431
|
}
|
|
@@ -1854,10 +1893,27 @@ export class PaneStreamLiveConnection {
|
|
|
1854
1893
|
await this.#awaitSemanticCredit(pane);
|
|
1855
1894
|
}
|
|
1856
1895
|
|
|
1896
|
+
#observeSurfaceDemand(panes: readonly string[]): void {
|
|
1897
|
+
for (const pane of panes) {
|
|
1898
|
+
if (this.#surfaceDemands.has(pane)) continue;
|
|
1899
|
+
const demand = this.#nativeSurfaces?.demand.viewer(this.#descriptor.sessionName, [pane]);
|
|
1900
|
+
if (!demand) return;
|
|
1901
|
+
demand.native(this.#surfaceNative);
|
|
1902
|
+
this.#surfaceDemands.set(pane, demand);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
#surfaceControl(type: string): void {
|
|
1907
|
+
if (type === "terminal-surface-offer") return;
|
|
1908
|
+
this.#surfaceNative = type === "terminal-surface-peer";
|
|
1909
|
+
for (const demand of this.#surfaceDemands.values()) demand.native(this.#surfaceNative);
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1857
1912
|
/** Announce the pane's kitty graphics state; the client pulls bytes it lacks. */
|
|
1858
1913
|
#startGraphics(channel: PaneChannel): void {
|
|
1859
1914
|
const graphics = channel.delivery?.graphics;
|
|
1860
1915
|
if (!graphics) return;
|
|
1916
|
+
this.#observeSurfaceDemand([channel.semanticPaneId]);
|
|
1861
1917
|
// Renderers that cannot inflate see raw formats; their bytes are inflated
|
|
1862
1918
|
// when served.
|
|
1863
1919
|
const zlib =
|
|
@@ -1871,7 +1927,14 @@ export class PaneStreamLiveConnection {
|
|
|
1871
1927
|
(version) =>
|
|
1872
1928
|
version === TERMINAL_GRAPHICS_LOCAL_CAPABILITY_VERSION ||
|
|
1873
1929
|
version === TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION ||
|
|
1874
|
-
version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION
|
|
1930
|
+
version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION ||
|
|
1931
|
+
version === TERMINAL_GRAPHICS_COMPRESSED_LOCAL_CAPABILITY_VERSION,
|
|
1932
|
+
) === true;
|
|
1933
|
+
channel.graphicsCompressed =
|
|
1934
|
+
this.#descriptor.terminalDelivery?.protocolVersions.some(
|
|
1935
|
+
(version) =>
|
|
1936
|
+
version === TERMINAL_GRAPHICS_COMPRESSED_CAPABILITY_VERSION ||
|
|
1937
|
+
version === TERMINAL_GRAPHICS_COMPRESSED_LOCAL_CAPABILITY_VERSION,
|
|
1875
1938
|
) === true;
|
|
1876
1939
|
channel.graphicsInline = new Set();
|
|
1877
1940
|
channel.graphicsBinary =
|
|
@@ -1882,7 +1945,9 @@ export class PaneStreamLiveConnection {
|
|
|
1882
1945
|
version === TERMINAL_DELIVERY_BINARY_CAPABILITY_VERSION ||
|
|
1883
1946
|
version === TERMINAL_PROJECTION_CAPABILITY_VERSION ||
|
|
1884
1947
|
version === TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION ||
|
|
1885
|
-
version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION
|
|
1948
|
+
version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION ||
|
|
1949
|
+
version === TERMINAL_GRAPHICS_COMPRESSED_CAPABILITY_VERSION ||
|
|
1950
|
+
version === TERMINAL_GRAPHICS_COMPRESSED_LOCAL_CAPABILITY_VERSION,
|
|
1886
1951
|
) === true;
|
|
1887
1952
|
// Renderers that report cell pixels learn up front that this daemon
|
|
1888
1953
|
// accepts them, before any image exists.
|
|
@@ -1916,6 +1981,7 @@ export class PaneStreamLiveConnection {
|
|
|
1916
1981
|
...(cellPixels ? { cellPixels: true as const } : {}),
|
|
1917
1982
|
...(preciseCellPixels ? { preciseCellPixels: true as const } : {}),
|
|
1918
1983
|
...(channel.graphicsBinary ? { binaryBlobs: true as const } : {}),
|
|
1984
|
+
...(channel.graphicsCompressed ? { compressedBlobs: true as const } : {}),
|
|
1919
1985
|
...(channel.graphicsLocal ? { localFiles: true as const } : {}),
|
|
1920
1986
|
},
|
|
1921
1987
|
});
|
|
@@ -1933,6 +1999,7 @@ export class PaneStreamLiveConnection {
|
|
|
1933
1999
|
cellPixels: true,
|
|
1934
2000
|
...(preciseCellPixels ? { preciseCellPixels: true as const } : {}),
|
|
1935
2001
|
...(channel.graphicsBinary ? { binaryBlobs: true as const } : {}),
|
|
2002
|
+
...(channel.graphicsCompressed ? { compressedBlobs: true as const } : {}),
|
|
1936
2003
|
...(channel.graphicsLocal ? { localFiles: true as const } : {}),
|
|
1937
2004
|
},
|
|
1938
2005
|
});
|
|
@@ -2046,6 +2113,27 @@ export class PaneStreamLiveConnection {
|
|
|
2046
2113
|
if (offset > 0 && graphics.image(digest) !== image) break;
|
|
2047
2114
|
const end = Math.min(total, offset + TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES);
|
|
2048
2115
|
if (channel.graphicsBinary) {
|
|
2116
|
+
const header = {
|
|
2117
|
+
pane: channel.semanticPaneId,
|
|
2118
|
+
blob: {
|
|
2119
|
+
digest,
|
|
2120
|
+
format,
|
|
2121
|
+
width: image.width,
|
|
2122
|
+
height: image.height,
|
|
2123
|
+
total,
|
|
2124
|
+
offset,
|
|
2125
|
+
},
|
|
2126
|
+
};
|
|
2127
|
+
const compressed = channel.graphicsCompressed
|
|
2128
|
+
? await compressGraphicsChunk(header, data.subarray(offset, end))
|
|
2129
|
+
: null;
|
|
2130
|
+
// Compression yields: a pane may close or replace this image meanwhile.
|
|
2131
|
+
if (this.#closed || channel.closed) return;
|
|
2132
|
+
if (graphics.image(digest) !== image) break;
|
|
2133
|
+
if (compressed) {
|
|
2134
|
+
this.#sendEncoded(null, Buffer.from(compressed), true);
|
|
2135
|
+
continue;
|
|
2136
|
+
}
|
|
2049
2137
|
this.#sendEncoded(
|
|
2050
2138
|
null,
|
|
2051
2139
|
Buffer.from(
|
|
@@ -2223,6 +2311,8 @@ export class PaneStreamLiveConnection {
|
|
|
2223
2311
|
#emitClosed(channel: PaneChannel): void {
|
|
2224
2312
|
if (channel.closed) return;
|
|
2225
2313
|
channel.closed = true;
|
|
2314
|
+
this.#surfaceDemands.get(channel.semanticPaneId)?.close();
|
|
2315
|
+
this.#surfaceDemands.delete(channel.semanticPaneId);
|
|
2226
2316
|
const sub = channel.sub;
|
|
2227
2317
|
channel.sub = null;
|
|
2228
2318
|
if (sub) void sub.close().catch(() => undefined);
|
|
@@ -2678,6 +2768,143 @@ export class PaneStreamLiveConnection {
|
|
|
2678
2768
|
this.#acceptSemanticIntent(frame.operationId, frame.intent);
|
|
2679
2769
|
return;
|
|
2680
2770
|
}
|
|
2771
|
+
if (frame.type === "terminal-surface-watch") {
|
|
2772
|
+
if (
|
|
2773
|
+
byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES ||
|
|
2774
|
+
frame.generation !== this.#sessionRuntimeBinding?.generation ||
|
|
2775
|
+
!this.#descriptor.terminalDelivery?.protocolVersions.includes(
|
|
2776
|
+
TERMINAL_SURFACE_CAPABILITY_VERSION,
|
|
2777
|
+
) ||
|
|
2778
|
+
!this.#nativeSurfaces ||
|
|
2779
|
+
this.#surfaceReceiver
|
|
2780
|
+
)
|
|
2781
|
+
return this.#failProtocol("protocol-error");
|
|
2782
|
+
if (this.#surfaceViewer) return;
|
|
2783
|
+
this.#surfaceViewer = new NativeSurfaceViewer(
|
|
2784
|
+
this.#nativeSurfaces,
|
|
2785
|
+
this.#descriptor.panes,
|
|
2786
|
+
(control) => {
|
|
2787
|
+
if (this.#closed) return false;
|
|
2788
|
+
this.#surfaceControl(control.type);
|
|
2789
|
+
this.#sendFrame(null, control);
|
|
2790
|
+
return !this.#closed;
|
|
2791
|
+
},
|
|
2792
|
+
(pane, generation) => {
|
|
2793
|
+
const channel = this.#panes.get(pane);
|
|
2794
|
+
const address = channel?.deliveryAddress;
|
|
2795
|
+
return !this.#closed &&
|
|
2796
|
+
channel &&
|
|
2797
|
+
!channel.closed &&
|
|
2798
|
+
channel.delivery &&
|
|
2799
|
+
address?.generation === generation
|
|
2800
|
+
? address.deliveryNonce
|
|
2801
|
+
: null;
|
|
2802
|
+
},
|
|
2803
|
+
);
|
|
2804
|
+
this.#surfaceViewer.start();
|
|
2805
|
+
return;
|
|
2806
|
+
}
|
|
2807
|
+
if (frame.type === "terminal-surface-receiver") {
|
|
2808
|
+
if (this.#surfaceViewer) {
|
|
2809
|
+
if (byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES || !this.#surfaceViewer.receive(frame))
|
|
2810
|
+
this.#failProtocol("protocol-error");
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
if (
|
|
2814
|
+
byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES ||
|
|
2815
|
+
!this.#descriptor.terminalDelivery ||
|
|
2816
|
+
frame.generation !== this.#sessionRuntimeBinding?.generation
|
|
2817
|
+
) {
|
|
2818
|
+
return this.#failProtocol("protocol-error");
|
|
2819
|
+
}
|
|
2820
|
+
if (this.#surfaceReceiver) {
|
|
2821
|
+
// Parsed schema order is deterministic. A duplicate cannot change the
|
|
2822
|
+
// challenge, kernel identity or endpoint, nor amplify fallback replies.
|
|
2823
|
+
if (JSON.stringify(frame) !== JSON.stringify(this.#surfaceReceiver)) {
|
|
2824
|
+
return this.#failProtocol("protocol-error");
|
|
2825
|
+
}
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
this.#surfaceReceiver = frame;
|
|
2829
|
+
if (
|
|
2830
|
+
this.#descriptor.terminalDelivery.protocolVersions.includes(
|
|
2831
|
+
TERMINAL_SURFACE_CAPABILITY_VERSION,
|
|
2832
|
+
)
|
|
2833
|
+
) {
|
|
2834
|
+
const native =
|
|
2835
|
+
this.#nativeSurfaces?.open(
|
|
2836
|
+
frame,
|
|
2837
|
+
this.#descriptor.panes,
|
|
2838
|
+
(control) => {
|
|
2839
|
+
if (this.#closed) return false;
|
|
2840
|
+
this.#surfaceControl(control.type);
|
|
2841
|
+
this.#sendFrame(null, control);
|
|
2842
|
+
return !this.#closed;
|
|
2843
|
+
},
|
|
2844
|
+
(pane, generation) => {
|
|
2845
|
+
const channel = this.#panes.get(pane);
|
|
2846
|
+
const address = channel?.deliveryAddress;
|
|
2847
|
+
if (
|
|
2848
|
+
this.#closed ||
|
|
2849
|
+
!channel ||
|
|
2850
|
+
channel.closed ||
|
|
2851
|
+
!channel.delivery ||
|
|
2852
|
+
!address ||
|
|
2853
|
+
address.generation !== generation
|
|
2854
|
+
)
|
|
2855
|
+
return null;
|
|
2856
|
+
return address.deliveryNonce;
|
|
2857
|
+
},
|
|
2858
|
+
) ?? null;
|
|
2859
|
+
if (this.#closed) {
|
|
2860
|
+
native?.close();
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
this.#surfaceSession = native;
|
|
2864
|
+
if (native) return;
|
|
2865
|
+
}
|
|
2866
|
+
// No provider/capability means no PID/slot lookup and no inferred locality.
|
|
2867
|
+
this.#sendFrame(null, {
|
|
2868
|
+
type: "terminal-surface-fallback",
|
|
2869
|
+
version: 1,
|
|
2870
|
+
generation: frame.generation,
|
|
2871
|
+
connectionNonce: frame.connectionNonce,
|
|
2872
|
+
reason: "unsupported",
|
|
2873
|
+
});
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
if (frame.type === "terminal-surface-fallback") {
|
|
2877
|
+
if (this.#surfaceViewer) {
|
|
2878
|
+
if (byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES || !this.#surfaceViewer.fallback(frame))
|
|
2879
|
+
this.#failProtocol("protocol-error");
|
|
2880
|
+
return;
|
|
2881
|
+
}
|
|
2882
|
+
if (
|
|
2883
|
+
byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES ||
|
|
2884
|
+
frame.generation !== this.#surfaceReceiver?.generation ||
|
|
2885
|
+
frame.connectionNonce !== this.#surfaceReceiver.connectionNonce
|
|
2886
|
+
) {
|
|
2887
|
+
return this.#failProtocol("protocol-error");
|
|
2888
|
+
}
|
|
2889
|
+
this.#surfaceSession?.fallback(frame.reason);
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
if (frame.type === "terminal-surface-retired") {
|
|
2893
|
+
if (this.#surfaceViewer) {
|
|
2894
|
+
if (byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES || !this.#surfaceViewer.retire(frame))
|
|
2895
|
+
this.#failProtocol("protocol-error");
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
if (
|
|
2899
|
+
byteLength <= TERMINAL_SURFACE_MAX_CONTROL_BYTES &&
|
|
2900
|
+
frame.generation === this.#surfaceReceiver?.generation &&
|
|
2901
|
+
frame.connectionNonce === this.#surfaceReceiver.connectionNonce &&
|
|
2902
|
+
this.#surfaceSession?.retire(frame.sequence)
|
|
2903
|
+
)
|
|
2904
|
+
return;
|
|
2905
|
+
// Unissued/replayed ACKs must not retire another socket's native storage.
|
|
2906
|
+
return this.#failProtocol("protocol-error");
|
|
2907
|
+
}
|
|
2681
2908
|
if (frame.type === "terminal-graphics-request") {
|
|
2682
2909
|
this.#requestGraphics(frame.pane, frame.digests, frame.preferInline === true);
|
|
2683
2910
|
return;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LocalImageOwner } from "../graphics/local-image-owner.ts";
|
|
2
|
+
import { NativeSurfaceProviderRegistry } from "../graphics/native-surface-provider.ts";
|
|
2
3
|
import type { SemanticPaneCatalog } from "../attachments/semantic-pane-catalog.ts";
|
|
3
4
|
import type { SessionRuntimeRegistry } from "../session-runtime/registry.ts";
|
|
4
5
|
import { SessionRuntimeTransportBinder } from "../session-runtime/transport-binding.ts";
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
} from "./pane-stream-websocket.ts";
|
|
11
12
|
|
|
12
13
|
export interface PaneStreamRuntimeOptions {
|
|
14
|
+
readonly nativeSurfaces?: NativeSurfaceProviderRegistry;
|
|
13
15
|
readonly localImages?: LocalImageOwner;
|
|
14
16
|
readonly daemonInstanceId: string;
|
|
15
17
|
readonly webSocketUrl: string;
|
|
@@ -20,7 +22,7 @@ export interface PaneStreamRuntimeOptions {
|
|
|
20
22
|
readonly observability?: SessionRuntimeObservability;
|
|
21
23
|
readonly admission?: Omit<
|
|
22
24
|
PaneStreamAdmissionCoordinatorOptions,
|
|
23
|
-
"daemonInstanceId" | "webSocketUrl" | "leaseManager" | "mirror"
|
|
25
|
+
"daemonInstanceId" | "webSocketUrl" | "leaseManager" | "mirror" | "nativeSurfaces"
|
|
24
26
|
>;
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -31,9 +33,15 @@ export interface PaneStreamRuntimeOptions {
|
|
|
31
33
|
*/
|
|
32
34
|
export class PaneStreamRuntime {
|
|
33
35
|
readonly coordinator: PaneStreamAdmissionCoordinator;
|
|
36
|
+
readonly nativeSurfaces: NativeSurfaceProviderRegistry | undefined;
|
|
34
37
|
#disposePromise: Promise<void> | null = null;
|
|
35
38
|
|
|
36
39
|
constructor(options: PaneStreamRuntimeOptions) {
|
|
40
|
+
this.nativeSurfaces =
|
|
41
|
+
options.nativeSurfaces ??
|
|
42
|
+
(process.platform === "darwin"
|
|
43
|
+
? new NativeSurfaceProviderRegistry(options.daemonInstanceId)
|
|
44
|
+
: undefined);
|
|
37
45
|
const leaseManager = new PaneStreamLeaseManager({
|
|
38
46
|
daemonInstanceId: options.daemonInstanceId,
|
|
39
47
|
});
|
|
@@ -41,6 +49,7 @@ export class PaneStreamRuntime {
|
|
|
41
49
|
this.coordinator = new PaneStreamAdmissionCoordinator({
|
|
42
50
|
...options.admission,
|
|
43
51
|
localImages: options.localImages,
|
|
52
|
+
nativeSurfaces: this.nativeSurfaces,
|
|
44
53
|
daemonInstanceId: options.daemonInstanceId,
|
|
45
54
|
webSocketUrl: options.webSocketUrl,
|
|
46
55
|
leaseManager,
|
|
@@ -69,6 +78,7 @@ export class PaneStreamRuntime {
|
|
|
69
78
|
dispose(): Promise<void> {
|
|
70
79
|
if (!this.#disposePromise) {
|
|
71
80
|
this.#disposePromise = (async () => {
|
|
81
|
+
this.nativeSurfaces?.close();
|
|
72
82
|
await this.coordinator.shutdown();
|
|
73
83
|
})();
|
|
74
84
|
}
|