dsh-vision-router 2.2.1 → 2.2.2

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.
@@ -1,8 +1,208 @@
1
+ import { htmlHasScriptMarker } from '../html-script-marker.js'
1
2
  import { installVisionRouterRemoteSettingsBridge } from '../remote-settings-bridge.js'
2
3
  import { installSettingsRc8ClientLifecycle } from '../settings-client-rc8-lifecycle.js'
3
4
 
5
+ const SETTINGS_CONFIG_FORMS_MARK = 'data-vision-router-settings-configforms-compat'
6
+
7
+ /**
8
+ * DSH 0.1.7 replaces the browser `settingsScope` service with `configForms`.
9
+ * Vision Router still supports older Hosts, so the browser bundle cannot hard
10
+ * depend on either service name. This prelude removes the legacy hard service
11
+ * edge at module-factory time and presents the old `settingsScope.bind()` face
12
+ * over whichever official settings service the active Host provides.
13
+ */
14
+ export const SETTINGS_CONFIG_FORMS_CLIENT_PRELUDE = String.raw`(function(){
15
+ 'use strict';
16
+ var TARGET = 'dsh-vision-router';
17
+ var FLAG = '__visionRouterSettingsConfigFormsCompat';
18
+ var contextCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
19
+ var binderCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
20
+ var connectionCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
21
+
22
+ function safeGet(ctx, name) {
23
+ if (!ctx) return undefined;
24
+ try {
25
+ if (typeof ctx.get === 'function') {
26
+ var value = ctx.get(name);
27
+ if (value !== undefined && value !== null) return value;
28
+ }
29
+ } catch (_) {}
30
+ try { return ctx[name]; } catch (_) { return undefined; }
31
+ }
32
+
33
+ function isLoopbackLocation(locationLike) {
34
+ var hostname = locationLike && typeof locationLike.hostname === 'string'
35
+ ? locationLike.hostname.toLowerCase().replace(/^\[|\]$/g, '')
36
+ : '';
37
+ if (hostname === 'localhost' || hostname.endsWith('.localhost') || hostname === '::1') return true;
38
+ return /^127(?:\.\d{1,3}){3}$/.test(hostname);
39
+ }
40
+
41
+ function normalizeConnection(connection) {
42
+ if (!connection || typeof connection !== 'object') return connection;
43
+ var locationLike;
44
+ try { locationLike = window && window.location; } catch (_) { locationLike = undefined; }
45
+ if (!isLoopbackLocation(locationLike) || connection.isLoopback !== false) return connection;
46
+ if (connectionCache && connectionCache.has(connection)) return connectionCache.get(connection);
47
+ var wrapped = new Proxy(connection, {
48
+ get: function(target, property) {
49
+ if (property === 'isLoopback') return true;
50
+ var value = Reflect.get(target, property, target);
51
+ return typeof value === 'function' ? value.bind(target) : value;
52
+ }
53
+ });
54
+ if (connectionCache) connectionCache.set(connection, wrapped);
55
+ return wrapped;
56
+ }
57
+
58
+ function legacyBinder(ctx) {
59
+ var binder = safeGet(ctx, 'settingsScope');
60
+ return binder && typeof binder.bind === 'function' ? binder : undefined;
61
+ }
62
+
63
+ function configFormsBinder(ctx) {
64
+ var forms = safeGet(ctx, 'configForms');
65
+ if (!forms || typeof forms.get !== 'function') return undefined;
66
+ if (binderCache && binderCache.has(forms)) return binderCache.get(forms);
67
+ var binder = {
68
+ bind: function(spec) {
69
+ var namespace = spec && spec.namespace;
70
+ if (typeof namespace !== 'string' || namespace.length === 0) {
71
+ throw new TypeError('settings namespace must be a non-empty string');
72
+ }
73
+ return forms.get(namespace);
74
+ }
75
+ };
76
+ if (binderCache) binderCache.set(forms, binder);
77
+ return binder;
78
+ }
79
+
80
+ function resolveBinder(ctx) {
81
+ return legacyBinder(ctx) || configFormsBinder(ctx);
82
+ }
83
+
84
+ function wrapContext(ctx) {
85
+ if (!ctx || typeof ctx !== 'object') return ctx;
86
+ if (contextCache && contextCache.has(ctx)) return contextCache.get(ctx);
87
+ var wrapped = new Proxy(ctx, {
88
+ get: function(target, property) {
89
+ if (property === 'settingsScope') {
90
+ var binder = resolveBinder(target);
91
+ if (!binder) {
92
+ throw new Error('Vision Router requires DSH settingsScope or configForms');
93
+ }
94
+ return binder;
95
+ }
96
+ if (property === 'get') {
97
+ var getter = Reflect.get(target, property, target);
98
+ if (typeof getter !== 'function') return getter;
99
+ return function(name) {
100
+ var value = getter.call(target, name);
101
+ return name === 'connection' ? normalizeConnection(value) : value;
102
+ };
103
+ }
104
+ var value = Reflect.get(target, property, target);
105
+ return typeof value === 'function' ? value.bind(target) : value;
106
+ }
107
+ });
108
+ if (contextCache) contextCache.set(ctx, wrapped);
109
+ return wrapped;
110
+ }
111
+
112
+ function rewriteInject(exports) {
113
+ if (!exports || !Array.isArray(exports.inject) || exports.inject.indexOf('settingsScope') === -1) return;
114
+ exports.inject = exports.inject.filter(function(name){ return name !== 'settingsScope'; });
115
+ }
116
+
117
+ function patchLiveLoader(loader) {
118
+ if (!loader || typeof loader.load !== 'function' || loader.load[FLAG]) return;
119
+ var original = loader.load;
120
+ function load(spec) {
121
+ if (spec && spec.id === TARGET && typeof spec.factory === 'function') {
122
+ var factory = spec.factory;
123
+ spec = Object.assign({}, spec, {
124
+ factory: function(require) {
125
+ var exports = factory(require);
126
+ rewriteInject(exports);
127
+ if (exports && typeof exports.apply === 'function' && !exports.apply[FLAG]) {
128
+ var apply = exports.apply;
129
+ var wrappedApply = function(ctx) {
130
+ var rest = Array.prototype.slice.call(arguments, 1);
131
+ return apply.apply(exports, [wrapContext(ctx)].concat(rest));
132
+ };
133
+ Object.defineProperty(wrappedApply, FLAG, { value: true });
134
+ exports.apply = wrappedApply;
135
+ }
136
+ return exports;
137
+ }
138
+ });
139
+ }
140
+ return original.call(this, spec);
141
+ }
142
+ Object.defineProperty(load, FLAG, { value: true });
143
+ loader.load = load;
144
+ }
145
+
146
+ function patchCreate(loader) {
147
+ if (!loader) return;
148
+ patchLiveLoader(loader);
149
+ if (typeof loader.create !== 'function' || loader.create[FLAG]) return;
150
+ var originalCreate = loader.create;
151
+ function create() {
152
+ var result = originalCreate.apply(this, arguments);
153
+ patchLiveLoader(loader);
154
+ return result;
155
+ }
156
+ Object.defineProperty(create, FLAG, { value: true });
157
+ loader.create = create;
158
+ }
159
+
160
+ function install() {
161
+ if (window.__ModuleLoader__) {
162
+ patchCreate(window.__ModuleLoader__);
163
+ return;
164
+ }
165
+ var descriptor = Object.getOwnPropertyDescriptor(window, '__ModuleLoader__');
166
+ if (descriptor && descriptor.configurable === false) return;
167
+ var previousGet = descriptor && descriptor.get;
168
+ var previousSet = descriptor && descriptor.set;
169
+ var stored = descriptor && Object.prototype.hasOwnProperty.call(descriptor, 'value') ? descriptor.value : undefined;
170
+ Object.defineProperty(window, '__ModuleLoader__', {
171
+ configurable: true,
172
+ enumerable: !descriptor || descriptor.enumerable !== false,
173
+ get: function(){ return previousGet ? previousGet.call(window) : stored; },
174
+ set: function(value) {
175
+ if (previousSet) previousSet.call(window, value); else stored = value;
176
+ try { patchCreate(previousGet ? previousGet.call(window) : value); } catch (_) {}
177
+ }
178
+ });
179
+ if (stored) patchCreate(stored);
180
+ }
181
+
182
+ try { install(); } catch (_) {}
183
+ })();`
184
+
185
+ export function injectSettingsConfigFormsCompatPrelude(html) {
186
+ if (typeof html !== 'string' || htmlHasScriptMarker(html, SETTINGS_CONFIG_FORMS_MARK)) return html
187
+ const safe = SETTINGS_CONFIG_FORMS_CLIENT_PRELUDE.replace(/<\/script/gi, '<\\/script')
188
+ const script = `<script ${SETTINGS_CONFIG_FORMS_MARK}>${safe}</script>`
189
+ const closeHead = html.indexOf('</head>')
190
+ return closeHead === -1 ? `${html}${script}` : `${html.slice(0, closeHead)}${script}${html.slice(closeHead)}`
191
+ }
192
+
193
+ export function installSettingsConfigFormsCompat(ctx) {
194
+ if (!ctx || typeof ctx.inject !== 'function') return
195
+ ctx.inject(['webServer'], (webCtx) => {
196
+ webCtx.effect(
197
+ () => webCtx.webServer.tapIndex(injectSettingsConfigFormsCompatPrelude),
198
+ 'vision-router: settings configForms compatibility',
199
+ )
200
+ })
201
+ }
202
+
4
203
  export function installVisionRemoteSettingsClient(ctx, logger) {
5
204
  installVisionRouterRemoteSettingsBridge(ctx, logger)
205
+ installSettingsConfigFormsCompat(ctx)
6
206
  installSettingsRc8ClientLifecycle(ctx)
7
207
  return ctx
8
208
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vision-router",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "Eyes for text-only DeepSeek Harness agents: built-in free vision chain (no key) + pixel-level vision tools (Q&A, grounding, crop, pixel diff, colors, OCR, SVG trace, cutout, screenshots). One-command install, no Python, image turns work like ordinary tool-calling turns.",
5
5
  "license": "MIT",
6
6
  "repository": {