dsh-vision-router 2.2.7 → 2.3.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.
Files changed (69) hide show
  1. package/README.md +8 -8
  2. package/README.zh.md +8 -8
  3. package/docs/architecture/2.3.0-compat-retirement.md +147 -0
  4. package/docs/architecture/2.3.0-execution-plan.md +410 -0
  5. package/docs/architecture/2.3.0-final-audit.md +279 -0
  6. package/docs/architecture/2.3.0-reopened-plan.md +720 -0
  7. package/docs/architecture/compat-inventory.md +26 -34
  8. package/docs/architecture/dsh-020-rc2-adaptation.md +23 -0
  9. package/docs/architecture/dsh-compatibility-matrix.md +65 -54
  10. package/docs/architecture/dsh-support-window.md +29 -21
  11. package/docs/architecture/final-closure-audit.md +2 -0
  12. package/docs/architecture/host-first-proxy-convergence.md +1 -1
  13. package/docs/releases/v2.2.8.md +25 -0
  14. package/docs/releases/v2.3.0.md +53 -0
  15. package/entry.js +8 -56
  16. package/index.js +161 -691
  17. package/lib/catalog-corrections.js +1 -2
  18. package/lib/client-host-compat-prelude.js +7 -12
  19. package/lib/client-maintenance-actions.js +145 -0
  20. package/lib/client-presentation-boundary-main.js +82 -32
  21. package/lib/client-presentation-boundary.js +60 -40
  22. package/lib/client-script-carrier.js +90 -0
  23. package/lib/client.js +248 -146
  24. package/lib/core-apply-lifecycle.js +47 -0
  25. package/lib/core-primitives.js +88 -135
  26. package/lib/desktop-screenshot-tool.js +262 -0
  27. package/lib/dsh-contract-compat.js +11 -26
  28. package/lib/dsh-support-window.js +4 -4
  29. package/lib/guide-vision-toggle-highlight.js +7 -10
  30. package/lib/http-compat.js +1 -2
  31. package/lib/live-model-client-prelude.js +7 -14
  32. package/lib/local-remote-settings-permission.js +41 -16
  33. package/lib/maintenance-routes.js +136 -0
  34. package/lib/public-config.js +57 -0
  35. package/lib/public-entry.js +2 -7
  36. package/lib/remote-settings-bridge.js +11 -0
  37. package/lib/remote-settings-risk-client-boundary.js +155 -0
  38. package/lib/remote-settings-risk-confirmation.js +17 -105
  39. package/lib/runtime-composition.js +105 -30
  40. package/lib/runtime-i18n-boundary.js +10 -11
  41. package/lib/session-message-source-compat.js +7 -3
  42. package/lib/session-vision-index.js +154 -9
  43. package/lib/session-vision-mode-boundary.js +43 -8
  44. package/lib/settings-client-loader-lifecycle.js +129 -0
  45. package/lib/settings-factory-lifecycle.js +25 -14
  46. package/lib/settings-ia-client-prelude.js +11 -11
  47. package/lib/settings-limit-client-prelude.js +7 -9
  48. package/lib/sharp-runtime.js +0 -7
  49. package/lib/strict-live-model-client-prelude.js +7 -10
  50. package/lib/structured-flow-hardening.js +3 -2
  51. package/lib/v2-settings-ia-integration.js +21 -6
  52. package/lib/vision-backend-runtime-policy.js +3 -2
  53. package/lib/vision-background-benchmark.js +3 -5
  54. package/lib/vision-capability-benchmark-client.js +7 -10
  55. package/lib/vision-capability-benchmark-service.js +1 -0
  56. package/lib/vision-diagnostics-routes.js +216 -0
  57. package/lib/vision-evidence-guidance.js +3 -1
  58. package/lib/vision-exact-check-client.js +11 -6
  59. package/lib/vision-model-visibility-boundary-main.js +7 -11
  60. package/lib/vision-model-visibility-boundary.js +7 -12
  61. package/lib/vision-provider-transport.js +0 -29
  62. package/lib/vision-routing-settings-prelude.js +7 -11
  63. package/lib/vision-toggle-root-hardening.js +50 -8
  64. package/lib/vision-tool-runtime-boundary.js +5 -1
  65. package/lib/vision-turn-budget-client-prelude.js +7 -10
  66. package/lib/web/remote-settings-client.js +9 -14
  67. package/package.json +23 -13
  68. package/lib/android-attachment-compat.js +0 -244
  69. package/lib/settings-client-rc8-lifecycle.js +0 -402
@@ -1,402 +0,0 @@
1
- import { htmlHasScriptMarker } from './html-script-marker.js'
2
-
3
- const SETTINGS_RC8_MARK = 'data-vision-router-settings-rc8-lifecycle'
4
-
5
- /**
6
- * rc.8 replaces ModuleLoader.load() inside loader.create(). The original local
7
- * permission and remote-risk shims predate that lifecycle and are intentionally
8
- * left untouched for rc.6/rc.7. This narrow safety net activates only when a
9
- * create() transition exists, then re-installs both settings context wrappers
10
- * on the new live loader.
11
- */
12
- export const SETTINGS_RC8_CLIENT_PRELUDE = String.raw`(function(){
13
- 'use strict';
14
- var TARGET = 'dsh-vision-router';
15
- var FIELD = 'allowRemoteSettings';
16
- var ENDPOINT = '/_dsh/vision-router/remote-settings-permission';
17
- var CHANNEL = '/vision-router-settings';
18
- var AUTHORIZE_ENDPOINT = 'authorize';
19
- var scopeCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
20
- var binderCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
21
- var localContextCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
22
- var connectionCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
23
- var rpcCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
24
- var riskContextCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
25
- var authorizationPromise;
26
-
27
- function normalizePermissionValue(value) {
28
- if (typeof value === 'boolean') return value;
29
- if (value === 'true') return true;
30
- if (value === 'false') return false;
31
- return undefined;
32
- }
33
-
34
- function isLoopbackLocation(locationLike) {
35
- var hostname = locationLike && typeof locationLike.hostname === 'string'
36
- ? locationLike.hostname.toLowerCase().replace(/^\[|\]$/g, '')
37
- : '';
38
- var protocol = locationLike && typeof locationLike.protocol === 'string'
39
- ? locationLike.protocol.toLowerCase()
40
- : '';
41
- if (protocol === 'dsh-app:' && hostname === 'app') return true;
42
- if (hostname === 'localhost' || hostname.endsWith('.localhost') || hostname === '::1') return true;
43
- return /^127(?:\.\d{1,3}){3}$/.test(hostname);
44
- }
45
-
46
- async function writePermission(operation, value, revision) {
47
- if (typeof fetch !== 'function') throw new Error('Vision Router local settings transport is unavailable');
48
- var payload = { operation: operation };
49
- if (operation === 'set') payload.value = value;
50
- if (Number.isInteger(revision) && revision >= 0) payload.expectedRevision = revision;
51
- var response = await fetch(ENDPOINT, {
52
- method: 'POST',
53
- headers: { 'content-type': 'application/json', accept: 'application/json' },
54
- body: JSON.stringify(payload)
55
- });
56
- var body;
57
- try { body = await response.json(); } catch (_) { body = undefined; }
58
- if (!response.ok || !body || body.ok !== true) {
59
- var message = body && body.error && body.error.message ? body.error.message : ('HTTP ' + response.status);
60
- var error = new Error(message);
61
- if (body && body.error && body.error.code) error.code = body.error.code;
62
- throw error;
63
- }
64
- return body.value;
65
- }
66
-
67
- function wrapScope(scope) {
68
- if (!scope || (typeof scope !== 'object' && typeof scope !== 'function')) return scope;
69
- if (scopeCache && scopeCache.has(scope)) return scopeCache.get(scope);
70
- var overlay;
71
- var cachedRawSnapshot;
72
- var cachedOverlayKey;
73
- var cachedClientSnapshot;
74
-
75
- function overlayKey() {
76
- if (!overlay) return 'none';
77
- return (overlay.present ? '1' : '0') + ':' + String(overlay.value) + ':' + String(overlay.revision);
78
- }
79
-
80
- function rawSnapshot() {
81
- try { return typeof scope.getSnapshot === 'function' ? scope.getSnapshot() : undefined; } catch (_) { return undefined; }
82
- }
83
-
84
- function snapshotWithOverlay(snapshot) {
85
- if (!overlay || !snapshot || typeof snapshot !== 'object') return snapshot;
86
- var user = snapshot.user && typeof snapshot.user === 'object' && !Array.isArray(snapshot.user) ? snapshot.user : {};
87
- var present = Object.prototype.hasOwnProperty.call(user, FIELD);
88
- var matches = overlay.present ? present && user[FIELD] === overlay.value : !present;
89
- if (matches && (!Number.isInteger(overlay.revision) || !Number.isInteger(snapshot.revision) || snapshot.revision >= overlay.revision)) {
90
- overlay = undefined;
91
- return snapshot;
92
- }
93
- var nextUser = Object.assign({}, user);
94
- if (overlay.present) nextUser[FIELD] = overlay.value;
95
- else delete nextUser[FIELD];
96
- var nextValue = snapshot.value && typeof snapshot.value === 'object' && !Array.isArray(snapshot.value)
97
- ? Object.assign({}, snapshot.value)
98
- : snapshot.value;
99
- if (nextValue && typeof nextValue === 'object') nextValue[FIELD] = overlay.present ? overlay.value : false;
100
- return Object.assign({}, snapshot, {
101
- user: nextUser,
102
- value: nextValue,
103
- revision: Number.isInteger(overlay.revision) ? overlay.revision : snapshot.revision
104
- });
105
- }
106
-
107
- function snapshotForClient(snapshot) {
108
- var keyBefore = overlayKey();
109
- if (snapshot === cachedRawSnapshot && keyBefore === cachedOverlayKey) return cachedClientSnapshot;
110
- var effective = snapshotWithOverlay(snapshot);
111
- var keyAfter = overlayKey();
112
- if (snapshot === cachedRawSnapshot && keyAfter === cachedOverlayKey) return cachedClientSnapshot;
113
- if (!effective || typeof effective !== 'object' || effective.mode !== 'host') {
114
- cachedRawSnapshot = snapshot;
115
- cachedOverlayKey = keyAfter;
116
- cachedClientSnapshot = effective;
117
- return effective;
118
- }
119
- var next = effective;
120
- var value = effective.value;
121
- if (value && typeof value === 'object' && !Array.isArray(value)
122
- && Object.prototype.hasOwnProperty.call(value, FIELD)
123
- && typeof value[FIELD] === 'boolean') {
124
- value = Object.assign({}, value, { [FIELD]: value[FIELD] ? 'true' : '' });
125
- next = Object.assign({}, next, { value: value });
126
- }
127
- var user = effective.user;
128
- if (user && typeof user === 'object' && !Array.isArray(user)
129
- && Object.prototype.hasOwnProperty.call(user, FIELD)
130
- && typeof user[FIELD] === 'boolean') {
131
- user = Object.assign({}, user, { [FIELD]: user[FIELD] ? 'true' : 'false' });
132
- next = Object.assign({}, next, { user: user });
133
- }
134
- cachedRawSnapshot = snapshot;
135
- cachedOverlayKey = keyAfter;
136
- cachedClientSnapshot = next;
137
- return next;
138
- }
139
-
140
- var wrapped = new Proxy(scope, {
141
- get: function(target, property) {
142
- if (property === 'getSnapshot') return function(){ return snapshotForClient(rawSnapshot()); };
143
- if (property === 'set') {
144
- var set = Reflect.get(target, property, target);
145
- return async function(field, value) {
146
- var snapshot = rawSnapshot();
147
- if (field === FIELD && snapshot && snapshot.mode === 'host') {
148
- var normalized = normalizePermissionValue(value);
149
- if (normalized === undefined) throw new TypeError('allowRemoteSettings must be boolean');
150
- var result = await writePermission('set', normalized, snapshot.revision);
151
- overlay = { present: true, value: normalized, revision: result && result.revision };
152
- cachedRawSnapshot = undefined;
153
- if (typeof target.load === 'function') await target.load();
154
- return;
155
- }
156
- return typeof set === 'function' ? set.apply(target, arguments) : undefined;
157
- };
158
- }
159
- if (property === 'unset') {
160
- var unset = Reflect.get(target, property, target);
161
- return async function(field) {
162
- var snapshot = rawSnapshot();
163
- if (field === FIELD && snapshot && snapshot.mode === 'host') {
164
- var result = await writePermission('unset', undefined, snapshot.revision);
165
- overlay = { present: false, revision: result && result.revision };
166
- cachedRawSnapshot = undefined;
167
- if (typeof target.load === 'function') await target.load();
168
- return;
169
- }
170
- return typeof unset === 'function' ? unset.apply(target, arguments) : undefined;
171
- };
172
- }
173
- var value = Reflect.get(target, property, target);
174
- return typeof value === 'function' ? value.bind(target) : value;
175
- }
176
- });
177
- if (scopeCache) scopeCache.set(scope, wrapped);
178
- return wrapped;
179
- }
180
-
181
- function wrapBinder(binder) {
182
- if (!binder || (typeof binder !== 'object' && typeof binder !== 'function')) return binder;
183
- if (binderCache && binderCache.has(binder)) return binderCache.get(binder);
184
- var wrapped = new Proxy(binder, {
185
- get: function(target, property) {
186
- if (property === 'bind') {
187
- var bind = Reflect.get(target, property, target);
188
- if (typeof bind !== 'function') return bind;
189
- return function(spec) {
190
- var scope = bind.apply(target, arguments);
191
- return spec && spec.namespace === 'vision-router' ? wrapScope(scope) : scope;
192
- };
193
- }
194
- var value = Reflect.get(target, property, target);
195
- return typeof value === 'function' ? value.bind(target) : value;
196
- }
197
- });
198
- if (binderCache) binderCache.set(binder, wrapped);
199
- return wrapped;
200
- }
201
-
202
- function wrapLocalContext(ctx) {
203
- if (!ctx || typeof ctx !== 'object') return ctx;
204
- if (localContextCache && localContextCache.has(ctx)) return localContextCache.get(ctx);
205
- var wrapped = new Proxy(ctx, {
206
- get: function(target, property) {
207
- if (property === 'settingsScope') return wrapBinder(Reflect.get(target, property, target));
208
- var value = Reflect.get(target, property, target);
209
- return typeof value === 'function' ? value.bind(target) : value;
210
- }
211
- });
212
- if (localContextCache) localContextCache.set(ctx, wrapped);
213
- return wrapped;
214
- }
215
-
216
- function chineseLocale() {
217
- var lang = '';
218
- try {
219
- lang = String((document && document.documentElement && document.documentElement.lang) || (navigator && navigator.language) || '').toLowerCase();
220
- } catch (_) {}
221
- return lang === '' || lang.indexOf('zh') === 0;
222
- }
223
-
224
- function riskMessage() {
225
- return chineseLocale()
226
- ? '启用远程设置?\n\n当前页面通过远程地址访问 DSH。启用后,能够访问此 trusted host 的客户端可以修改 Vision Router 已开放的设置。\n\nDSH trustedHosts 不是身份认证,请仅在你信任当前网络和访问者时继续。API Key、HTTP Provider 凭据、本地 Ollama / LM Studio、产物路径等未列入远程白名单的敏感配置仍不会开放。\n\n确定要允许远程修改设置吗?'
227
- : 'Enable remote settings?\n\nThis page is accessing DSH through a remote address. After enabling this, clients that can reach this trusted host can change the Vision Router settings exposed by the remote allow-list.\n\nDSH trustedHosts is not authentication. Continue only if you trust the current network and its users. Sensitive settings outside the remote allow-list, including API keys, HTTP-provider credentials, local Ollama / LM Studio, and artifact paths, remain unavailable remotely.\n\nAllow remote settings?';
228
- }
229
-
230
- function isPermissionDisabledDescribe(channel, endpoint, result) {
231
- return channel === CHANNEL
232
- && endpoint === 'describe'
233
- && result && result.ok === true
234
- && result.value && result.value.enabled !== true
235
- && result.value.reason === 'permission-disabled';
236
- }
237
-
238
- async function authorizeAndRefresh(target, call) {
239
- if (authorizationPromise) return authorizationPromise;
240
- authorizationPromise = (async function(){
241
- if (typeof window.confirm !== 'function' || window.confirm(riskMessage()) !== true) return undefined;
242
- var authorized = await call.call(target, CHANNEL, AUTHORIZE_ENDPOINT, { acceptedRisk: true });
243
- if (!authorized || authorized.ok !== true) {
244
- var message = authorized && authorized.error && authorized.error.message
245
- ? authorized.error.message
246
- : 'Vision Router could not enable remote settings.';
247
- try { if (typeof window.alert === 'function') window.alert(message); } catch (_) {}
248
- return undefined;
249
- }
250
- return call.call(target, CHANNEL, 'describe', {});
251
- })();
252
- try { return await authorizationPromise; }
253
- finally { authorizationPromise = undefined; }
254
- }
255
-
256
- function wrapRpc(rpc) {
257
- if (!rpc || (typeof rpc !== 'object' && typeof rpc !== 'function')) return rpc;
258
- if (rpcCache && rpcCache.has(rpc)) return rpcCache.get(rpc);
259
- var wrapped = new Proxy(rpc, {
260
- get: function(target, property) {
261
- if (property === 'call') {
262
- var call = Reflect.get(target, property, target);
263
- if (typeof call !== 'function') return call;
264
- return async function(channel, endpoint) {
265
- var result = await call.apply(target, arguments);
266
- if (!isPermissionDisabledDescribe(channel, endpoint, result)) return result;
267
- var refreshed = await authorizeAndRefresh(target, call);
268
- return refreshed || result;
269
- };
270
- }
271
- var value = Reflect.get(target, property, target);
272
- return typeof value === 'function' ? value.bind(target) : value;
273
- }
274
- });
275
- if (rpcCache) rpcCache.set(rpc, wrapped);
276
- return wrapped;
277
- }
278
-
279
- function wrapConnection(connection) {
280
- if (!connection || (typeof connection !== 'object' && typeof connection !== 'function')) return connection;
281
- if (connectionCache && connectionCache.has(connection)) return connectionCache.get(connection);
282
- var wrapped = new Proxy(connection, {
283
- get: function(target, property) {
284
- if (property === 'isLoopback') {
285
- var locationLike;
286
- try { locationLike = window && window.location; } catch (_) { locationLike = undefined; }
287
- if (isLoopbackLocation(locationLike)) return true;
288
- }
289
- if (property === 'rpc') return wrapRpc(Reflect.get(target, property, target));
290
- var value = Reflect.get(target, property, target);
291
- return typeof value === 'function' ? value.bind(target) : value;
292
- }
293
- });
294
- if (connectionCache) connectionCache.set(connection, wrapped);
295
- return wrapped;
296
- }
297
-
298
- function wrapRiskContext(ctx) {
299
- if (!ctx || typeof ctx !== 'object') return ctx;
300
- if (riskContextCache && riskContextCache.has(ctx)) return riskContextCache.get(ctx);
301
- var wrapped = new Proxy(ctx, {
302
- get: function(target, property) {
303
- if (property === 'get') {
304
- var get = Reflect.get(target, property, target);
305
- if (typeof get !== 'function') return get;
306
- return function(name) {
307
- var value = get.apply(target, arguments);
308
- return name === 'connection' ? wrapConnection(value) : value;
309
- };
310
- }
311
- var value = Reflect.get(target, property, target);
312
- return typeof value === 'function' ? value.bind(target) : value;
313
- }
314
- });
315
- if (riskContextCache) riskContextCache.set(ctx, wrapped);
316
- return wrapped;
317
- }
318
-
319
- function patchLiveLoader(loader) {
320
- if (!loader || typeof loader.load !== 'function' || loader.load.__visionRouterSettingsRc8Lifecycle) return;
321
- var original = loader.load;
322
- function load(spec) {
323
- if (spec && spec.id === TARGET && typeof spec.factory === 'function') {
324
- var factory = spec.factory;
325
- spec = Object.assign({}, spec, {
326
- factory: function(require) {
327
- var exports = factory(require);
328
- if (exports && typeof exports.apply === 'function' && !exports.apply.__visionRouterSettingsRc8Lifecycle) {
329
- var apply = exports.apply;
330
- var wrappedApply = function(ctx) {
331
- var rest = Array.prototype.slice.call(arguments, 1);
332
- var nextCtx = wrapRiskContext(wrapLocalContext(ctx));
333
- return apply.apply(exports, [nextCtx].concat(rest));
334
- };
335
- Object.defineProperty(wrappedApply, '__visionRouterSettingsRc8Lifecycle', { value: true });
336
- exports.apply = wrappedApply;
337
- }
338
- return exports;
339
- }
340
- });
341
- }
342
- return original.call(this, spec);
343
- }
344
- Object.defineProperty(load, '__visionRouterSettingsRc8Lifecycle', { value: true });
345
- loader.load = load;
346
- }
347
-
348
- function patchCreate(loader) {
349
- if (!loader || typeof loader.create !== 'function' || loader.create.__visionRouterSettingsRc8Lifecycle) return;
350
- var originalCreate = loader.create;
351
- function create() {
352
- var result = originalCreate.apply(this, arguments);
353
- patchLiveLoader(loader);
354
- return result;
355
- }
356
- Object.defineProperty(create, '__visionRouterSettingsRc8Lifecycle', { value: true });
357
- loader.create = create;
358
- if (loader.mode === 'live') patchLiveLoader(loader);
359
- }
360
-
361
- function install() {
362
- if (window.__ModuleLoader__) {
363
- patchCreate(window.__ModuleLoader__);
364
- return;
365
- }
366
- var descriptor = Object.getOwnPropertyDescriptor(window, '__ModuleLoader__');
367
- if (descriptor && descriptor.configurable === false) return;
368
- var previousGet = descriptor && descriptor.get;
369
- var previousSet = descriptor && descriptor.set;
370
- var stored = descriptor && Object.prototype.hasOwnProperty.call(descriptor, 'value') ? descriptor.value : undefined;
371
- Object.defineProperty(window, '__ModuleLoader__', {
372
- configurable: true,
373
- enumerable: !descriptor || descriptor.enumerable !== false,
374
- get: function(){ return previousGet ? previousGet.call(window) : stored; },
375
- set: function(value) {
376
- if (previousSet) previousSet.call(window, value); else stored = value;
377
- try { patchCreate(previousGet ? previousGet.call(window) : value); } catch (_) {}
378
- }
379
- });
380
- if (stored) patchCreate(stored);
381
- }
382
-
383
- try { install(); } catch (_) {}
384
- })();`
385
-
386
- export function injectSettingsRc8ClientPrelude(html) {
387
- if (typeof html !== 'string' || htmlHasScriptMarker(html, SETTINGS_RC8_MARK)) return html
388
- const safe = SETTINGS_RC8_CLIENT_PRELUDE.replace(/<\/script/gi, '<\\/script')
389
- const script = `<script ${SETTINGS_RC8_MARK}>${safe}</script>`
390
- const closeHead = html.indexOf('</head>')
391
- return closeHead === -1 ? `${html}${script}` : `${html.slice(0, closeHead)}${script}${html.slice(closeHead)}`
392
- }
393
-
394
- export function installSettingsRc8ClientLifecycle(ctx) {
395
- if (!ctx || typeof ctx.inject !== 'function') return
396
- ctx.inject(['webServer'], (webCtx) => {
397
- webCtx.effect(
398
- () => webCtx.webServer.tapIndex(injectSettingsRc8ClientPrelude),
399
- 'vision-router: rc8 settings client lifecycle',
400
- )
401
- })
402
- }