tmux-ide 2.9.11 → 2.9.12
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 +4158 -2545
- package/package.json +1 -1
- package/packages/contracts/src/index.ts +4 -0
- package/packages/contracts/src/pane-stream.ts +60 -0
- package/packages/contracts/src/terminal-delivery-binary-chunk.ts +55 -0
- package/packages/contracts/src/terminal-delivery.ts +17 -2
- package/packages/contracts/src/terminal-graphics.ts +68 -0
- package/packages/contracts/src/terminal-projection.ts +153 -0
- package/packages/core/src/fixtures/terminal-projection-vectors.json +585 -0
- package/packages/core/src/index.ts +2 -0
- package/packages/core/src/terminal-delivery-binary.ts +160 -0
- package/packages/core/src/terminal-delivery.ts +371 -16
- package/packages/core/src/terminal-projection-vectors.mjs +82 -0
- package/packages/core/src/terminal-projection.ts +476 -0
- package/packages/core/src/terminal-replica.ts +1 -1
- package/packages/daemon/dist/lib/daemon-embed.js +39 -3
- package/packages/daemon/dist/lib/embedded-tmux-server-owners.js +1 -0
- package/packages/daemon/dist/lib/tmux-server-owner.js +1 -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/terminal/graphics/local-image-owner.js +330 -0
- package/packages/daemon/dist/terminal/graphics/local-image-store.js +255 -0
- package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +176 -16
- package/packages/daemon/dist/terminal/pane-stream/runtime.js +1 -0
- package/packages/daemon/dist/terminal/session-runtime/registry.js +2 -0
- package/packages/daemon/dist/terminal/session-runtime/terminal-delivery-hub.js +146 -16
- package/packages/daemon/dist/terminal/session-runtime/terminal-projection-delivery.js +32 -0
- package/packages/daemon/dist/terminal/session-runtime/terminal-replica-owner.js +3 -4
- package/packages/daemon/dist/tui/mirror/open-tui-workspace-runtime-port.js +4 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +28 -3
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace.jsx +13 -1
- package/packages/daemon/dist/tui/mirror/runtime/renderer-probe.js +195 -0
- package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +6 -0
- package/packages/daemon/dist/tui/mirror/semantic-pane-render-source.js +4 -0
- package/packages/daemon/src/lib/daemon-embed.ts +38 -2
- package/packages/daemon/src/lib/embedded-tmux-server-owners.ts +3 -0
- package/packages/daemon/src/lib/tmux-server-owner.ts +3 -0
- package/packages/daemon/src/terminal/graphics/local-image-owner.ts +346 -0
- package/packages/daemon/src/terminal/graphics/local-image-store.ts +286 -0
- package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +219 -15
- package/packages/daemon/src/terminal/pane-stream/runtime.ts +3 -0
- package/packages/daemon/src/terminal/session-runtime/registry.ts +2 -0
- package/packages/daemon/src/terminal/session-runtime/runtime-observability.ts +1 -1
- package/packages/daemon/src/terminal/session-runtime/terminal-delivery-hub.ts +216 -22
- package/packages/daemon/src/terminal/session-runtime/terminal-delivery-observation-identity.ts +7 -4
- package/packages/daemon/src/terminal/session-runtime/terminal-projection-delivery.ts +59 -0
- package/packages/daemon/src/terminal/session-runtime/terminal-replica-owner.ts +3 -4
- package/packages/daemon/src/tui/mirror/open-tui-workspace-runtime-port.ts +4 -0
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +26 -4
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace.tsx +20 -1
- package/packages/daemon/src/tui/mirror/runtime/renderer-probe.ts +257 -0
- package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +15 -0
- package/packages/daemon/src/tui/mirror/semantic-pane-render-source.ts +4 -0
- package/packages/daemon-client/src/pane-stream-client.ts +75 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TERMINAL_PROJECTION_CAPABILITY_VERSION, TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION, } from "@tmux-ide/contracts";
|
|
2
|
+
import { PANE_STREAM_INPUT_LIFETIME_CAPABILITY_VERSION, PANE_STREAM_INPUT_BYTES_CAPABILITY_VERSION, } from "@tmux-ide/contracts";
|
|
2
3
|
import { inflateSync } from "node:zlib";
|
|
3
4
|
import { PaneStreamLayoutSnapshotFrameSchemaZ } from "@tmux-ide/contracts";
|
|
4
5
|
import { z } from "zod";
|
|
5
|
-
import { PANE_STREAM_MAX_HELD_DELTAS, PANE_STREAM_MAX_OUTPUT_BYTES, PANE_STREAM_MAX_PANES, PANE_STREAM_MAX_SEED_BYTES, PANE_STREAM_PROTOCOL_VERSION, PANE_STREAM_REDEEM_PATH, PANE_STREAM_WEBSOCKET_SUBPROTOCOL, TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES, TERMINAL_GRAPHICS_CELL_PIXELS_CAPABILITY_VERSION, TERMINAL_GRAPHICS_PRECISE_PIXELS_CAPABILITY_VERSION, TERMINAL_GRAPHICS_LEGACY_MAX_IMAGE_BYTES, TERMINAL_GRAPHICS_ZLIB_CAPABILITY_VERSION, PaneStreamClientFrameSchemaZ, PaneStreamLoopbackWebSocketUrlSchemaZ, PaneStreamRedeemFrameSchemaZ, PaneStreamServerFrameSchemaZ, sharedMonotonicMicros, } from "@tmux-ide/contracts";
|
|
6
|
+
import { PANE_STREAM_MAX_HELD_DELTAS, PANE_STREAM_MAX_OUTPUT_BYTES, PANE_STREAM_MAX_PANES, PANE_STREAM_MAX_SEED_BYTES, PANE_STREAM_PROTOCOL_VERSION, PANE_STREAM_REDEEM_PATH, PANE_STREAM_WEBSOCKET_SUBPROTOCOL, TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES, TERMINAL_DELIVERY_BINARY_CAPABILITY_VERSION, TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION, TERMINAL_GRAPHICS_BINARY_CAPABILITY_VERSION, TERMINAL_GRAPHICS_LOCAL_CAPABILITY_VERSION, encodeTerminalGraphicsBinary, encodeTerminalDeliveryBinaryChunk, TERMINAL_GRAPHICS_CELL_PIXELS_CAPABILITY_VERSION, TERMINAL_GRAPHICS_PRECISE_PIXELS_CAPABILITY_VERSION, TERMINAL_GRAPHICS_LEGACY_MAX_IMAGE_BYTES, TERMINAL_GRAPHICS_ZLIB_CAPABILITY_VERSION, PaneStreamClientFrameSchemaZ, PaneStreamLoopbackWebSocketUrlSchemaZ, PaneStreamRedeemFrameSchemaZ, PaneStreamServerFrameSchemaZ, sharedMonotonicMicros, } from "@tmux-ide/contracts";
|
|
6
7
|
import { canonicalOriginOrNull, digestSecret, digestsEqual, rawDataByteLength, rawDataToBuffer, safeCloseSocket, strictJsonParse, } from "../attachments/admission-util.js";
|
|
7
8
|
import { SessionRuntimeControllerLeaseError } from "../session-runtime/registry.js";
|
|
8
9
|
import { terminalDeliveryObservationOrdinal } from "../session-runtime/terminal-delivery-observation-identity.js";
|
|
@@ -115,6 +116,7 @@ function errorFrameCode(error) {
|
|
|
115
116
|
* ticket digests; a daemon restart makes every prior descriptor inert.
|
|
116
117
|
*/
|
|
117
118
|
export class PaneStreamAdmissionCoordinator {
|
|
119
|
+
#localImages;
|
|
118
120
|
#instanceId;
|
|
119
121
|
#webSocketUrl;
|
|
120
122
|
#leaseManager;
|
|
@@ -145,6 +147,7 @@ export class PaneStreamAdmissionCoordinator {
|
|
|
145
147
|
#shuttingDown = false;
|
|
146
148
|
#shutdownPromise = null;
|
|
147
149
|
constructor(options) {
|
|
150
|
+
this.#localImages = options.localImages;
|
|
148
151
|
this.#instanceId = BindingIdSchemaZ.parse(options.daemonInstanceId);
|
|
149
152
|
this.#webSocketUrl = PaneStreamLoopbackWebSocketUrlSchemaZ.parse(options.webSocketUrl);
|
|
150
153
|
this.#leaseManager = options.leaseManager;
|
|
@@ -407,6 +410,7 @@ export class PaneStreamAdmissionCoordinator {
|
|
|
407
410
|
let live;
|
|
408
411
|
try {
|
|
409
412
|
live = new PaneStreamLiveConnection({
|
|
413
|
+
localImages: this.#localImages,
|
|
410
414
|
clientId: `psc-${this.#clientCounter}`,
|
|
411
415
|
socket,
|
|
412
416
|
descriptor: structuredClone(descriptor),
|
|
@@ -614,6 +618,8 @@ export function rawGraphicsState(state) {
|
|
|
614
618
|
}
|
|
615
619
|
const GRAPHICS_SEND_WINDOW_BYTES = 2 * 1024 * 1024;
|
|
616
620
|
export class PaneStreamLiveConnection {
|
|
621
|
+
#localImages;
|
|
622
|
+
#localLeases = new Map();
|
|
617
623
|
#clientId;
|
|
618
624
|
#socket;
|
|
619
625
|
#descriptor;
|
|
@@ -701,6 +707,7 @@ export class PaneStreamLiveConnection {
|
|
|
701
707
|
return elapsed;
|
|
702
708
|
}
|
|
703
709
|
constructor(options) {
|
|
710
|
+
this.#localImages = options.localImages;
|
|
704
711
|
this.#clientId = options.clientId;
|
|
705
712
|
this.#socket = options.socket;
|
|
706
713
|
this.#descriptor = options.descriptor;
|
|
@@ -770,6 +777,10 @@ export class PaneStreamLiveConnection {
|
|
|
770
777
|
this.#descriptor.terminalDelivery?.protocolVersions.includes(PANE_STREAM_INPUT_LIFETIME_CAPABILITY_VERSION)
|
|
771
778
|
? { inputLifetime: true }
|
|
772
779
|
: {}),
|
|
780
|
+
...(this.#sessionRuntimeBinding &&
|
|
781
|
+
this.#descriptor.terminalDelivery?.protocolVersions.includes(PANE_STREAM_INPUT_BYTES_CAPABILITY_VERSION)
|
|
782
|
+
? { inputBytes: true }
|
|
783
|
+
: {}),
|
|
773
784
|
protocolVersion: PANE_STREAM_PROTOCOL_VERSION,
|
|
774
785
|
daemonInstanceId: this.#binding.daemonInstanceId,
|
|
775
786
|
requestId: this.#binding.requestId,
|
|
@@ -812,6 +823,12 @@ export class PaneStreamLiveConnection {
|
|
|
812
823
|
this.#stopAuthoritySnapshots?.();
|
|
813
824
|
this.#stopAuthoritySnapshots = null;
|
|
814
825
|
const closures = [];
|
|
826
|
+
this.#localLeases.clear();
|
|
827
|
+
if (this.#localImages)
|
|
828
|
+
closures.push(this.#localImages.releaseClient({
|
|
829
|
+
generation: this.#localImages.identity.generation,
|
|
830
|
+
client: this.#descriptor.leaseId,
|
|
831
|
+
}));
|
|
815
832
|
const layoutSubscription = this.#layoutSubscription;
|
|
816
833
|
this.#layoutSubscription = null;
|
|
817
834
|
if (layoutSubscription)
|
|
@@ -1024,7 +1041,7 @@ export class PaneStreamLiveConnection {
|
|
|
1024
1041
|
return true;
|
|
1025
1042
|
let incarnation = null;
|
|
1026
1043
|
for (const message of pending) {
|
|
1027
|
-
if (message.type !== "terminal.delivery")
|
|
1044
|
+
if (message.type !== "terminal.delivery" && message.type !== "terminal.projection")
|
|
1028
1045
|
continue;
|
|
1029
1046
|
if (message.workspaceName !== this.#descriptor.workspaceName ||
|
|
1030
1047
|
message.semanticPaneId !== channel.semanticPaneId ||
|
|
@@ -1132,7 +1149,15 @@ export class PaneStreamLiveConnection {
|
|
|
1132
1149
|
this.#closeIfAllPanesGone();
|
|
1133
1150
|
}
|
|
1134
1151
|
async #sendTerminalDelivery(pane, message) {
|
|
1135
|
-
if (message.type === "terminal.
|
|
1152
|
+
if (message.type === "terminal.projection") {
|
|
1153
|
+
const channel = this.#panes.get(pane);
|
|
1154
|
+
if (channel?.deliveryAddress)
|
|
1155
|
+
channel.deliveryAddress.incarnation = message.incarnation;
|
|
1156
|
+
if (channel)
|
|
1157
|
+
channel.binaryCells = true;
|
|
1158
|
+
this.#sendFrame(pane, { type: "terminal-projection-envelope", pane, envelope: message });
|
|
1159
|
+
}
|
|
1160
|
+
else if (message.type === "terminal.delivery") {
|
|
1136
1161
|
if (!this.#diagnosticFirstSeedObserved) {
|
|
1137
1162
|
this.#diagnosticFirstSeedObserved = true;
|
|
1138
1163
|
this.#recordDiagnosticLifecycle("pane-stream-first-seed");
|
|
@@ -1140,6 +1165,8 @@ export class PaneStreamLiveConnection {
|
|
|
1140
1165
|
const channel = this.#panes.get(pane);
|
|
1141
1166
|
if (channel?.deliveryAddress)
|
|
1142
1167
|
channel.deliveryAddress.incarnation = message.incarnation;
|
|
1168
|
+
if (channel)
|
|
1169
|
+
channel.binaryCells = message.encoding === "semantic-binary-v1";
|
|
1143
1170
|
// The trusted lease address was fixed at delivery admission; preserve the
|
|
1144
1171
|
// exact envelope and its observation identity through transmission.
|
|
1145
1172
|
const detailedDeliveryObservation = this.#observability?.enabled === true;
|
|
@@ -1215,13 +1242,18 @@ export class PaneStreamLiveConnection {
|
|
|
1215
1242
|
}
|
|
1216
1243
|
}
|
|
1217
1244
|
else if (message.type === "terminal.delivery.chunk") {
|
|
1218
|
-
this.#
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1245
|
+
if (this.#panes.get(pane)?.binaryCells) {
|
|
1246
|
+
this.#sendEncoded(pane, Buffer.from(encodeTerminalDeliveryBinaryChunk({ pane, transactionId: message.transactionId, index: message.index }, message.bytes)), true);
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
this.#sendFrame(pane, {
|
|
1250
|
+
type: "terminal-delivery-chunk",
|
|
1251
|
+
pane,
|
|
1252
|
+
transactionId: message.transactionId,
|
|
1253
|
+
index: message.index,
|
|
1254
|
+
data: Buffer.from(message.bytes).toString("base64"),
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1225
1257
|
}
|
|
1226
1258
|
else {
|
|
1227
1259
|
this.#sendFrame(pane, { type: "terminal-delivery-fault", pane, fault: message });
|
|
@@ -1241,6 +1273,19 @@ export class PaneStreamLiveConnection {
|
|
|
1241
1273
|
// when served.
|
|
1242
1274
|
const zlib = this.#descriptor.terminalDelivery?.protocolVersions.includes(TERMINAL_GRAPHICS_ZLIB_CAPABILITY_VERSION) === true;
|
|
1243
1275
|
channel.graphicsZlib = zlib;
|
|
1276
|
+
channel.graphicsLocal =
|
|
1277
|
+
!!this.#localImages &&
|
|
1278
|
+
this.#descriptor.terminalDelivery?.protocolVersions.some((version) => version === TERMINAL_GRAPHICS_LOCAL_CAPABILITY_VERSION ||
|
|
1279
|
+
version === TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION ||
|
|
1280
|
+
version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION) === true;
|
|
1281
|
+
channel.graphicsInline = new Set();
|
|
1282
|
+
channel.graphicsBinary =
|
|
1283
|
+
this.#descriptor.terminalDelivery?.protocolVersions.some((version) => version === TERMINAL_GRAPHICS_BINARY_CAPABILITY_VERSION ||
|
|
1284
|
+
version === TERMINAL_GRAPHICS_LOCAL_CAPABILITY_VERSION ||
|
|
1285
|
+
version === TERMINAL_DELIVERY_BINARY_CAPABILITY_VERSION ||
|
|
1286
|
+
version === TERMINAL_PROJECTION_CAPABILITY_VERSION ||
|
|
1287
|
+
version === TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION ||
|
|
1288
|
+
version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION) === true;
|
|
1244
1289
|
// Renderers that report cell pixels learn up front that this daemon
|
|
1245
1290
|
// accepts them, before any image exists.
|
|
1246
1291
|
const cellPixels = this.#descriptor.terminalDelivery?.protocolVersions.includes(TERMINAL_GRAPHICS_CELL_PIXELS_CAPABILITY_VERSION) === true;
|
|
@@ -1268,6 +1313,8 @@ export class PaneStreamLiveConnection {
|
|
|
1268
1313
|
...(zlib ? state : rawGraphicsState(state)),
|
|
1269
1314
|
...(cellPixels ? { cellPixels: true } : {}),
|
|
1270
1315
|
...(preciseCellPixels ? { preciseCellPixels: true } : {}),
|
|
1316
|
+
...(channel.graphicsBinary ? { binaryBlobs: true } : {}),
|
|
1317
|
+
...(channel.graphicsLocal ? { localFiles: true } : {}),
|
|
1271
1318
|
},
|
|
1272
1319
|
});
|
|
1273
1320
|
if (pushed.length > 0)
|
|
@@ -1284,15 +1331,23 @@ export class PaneStreamLiveConnection {
|
|
|
1284
1331
|
placements: [],
|
|
1285
1332
|
cellPixels: true,
|
|
1286
1333
|
...(preciseCellPixels ? { preciseCellPixels: true } : {}),
|
|
1334
|
+
...(channel.graphicsBinary ? { binaryBlobs: true } : {}),
|
|
1335
|
+
...(channel.graphicsLocal ? { localFiles: true } : {}),
|
|
1287
1336
|
},
|
|
1288
1337
|
});
|
|
1289
1338
|
}
|
|
1290
1339
|
}
|
|
1291
|
-
#requestGraphics(pane, digests) {
|
|
1340
|
+
#requestGraphics(pane, digests, preferInline = false) {
|
|
1292
1341
|
const channel = this.#panes.get(pane);
|
|
1293
1342
|
// Requests are only meaningful after an announcement; ignore stray ones.
|
|
1294
1343
|
if (!channel || channel.closed || !channel.graphicsAnnounced)
|
|
1295
1344
|
return;
|
|
1345
|
+
if (preferInline && channel.graphicsLocal) {
|
|
1346
|
+
for (const digest of digests) {
|
|
1347
|
+
if ((channel.graphicsInline?.size ?? 0) < GRAPHICS_MAX_QUEUED_DIGESTS)
|
|
1348
|
+
channel.graphicsInline?.add(digest);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1296
1351
|
this.#queueGraphics(channel, digests);
|
|
1297
1352
|
}
|
|
1298
1353
|
#queueGraphics(channel, digests) {
|
|
@@ -1313,6 +1368,7 @@ export class PaneStreamLiveConnection {
|
|
|
1313
1368
|
try {
|
|
1314
1369
|
while (!this.#closed && !channel.closed && channel.graphicsQueue.length > 0) {
|
|
1315
1370
|
const digest = channel.graphicsQueue.shift();
|
|
1371
|
+
const inline = channel.graphicsInline?.delete(digest) ?? false;
|
|
1316
1372
|
const graphics = channel.delivery?.graphics;
|
|
1317
1373
|
const image = graphics?.image(digest);
|
|
1318
1374
|
if (!graphics || !image)
|
|
@@ -1339,6 +1395,49 @@ export class PaneStreamLiveConnection {
|
|
|
1339
1395
|
continue;
|
|
1340
1396
|
const format = raw ? (image.format === "rgba-zlib" ? "rgba" : "rgb") : image.format;
|
|
1341
1397
|
const total = data.byteLength;
|
|
1398
|
+
if (!inline && channel.graphicsLocal && !raw && this.#localImages) {
|
|
1399
|
+
for (const [id, lease] of this.#localLeases)
|
|
1400
|
+
if (lease.expiresAt <= Date.now())
|
|
1401
|
+
this.#localLeases.delete(id);
|
|
1402
|
+
if (this.#localLeases.size < 64) {
|
|
1403
|
+
try {
|
|
1404
|
+
const lease = await this.#localImages.acquire({
|
|
1405
|
+
generation: this.#localImages.identity.generation,
|
|
1406
|
+
client: this.#descriptor.leaseId,
|
|
1407
|
+
digest,
|
|
1408
|
+
bytes: data,
|
|
1409
|
+
});
|
|
1410
|
+
if (this.#closed || channel.closed || graphics.image(digest) !== image) {
|
|
1411
|
+
await this.#localImages.release(lease);
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
this.#localLeases.set(lease.id, {
|
|
1415
|
+
pane: channel.semanticPaneId,
|
|
1416
|
+
expiresAt: lease.expiresAt,
|
|
1417
|
+
});
|
|
1418
|
+
this.#sendFrame(null, {
|
|
1419
|
+
type: "terminal-graphics-local",
|
|
1420
|
+
pane: channel.semanticPaneId,
|
|
1421
|
+
generation: lease.generation,
|
|
1422
|
+
leaseId: lease.id,
|
|
1423
|
+
path: lease.path,
|
|
1424
|
+
expiresAt: lease.expiresAt,
|
|
1425
|
+
blob: {
|
|
1426
|
+
digest,
|
|
1427
|
+
format,
|
|
1428
|
+
width: image.width,
|
|
1429
|
+
height: image.height,
|
|
1430
|
+
total,
|
|
1431
|
+
offset: 0,
|
|
1432
|
+
},
|
|
1433
|
+
});
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
catch {
|
|
1437
|
+
/* Optional transport: retain the negotiated inline fallback. */
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1342
1441
|
for (let offset = 0; offset < total; offset += TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES) {
|
|
1343
1442
|
while ((this.#socket.bufferedAmount ?? 0) > GRAPHICS_SEND_WINDOW_BYTES) {
|
|
1344
1443
|
if (this.#closed || channel.closed)
|
|
@@ -1352,6 +1451,20 @@ export class PaneStreamLiveConnection {
|
|
|
1352
1451
|
if (offset > 0 && graphics.image(digest) !== image)
|
|
1353
1452
|
break;
|
|
1354
1453
|
const end = Math.min(total, offset + TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES);
|
|
1454
|
+
if (channel.graphicsBinary) {
|
|
1455
|
+
this.#sendEncoded(null, Buffer.from(encodeTerminalGraphicsBinary({
|
|
1456
|
+
pane: channel.semanticPaneId,
|
|
1457
|
+
blob: {
|
|
1458
|
+
digest,
|
|
1459
|
+
format,
|
|
1460
|
+
width: image.width,
|
|
1461
|
+
height: image.height,
|
|
1462
|
+
total,
|
|
1463
|
+
offset,
|
|
1464
|
+
},
|
|
1465
|
+
}, data.subarray(offset, end))), true);
|
|
1466
|
+
continue;
|
|
1467
|
+
}
|
|
1355
1468
|
this.#sendFrame(null, {
|
|
1356
1469
|
type: "terminal-graphics-blob",
|
|
1357
1470
|
pane: channel.semanticPaneId,
|
|
@@ -1671,7 +1784,12 @@ export class PaneStreamLiveConnection {
|
|
|
1671
1784
|
if (this.#closed || this.#socket.readyState !== WS_OPEN)
|
|
1672
1785
|
return;
|
|
1673
1786
|
const encoded = JSON.stringify(frame);
|
|
1674
|
-
|
|
1787
|
+
this.#sendEncoded(pane, encoded, false);
|
|
1788
|
+
}
|
|
1789
|
+
#sendEncoded(pane, encoded, binary) {
|
|
1790
|
+
if (this.#closed || this.#socket.readyState !== WS_OPEN)
|
|
1791
|
+
return;
|
|
1792
|
+
const bytes = Buffer.byteLength(encoded);
|
|
1675
1793
|
if (pane !== null) {
|
|
1676
1794
|
this.#ledger.take(this.#clientId, pane, "ws-send-buffer", bytes);
|
|
1677
1795
|
if (this.#deliveryAcks)
|
|
@@ -1680,7 +1798,7 @@ export class PaneStreamLiveConnection {
|
|
|
1680
1798
|
this.#sendQueue.push({ pane, remaining: bytes });
|
|
1681
1799
|
this.#sentBytesTotal += bytes;
|
|
1682
1800
|
try {
|
|
1683
|
-
this.#socket.send(encoded, { binary
|
|
1801
|
+
this.#socket.send(encoded, { binary });
|
|
1684
1802
|
}
|
|
1685
1803
|
catch {
|
|
1686
1804
|
this.close(1011, "stream-unavailable");
|
|
@@ -1914,6 +2032,24 @@ export class PaneStreamLiveConnection {
|
|
|
1914
2032
|
});
|
|
1915
2033
|
return;
|
|
1916
2034
|
}
|
|
2035
|
+
if (frame.type === "terminal-projection-receipt") {
|
|
2036
|
+
const channel = this.#deliveryChannel(frame.receipt);
|
|
2037
|
+
if (!channel)
|
|
2038
|
+
return;
|
|
2039
|
+
if (!channel.delivery?.projectionReceipt)
|
|
2040
|
+
return this.#failProtocol("protocol-error");
|
|
2041
|
+
channel.delivery.projectionReceipt(frame.receipt);
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
if (frame.type === "terminal-projection-materialize") {
|
|
2045
|
+
const channel = this.#deliveryChannel(frame.request);
|
|
2046
|
+
if (!channel)
|
|
2047
|
+
return;
|
|
2048
|
+
if (!channel.delivery?.materialize)
|
|
2049
|
+
return this.#failProtocol("protocol-error");
|
|
2050
|
+
channel.delivery.materialize(frame.request);
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
1917
2053
|
if (frame.type === "terminal-delivery-ack") {
|
|
1918
2054
|
const channel = this.#deliveryChannel(frame.ack);
|
|
1919
2055
|
if (!channel)
|
|
@@ -1946,7 +2082,23 @@ export class PaneStreamLiveConnection {
|
|
|
1946
2082
|
return;
|
|
1947
2083
|
}
|
|
1948
2084
|
if (frame.type === "terminal-graphics-request") {
|
|
1949
|
-
this.#requestGraphics(frame.pane, frame.digests);
|
|
2085
|
+
this.#requestGraphics(frame.pane, frame.digests, frame.preferInline === true);
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2088
|
+
if (frame.type === "terminal-graphics-local-release") {
|
|
2089
|
+
const lease = this.#localLeases.get(frame.leaseId);
|
|
2090
|
+
if (lease &&
|
|
2091
|
+
lease.pane === frame.pane &&
|
|
2092
|
+
frame.generation === this.#localImages?.identity.generation) {
|
|
2093
|
+
this.#localLeases.delete(frame.leaseId);
|
|
2094
|
+
void this.#localImages
|
|
2095
|
+
?.release({
|
|
2096
|
+
generation: frame.generation,
|
|
2097
|
+
client: this.#descriptor.leaseId,
|
|
2098
|
+
id: frame.leaseId,
|
|
2099
|
+
})
|
|
2100
|
+
.catch(() => { });
|
|
2101
|
+
}
|
|
1950
2102
|
return;
|
|
1951
2103
|
}
|
|
1952
2104
|
if (frame.type === "terminal-cell-pixels") {
|
|
@@ -2127,6 +2279,12 @@ export class PaneStreamLiveConnection {
|
|
|
2127
2279
|
this.#evaluateResume(pane);
|
|
2128
2280
|
}
|
|
2129
2281
|
#acceptInput(pane, seq, kind, data, frameBytes, performanceTraceId, causalProbe, ingressAtMicros = null, callbackAtSharedMicros = null, ingressAtSharedMicros = null, expectedLifetime) {
|
|
2282
|
+
if (kind === "bytes" &&
|
|
2283
|
+
(!this.#sessionRuntimeBinding ||
|
|
2284
|
+
!this.#descriptor.terminalDelivery?.protocolVersions.includes(PANE_STREAM_INPUT_BYTES_CAPABILITY_VERSION))) {
|
|
2285
|
+
this.#failProtocol("input-rejected");
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2130
2288
|
if (expectedLifetime &&
|
|
2131
2289
|
(!this.#sessionRuntimeBinding ||
|
|
2132
2290
|
!this.#descriptor.terminalDelivery?.protocolVersions.includes(PANE_STREAM_INPUT_LIFETIME_CAPABILITY_VERSION))) {
|
|
@@ -2227,8 +2385,10 @@ export class PaneStreamLiveConnection {
|
|
|
2227
2385
|
: undefined, expectedLifetime);
|
|
2228
2386
|
else if (kind === "text")
|
|
2229
2387
|
channel.sub.sendText(data);
|
|
2230
|
-
else
|
|
2388
|
+
else if (kind === "key")
|
|
2231
2389
|
channel.sub.sendKey(data);
|
|
2390
|
+
else
|
|
2391
|
+
throw new Error("Byte input requires semantic runtime");
|
|
2232
2392
|
let ackStartedAtMicros = null;
|
|
2233
2393
|
let ackStartedAtSharedMicros = null;
|
|
2234
2394
|
if (trace) {
|
|
@@ -16,6 +16,7 @@ export class PaneStreamRuntime {
|
|
|
16
16
|
const transportBinder = new SessionRuntimeTransportBinder(options.sessionRuntimeRegistry);
|
|
17
17
|
this.coordinator = new PaneStreamAdmissionCoordinator({
|
|
18
18
|
...options.admission,
|
|
19
|
+
localImages: options.localImages,
|
|
19
20
|
daemonInstanceId: options.daemonInstanceId,
|
|
20
21
|
webSocketUrl: options.webSocketUrl,
|
|
21
22
|
leaseManager,
|
|
@@ -1653,6 +1653,8 @@ class SessionRuntimeConsumerImpl {
|
|
|
1653
1653
|
negotiation: upstream.negotiation,
|
|
1654
1654
|
closed: upstream.closed,
|
|
1655
1655
|
ack: (ack) => upstream.ack(ack),
|
|
1656
|
+
...(upstream.projectionReceipt ? { projectionReceipt: upstream.projectionReceipt } : {}),
|
|
1657
|
+
...(upstream.materialize ? { materialize: upstream.materialize } : {}),
|
|
1656
1658
|
nack: (nack) => upstream.nack(nack),
|
|
1657
1659
|
setVisibility: (visibility) => upstream.setVisibility(visibility),
|
|
1658
1660
|
close: async () => {
|