@reticlehq/browser 3.8.2 → 3.10.0
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/dist/actions/action-report.d.ts +14 -0
- package/dist/actions/action-report.js +95 -22
- package/dist/actions/actions-dom.d.ts +17 -6
- package/dist/actions/actions-dom.js +98 -47
- package/dist/actions/actions-press.d.ts +10 -0
- package/dist/actions/actions-press.js +146 -2
- package/dist/actions/actions.js +64 -24
- package/dist/actions/insert-text.d.ts +3 -0
- package/dist/actions/insert-text.js +90 -0
- package/dist/actions/scroll.d.ts +1 -1
- package/dist/actions/scroll.js +25 -1
- package/dist/actions/value-input.d.ts +2 -0
- package/dist/actions/value-input.js +30 -9
- package/dist/commands/commands.js +85 -9
- package/dist/connect-options.d.ts +11 -1
- package/dist/connect-options.js +27 -1
- package/dist/dom/a11y.d.ts +1 -1
- package/dist/dom/a11y.js +11 -10
- package/dist/dom/chart-geometry.d.ts +7 -0
- package/dist/dom/chart-geometry.js +39 -0
- package/dist/dom/occlusion.js +21 -0
- package/dist/dom/query-finders.d.ts +1 -1
- package/dist/dom/query-finders.js +10 -7
- package/dist/dom/query.d.ts +6 -2
- package/dist/dom/query.js +165 -54
- package/dist/dom/scope-roots.d.ts +8 -0
- package/dist/dom/scope-roots.js +44 -0
- package/dist/dom/snapshot.d.ts +3 -0
- package/dist/dom/snapshot.js +9 -2
- package/dist/dom/text-match.js +6 -4
- package/dist/dom/theme.js +6 -7
- package/dist/dom/visibility.d.ts +3 -0
- package/dist/dom/visibility.js +125 -10
- package/dist/observers/canvas.d.ts +1 -0
- package/dist/observers/canvas.js +10 -1
- package/dist/observers/console.js +46 -35
- package/dist/observers/dom.js +28 -4
- package/dist/observers/net-detail/resource-timing.d.ts +12 -0
- package/dist/observers/net-detail/resource-timing.js +69 -0
- package/dist/observers/network.js +13 -44
- package/dist/observers/perf.js +15 -1
- package/dist/presenter/chrome/presenter-verbs.js +1 -0
- package/dist/presenter/presenter-config.d.ts +1 -0
- package/dist/presenter/presenter-config.js +1 -0
- package/dist/presenter/presenter-shell.d.ts +1 -0
- package/dist/presenter/presenter-shell.js +6 -3
- package/dist/presenter/tour/tour.js +4 -0
- package/dist/recorder/recorder.js +82 -39
- package/dist/reticle-inject.js +618 -618
- package/dist/reticle.d.ts +3 -19
- package/dist/reticle.js +76 -110
- package/dist/review/annotator.js +4 -0
- package/dist/security/serialization.js +35 -0
- package/dist/session-continuity.d.ts +1 -0
- package/dist/session-continuity.js +18 -0
- package/dist/storage-keys.js +2 -1
- package/dist/timers/native/native-timers.js +21 -4
- package/dist/transport/bridge-url.d.ts +8 -0
- package/dist/transport/bridge-url.js +52 -0
- package/package.json +10 -10
package/dist/reticle.d.ts
CHANGED
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
import { EventType, RETICLE_URL_PARAM,
|
|
1
|
+
import { EventType, RETICLE_URL_PARAM, type ReticleEvent } from '@reticlehq/core';
|
|
2
2
|
import { type CapabilitiesInput } from './registry/capabilities.js';
|
|
3
3
|
import { type ReticleConnectOptions } from './connect-options.js';
|
|
4
|
-
export { helloMode, shouldBlockProduction } from './connect-options.js';
|
|
4
|
+
export { helloMode, hudWithoutPanel, reticleParamsFromSearch, shouldBlockProduction, } from './connect-options.js';
|
|
5
5
|
export type { ReticleConnectOptions };
|
|
6
|
-
export
|
|
7
|
-
url: string;
|
|
8
|
-
adjusted: boolean;
|
|
9
|
-
};
|
|
10
|
-
export declare function connectionPolicy(pageHostname: string, bridgeUrl: string, allowNonLocalhost: boolean, token: string | undefined, pageProtocol?: string): {
|
|
11
|
-
allowed: boolean;
|
|
12
|
-
reason?: string;
|
|
13
|
-
};
|
|
6
|
+
export { connectionPolicy, withBridgePath } from './transport/bridge-url.js';
|
|
14
7
|
export declare function resolveSessionLabel(option: string | undefined, gen: () => string): string;
|
|
15
8
|
export { RETICLE_URL_PARAM };
|
|
16
|
-
export declare function reticleParamsFromSearch(search: string): {
|
|
17
|
-
session?: string;
|
|
18
|
-
projectId?: string;
|
|
19
|
-
hud?: HudVisibility;
|
|
20
|
-
};
|
|
21
|
-
export declare function hudWithoutPanel(hud: HudVisibility | undefined): {
|
|
22
|
-
remember?: HudVisibility;
|
|
23
|
-
placed: HudVisibility[];
|
|
24
|
-
};
|
|
25
9
|
export declare function resolveConnectIdentity(options: {
|
|
26
10
|
session?: string;
|
|
27
11
|
projectId?: string;
|
package/dist/reticle.js
CHANGED
|
@@ -3,8 +3,8 @@ import { trackBusy } from './busy.js';
|
|
|
3
3
|
import { declareRoutes } from './observers/route.js';
|
|
4
4
|
import { setOutwardPolicy } from './actions/danger-context.js';
|
|
5
5
|
import { coverageMarkers, notApplicableFor } from './dom/addressing/coverage-markers.js';
|
|
6
|
-
import { EventType, RETICLE_DEFAULT_PORT, RETICLE_PROTOCOL_VERSION, RETICLE_URL_PARAM, RETICLE_WS_PATH, bridgeWsUrl, ReticleCommand, MessageKind, SESSION_AUTO, SessionState,
|
|
7
|
-
import { rememberSessionLabel } from './session-continuity.js';
|
|
6
|
+
import { EventType, RETICLE_DEFAULT_PORT, RETICLE_PROTOCOL_VERSION, RETICLE_URL_PARAM, RETICLE_WS_PATH, bridgeWsUrl, ReticleCommand, MessageKind, SESSION_AUTO, SessionState, buildRedactionPolicy, setActiveRedactionPolicy, wireRedactionKeys, RETICLE_ROOT_GLOBAL, RETICLE_SDK_VERSION_GLOBAL, CONTRACT_FINGERPRINT, CONTRACT_PARTS, newDocumentId, NO_EDITS_OBSERVED, PresenterMode, CommandActor, HudCorner, HudVisibility, } from '@reticlehq/core';
|
|
7
|
+
import { rememberSessionLabel, tabSessionStore } from './session-continuity.js';
|
|
8
8
|
import { editEpoch } from './edit-epoch.js';
|
|
9
9
|
import { createCommandRegistry, RELOAD_CACHE_BUST_PARAM, } from './commands/commands.js';
|
|
10
10
|
import { Transport } from './transport/transport.js';
|
|
@@ -24,60 +24,11 @@ import { actionVerb } from './presenter/chrome/presenter-verbs.js';
|
|
|
24
24
|
import { str, refLabel, modeForCommand, presentStatus } from './reticle-presenter-helpers.js';
|
|
25
25
|
import { resetClock } from './timers/clock.js';
|
|
26
26
|
import { nativeWarn } from './timers/console/native-console.js';
|
|
27
|
-
import { installRecorder } from './recorder/recorder.js';
|
|
28
27
|
import { Annotator } from './review/annotator.js';
|
|
29
|
-
import { helloMode, leaseToken, shouldBlockProduction, } from './connect-options.js';
|
|
30
|
-
export { helloMode, shouldBlockProduction } from './connect-options.js';
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
parsed = new URL(bridgeUrl);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return { url: bridgeUrl, adjusted: false };
|
|
38
|
-
}
|
|
39
|
-
if ((parsed.protocol !== 'ws:' && parsed.protocol !== 'wss:') ||
|
|
40
|
-
(parsed.pathname !== '' && parsed.pathname !== '/')) {
|
|
41
|
-
return { url: bridgeUrl, adjusted: false };
|
|
42
|
-
}
|
|
43
|
-
parsed.pathname = RETICLE_WS_PATH;
|
|
44
|
-
return { url: parsed.toString(), adjusted: true };
|
|
45
|
-
}
|
|
46
|
-
export function connectionPolicy(pageHostname, bridgeUrl, allowNonLocalhost, token, pageProtocol = 'http:') {
|
|
47
|
-
let bridge;
|
|
48
|
-
try {
|
|
49
|
-
bridge = new URL(bridgeUrl);
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return { allowed: false, reason: 'invalid Reticle bridge URL' };
|
|
53
|
-
}
|
|
54
|
-
if (bridge.protocol !== 'ws:' && bridge.protocol !== 'wss:') {
|
|
55
|
-
return { allowed: false, reason: 'Reticle bridge URL must use ws:// or wss://' };
|
|
56
|
-
}
|
|
57
|
-
if ((token?.length ?? 0) > TRANSPORT_LIMITS.MAX_TOKEN_LENGTH) {
|
|
58
|
-
return {
|
|
59
|
-
allowed: false,
|
|
60
|
-
reason: `Reticle pairing token exceeds ${String(TRANSPORT_LIMITS.MAX_TOKEN_LENGTH)} characters`,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
const remoteBridge = !isLoopbackHostname(bridge.hostname);
|
|
64
|
-
if (remoteBridge && bridge.protocol !== 'wss:') {
|
|
65
|
-
return { allowed: false, reason: 'a non-local Reticle bridge must use wss://' };
|
|
66
|
-
}
|
|
67
|
-
const remote = !isLocalPage(pageProtocol, pageHostname) || remoteBridge;
|
|
68
|
-
if (!remote)
|
|
69
|
-
return { allowed: true };
|
|
70
|
-
if (!allowNonLocalhost) {
|
|
71
|
-
return {
|
|
72
|
-
allowed: false,
|
|
73
|
-
reason: 'Reticle is disabled outside localhost unless allowNonLocalhost is explicitly enabled',
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
if (token === undefined || 0 === token.length) {
|
|
77
|
-
return { allowed: false, reason: 'a pairing token is required outside localhost' };
|
|
78
|
-
}
|
|
79
|
-
return { allowed: true };
|
|
80
|
-
}
|
|
28
|
+
import { helloMode, hudWithoutPanel, leaseToken, oneOf, reticleParamsFromSearch, shouldBlockProduction, } from './connect-options.js';
|
|
29
|
+
export { helloMode, hudWithoutPanel, reticleParamsFromSearch, shouldBlockProduction, } from './connect-options.js';
|
|
30
|
+
export { connectionPolicy, withBridgePath } from './transport/bridge-url.js';
|
|
31
|
+
import { connectionPolicy, withBridgePath } from './transport/bridge-url.js';
|
|
81
32
|
const MAX_PENDING_NARRATIONS = 20;
|
|
82
33
|
const KILLED_MESSAGE = '[Reticle] killed for this page. Restart your dev server or reload the page to bring it back.';
|
|
83
34
|
const BRIDGE_LOST_SUMMARY = 'Session ended - lost connection to Reticle (the agent is no longer running).';
|
|
@@ -88,33 +39,15 @@ export { RETICLE_URL_PARAM };
|
|
|
88
39
|
function stripReloadCacheBustParam() {
|
|
89
40
|
try {
|
|
90
41
|
const current = new URL(window.location.href);
|
|
91
|
-
if (!current.searchParams.has(RELOAD_CACHE_BUST_PARAM))
|
|
42
|
+
if (!current.searchParams.has(RELOAD_CACHE_BUST_PARAM)) {
|
|
92
43
|
return;
|
|
44
|
+
}
|
|
93
45
|
current.searchParams.delete(RELOAD_CACHE_BUST_PARAM);
|
|
94
46
|
window.history.replaceState(window.history.state, '', current.toString());
|
|
95
47
|
}
|
|
96
48
|
catch {
|
|
97
49
|
}
|
|
98
50
|
}
|
|
99
|
-
export function reticleParamsFromSearch(search) {
|
|
100
|
-
const params = new URLSearchParams(search);
|
|
101
|
-
const out = {};
|
|
102
|
-
const session = params.get(RETICLE_URL_PARAM.SESSION);
|
|
103
|
-
const projectId = params.get(RETICLE_URL_PARAM.PROJECT);
|
|
104
|
-
const hud = oneOf(HudVisibility, params.get(RETICLE_URL_PARAM.HUD));
|
|
105
|
-
if (session !== null && session.length > 0)
|
|
106
|
-
out.session = session;
|
|
107
|
-
if (projectId !== null && projectId.length > 0)
|
|
108
|
-
out.projectId = projectId;
|
|
109
|
-
if (hud !== undefined)
|
|
110
|
-
out.hud = hud;
|
|
111
|
-
return out;
|
|
112
|
-
}
|
|
113
|
-
export function hudWithoutPanel(hud) {
|
|
114
|
-
if (hud === undefined)
|
|
115
|
-
return { placed: [] };
|
|
116
|
-
return { remember: hud, placed: HudVisibility.SHOWN === hud ? [] : [hud] };
|
|
117
|
-
}
|
|
118
51
|
export function resolveConnectIdentity(options, search) {
|
|
119
52
|
const url = reticleParamsFromSearch(search);
|
|
120
53
|
const explicitSession = options.session !== undefined && options.session !== SESSION_AUTO ? options.session : undefined;
|
|
@@ -173,8 +106,9 @@ export class Reticle {
|
|
|
173
106
|
this.#adoptLateProjectId(options);
|
|
174
107
|
return;
|
|
175
108
|
}
|
|
176
|
-
if ('undefined' === typeof window || 'undefined' === typeof document)
|
|
109
|
+
if ('undefined' === typeof window || 'undefined' === typeof document) {
|
|
177
110
|
return;
|
|
111
|
+
}
|
|
178
112
|
const proc = globalThis.process;
|
|
179
113
|
const nodeEnv = proc?.env?.['NODE_ENV'];
|
|
180
114
|
if (shouldBlockProduction(nodeEnv, true === options.allowInProduction)) {
|
|
@@ -203,7 +137,7 @@ export class Reticle {
|
|
|
203
137
|
: undefined;
|
|
204
138
|
const identity = resolveConnectIdentity(options, window.location.search);
|
|
205
139
|
const explicitSession = resolveSessionLabel(identity.session, () => '');
|
|
206
|
-
this.#session = rememberSessionLabel(explicitSession.length > 0 ? explicitSession : undefined,
|
|
140
|
+
this.#session = rememberSessionLabel(explicitSession.length > 0 ? explicitSession : undefined, tabSessionStore(window), () => 'function' === typeof globalThis.crypto?.randomUUID
|
|
207
141
|
? `s${globalThis.crypto.randomUUID()}`
|
|
208
142
|
: `s${Date.now().toString(36)}`);
|
|
209
143
|
this.#token =
|
|
@@ -253,11 +187,19 @@ export class Reticle {
|
|
|
253
187
|
this.#overlay.update({ connected: true, events: 0 });
|
|
254
188
|
}
|
|
255
189
|
this.#pendingHud = reticleParamsFromSearch(window.location.search).hud;
|
|
256
|
-
if (options.present !== false && HudVisibility.REMOVED !== this.#pendingHud)
|
|
190
|
+
if (options.present !== false && HudVisibility.REMOVED !== this.#pendingHud) {
|
|
257
191
|
this.#fetchAndShowPanel(options);
|
|
192
|
+
}
|
|
258
193
|
if (true === options.recorder) {
|
|
259
|
-
|
|
260
|
-
|
|
194
|
+
void import('./recorder/recorder.js')
|
|
195
|
+
.then(({ installRecorder }) => {
|
|
196
|
+
if (!this.#connected || this.#recorder !== undefined) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
this.#recorder = installRecorder({ emit, now: () => Date.now() });
|
|
200
|
+
this.#recorder.mount();
|
|
201
|
+
})
|
|
202
|
+
.catch(() => undefined);
|
|
261
203
|
}
|
|
262
204
|
if (options.annotate ?? options.present !== false) {
|
|
263
205
|
this.#annotator = new Annotator({
|
|
@@ -273,32 +215,40 @@ export class Reticle {
|
|
|
273
215
|
this.#connected = true;
|
|
274
216
|
}
|
|
275
217
|
#fetchAndShowPanel(options) {
|
|
276
|
-
if (this.#panelRequested)
|
|
218
|
+
if (this.#panelRequested) {
|
|
277
219
|
return;
|
|
220
|
+
}
|
|
278
221
|
this.#panelRequested = true;
|
|
279
222
|
void import('./presenter/presenter.js')
|
|
280
223
|
.then(({ Presenter }) => import('./presenter/presenter-settings.js').then((settings) => ({ Presenter, settings })))
|
|
281
224
|
.then(({ Presenter, settings }) => {
|
|
282
|
-
if (!this.#connected)
|
|
225
|
+
if (!this.#connected) {
|
|
283
226
|
return;
|
|
227
|
+
}
|
|
284
228
|
this.#readPanelSettings = settings.getPresenterSettings;
|
|
285
229
|
const panelOptions = {};
|
|
286
|
-
if (options.pace !== undefined)
|
|
230
|
+
if (options.pace !== undefined) {
|
|
287
231
|
panelOptions.paceMs = options.pace;
|
|
232
|
+
}
|
|
288
233
|
if (options.narrationDwellMs !== undefined) {
|
|
289
234
|
panelOptions.narrationDwellMs = options.narrationDwellMs;
|
|
290
235
|
}
|
|
291
|
-
if (options.border !== undefined)
|
|
236
|
+
if (options.border !== undefined) {
|
|
292
237
|
panelOptions.border = options.border;
|
|
293
|
-
|
|
238
|
+
}
|
|
239
|
+
if (options.logMax !== undefined) {
|
|
294
240
|
panelOptions.logMax = options.logMax;
|
|
295
|
-
|
|
241
|
+
}
|
|
242
|
+
if (options.endedFadeMs !== undefined) {
|
|
296
243
|
panelOptions.endedFadeMs = options.endedFadeMs;
|
|
297
|
-
|
|
244
|
+
}
|
|
245
|
+
if (options.idleEndMs !== undefined) {
|
|
298
246
|
panelOptions.idleEndMs = options.idleEndMs;
|
|
247
|
+
}
|
|
299
248
|
panelOptions.sessionId = this.#session;
|
|
300
|
-
if (this.#projectId !== undefined)
|
|
249
|
+
if (this.#projectId !== undefined) {
|
|
301
250
|
panelOptions.projectId = this.#projectId;
|
|
251
|
+
}
|
|
302
252
|
panelOptions.onControl = (intent) => this.#emit(EventType.HUMAN_CONTROL, intent.text !== undefined
|
|
303
253
|
? { kind: intent.kind, text: intent.text }
|
|
304
254
|
: { kind: intent.kind });
|
|
@@ -311,19 +261,24 @@ export class Reticle {
|
|
|
311
261
|
this.#presenter = panel;
|
|
312
262
|
panel.mount();
|
|
313
263
|
panel.showSdkVersion(this.#sdkVersion);
|
|
314
|
-
for (const buffered of this.#pendingPushes.values())
|
|
264
|
+
for (const buffered of this.#pendingPushes.values()) {
|
|
315
265
|
panel.handlePush(buffered);
|
|
266
|
+
}
|
|
316
267
|
this.#pendingPushes.clear();
|
|
317
|
-
if (0 < this.#pendingNarrations.length)
|
|
268
|
+
if (0 < this.#pendingNarrations.length) {
|
|
318
269
|
panel.sessionStart();
|
|
319
|
-
|
|
270
|
+
}
|
|
271
|
+
for (const early of this.#pendingNarrations.splice(0)) {
|
|
320
272
|
panel.narrate(early.text, early.level, early.actor, early.summary);
|
|
321
|
-
|
|
273
|
+
}
|
|
274
|
+
if (this.#pendingHud !== undefined) {
|
|
322
275
|
panel.placeHud(this.#pendingHud, undefined);
|
|
276
|
+
}
|
|
323
277
|
this.#pendingHud = undefined;
|
|
324
278
|
void import('./presenter/tour/tour.js').then((tour) => {
|
|
325
|
-
if (!this.#connected)
|
|
279
|
+
if (!this.#connected) {
|
|
326
280
|
return;
|
|
281
|
+
}
|
|
327
282
|
const shown = tour.mountTour({
|
|
328
283
|
document,
|
|
329
284
|
storage: tour.safeLocalStorage(),
|
|
@@ -334,21 +289,25 @@ export class Reticle {
|
|
|
334
289
|
copy: (text) => navigator.clipboard?.writeText(text).then(() => true, () => false) ?? false,
|
|
335
290
|
select: (element) => {
|
|
336
291
|
const selection = window.getSelection();
|
|
337
|
-
if (null === selection)
|
|
292
|
+
if (null === selection) {
|
|
338
293
|
return;
|
|
294
|
+
}
|
|
339
295
|
const range = document.createRange();
|
|
340
296
|
range.selectNodeContents(element);
|
|
341
297
|
selection.removeAllRanges();
|
|
342
298
|
selection.addRange(range);
|
|
343
299
|
},
|
|
344
300
|
});
|
|
345
|
-
if (shown !== undefined)
|
|
301
|
+
if (shown !== undefined) {
|
|
346
302
|
this.#teardowns.push(shown.destroy);
|
|
303
|
+
}
|
|
347
304
|
});
|
|
348
|
-
if (this.#bridgeConnected)
|
|
305
|
+
if (this.#bridgeConnected) {
|
|
349
306
|
panel.sessionStart();
|
|
350
|
-
|
|
307
|
+
}
|
|
308
|
+
if (this.#annotator !== undefined) {
|
|
351
309
|
panel.bindAnnotator(this.#annotator);
|
|
310
|
+
}
|
|
352
311
|
})
|
|
353
312
|
.catch(() => {
|
|
354
313
|
});
|
|
@@ -371,8 +330,9 @@ export class Reticle {
|
|
|
371
330
|
this.#emit(EventType.STATE_CHANGE, { name, value });
|
|
372
331
|
}
|
|
373
332
|
renderCommit(commits) {
|
|
374
|
-
if (commits > 0)
|
|
333
|
+
if (commits > 0) {
|
|
375
334
|
this.#emit(EventType.RENDER_COMMIT, { commits });
|
|
335
|
+
}
|
|
376
336
|
}
|
|
377
337
|
describe(input) {
|
|
378
338
|
registerCapabilities(input);
|
|
@@ -384,8 +344,9 @@ export class Reticle {
|
|
|
384
344
|
editEpoch.observe(hot);
|
|
385
345
|
}
|
|
386
346
|
disconnect() {
|
|
387
|
-
if (!this.#connected)
|
|
347
|
+
if (!this.#connected) {
|
|
388
348
|
return;
|
|
349
|
+
}
|
|
389
350
|
runTeardowns(this.#emit, this.#teardowns);
|
|
390
351
|
this.#teardowns = [];
|
|
391
352
|
this.#transport?.close();
|
|
@@ -417,8 +378,9 @@ export class Reticle {
|
|
|
417
378
|
this.#transport?.sendEvent(event);
|
|
418
379
|
this.#eventCount += 1;
|
|
419
380
|
this.#overlay?.update({ connected: true, events: this.#eventCount });
|
|
420
|
-
if (type === EventType.ROUTE_CHANGE)
|
|
381
|
+
if (type === EventType.ROUTE_CHANGE) {
|
|
421
382
|
this.#presenter?.refilterFlows();
|
|
383
|
+
}
|
|
422
384
|
}
|
|
423
385
|
catch {
|
|
424
386
|
}
|
|
@@ -428,11 +390,13 @@ export class Reticle {
|
|
|
428
390
|
this.#redactKeys = wireRedactionKeys(config);
|
|
429
391
|
}
|
|
430
392
|
#adoptLateProjectId(options) {
|
|
431
|
-
if (this.#projectId !== undefined)
|
|
393
|
+
if (this.#projectId !== undefined) {
|
|
432
394
|
return;
|
|
395
|
+
}
|
|
433
396
|
const late = resolveConnectIdentity(options, window.location.search).projectId;
|
|
434
|
-
if (late === undefined)
|
|
397
|
+
if (late === undefined) {
|
|
435
398
|
return;
|
|
399
|
+
}
|
|
436
400
|
this.#projectId = late;
|
|
437
401
|
this.#transport?.reannounce();
|
|
438
402
|
}
|
|
@@ -472,13 +436,14 @@ export class Reticle {
|
|
|
472
436
|
}
|
|
473
437
|
if (command.name === ReticleCommand.NARRATE) {
|
|
474
438
|
if (this.#presenter === undefined && this.#panelRequested) {
|
|
475
|
-
if (this.#pendingNarrations.length < MAX_PENDING_NARRATIONS)
|
|
439
|
+
if (this.#pendingNarrations.length < MAX_PENDING_NARRATIONS) {
|
|
476
440
|
this.#pendingNarrations.push({
|
|
477
441
|
text: str(command.args['text']),
|
|
478
442
|
level: str(command.args['level'], 'info'),
|
|
479
443
|
actor: narrationActor(command.args),
|
|
480
444
|
summary: true === command.args['summary'],
|
|
481
445
|
});
|
|
446
|
+
}
|
|
482
447
|
return { ok: true, result: { shown: false, buffered: true } };
|
|
483
448
|
}
|
|
484
449
|
this.#presenter?.sessionStart();
|
|
@@ -487,18 +452,21 @@ export class Reticle {
|
|
|
487
452
|
}
|
|
488
453
|
if (command.name === ReticleCommand.SESSION_CONFIG) {
|
|
489
454
|
const idleEndMs = command.args['idleEndMs'];
|
|
490
|
-
if ('number' === typeof idleEndMs)
|
|
455
|
+
if ('number' === typeof idleEndMs) {
|
|
491
456
|
this.#presenter?.setIdleEndMs(idleEndMs);
|
|
457
|
+
}
|
|
492
458
|
const hud = oneOf(HudVisibility, command.args['hud']);
|
|
493
459
|
const corner = oneOf(HudCorner, command.args['corner']);
|
|
494
460
|
const presenter = this.#presenter;
|
|
495
461
|
let placed;
|
|
496
|
-
if (presenter !== undefined)
|
|
462
|
+
if (presenter !== undefined) {
|
|
497
463
|
placed = presenter.placeHud(hud, corner);
|
|
464
|
+
}
|
|
498
465
|
else {
|
|
499
466
|
const early = hudWithoutPanel(hud);
|
|
500
|
-
if (early.remember !== undefined)
|
|
467
|
+
if (early.remember !== undefined) {
|
|
501
468
|
this.#pendingHud = early.remember;
|
|
469
|
+
}
|
|
502
470
|
placed = early.placed;
|
|
503
471
|
}
|
|
504
472
|
return {
|
|
@@ -548,8 +516,9 @@ export class Reticle {
|
|
|
548
516
|
}
|
|
549
517
|
async #presentBefore(command) {
|
|
550
518
|
const p = this.#presenter;
|
|
551
|
-
if (p === undefined)
|
|
519
|
+
if (p === undefined) {
|
|
552
520
|
return;
|
|
521
|
+
}
|
|
553
522
|
p.setMode(modeForCommand(command.name));
|
|
554
523
|
this.#actHandle = undefined;
|
|
555
524
|
const actor = CommandActor.HARNESS === command.by ? LOG_ACTOR.HARNESS : LOG_ACTOR.AGENT;
|
|
@@ -579,6 +548,3 @@ export class Reticle {
|
|
|
579
548
|
function narrationActor(args) {
|
|
580
549
|
return CommandActor.HARNESS === args['actor'] ? LOG_ACTOR.HARNESS : LOG_ACTOR.AGENT;
|
|
581
550
|
}
|
|
582
|
-
function oneOf(values, raw) {
|
|
583
|
-
return Object.values(values).includes(raw) ? raw : undefined;
|
|
584
|
-
}
|
package/dist/review/annotator.js
CHANGED
|
@@ -69,6 +69,10 @@ export class Annotator {
|
|
|
69
69
|
mount() {
|
|
70
70
|
if (this.#root !== undefined || 'undefined' === typeof document)
|
|
71
71
|
return;
|
|
72
|
+
if (null === document.head || null === document.body) {
|
|
73
|
+
document.addEventListener('DOMContentLoaded', () => this.mount(), { once: true });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
72
76
|
const style = document.createElement('style');
|
|
73
77
|
style.setAttribute(MARK_ATTR, 'style');
|
|
74
78
|
style.textContent = ANNOTATOR_CSS;
|
|
@@ -2,6 +2,12 @@ import { REDACTED_VALUE, TRANSPORT_LIMITS, isSensitiveKey, scrubKnownSecrets, }
|
|
|
2
2
|
const TRUNCATED_VALUE = '[TRUNCATED]';
|
|
3
3
|
const UNSERIALIZABLE_VALUE = '[UNSERIALIZABLE]';
|
|
4
4
|
const OMIT_VALUE = Symbol('omit');
|
|
5
|
+
const PROMISE_SUMMARY = '[Promise]';
|
|
6
|
+
const WINDOW_SUMMARY = '[Window]';
|
|
7
|
+
const WINDOW_TAG = 'Window';
|
|
8
|
+
const PROMISE_TAG = 'Promise';
|
|
9
|
+
const PLAIN_OBJECT_TAG = 'Object';
|
|
10
|
+
const OBJECT_TAG_PATTERN = /^\[object (\w+)\]$/;
|
|
5
11
|
const MAX_KEY_LENGTH = 256;
|
|
6
12
|
const MAX_TOTAL_CHARACTERS = Math.floor(TRANSPORT_LIMITS.MAX_MESSAGE_BYTES / 4);
|
|
7
13
|
const MAX_TOTAL_NODES = TRANSPORT_LIMITS.MAX_COLLECTION_ITEMS * 5;
|
|
@@ -18,6 +24,30 @@ function boundedString(value, state, max) {
|
|
|
18
24
|
state.remainingCharacters -= truncated.length;
|
|
19
25
|
return truncated;
|
|
20
26
|
}
|
|
27
|
+
function opaqueSummary(value) {
|
|
28
|
+
try {
|
|
29
|
+
const tag = OBJECT_TAG_PATTERN.exec(Object.prototype.toString.call(value))?.[1];
|
|
30
|
+
if (WINDOW_TAG === tag) {
|
|
31
|
+
return WINDOW_SUMMARY;
|
|
32
|
+
}
|
|
33
|
+
if (PROMISE_TAG === tag) {
|
|
34
|
+
return PROMISE_SUMMARY;
|
|
35
|
+
}
|
|
36
|
+
if (tag !== undefined && PLAIN_OBJECT_TAG !== tag) {
|
|
37
|
+
const node = value;
|
|
38
|
+
if ('number' === typeof node.nodeType && 'string' === typeof node.nodeName) {
|
|
39
|
+
return `[${tag}]`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if ('function' === typeof value.then) {
|
|
43
|
+
return PROMISE_SUMMARY;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
21
51
|
function sanitize(value, state, depth, key) {
|
|
22
52
|
if (key !== undefined && isSensitiveKey(key))
|
|
23
53
|
return REDACTED_VALUE;
|
|
@@ -40,6 +70,11 @@ function sanitize(value, state, depth, key) {
|
|
|
40
70
|
if ('undefined' === typeof value || 'function' === typeof value || 'symbol' === typeof value) {
|
|
41
71
|
return OMIT_VALUE;
|
|
42
72
|
}
|
|
73
|
+
const opaque = opaqueSummary(value);
|
|
74
|
+
if (opaque !== undefined) {
|
|
75
|
+
state.truncatedValues += 1;
|
|
76
|
+
return boundedString(opaque, state, MAX_KEY_LENGTH);
|
|
77
|
+
}
|
|
43
78
|
if (value instanceof Date)
|
|
44
79
|
return Number.isNaN(value.getTime()) ? null : value.toISOString();
|
|
45
80
|
if (value instanceof Map) {
|
|
@@ -22,3 +22,21 @@ export function rememberSessionLabel(explicit, store, generate) {
|
|
|
22
22
|
write(chosen);
|
|
23
23
|
return chosen;
|
|
24
24
|
}
|
|
25
|
+
export function tabSessionStore(win) {
|
|
26
|
+
let topFrame;
|
|
27
|
+
try {
|
|
28
|
+
topFrame = win.top === win;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
topFrame = false;
|
|
32
|
+
}
|
|
33
|
+
if (!topFrame) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return win.sessionStorage;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
package/dist/storage-keys.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { RETICLE_SESSION_STORAGE_KEY } from '@reticlehq/core';
|
|
1
2
|
export const ReticleStorageKey = {
|
|
2
|
-
SESSION:
|
|
3
|
+
SESSION: RETICLE_SESSION_STORAGE_KEY,
|
|
3
4
|
REF_BASE: '__reticle_ref_base',
|
|
4
5
|
PRESENTER_LOG: 'reticle-presenter-log',
|
|
5
6
|
PRESENTER_LOG_OWNER: 'reticle-presenter-log-owner',
|
|
@@ -1,8 +1,25 @@
|
|
|
1
|
+
import { DETERMINISM_GLOBAL_KEY } from '@reticlehq/core';
|
|
1
2
|
const g = globalThis;
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
function kept(name) {
|
|
4
|
+
const holder = g[Symbol.for(DETERMINISM_GLOBAL_KEY)];
|
|
5
|
+
if ('object' !== typeof holder || null === holder) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
const natives = holder.natives;
|
|
9
|
+
if ('object' !== typeof natives || null === natives) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const fn = natives[name];
|
|
13
|
+
return 'function' === typeof fn ? fn : undefined;
|
|
14
|
+
}
|
|
15
|
+
const realSetTimeout = kept('setTimeout') ??
|
|
16
|
+
('function' === typeof g.setTimeout ? g.setTimeout.bind(g) : null);
|
|
17
|
+
const realClearTimeout = kept('clearTimeout') ??
|
|
18
|
+
('function' === typeof g.clearTimeout ? g.clearTimeout.bind(g) : null);
|
|
19
|
+
const realRaf = kept('requestAnimationFrame') ??
|
|
20
|
+
('function' === typeof g.requestAnimationFrame ? g.requestAnimationFrame.bind(g) : null);
|
|
21
|
+
const realPerfNow = kept('performanceNow') ??
|
|
22
|
+
('function' === typeof g.performance?.now ? g.performance.now.bind(g.performance) : null);
|
|
6
23
|
export const nativeNow = () => (realPerfNow ? realPerfNow() : 0);
|
|
7
24
|
export const nativeSetTimeout = (cb, ms = 0) => realSetTimeout ? realSetTimeout(cb, ms) : 0;
|
|
8
25
|
export const nativeClearTimeout = (id) => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function withBridgePath(bridgeUrl: string): {
|
|
2
|
+
url: string;
|
|
3
|
+
adjusted: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function connectionPolicy(pageHostname: string, bridgeUrl: string, allowNonLocalhost: boolean, token: string | undefined, pageProtocol?: string): {
|
|
6
|
+
allowed: boolean;
|
|
7
|
+
reason?: string;
|
|
8
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { RETICLE_WS_PATH, TRANSPORT_LIMITS, isLocalPage, isLoopbackHostname, } from '@reticlehq/core';
|
|
2
|
+
export function withBridgePath(bridgeUrl) {
|
|
3
|
+
let parsed;
|
|
4
|
+
try {
|
|
5
|
+
parsed = new URL(bridgeUrl);
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return { url: bridgeUrl, adjusted: false };
|
|
9
|
+
}
|
|
10
|
+
if ((parsed.protocol !== 'ws:' && parsed.protocol !== 'wss:') ||
|
|
11
|
+
(parsed.pathname !== '' && parsed.pathname !== '/')) {
|
|
12
|
+
return { url: bridgeUrl, adjusted: false };
|
|
13
|
+
}
|
|
14
|
+
parsed.pathname = RETICLE_WS_PATH;
|
|
15
|
+
return { url: parsed.toString(), adjusted: true };
|
|
16
|
+
}
|
|
17
|
+
export function connectionPolicy(pageHostname, bridgeUrl, allowNonLocalhost, token, pageProtocol = 'http:') {
|
|
18
|
+
let bridge;
|
|
19
|
+
try {
|
|
20
|
+
bridge = new URL(bridgeUrl);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return { allowed: false, reason: 'invalid Reticle bridge URL' };
|
|
24
|
+
}
|
|
25
|
+
if (bridge.protocol !== 'ws:' && bridge.protocol !== 'wss:') {
|
|
26
|
+
return { allowed: false, reason: 'Reticle bridge URL must use ws:// or wss://' };
|
|
27
|
+
}
|
|
28
|
+
if ((token?.length ?? 0) > TRANSPORT_LIMITS.MAX_TOKEN_LENGTH) {
|
|
29
|
+
return {
|
|
30
|
+
allowed: false,
|
|
31
|
+
reason: `Reticle pairing token exceeds ${String(TRANSPORT_LIMITS.MAX_TOKEN_LENGTH)} characters`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const remoteBridge = !isLoopbackHostname(bridge.hostname);
|
|
35
|
+
if (remoteBridge && bridge.protocol !== 'wss:') {
|
|
36
|
+
return { allowed: false, reason: 'a non-local Reticle bridge must use wss://' };
|
|
37
|
+
}
|
|
38
|
+
const remote = !isLocalPage(pageProtocol, pageHostname) || remoteBridge;
|
|
39
|
+
if (!remote) {
|
|
40
|
+
return { allowed: true };
|
|
41
|
+
}
|
|
42
|
+
if (!allowNonLocalhost) {
|
|
43
|
+
return {
|
|
44
|
+
allowed: false,
|
|
45
|
+
reason: 'Reticle is disabled outside localhost unless allowNonLocalhost is explicitly enabled',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (token === undefined || 0 === token.length) {
|
|
49
|
+
return { allowed: false, reason: 'a pairing token is required outside localhost' };
|
|
50
|
+
}
|
|
51
|
+
return { allowed: true };
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reticlehq/browser",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.0",
|
|
4
4
|
"description": "Reticle browser SDK — installs observers, builds semantic snapshots, executes actions, talks to the bridge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,23 +22,23 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@reticlehq/core": "3.
|
|
25
|
+
"@reticlehq/core": "3.10.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@iconify-json/heroicons-outline": "^1.2.1",
|
|
29
29
|
"@iconify-json/heroicons-solid": "^1.2.1",
|
|
30
|
-
"@tanstack/query-core": "^5.
|
|
30
|
+
"@tanstack/query-core": "^5.105.2",
|
|
31
31
|
"esbuild": "^0.28.2",
|
|
32
|
-
"jotai": "^3.0.
|
|
32
|
+
"jotai": "^3.0.1",
|
|
33
33
|
"jsdom": "^30.0.1",
|
|
34
|
-
"mobx": "^7.
|
|
35
|
-
"pinia": "^4.0
|
|
36
|
-
"react": "^19.3.
|
|
34
|
+
"mobx": "^7.1.4",
|
|
35
|
+
"pinia": "^4.1.0",
|
|
36
|
+
"react": "^19.3.1",
|
|
37
37
|
"recoil": "^0.7.7",
|
|
38
|
-
"svelte": "^5.57.
|
|
38
|
+
"svelte": "^5.57.3",
|
|
39
39
|
"valtio": "^2.3.2",
|
|
40
|
-
"vue": "^3.5.
|
|
41
|
-
"xstate": "^5.33.
|
|
40
|
+
"vue": "^3.5.44",
|
|
41
|
+
"xstate": "^5.33.3"
|
|
42
42
|
},
|
|
43
43
|
"license": "Apache-2.0",
|
|
44
44
|
"author": "Reticle contributors",
|