instar 1.3.1592 → 1.3.1593
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/dashboard/desktop.css +1 -0
- package/dashboard/desktop.html +7 -0
- package/dashboard/desktop.js +205 -6
- package/dist/core/AutonomousStartAdmission.d.ts +10 -4
- package/dist/core/AutonomousStartAdmission.d.ts.map +1 -1
- package/dist/core/AutonomousStartAdmission.js +14 -9
- package/dist/core/AutonomousStartAdmission.js.map +1 -1
- package/dist/core/DesktopHost.d.ts +30 -0
- package/dist/core/DesktopHost.d.ts.map +1 -1
- package/dist/core/DesktopHost.js +24 -0
- package/dist/core/DesktopHost.js.map +1 -1
- package/dist/core/DirectDesktopAccess.d.ts +11 -1
- package/dist/core/DirectDesktopAccess.d.ts.map +1 -1
- package/dist/core/DirectDesktopAccess.js +46 -0
- package/dist/core/DirectDesktopAccess.js.map +1 -1
- package/dist/core/MacDesktopHost.d.ts +12 -1
- package/dist/core/MacDesktopHost.d.ts.map +1 -1
- package/dist/core/MacDesktopHost.js +70 -6
- package/dist/core/MacDesktopHost.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +12 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/WindowRunCadenceExecutor.d.ts +24 -4
- package/dist/core/WindowRunCadenceExecutor.d.ts.map +1 -1
- package/dist/core/WindowRunCadenceExecutor.js +138 -18
- package/dist/core/WindowRunCadenceExecutor.js.map +1 -1
- package/dist/core/WindowRunLivenessAuthority.d.ts +48 -4
- package/dist/core/WindowRunLivenessAuthority.d.ts.map +1 -1
- package/dist/core/WindowRunLivenessAuthority.js +186 -30
- package/dist/core/WindowRunLivenessAuthority.js.map +1 -1
- package/dist/core/WindowsDesktopHost.d.ts +9 -1
- package/dist/core/WindowsDesktopHost.d.ts.map +1 -1
- package/dist/core/WindowsDesktopHost.js +43 -4
- package/dist/core/WindowsDesktopHost.js.map +1 -1
- package/dist/core/WindowsDesktopLocalTransport.js +1 -1
- package/dist/core/WindowsDesktopLocalTransport.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/native/instar-desktop-host +0 -0
- package/dist/native/windows/InstarDesktopCompanion.cs +84 -16
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +6 -7
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.js +1 -1
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/DesktopMeshBroker.d.ts +1 -1
- package/dist/server/DesktopMeshBroker.d.ts.map +1 -1
- package/dist/server/DesktopMeshBroker.js.map +1 -1
- package/dist/server/DesktopStreamManager.d.ts +16 -0
- package/dist/server/DesktopStreamManager.d.ts.map +1 -1
- package/dist/server/DesktopStreamManager.js +39 -1
- package/dist/server/DesktopStreamManager.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +85 -23
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/windows-desktop-local-transport.ps1 +2 -2
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/src/scaffold/templates.ts +1 -1
- package/upgrades/1.3.1593.md +53 -0
- package/upgrades/side-effects/desktop-multi-display-gestures.md +73 -0
- package/upgrades/side-effects/desktop-stream-frame-stats.md +45 -0
- package/upgrades/side-effects/window-run-liveness-per-topic.eli16.md +28 -0
- package/upgrades/side-effects/window-run-liveness-per-topic.md +81 -0
- package/upgrades/1.3.1592.md +0 -22
package/dashboard/desktop.css
CHANGED
|
@@ -20,6 +20,7 @@ body.desktop-live #keyboardBar input{-webkit-touch-callout:default;-webkit-user-
|
|
|
20
20
|
.desktop-dock button{min-width:2.9rem;min-height:2.9rem;padding:.35rem;background:#111827e8;color:#f8fafc;border:1px solid #64748b;box-shadow:0 2px 12px #0009}
|
|
21
21
|
.desktop-tools{position:absolute;z-index:6;right:4rem;bottom:max(.7rem,env(safe-area-inset-bottom));width:min(19rem,calc(100vw - 5rem));max-height:min(60dvh,32rem);overflow:auto;display:flex;flex-wrap:wrap;gap:.4rem;padding:.55rem;background:#111827f2;border:1px solid #64748b;border-radius:10px;box-shadow:0 4px 18px #0009}
|
|
22
22
|
.desktop-tools[hidden],#keyboardBar[hidden],.desktop-dock button[hidden],.desktop-tools button[hidden]{display:none}
|
|
23
|
+
.display-note{flex:1 0 100%;margin:0;font-size:.8rem;color:#f8fafc}.display-note[hidden]{display:none}#displayMenu button{flex:1 0 100%;text-align:left}
|
|
23
24
|
.desktop-tools button{flex:1 0 auto;padding:.5rem .65rem}.type-label{display:block;margin:0;font-size:.8rem}
|
|
24
25
|
.desktop-tools button[aria-pressed="true"]{background:#fbbf24;color:#422006}
|
|
25
26
|
#keyboardBar{position:absolute;z-index:7;left:max(.5rem,env(safe-area-inset-left));right:max(.5rem,env(safe-area-inset-right));bottom:max(.5rem,env(safe-area-inset-bottom));display:flex;align-items:center;gap:.4rem;padding:.4rem;background:#111827f2;border:1px solid #64748b;border-radius:10px}
|
package/dashboard/desktop.html
CHANGED
|
@@ -36,11 +36,18 @@
|
|
|
36
36
|
</div>
|
|
37
37
|
<div class="desktop-dock" aria-label="Remote desktop controls">
|
|
38
38
|
<button id="toolsButton" type="button" aria-label="More controls" aria-expanded="false" aria-controls="desktopTools">☰</button>
|
|
39
|
+
<button id="displaysButton" type="button" aria-label="Screens" aria-expanded="false" aria-controls="displayMenu" hidden>⧉</button>
|
|
39
40
|
<button id="keyboardButton" type="button" aria-label="Show keyboard" aria-expanded="false" aria-controls="keyboardBar">⌨</button>
|
|
40
41
|
<button id="zoomResetButton" type="button" aria-label="Fit screen" hidden>Fit</button>
|
|
41
42
|
<button id="fullscreenButton" type="button" aria-label="Full screen">⛶</button>
|
|
42
43
|
</div>
|
|
44
|
+
<div class="desktop-tools" id="displayMenu" aria-label="Screens" hidden></div>
|
|
43
45
|
<div class="desktop-tools" id="desktopTools" hidden>
|
|
46
|
+
<p id="displayNote" class="display-note" hidden></p>
|
|
47
|
+
<button data-shortcut="overview" type="button" hidden></button>
|
|
48
|
+
<button data-shortcut="appWindows" type="button" hidden></button>
|
|
49
|
+
<button data-shortcut="previousSpace" type="button" hidden></button>
|
|
50
|
+
<button data-shortcut="nextSpace" type="button" hidden></button>
|
|
44
51
|
<button id="modeButton" type="button" aria-pressed="true" hidden>Trackpad · swipe to move</button>
|
|
45
52
|
<button id="dragButton" type="button" aria-pressed="false">Drag</button>
|
|
46
53
|
<button id="rightButton" type="button">Right click</button>
|
package/dashboard/desktop.js
CHANGED
|
@@ -89,8 +89,38 @@ const LONG_PRESS_MS = 550;
|
|
|
89
89
|
const DOUBLE_TAP_MS = 300;
|
|
90
90
|
let mousePress = null;
|
|
91
91
|
let lastTap = null;
|
|
92
|
+
// A lone tap's click waits this long. A click followed at once by a press reads on
|
|
93
|
+
// the host as a double-click, which maximizes a window being dragged by its title.
|
|
94
|
+
let pendingTapClick = 0;
|
|
92
95
|
let dragPointerId = null;
|
|
93
96
|
let multiTap = null;
|
|
97
|
+
// What the connected machine says about its screens. Until it answers
|
|
98
|
+
// supported, selectDisplay is never sent: an older host ends the session on a
|
|
99
|
+
// command it does not know.
|
|
100
|
+
let displayInfo = null;
|
|
101
|
+
let overviewOpen = null;
|
|
102
|
+
let threeFinger = null;
|
|
103
|
+
const THREE_FINGER_TRAVEL = 60;
|
|
104
|
+
// While a switch settles the picture still shows the old screen, and a tap would
|
|
105
|
+
// land on the new one at the wrong place. Hold pointer presses until a new frame.
|
|
106
|
+
let screenSwitch = null;
|
|
107
|
+
const SCREEN_SWITCH_SETTLE_MS = 400;
|
|
108
|
+
const SCREEN_SWITCH_LIMIT_MS = 3_000;
|
|
109
|
+
// Standard shortcuts, sent as ordinary keys. Spaces follow the fingers as on a
|
|
110
|
+
// Mac trackpad: swiping left brings in the Space on the right.
|
|
111
|
+
const DESKTOP_SHORTCUTS = {
|
|
112
|
+
macos: {
|
|
113
|
+
overview: { label: 'Mission Control', hint: 'Control+Up', modifiers: ['Control'], key: 'ArrowUp' },
|
|
114
|
+
appWindows: { label: 'App windows', hint: 'Control+Down', modifiers: ['Control'], key: 'ArrowDown' },
|
|
115
|
+
nextSpace: { label: 'Next Space', hint: 'Control+Right', modifiers: ['Control'], key: 'ArrowRight' },
|
|
116
|
+
previousSpace: { label: 'Previous Space', hint: 'Control+Left', modifiers: ['Control'], key: 'ArrowLeft' },
|
|
117
|
+
},
|
|
118
|
+
windows: {
|
|
119
|
+
overview: { label: 'Task View', hint: 'Win+Tab', modifiers: ['Meta'], key: 'Tab' },
|
|
120
|
+
nextSpace: { label: 'Next desktop', hint: 'Ctrl+Win+Right', modifiers: ['Control', 'Meta'], key: 'ArrowRight' },
|
|
121
|
+
previousSpace: { label: 'Previous desktop', hint: 'Ctrl+Win+Left', modifiers: ['Control', 'Meta'], key: 'ArrowLeft' },
|
|
122
|
+
},
|
|
123
|
+
};
|
|
94
124
|
|
|
95
125
|
// Watching counts as using the session: while the viewer is visible a light
|
|
96
126
|
// ping keeps the two-minute idle limit from ending it.
|
|
@@ -134,6 +164,7 @@ function releaseButton() {
|
|
|
134
164
|
}
|
|
135
165
|
function beginHold(button) {
|
|
136
166
|
if (heldButton) return;
|
|
167
|
+
overviewOpen = null; // This press may close it.
|
|
137
168
|
flushPendingMove();
|
|
138
169
|
buttonReleaseRequested = false;
|
|
139
170
|
const epoch = streamEpoch;
|
|
@@ -393,6 +424,7 @@ async function receiveFrame(recordBuffer, epoch) {
|
|
|
393
424
|
additionalData: recordAad('frame') }, aesKey, record.subarray(8));
|
|
394
425
|
if (!inputEnabled || epoch !== streamEpoch) return;
|
|
395
426
|
frameSequence = sequence;
|
|
427
|
+
if (screenSwitch && Date.now() - screenSwitch.at >= SCREEN_SWITCH_SETTLE_MS) screenSwitch = null;
|
|
396
428
|
const bytes = new Uint8Array(frame);
|
|
397
429
|
if (bytes[0] !== 0xff || bytes[1] !== 0xd8) {
|
|
398
430
|
try { receiveH264(bytes, epoch); }
|
|
@@ -428,6 +460,11 @@ function leave(message) {
|
|
|
428
460
|
pendingWheel = null;
|
|
429
461
|
stopEdgePan();
|
|
430
462
|
pointers.clear();
|
|
463
|
+
displayInfo = null;
|
|
464
|
+
overviewOpen = null;
|
|
465
|
+
threeFinger = null;
|
|
466
|
+
screenSwitch = null;
|
|
467
|
+
renderDisplays();
|
|
431
468
|
setHeldButton(null);
|
|
432
469
|
for (const key of heldKeys.keys()) { heldKeys.delete(key); keyOperationIds.delete(key); updateKeyButton(key); }
|
|
433
470
|
keyReleaseRequested.clear();
|
|
@@ -868,10 +905,12 @@ async function openStream(result, resume = null) {
|
|
|
868
905
|
updateCursor();
|
|
869
906
|
$('machineName').textContent = ticket.label;
|
|
870
907
|
status('Remote desktop is live.');
|
|
908
|
+
void refreshDisplays(epoch);
|
|
871
909
|
if (resume !== null) resumeAttempt = null;
|
|
872
910
|
resumeTransportFailures = 0;
|
|
873
911
|
heartbeat = setInterval(() => {
|
|
874
912
|
sendCommand({ type: 'ping' });
|
|
913
|
+
void refreshDisplays(epoch); // Picks up a screen plugged in, unplugged, or a read that failed.
|
|
875
914
|
}, 30_000);
|
|
876
915
|
resolve();
|
|
877
916
|
});
|
|
@@ -893,6 +932,8 @@ async function openStream(result, resume = null) {
|
|
|
893
932
|
|
|
894
933
|
function send(action) {
|
|
895
934
|
if (!inputEnabled) return Promise.resolve(false);
|
|
935
|
+
if (screenSwitch && Date.now() - screenSwitch.at >= SCREEN_SWITCH_LIMIT_MS) screenSwitch = null;
|
|
936
|
+
if (screenSwitch && ['click', 'buttonDown', 'move'].includes(action.kind)) return Promise.resolve(false);
|
|
896
937
|
return sendCommand({ type: 'input', action });
|
|
897
938
|
}
|
|
898
939
|
function imageArea() {
|
|
@@ -981,9 +1022,127 @@ function edgePanTick() {
|
|
|
981
1022
|
}
|
|
982
1023
|
}
|
|
983
1024
|
function clickCursor(button = 'left', count = 1) {
|
|
1025
|
+
overviewOpen = null; // A click may have closed it.
|
|
984
1026
|
if (!heldButton) send(count > 1 ? { kind: 'click', ...cursor, button, count } : { kind: 'click', ...cursor, button });
|
|
985
1027
|
}
|
|
986
1028
|
function heldModifiers() { return modifierKeys.filter(key => heldKeys.has(key)); }
|
|
1029
|
+
/** Resolves to the new list, or null when this read told us nothing new. */
|
|
1030
|
+
async function refreshDisplays(epoch = streamEpoch) {
|
|
1031
|
+
let info;
|
|
1032
|
+
try { info = await api(`/desktop/requests/${encodeURIComponent(requestId)}/displays`); }
|
|
1033
|
+
catch { info = { supported: false, platform: null, displays: [], currentDisplayId: null, canShowAll: false }; }
|
|
1034
|
+
if (epoch !== streamEpoch) return null;
|
|
1035
|
+
// One failed read must not take working controls away; the next one retries.
|
|
1036
|
+
if (displayInfo?.supported && !info.supported) return null;
|
|
1037
|
+
displayInfo = info;
|
|
1038
|
+
renderDisplays();
|
|
1039
|
+
return info;
|
|
1040
|
+
}
|
|
1041
|
+
function displayChoices() {
|
|
1042
|
+
const info = displayInfo;
|
|
1043
|
+
if (!info?.supported) return [];
|
|
1044
|
+
const rows = info.displays.map(display => ({ id: display.id,
|
|
1045
|
+
label: `${display.name} · ${display.width}×${display.height}${display.main ? ' · Main' : ''}` }));
|
|
1046
|
+
return info.canShowAll ? [{ id: null, label: 'All screens' }, ...rows] : rows;
|
|
1047
|
+
}
|
|
1048
|
+
function renderDisplays() {
|
|
1049
|
+
const menu = $('displayMenu');
|
|
1050
|
+
const button = $('displaysButton');
|
|
1051
|
+
if (!menu || !button) return;
|
|
1052
|
+
const info = displayInfo;
|
|
1053
|
+
const choices = displayChoices();
|
|
1054
|
+
button.hidden = !info?.supported || info.displays.length < 2;
|
|
1055
|
+
if (button.hidden) { menu.hidden = true; button.setAttribute('aria-expanded', 'false'); }
|
|
1056
|
+
menu.replaceChildren(...choices.map(choice => {
|
|
1057
|
+
const item = document.createElement('button');
|
|
1058
|
+
item.type = 'button';
|
|
1059
|
+
item.textContent = choice.label; // Host text: never markup.
|
|
1060
|
+
item.setAttribute('aria-pressed', String(choice.id === info.currentDisplayId));
|
|
1061
|
+
item.addEventListener('click', () => { void chooseDisplay(choice.id); });
|
|
1062
|
+
return item;
|
|
1063
|
+
}));
|
|
1064
|
+
const shortcuts = DESKTOP_SHORTCUTS[info?.platform] ?? null;
|
|
1065
|
+
for (const node of document.querySelectorAll('[data-shortcut]')) {
|
|
1066
|
+
const action = shortcuts?.[node.dataset.shortcut];
|
|
1067
|
+
node.hidden = !action;
|
|
1068
|
+
if (action) node.textContent = action.label;
|
|
1069
|
+
}
|
|
1070
|
+
const note = $('displayNote');
|
|
1071
|
+
if (note) {
|
|
1072
|
+
note.hidden = !info || info.supported;
|
|
1073
|
+
if (!note.hidden) note.textContent = 'This machine needs an update before you can switch screens.';
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
async function chooseDisplay(displayId) {
|
|
1077
|
+
if (!displayInfo?.supported || !inputEnabled) return;
|
|
1078
|
+
const epoch = streamEpoch;
|
|
1079
|
+
const chosen = displayChoices().find(choice => choice.id === displayId);
|
|
1080
|
+
$('displayMenu').hidden = true;
|
|
1081
|
+
$('displaysButton').setAttribute('aria-expanded', 'false');
|
|
1082
|
+
overviewOpen = null;
|
|
1083
|
+
flushPendingMove();
|
|
1084
|
+
resetZoom();
|
|
1085
|
+
releaseButton();
|
|
1086
|
+
if (!await send({ kind: 'selectDisplay', displayId })) return;
|
|
1087
|
+
// macOS applies Spaces to the screen under the pointer, so the gestures
|
|
1088
|
+
// follow the picture only if the pointer does.
|
|
1089
|
+
cursor = { x: 0.5, y: 0.5 };
|
|
1090
|
+
void send({ kind: 'move', ...cursor });
|
|
1091
|
+
screenSwitch = { at: Date.now() };
|
|
1092
|
+
updateCursor();
|
|
1093
|
+
status(`Showing ${chosen?.label.split(' · ')[0] ?? 'the chosen screen'}.`);
|
|
1094
|
+
await new Promise(resolve => setTimeout(resolve, 1_200));
|
|
1095
|
+
const info = await refreshDisplays(epoch);
|
|
1096
|
+
if (info?.supported && info.currentDisplayId !== displayId) {
|
|
1097
|
+
status('That screen is not available any more.');
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
async function sendChord(modifiers, key) {
|
|
1101
|
+
const epoch = streamEpoch;
|
|
1102
|
+
const pressed = [];
|
|
1103
|
+
let allDown = true;
|
|
1104
|
+
for (const modifier of modifiers) {
|
|
1105
|
+
if (heldKeys.has(modifier)) continue; // A key the operator latched stays latched.
|
|
1106
|
+
if (!await beginKey(modifier)) { allDown = false; break; }
|
|
1107
|
+
pressed.push(modifier);
|
|
1108
|
+
}
|
|
1109
|
+
if (allDown && epoch === streamEpoch) await tapKey(key);
|
|
1110
|
+
for (const modifier of pressed.reverse()) if (epoch === streamEpoch) await releaseKey(modifier);
|
|
1111
|
+
}
|
|
1112
|
+
function runShortcut(name) {
|
|
1113
|
+
const shortcuts = DESKTOP_SHORTCUTS[displayInfo?.platform];
|
|
1114
|
+
const action = shortcuts?.[name];
|
|
1115
|
+
if (!action || !inputEnabled) return;
|
|
1116
|
+
// Named beside the action: this Mac may have the shortcut turned off, and
|
|
1117
|
+
// the viewer cannot see that.
|
|
1118
|
+
status(`${action.label} (${action.hint})`);
|
|
1119
|
+
overviewOpen = name === 'overview' || name === 'appWindows' ? name : null;
|
|
1120
|
+
void sendChord(action.modifiers, action.key);
|
|
1121
|
+
}
|
|
1122
|
+
function closeOverview() {
|
|
1123
|
+
overviewOpen = null;
|
|
1124
|
+
status('Back');
|
|
1125
|
+
void tapKey('Escape');
|
|
1126
|
+
}
|
|
1127
|
+
function swipeThreeFingers(direction) {
|
|
1128
|
+
if (direction === 'left') runShortcut('nextSpace');
|
|
1129
|
+
else if (direction === 'right') runShortcut('previousSpace');
|
|
1130
|
+
else if (direction === 'up') {
|
|
1131
|
+
if (overviewOpen === 'appWindows') closeOverview();
|
|
1132
|
+
else if (!overviewOpen) runShortcut('overview');
|
|
1133
|
+
} else if (overviewOpen) closeOverview();
|
|
1134
|
+
else runShortcut('appWindows');
|
|
1135
|
+
}
|
|
1136
|
+
function moveThreeFingers() {
|
|
1137
|
+
if (!threeFinger || threeFinger.fired || pointers.size < 3 || heldButton) return;
|
|
1138
|
+
const fingers = [...pointers.values()];
|
|
1139
|
+
const dx = fingers.reduce((sum, state) => sum + state.x - state.startX, 0) / fingers.length;
|
|
1140
|
+
const dy = fingers.reduce((sum, state) => sum + state.y - state.startY, 0) / fingers.length;
|
|
1141
|
+
const major = Math.max(Math.abs(dx), Math.abs(dy));
|
|
1142
|
+
if (major < THREE_FINGER_TRAVEL || major < 1.5 * Math.min(Math.abs(dx), Math.abs(dy))) return;
|
|
1143
|
+
threeFinger.fired = true; // One swipe, one action.
|
|
1144
|
+
swipeThreeFingers(Math.abs(dx) > Math.abs(dy) ? (dx < 0 ? 'left' : 'right') : (dy < 0 ? 'up' : 'down'));
|
|
1145
|
+
}
|
|
987
1146
|
function syncTouchModeButton() {
|
|
988
1147
|
$('modeButton').hidden = !touchCapable;
|
|
989
1148
|
$('modeButton').textContent = trackpad ? 'Trackpad · swipe to move' : 'Direct · touch position';
|
|
@@ -1076,6 +1235,18 @@ $('keyboardButton').addEventListener('click', () => {
|
|
|
1076
1235
|
syncVisualViewport();
|
|
1077
1236
|
});
|
|
1078
1237
|
$('hideKeyboardButton').addEventListener('click', closeKeyboard);
|
|
1238
|
+
$('displaysButton')?.addEventListener('click', () => {
|
|
1239
|
+
const open = $('displayMenu').hidden;
|
|
1240
|
+
$('displayMenu').hidden = !open;
|
|
1241
|
+
$('displaysButton').setAttribute('aria-expanded', String(open));
|
|
1242
|
+
if (open) void refreshDisplays(); // A screen may have been plugged in since.
|
|
1243
|
+
});
|
|
1244
|
+
for (const button of document.querySelectorAll('[data-shortcut]')) {
|
|
1245
|
+
button.addEventListener('click', () => {
|
|
1246
|
+
if (overviewOpen && button.dataset.shortcut === overviewOpen) closeOverview();
|
|
1247
|
+
else runShortcut(button.dataset.shortcut);
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1079
1250
|
$('zoomResetButton').addEventListener('click', resetZoom);
|
|
1080
1251
|
$('fullscreenButton').hidden = !document.fullscreenEnabled || !$('control').requestFullscreen;
|
|
1081
1252
|
$('fullscreenButton').addEventListener('click', () => {
|
|
@@ -1159,8 +1330,10 @@ $('surface').addEventListener('pointerdown', event => {
|
|
|
1159
1330
|
multiTap = null;
|
|
1160
1331
|
const state = pointers.get(event.pointerId);
|
|
1161
1332
|
const now = Date.now();
|
|
1162
|
-
state.secondTap = !!lastTap && now - lastTap.at < DOUBLE_TAP_MS
|
|
1333
|
+
state.secondTap = !!pendingTapClick && !!lastTap && now - lastTap.at < DOUBLE_TAP_MS
|
|
1163
1334
|
&& Math.hypot(event.clientX - lastTap.x, event.clientY - lastTap.y) < 40;
|
|
1335
|
+
if (state.secondTap) { clearTimeout(pendingTapClick); pendingTapClick = 0; }
|
|
1336
|
+
else flushTapClick();
|
|
1164
1337
|
// Hold still to right-click, as on AnyDesk and most phone remote desktops.
|
|
1165
1338
|
state.longPress = setTimeout(() => {
|
|
1166
1339
|
if (pointers.get(event.pointerId) !== state || state.maxDistance >= 10 || state.multi || state.secondTap) return;
|
|
@@ -1175,6 +1348,12 @@ $('surface').addEventListener('pointerdown', event => {
|
|
|
1175
1348
|
const pair = pairMetrics();
|
|
1176
1349
|
twoFinger = { ...pair, zoom, panX, panY, lastX: pair.x, lastY: pair.y, mode: 'pending' };
|
|
1177
1350
|
}
|
|
1351
|
+
if (pointers.size === 3 && event.pointerType !== 'mouse') {
|
|
1352
|
+
stopEdgePan();
|
|
1353
|
+
twoFinger = null;
|
|
1354
|
+
for (const state of pointers.values()) { state.startX = state.x; state.startY = state.y; }
|
|
1355
|
+
threeFinger = { fired: false };
|
|
1356
|
+
}
|
|
1178
1357
|
if (!trackpad || event.pointerType === 'mouse') {
|
|
1179
1358
|
const at = position(event);
|
|
1180
1359
|
if (at) { cursor = at; updateCursor(); queueMove(at); }
|
|
@@ -1211,7 +1390,9 @@ $('surface').addEventListener('pointermove', event => {
|
|
|
1211
1390
|
state.x = event.clientX; state.y = event.clientY;
|
|
1212
1391
|
state.maxDistance = Math.max(state.maxDistance,
|
|
1213
1392
|
Math.hypot(event.clientX - state.startX, event.clientY - state.startY));
|
|
1214
|
-
if (
|
|
1393
|
+
if (threeFinger && event.pointerType !== 'mouse') {
|
|
1394
|
+
moveThreeFingers();
|
|
1395
|
+
} else if (pointers.size > 1 && event.pointerType !== 'mouse') {
|
|
1215
1396
|
moveTwoFingers();
|
|
1216
1397
|
} else if (state.multi && event.pointerType !== 'mouse') {
|
|
1217
1398
|
// Do not turn the trailing finger of a scroll gesture into pointer motion.
|
|
@@ -1233,6 +1414,7 @@ $('surface').addEventListener('pointerup', event => {
|
|
|
1233
1414
|
clearTimeout(state.longPress);
|
|
1234
1415
|
pointers.delete(event.pointerId);
|
|
1235
1416
|
if (pointers.size < 2) twoFinger = null;
|
|
1417
|
+
if (pointers.size === 0) threeFinger = null;
|
|
1236
1418
|
if (event.pointerType === 'mouse' && mousePress?.pointerId === event.pointerId) {
|
|
1237
1419
|
const press = mousePress; mousePress = null;
|
|
1238
1420
|
const at = position(event);
|
|
@@ -1268,17 +1450,34 @@ $('surface').addEventListener('pointerup', event => {
|
|
|
1268
1450
|
}
|
|
1269
1451
|
const distance = Math.max(state.maxDistance,
|
|
1270
1452
|
Math.hypot(event.clientX - state.startX, event.clientY - state.startY));
|
|
1271
|
-
if (!state.multi && distance < 10 &&
|
|
1453
|
+
if (!state.multi && distance < 10 && state.secondTap) {
|
|
1454
|
+
// The first tap's click is still unsent: a real double tap goes out as one
|
|
1455
|
+
// double-click, and a second press held without moving as one click.
|
|
1456
|
+
if (!trackpad) { const at = position(event); if (at) cursor = at; }
|
|
1457
|
+
flushPendingMove();
|
|
1458
|
+
clickCursor('left', Date.now() - state.at < 500 ? 2 : 1);
|
|
1459
|
+
lastTap = null;
|
|
1460
|
+
} else if (!state.multi && distance < 10 && Date.now() - state.at < 500) {
|
|
1272
1461
|
if (!trackpad || event.pointerType === 'mouse') { const at = position(event); if (at) cursor = at; }
|
|
1273
|
-
clickCursor(event.button === 2 ? 'right' : 'left');
|
|
1274
|
-
|
|
1462
|
+
if (event.pointerType === 'mouse') { clickCursor(event.button === 2 ? 'right' : 'left'); lastTap = null; }
|
|
1463
|
+
else {
|
|
1464
|
+
lastTap = { at: Date.now(), x: event.clientX, y: event.clientY };
|
|
1465
|
+
pendingTapClick = setTimeout(flushTapClick, DOUBLE_TAP_MS);
|
|
1466
|
+
}
|
|
1275
1467
|
}
|
|
1276
1468
|
});
|
|
1277
1469
|
function twoFingerUsed(tap) { return tap.zoomed === true; }
|
|
1470
|
+
function flushTapClick() {
|
|
1471
|
+
if (!pendingTapClick) return;
|
|
1472
|
+
clearTimeout(pendingTapClick);
|
|
1473
|
+
pendingTapClick = 0;
|
|
1474
|
+
clickCursor('left');
|
|
1475
|
+
}
|
|
1278
1476
|
function cancelGestures() {
|
|
1279
1477
|
stopEdgePan();
|
|
1478
|
+
clearTimeout(pendingTapClick); pendingTapClick = 0;
|
|
1280
1479
|
for (const state of pointers.values()) clearTimeout(state.longPress);
|
|
1281
|
-
pointers.clear(); twoFinger = null; multiTap = null; mousePress = null; dragPointerId = null; lastTap = null;
|
|
1480
|
+
pointers.clear(); twoFinger = null; threeFinger = null; multiTap = null; mousePress = null; dragPointerId = null; lastTap = null;
|
|
1282
1481
|
releaseControls();
|
|
1283
1482
|
}
|
|
1284
1483
|
$('surface').addEventListener('pointercancel', cancelGestures);
|
|
@@ -12,14 +12,18 @@
|
|
|
12
12
|
*
|
|
13
13
|
* THE FIX:
|
|
14
14
|
* 1. `decideAutonomousStartStatus` — 'preparing' only while a W32 window
|
|
15
|
-
* lifecycle is genuinely open AND
|
|
16
|
-
* run. Every other run starts 'active', so the reconciler revives it if its
|
|
15
|
+
* lifecycle is genuinely open AND this topic's liveness binding can take
|
|
16
|
+
* this run. Every other run starts 'active', so the reconciler revives it if its
|
|
17
17
|
* session dies.
|
|
18
18
|
* 2. `PreparingRunWatchdog` — any open run still 'preparing' past a few
|
|
19
19
|
* minutes gets ONE plain notice in its own topic. A run that cannot start
|
|
20
20
|
* is never silent. At most PREPARING_NOTICE_MAX_ATTEMPTS sends per run,
|
|
21
21
|
* counted on the durable run record, so a dead delivery path cannot loop.
|
|
22
22
|
*
|
|
23
|
+
* Liveness holds one binding PER TOPIC (2026-11-01, topic 10000001256): it
|
|
24
|
+
* used to hold one binding for the whole agent, so a run in one topic left a
|
|
25
|
+
* run in another 'preparing' with nothing able to promote it.
|
|
26
|
+
*
|
|
23
27
|
* @self-action-controller: preparing-run-watchdog
|
|
24
28
|
*/
|
|
25
29
|
import type { AutonomousRunRecord, AutonomousRunStore } from './AutonomousRunStore.js';
|
|
@@ -34,7 +38,7 @@ export declare function telegramTopicNotifier(send: (topic: number, text: string
|
|
|
34
38
|
export interface StartAdmissionInput {
|
|
35
39
|
projectName: string;
|
|
36
40
|
topicId: string;
|
|
37
|
-
/** WindowRunLivenessAuthority.status(), or null/undefined when the authority is dark. */
|
|
41
|
+
/** WindowRunLivenessAuthority.status(topicId) for THIS run's topic, or null/undefined when the authority is dark. */
|
|
38
42
|
liveness: {
|
|
39
43
|
enabled: boolean;
|
|
40
44
|
dryRun: boolean;
|
|
@@ -71,7 +75,7 @@ export interface BoundRun {
|
|
|
71
75
|
/** Open runs that have sat in 'preparing' too long and have not been reported yet. */
|
|
72
76
|
export declare function findStuckPreparingRuns(runs: readonly AutonomousRunRecord[], opts: {
|
|
73
77
|
nowMs: number;
|
|
74
|
-
boundRun?: BoundRun | null;
|
|
78
|
+
boundRun?: BoundRun | readonly BoundRun[] | null;
|
|
75
79
|
afterMs?: number;
|
|
76
80
|
afterBoundMs?: number;
|
|
77
81
|
}): AutonomousRunRecord[];
|
|
@@ -82,6 +86,8 @@ export interface PreparingRunWatchdogDeps {
|
|
|
82
86
|
notify: (topicId: string, text: string) => Promise<boolean>;
|
|
83
87
|
/** The run the liveness authority currently evaluates (non-terminal), if any. */
|
|
84
88
|
boundRun?: () => BoundRun | null;
|
|
89
|
+
/** Every run the liveness authority currently evaluates (one per topic). Wins over `boundRun`. */
|
|
90
|
+
boundRuns?: () => readonly BoundRun[];
|
|
85
91
|
now?: () => number;
|
|
86
92
|
}
|
|
87
93
|
export declare class PreparingRunWatchdog {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutonomousStartAdmission.d.ts","sourceRoot":"","sources":["../../src/core/AutonomousStartAdmission.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"AutonomousStartAdmission.d.ts","sourceRoot":"","sources":["../../src/core/AutonomousStartAdmission.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,14 +12,18 @@
|
|
|
12
12
|
*
|
|
13
13
|
* THE FIX:
|
|
14
14
|
* 1. `decideAutonomousStartStatus` — 'preparing' only while a W32 window
|
|
15
|
-
* lifecycle is genuinely open AND
|
|
16
|
-
* run. Every other run starts 'active', so the reconciler revives it if its
|
|
15
|
+
* lifecycle is genuinely open AND this topic's liveness binding can take
|
|
16
|
+
* this run. Every other run starts 'active', so the reconciler revives it if its
|
|
17
17
|
* session dies.
|
|
18
18
|
* 2. `PreparingRunWatchdog` — any open run still 'preparing' past a few
|
|
19
19
|
* minutes gets ONE plain notice in its own topic. A run that cannot start
|
|
20
20
|
* is never silent. At most PREPARING_NOTICE_MAX_ATTEMPTS sends per run,
|
|
21
21
|
* counted on the durable run record, so a dead delivery path cannot loop.
|
|
22
22
|
*
|
|
23
|
+
* Liveness holds one binding PER TOPIC (2026-11-01, topic 10000001256): it
|
|
24
|
+
* used to hold one binding for the whole agent, so a run in one topic left a
|
|
25
|
+
* run in another 'preparing' with nothing able to promote it.
|
|
26
|
+
*
|
|
23
27
|
* @self-action-controller: preparing-run-watchdog
|
|
24
28
|
*/
|
|
25
29
|
/** Lifecycle states in which a W32 window is admitting / running work. */
|
|
@@ -53,10 +57,10 @@ export function decideAutonomousStartStatus(input) {
|
|
|
53
57
|
&& (!Number.isFinite(ceilingMs) || input.nowMs < ceilingMs);
|
|
54
58
|
if (!windowOpen)
|
|
55
59
|
return { initialStatus: 'active', reason: 'no-open-window' };
|
|
56
|
-
// The authority holds
|
|
57
|
-
// binding
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
+
// The authority holds one binding per topic, and only a NEW run can bind it: a
|
|
61
|
+
// live binding in this topic (an earlier run's) refuses it, and a finished
|
|
62
|
+
// binding for this same window closes the window to rebinding. A run that
|
|
63
|
+
// cannot take its topic's binding could never be promoted, so it must not wait.
|
|
60
64
|
const bound = liveness.state;
|
|
61
65
|
if (bound && !TERMINAL_LIVENESS_STATES.includes(bound.status)) {
|
|
62
66
|
return { initialStatus: 'active', reason: 'liveness-slot-held' };
|
|
@@ -75,6 +79,7 @@ export const PREPARING_NOTICE_MAX_ATTEMPTS = 3;
|
|
|
75
79
|
export function findStuckPreparingRuns(runs, opts) {
|
|
76
80
|
const afterMs = opts.afterMs ?? PREPARING_NOTICE_AFTER_MS;
|
|
77
81
|
const afterBoundMs = opts.afterBoundMs ?? PREPARING_NOTICE_AFTER_BOUND_MS;
|
|
82
|
+
const bounds = !opts.boundRun ? [] : Array.isArray(opts.boundRun) ? opts.boundRun : [opts.boundRun];
|
|
78
83
|
return runs.filter((run) => {
|
|
79
84
|
if (run.status !== 'preparing' || run.preparingNoticeAt)
|
|
80
85
|
return false;
|
|
@@ -83,11 +88,11 @@ export function findStuckPreparingRuns(runs, opts) {
|
|
|
83
88
|
const registeredMs = Date.parse(run.registeredAt);
|
|
84
89
|
if (!Number.isFinite(registeredMs))
|
|
85
90
|
return true;
|
|
86
|
-
const bound =
|
|
91
|
+
const bound = bounds.find(entry => entry.topicId === run.topicId && entry.runId === run.runId);
|
|
87
92
|
if (!bound)
|
|
88
93
|
return opts.nowMs - registeredMs >= afterMs;
|
|
89
94
|
// The allowance for a first work receipt runs from when the authority took the run.
|
|
90
|
-
const boundMs = Date.parse(
|
|
95
|
+
const boundMs = Date.parse(bound.boundAt);
|
|
91
96
|
return opts.nowMs - Math.max(registeredMs, Number.isFinite(boundMs) ? boundMs : registeredMs) >= afterBoundMs;
|
|
92
97
|
});
|
|
93
98
|
}
|
|
@@ -112,7 +117,7 @@ export class PreparingRunWatchdog {
|
|
|
112
117
|
this.running = true;
|
|
113
118
|
try {
|
|
114
119
|
const nowMs = this.deps.now?.() ?? Date.now();
|
|
115
|
-
const stuck = findStuckPreparingRuns(this.deps.store.listOpen(nowMs), { nowMs, boundRun: this.deps.boundRun?.() ?? null });
|
|
120
|
+
const stuck = findStuckPreparingRuns(this.deps.store.listOpen(nowMs), { nowMs, boundRun: this.deps.boundRuns?.() ?? this.deps.boundRun?.() ?? null });
|
|
116
121
|
const reported = [];
|
|
117
122
|
for (const run of stuck) {
|
|
118
123
|
// Claim under the run lock, re-checking the live record: a run promoted
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutonomousStartAdmission.js","sourceRoot":"","sources":["../../src/core/AutonomousStartAdmission.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"AutonomousStartAdmission.js","sourceRoot":"","sources":["../../src/core/AutonomousStartAdmission.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,6 +28,11 @@ export type DesktopInput = {
|
|
|
28
28
|
} | {
|
|
29
29
|
kind: 'keyDown' | 'keyUp';
|
|
30
30
|
key: DesktopKey;
|
|
31
|
+
}
|
|
32
|
+
/** Handled by the grant authority, never by host.input. null = host default. */
|
|
33
|
+
| {
|
|
34
|
+
kind: 'selectDisplay';
|
|
35
|
+
displayId: string | null;
|
|
31
36
|
};
|
|
32
37
|
/** Every key a viewer may hold or tap. Shared by the browser viewer, the
|
|
33
38
|
* native app and both host helpers, which each map the same names. */
|
|
@@ -52,7 +57,32 @@ export interface DesktopProbe {
|
|
|
52
57
|
screenGrant?: 'own' | 'inherited' | 'none';
|
|
53
58
|
inputGrant?: 'own' | 'inherited' | 'none';
|
|
54
59
|
}
|
|
60
|
+
export type DesktopPlatform = 'macos' | 'windows';
|
|
61
|
+
export interface DesktopDisplay {
|
|
62
|
+
/** Opaque to viewers; the host resolves it. */
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
/** Real pixels. */
|
|
66
|
+
width: number;
|
|
67
|
+
height: number;
|
|
68
|
+
main: boolean;
|
|
69
|
+
}
|
|
70
|
+
export interface DesktopDisplayList {
|
|
71
|
+
displays: DesktopDisplay[];
|
|
72
|
+
/** null only where canShowAll is true: every screen in one picture. */
|
|
73
|
+
currentDisplayId: string | null;
|
|
74
|
+
canShowAll: boolean;
|
|
75
|
+
}
|
|
76
|
+
export declare function validDisplayId(value: unknown): value is string;
|
|
77
|
+
/** Host helpers report untrusted text. Bound it before any viewer sees it. */
|
|
78
|
+
export declare function parseDesktopDisplays(raw: unknown): DesktopDisplay[];
|
|
55
79
|
export interface DesktopHost {
|
|
80
|
+
/** Absent on a host that cannot tell a viewer which shortcuts apply. */
|
|
81
|
+
readonly platform?: DesktopPlatform;
|
|
82
|
+
/** Absent, or rejecting, means this host cannot switch screens. */
|
|
83
|
+
listDisplays?(): Promise<DesktopDisplayList>;
|
|
84
|
+
/** False when the display is not on this machine; the current one stays. */
|
|
85
|
+
selectDisplay?(displayId: string | null): Promise<boolean>;
|
|
56
86
|
probe(): Promise<DesktopProbe>;
|
|
57
87
|
requestPermission?(): Promise<{
|
|
58
88
|
screenPermission: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DesktopHost.d.ts","sourceRoot":"","sources":["../../src/core/DesktopHost.ts"],"names":[],"mappings":"AAAA;oFACoF;AACpF,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExD,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;AACxC,iEAAiE;GAC/D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAAE,GACjF;IAAE,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAChF;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAAC,GAAG,EAAE,UAAU,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"DesktopHost.d.ts","sourceRoot":"","sources":["../../src/core/DesktopHost.ts"],"names":[],"mappings":"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"}
|
package/dist/core/DesktopHost.js
CHANGED
|
@@ -41,4 +41,28 @@ export function validDesktopInput(action) {
|
|
|
41
41
|
return action.kind === 'type' && typeof action.text === 'string'
|
|
42
42
|
&& action.text.length > 0 && action.text.length <= 1_024 && !action.text.includes('\0');
|
|
43
43
|
}
|
|
44
|
+
const DISPLAY_ID = /^[A-Za-z0-9_-]{1,32}$/;
|
|
45
|
+
const MAX_DISPLAYS = 16;
|
|
46
|
+
export function validDisplayId(value) {
|
|
47
|
+
return typeof value === 'string' && DISPLAY_ID.test(value);
|
|
48
|
+
}
|
|
49
|
+
/** Host helpers report untrusted text. Bound it before any viewer sees it. */
|
|
50
|
+
export function parseDesktopDisplays(raw) {
|
|
51
|
+
const rows = raw?.displays;
|
|
52
|
+
if (!Array.isArray(rows) || rows.length > MAX_DISPLAYS)
|
|
53
|
+
throw new Error('desktop-displays-invalid');
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
return rows.map((row, index) => {
|
|
56
|
+
const { id, name, width, height, main } = (row ?? {});
|
|
57
|
+
if (!validDisplayId(id) || seen.has(id) || !Number.isSafeInteger(width) || !Number.isSafeInteger(height)
|
|
58
|
+
|| width < 1 || width > 65_535
|
|
59
|
+
|| height < 1 || height > 65_535 || typeof main !== 'boolean') {
|
|
60
|
+
throw new Error('desktop-displays-invalid');
|
|
61
|
+
}
|
|
62
|
+
seen.add(id);
|
|
63
|
+
const label = typeof name === 'string'
|
|
64
|
+
? [...name].map(c => (c.charCodeAt(0) < 32 || c.charCodeAt(0) === 127 ? ' ' : c)).join('').trim().slice(0, 64) : '';
|
|
65
|
+
return { id, name: label || `Display ${index + 1}`, width: width, height: height, main };
|
|
66
|
+
});
|
|
67
|
+
}
|
|
44
68
|
//# sourceMappingURL=DesktopHost.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DesktopHost.js","sourceRoot":"","sources":["../../src/core/DesktopHost.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DesktopHost.js","sourceRoot":"","sources":["../../src/core/DesktopHost.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
|
-
import type
|
|
2
|
+
import { type DesktopDisplay, type DesktopHost, type DesktopPlatform, type DesktopProbe } from './DesktopHost.js';
|
|
3
3
|
import type { RemoteStepUpLockoutPort } from './RemoteStepUpLockouts.js';
|
|
4
4
|
import type { DesktopStreamSession } from '../server/DesktopStreamManager.js';
|
|
5
5
|
import type { DesktopAccessAudit } from './DesktopAccessAudit.js';
|
|
@@ -15,6 +15,15 @@ export interface DirectDesktopRequestInput {
|
|
|
15
15
|
previousVideoCodec?: 'jpeg' | 'h264';
|
|
16
16
|
scopeConfirmed: boolean;
|
|
17
17
|
}
|
|
18
|
+
export interface DesktopDisplaysReply {
|
|
19
|
+
/** false: this host cannot switch screens, and selectDisplay must not be sent. */
|
|
20
|
+
supported: boolean;
|
|
21
|
+
/** Which shortcuts apply. null: unknown, so the viewer sends none. */
|
|
22
|
+
platform: DesktopPlatform | null;
|
|
23
|
+
displays: DesktopDisplay[];
|
|
24
|
+
currentDisplayId: string | null;
|
|
25
|
+
canShowAll: boolean;
|
|
26
|
+
}
|
|
18
27
|
export interface DesktopTargetIdentity {
|
|
19
28
|
/** DER SPKI Ed25519 public key pinned to this physical machine. */
|
|
20
29
|
publicKey: Buffer;
|
|
@@ -88,6 +97,7 @@ export declare class DirectDesktopAccess {
|
|
|
88
97
|
x: number;
|
|
89
98
|
y: number;
|
|
90
99
|
}>;
|
|
100
|
+
displays(requestId: string, operatorSessionToken: string): Promise<DesktopDisplaysReply>;
|
|
91
101
|
ticket(requestId: string, operatorSessionToken: string, nonce: string, signature: string): string;
|
|
92
102
|
admit(request: IncomingMessage): Promise<DesktopStreamSession>;
|
|
93
103
|
revoke(requestId: string, operatorSessionToken: string): void;
|