@jobshimo/browser-link 0.37.0 → 0.39.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/agent-instructions/content.js +5 -360
- package/dist/agent-instructions/content.js.map +1 -1
- package/dist/bridge/server.d.ts +5 -0
- package/dist/bridge/server.js +1 -1
- package/dist/bridge/server.js.map +1 -1
- package/dist/bridge/ws-bridge.d.ts +10 -1
- package/dist/bridge/ws-bridge.js +19 -6
- package/dist/bridge/ws-bridge.js.map +1 -1
- package/dist/cdp/flow.d.ts +8 -0
- package/dist/cdp/flow.js +11 -0
- package/dist/cdp/flow.js.map +1 -1
- package/dist/cdp/inpage/builders.d.ts +7 -4
- package/dist/cdp/inpage/builders.js +4 -1
- package/dist/cdp/inpage/builders.js.map +1 -1
- package/dist/cdp/inpage/deep-query.d.ts +1 -1
- package/dist/cdp/inpage/deep-query.js +12 -9
- package/dist/cdp/inpage/deep-query.js.map +1 -1
- package/dist/cdp/transport.js +7 -1
- package/dist/cdp/transport.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +78 -38
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.js +4 -2
- package/dist/commands/about.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/extension/background.js +13 -2
- package/dist/extension/background.js.map +1 -1
- package/dist/extension/flow.d.ts +8 -0
- package/dist/extension/flow.js +11 -0
- package/dist/extension/flow.js.map +1 -1
- package/dist/extension/inpage/builders.d.ts +7 -4
- package/dist/extension/inpage/builders.js +4 -1
- package/dist/extension/inpage/builders.js.map +1 -1
- package/dist/extension/inpage/deep-query.d.ts +1 -1
- package/dist/extension/inpage/deep-query.js +12 -9
- package/dist/extension/inpage/deep-query.js.map +1 -1
- package/dist/extension/manifest.json +1 -1
- package/dist/manual/core.d.ts +20 -0
- package/dist/manual/core.js +72 -0
- package/dist/manual/core.js.map +1 -0
- package/dist/manual/refresh.d.ts +36 -0
- package/dist/manual/refresh.js +60 -0
- package/dist/manual/refresh.js.map +1 -0
- package/dist/manual/render.d.ts +22 -0
- package/dist/manual/render.js +56 -0
- package/dist/manual/render.js.map +1 -0
- package/dist/manual/status.d.ts +23 -0
- package/dist/manual/status.js +52 -0
- package/dist/manual/status.js.map +1 -0
- package/dist/manual/tool.d.ts +20 -0
- package/dist/manual/tool.js +106 -0
- package/dist/manual/tool.js.map +1 -0
- package/dist/manual/topics.d.ts +18 -0
- package/dist/manual/topics.js +60 -0
- package/dist/manual/topics.js.map +1 -0
- package/dist/messages.d.ts +7 -0
- package/dist/permissions.js +6 -0
- package/dist/permissions.js.map +1 -1
- package/dist/server.js +28 -19
- package/dist/server.js.map +1 -1
- package/dist/tools/browser-definitions.js +34 -6
- package/dist/tools/browser-definitions.js.map +1 -1
- package/dist/tools/browser-dispatch.d.ts +11 -0
- package/dist/tools/browser-dispatch.js +30 -0
- package/dist/tools/browser-dispatch.js.map +1 -1
- package/dist/tools/server-instructions.d.ts +31 -13
- package/dist/tools/server-instructions.js +33 -314
- package/dist/tools/server-instructions.js.map +1 -1
- package/package.json +1 -1
|
@@ -178,6 +178,7 @@ export declare function buildFindJs(opts: FindOpts): string;
|
|
|
178
178
|
* { ok: false, reason: 'invalid-selector', error }
|
|
179
179
|
* { ok: false, reason: 'not-found' }
|
|
180
180
|
* { ok: false, reason: 'occluded', blocker }
|
|
181
|
+
* { ok: false, reason: 'retargeted', hit_element }
|
|
181
182
|
* { ok: false, reason: 'ref-stale', ref, doc_id }
|
|
182
183
|
* { ok: false, reason: 'observer-unavailable' }
|
|
183
184
|
*
|
|
@@ -187,11 +188,13 @@ export declare function buildFindJs(opts: FindOpts): string;
|
|
|
187
188
|
*
|
|
188
189
|
* `hit_element` is present ONLY when the dispatched click will land on a
|
|
189
190
|
* DIFFERENT element than the resolved target (neither the target nor a
|
|
190
|
-
* descendant): a pointer-events:none target whose real hit-target
|
|
191
|
-
* takes the click (allowed, not 'occluded' —
|
|
192
|
-
*
|
|
191
|
+
* descendant): a pointer-events:none target whose real hit-target (its
|
|
192
|
+
* own ancestor, or a sibling) takes the click (allowed, not 'occluded' —
|
|
193
|
+
* see checkOcclusion), or a `force` click landing on whatever covers the
|
|
193
194
|
* point — the hit-test runs under `force` too, which skips the BLOCK,
|
|
194
|
-
* not the truth-telling.
|
|
195
|
+
* not the truth-telling. Any other ancestor hit (the target is clipped by
|
|
196
|
+
* an overflow container, zero-size, or covered by its own parent) fails
|
|
197
|
+
* as 'retargeted' with the ancestor in `hit_element` unless `force`.
|
|
195
198
|
*
|
|
196
199
|
* The `invalid-selector` check runs BEFORE `deepQueryFirst` — a malformed
|
|
197
200
|
* CSS selector throws the identical `SyntaxError` from every root's own
|
|
@@ -544,10 +544,13 @@ export function buildClickResolveJs(opts) {
|
|
|
544
544
|
const tag = el.tagName.toLowerCase();
|
|
545
545
|
const occlusion = checkOcclusion(el, center.localX, center.localY);
|
|
546
546
|
if (!occlusion.allowed && !opts.force) {
|
|
547
|
+
if (occlusion.retargeted) {
|
|
548
|
+
return { ok: false, reason: 'retargeted', hit_element: occlusion.hit };
|
|
549
|
+
}
|
|
547
550
|
return { ok: false, reason: 'occluded', blocker: occlusion.blocker };
|
|
548
551
|
}
|
|
549
552
|
const result = { ok: true, x: center.x, y: center.y, tag };
|
|
550
|
-
const hit = occlusion.
|
|
553
|
+
const hit = occlusion.hit || occlusion.blocker;
|
|
551
554
|
if (hit) result.hit_element = hit;
|
|
552
555
|
if (target.reresolved) result.reresolved = true;
|
|
553
556
|
return result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builders.js","sourceRoot":"","sources":["../../src/inpage/builders.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"builders.js","sourceRoot":"","sources":["../../src/inpage/builders.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,4 +37,4 @@
|
|
|
37
37
|
* `dom-helpers.ts`), snapshot/find mark the affected entry with
|
|
38
38
|
* `ambiguous: true` so agents know not to cache that selector.
|
|
39
39
|
*/
|
|
40
|
-
export declare const DEEP_QUERY_JS = "\n function collectSearchRoots(start) {\n // start: a Document, Element or ShadowRoot to scope the search to.\n // Returns [start, ...every open shadow root and same-origin iframe\n // document reachable inside start's subtree, recursively]. The caller\n // runs .querySelectorAll()/.querySelector() on each entry directly \u2014\n // each entry's own querySelectorAll does NOT pierce into nested shadow\n // roots or iframes on its own, which is why we enumerate them here.\n var roots = [start];\n function walk(scope, depth) {\n // Depth guard, same pattern as the other recursive helpers here:\n // pathological nesting (or a cycle introduced by a broken mock)\n // must degrade to a truncated result, never a stack overflow.\n if (depth > 20) return;\n var all;\n try {\n all = scope.querySelectorAll('*');\n } catch (_) {\n return;\n }\n for (var i = 0; i < all.length; i++) {\n var el = all[i];\n if (el.shadowRoot) {\n roots.push(el.shadowRoot);\n walk(el.shadowRoot, depth + 1);\n }\n if (el.tagName === 'IFRAME') {\n var doc = null;\n try {\n doc = el.contentDocument;\n } catch (_) {\n doc = null;\n }\n if (doc) {\n roots.push(doc);\n walk(doc, depth + 1);\n }\n }\n }\n }\n walk(start, 0);\n return roots;\n }\n\n function deepWalkAll(callback, start) {\n // Tree-walk primitive: invoke callback(el) for every element reachable\n // from start (default: document) across the top document, every open\n // shadow root, and every same-origin iframe document.\n var root = start || document;\n var roots = collectSearchRoots(root);\n for (var r = 0; r < roots.length; r++) {\n var all;\n try {\n all = roots[r].querySelectorAll('*');\n } catch (_) {\n continue;\n }\n for (var i = 0; i < all.length; i++) callback(all[i]);\n }\n }\n\n function deepQueryAll(selector, start) {\n var root = start || document;\n var roots = collectSearchRoots(root);\n var out = [];\n for (var r = 0; r < roots.length; r++) {\n var found;\n try {\n found = roots[r].querySelectorAll(selector);\n } catch (_) {\n continue;\n }\n for (var i = 0; i < found.length; i++) out.push(found[i]);\n }\n return out;\n }\n\n function deepQueryFirst(selector, start) {\n var root = start || document;\n var roots = collectSearchRoots(root);\n for (var r = 0; r < roots.length; r++) {\n var el = null;\n try {\n el = roots[r].querySelector(selector);\n } catch (_) {\n el = null;\n }\n if (el) return el;\n }\n return null;\n }\n\n function frameElementChain(el) {\n // Walk up from el's own window to the top window, collecting the\n // <iframe> element that hosts each intermediate document. Outermost\n // frame first. Same-origin only \u2014 frameElement throws/returns null\n // across an origin boundary, at which point the chain simply stops\n // (the coordinate math below degrades gracefully rather than throwing).\n var chain = [];\n var doc = el.ownerDocument;\n var win = doc && doc.defaultView;\n if (!win) return chain;\n var topWin;\n try {\n topWin = win.top;\n } catch (_) {\n topWin = win;\n }\n var guard = 0;\n while (win && win !== topWin && guard < 20) {\n guard++;\n var fe = null;\n try {\n fe = win.frameElement;\n } catch (_) {\n fe = null;\n }\n if (!fe) break;\n chain.unshift(fe);\n win = fe.ownerDocument && fe.ownerDocument.defaultView;\n }\n return chain;\n }\n\n function frameViewportOriginOffset(fe) {\n // The embedded document's viewport origin sits at the frame element's\n // CONTENT box, but getBoundingClientRect() returns the BORDER box \u2014\n // border and padding must be added on top of rect.left/rect.top or\n // every coordinate inside a styled iframe lands short. clientLeft/\n // clientTop are the border widths; padding comes from computed style\n // (read through the frame's own view \u2014 same-origin, so reachable).\n var borderX = fe.clientLeft || 0;\n var borderY = fe.clientTop || 0;\n var padX = 0;\n var padY = 0;\n try {\n var view = (fe.ownerDocument && fe.ownerDocument.defaultView) || window;\n var style = view.getComputedStyle(fe);\n padX = parseFloat(style.paddingLeft) || 0;\n padY = parseFloat(style.paddingTop) || 0;\n } catch (_) {}\n return { x: borderX + padX, y: borderY + padY };\n }\n\n function clickRectOf(el) {\n // For a line-wrapped INLINE element (a link spanning two lines), the\n // center of getBoundingClientRect() can land in the unpainted gap\n // between the line boxes \u2014 elementFromPoint would report whatever sits\n // behind, and a dispatched click would miss. The first client rect is\n // always a painted fragment, so its center is a safe click point.\n // Block-level elements have exactly one client rect, identical to the\n // bounding rect \u2014 no behavior change for them.\n var rects = null;\n try {\n rects = el.getClientRects ? el.getClientRects() : null;\n } catch (_) {\n rects = null;\n }\n if (rects && rects.length > 0) return rects[0];\n return el.getBoundingClientRect();\n }\n\n function viewportCenterOf(el) {\n // Returns the element's click point in two coordinate spaces:\n // - localX/localY: relative to el's OWN document viewport (what\n // that document's elementFromPoint expects).\n // - x/y: relative to the TOP-LEVEL viewport (what CDP\n // Input.dispatchMouseEvent expects) \u2014 localX/localY plus the\n // accumulated content-box origin of every ancestor <iframe>\n // element (border-box rect + border + padding). Shadow DOM\n // boundaries need no offset: they do not introduce a new\n // coordinate space.\n // Both spaces derive from the SAME point (first-client-rect center),\n // so the occlusion hit-test and the CDP dispatch always agree.\n var rect = clickRectOf(el);\n var localX = rect.left + rect.width / 2;\n var localY = rect.top + rect.height / 2;\n var chain = frameElementChain(el);\n var offsetX = 0;\n var offsetY = 0;\n for (var i = 0; i < chain.length; i++) {\n var frameRect = chain[i].getBoundingClientRect();\n var origin = frameViewportOriginOffset(chain[i]);\n offsetX += frameRect.left + origin.x;\n offsetY += frameRect.top + origin.y;\n }\n return { x: offsetX + localX, y: offsetY + localY, localX: localX, localY: localY };\n }\n\n function composedContains(ancestor, node) {\n // Node.contains() does not cross shadow boundaries. This walks the\n // COMPOSED tree: when climbing hits a ShadowRoot (no parentNode), jump\n // to its host and keep climbing.\n var cur = node;\n var guard = 0;\n while (cur && guard < 1000) {\n guard++;\n if (cur === ancestor) return true;\n if (cur.parentNode) {\n cur = cur.parentNode;\n continue;\n }\n if (cur.host) {\n cur = cur.host;\n continue;\n }\n break;\n }\n return false;\n }\n\n function describeElement(el) {\n if (!el || !el.tagName) return 'an unknown element';\n var desc = el.tagName.toLowerCase();\n if (el.id) desc += '#' + el.id;\n if (el.classList && el.classList.length) {\n var classes = [];\n for (var i = 0; i < el.classList.length && i < 2; i++) classes.push(el.classList[i]);\n if (classes.length) desc += '.' + classes.join('.');\n }\n return desc;\n }\n\n function hitTestDeep(root, x, y) {\n var node = null;\n try {\n node = root && root.elementFromPoint ? root.elementFromPoint(x, y) : null;\n } catch (_) {\n node = null;\n }\n // The hit element may itself host a nested open shadow root whose\n // content visually covers the point \u2014 descend until elementFromPoint\n // stops finding a deeper shadow tree.\n var guard = 0;\n while (node && node.shadowRoot && guard < 20) {\n guard++;\n var inner = null;\n try {\n inner = node.shadowRoot.elementFromPoint ? node.shadowRoot.elementFromPoint(x, y) : null;\n } catch (_) {\n inner = null;\n }\n if (!inner || inner === node) break;\n node = inner;\n }\n return node;\n }\n\n function computedPointerEvents(el) {\n // Read through the element's own window (cross-document\n // getComputedStyle is unreliable); any failure fails open (not 'none').\n try {\n var view = (el.ownerDocument && el.ownerDocument.defaultView) || window;\n return view.getComputedStyle(el).pointerEvents;\n } catch (_) {\n return '';\n }\n }\n\n function checkOcclusion(target, localX, localY) {\n // Hit-test starting from the element's OWN root (its closest shadow\n // root, or the document that owns it when not in shadow DOM) using\n // LOCAL coordinates \u2014 the same coordinate space that root's own\n // elementFromPoint expects (an iframe document's viewport starts at\n // its own top-left, not the top-level page's).\n var root = target.getRootNode ? target.getRootNode() : document;\n var hit = hitTestDeep(root, localX, localY);\n if (!hit) {\n // Nothing hit-tested at all (e.g. elementFromPoint unsupported in\n // this environment, or the point is outside the rendered area).\n // Fail open rather than block a click we cannot actually verify.\n return { allowed: true };\n }\n if (hit === target || composedContains(target, hit)) {\n return { allowed: true };\n }\n if (composedContains(hit, target)) {\n // ANCESTOR hit: the click targets the ancestor and never reaches\n // the element itself (events bubble upward, not down). Allowed as\n // before, but surfaced so click results can report hit_element.\n return { allowed: true, hit: describeElement(hit) };\n }\n if (computedPointerEvents(target) === 'none') {\n // A pointer-events:none target can never be the hit-test result \u2014 a\n // real user click at this exact point reaches hit instead, by the\n // page's own design (invisible a11y layer over the visual control's\n // hit-target sibling). The hit is not a\n // blocker to dismiss; it IS what a real click activates, so allow\n // the dispatch and report the true recipient.\n return { allowed: true, hit: describeElement(hit) };\n }\n return { allowed: false, blocker: describeElement(hit) };\n }\n";
|
|
40
|
+
export declare const DEEP_QUERY_JS = "\n function collectSearchRoots(start) {\n // start: a Document, Element or ShadowRoot to scope the search to.\n // Returns [start, ...every open shadow root and same-origin iframe\n // document reachable inside start's subtree, recursively]. The caller\n // runs .querySelectorAll()/.querySelector() on each entry directly \u2014\n // each entry's own querySelectorAll does NOT pierce into nested shadow\n // roots or iframes on its own, which is why we enumerate them here.\n var roots = [start];\n function walk(scope, depth) {\n // Depth guard, same pattern as the other recursive helpers here:\n // pathological nesting (or a cycle introduced by a broken mock)\n // must degrade to a truncated result, never a stack overflow.\n if (depth > 20) return;\n var all;\n try {\n all = scope.querySelectorAll('*');\n } catch (_) {\n return;\n }\n for (var i = 0; i < all.length; i++) {\n var el = all[i];\n if (el.shadowRoot) {\n roots.push(el.shadowRoot);\n walk(el.shadowRoot, depth + 1);\n }\n if (el.tagName === 'IFRAME') {\n var doc = null;\n try {\n doc = el.contentDocument;\n } catch (_) {\n doc = null;\n }\n if (doc) {\n roots.push(doc);\n walk(doc, depth + 1);\n }\n }\n }\n }\n walk(start, 0);\n return roots;\n }\n\n function deepWalkAll(callback, start) {\n // Tree-walk primitive: invoke callback(el) for every element reachable\n // from start (default: document) across the top document, every open\n // shadow root, and every same-origin iframe document.\n var root = start || document;\n var roots = collectSearchRoots(root);\n for (var r = 0; r < roots.length; r++) {\n var all;\n try {\n all = roots[r].querySelectorAll('*');\n } catch (_) {\n continue;\n }\n for (var i = 0; i < all.length; i++) callback(all[i]);\n }\n }\n\n function deepQueryAll(selector, start) {\n var root = start || document;\n var roots = collectSearchRoots(root);\n var out = [];\n for (var r = 0; r < roots.length; r++) {\n var found;\n try {\n found = roots[r].querySelectorAll(selector);\n } catch (_) {\n continue;\n }\n for (var i = 0; i < found.length; i++) out.push(found[i]);\n }\n return out;\n }\n\n function deepQueryFirst(selector, start) {\n var root = start || document;\n var roots = collectSearchRoots(root);\n for (var r = 0; r < roots.length; r++) {\n var el = null;\n try {\n el = roots[r].querySelector(selector);\n } catch (_) {\n el = null;\n }\n if (el) return el;\n }\n return null;\n }\n\n function frameElementChain(el) {\n // Walk up from el's own window to the top window, collecting the\n // <iframe> element that hosts each intermediate document. Outermost\n // frame first. Same-origin only \u2014 frameElement throws/returns null\n // across an origin boundary, at which point the chain simply stops\n // (the coordinate math below degrades gracefully rather than throwing).\n var chain = [];\n var doc = el.ownerDocument;\n var win = doc && doc.defaultView;\n if (!win) return chain;\n var topWin;\n try {\n topWin = win.top;\n } catch (_) {\n topWin = win;\n }\n var guard = 0;\n while (win && win !== topWin && guard < 20) {\n guard++;\n var fe = null;\n try {\n fe = win.frameElement;\n } catch (_) {\n fe = null;\n }\n if (!fe) break;\n chain.unshift(fe);\n win = fe.ownerDocument && fe.ownerDocument.defaultView;\n }\n return chain;\n }\n\n function frameViewportOriginOffset(fe) {\n // The embedded document's viewport origin sits at the frame element's\n // CONTENT box, but getBoundingClientRect() returns the BORDER box \u2014\n // border and padding must be added on top of rect.left/rect.top or\n // every coordinate inside a styled iframe lands short. clientLeft/\n // clientTop are the border widths; padding comes from computed style\n // (read through the frame's own view \u2014 same-origin, so reachable).\n var borderX = fe.clientLeft || 0;\n var borderY = fe.clientTop || 0;\n var padX = 0;\n var padY = 0;\n try {\n var view = (fe.ownerDocument && fe.ownerDocument.defaultView) || window;\n var style = view.getComputedStyle(fe);\n padX = parseFloat(style.paddingLeft) || 0;\n padY = parseFloat(style.paddingTop) || 0;\n } catch (_) {}\n return { x: borderX + padX, y: borderY + padY };\n }\n\n function clickRectOf(el) {\n // For a line-wrapped INLINE element (a link spanning two lines), the\n // center of getBoundingClientRect() can land in the unpainted gap\n // between the line boxes \u2014 elementFromPoint would report whatever sits\n // behind, and a dispatched click would miss. The first client rect is\n // always a painted fragment, so its center is a safe click point.\n // Block-level elements have exactly one client rect, identical to the\n // bounding rect \u2014 no behavior change for them.\n var rects = null;\n try {\n rects = el.getClientRects ? el.getClientRects() : null;\n } catch (_) {\n rects = null;\n }\n if (rects && rects.length > 0) return rects[0];\n return el.getBoundingClientRect();\n }\n\n function viewportCenterOf(el) {\n // Returns the element's click point in two coordinate spaces:\n // - localX/localY: relative to el's OWN document viewport (what\n // that document's elementFromPoint expects).\n // - x/y: relative to the TOP-LEVEL viewport (what CDP\n // Input.dispatchMouseEvent expects) \u2014 localX/localY plus the\n // accumulated content-box origin of every ancestor <iframe>\n // element (border-box rect + border + padding). Shadow DOM\n // boundaries need no offset: they do not introduce a new\n // coordinate space.\n // Both spaces derive from the SAME point (first-client-rect center),\n // so the occlusion hit-test and the CDP dispatch always agree.\n var rect = clickRectOf(el);\n var localX = rect.left + rect.width / 2;\n var localY = rect.top + rect.height / 2;\n var chain = frameElementChain(el);\n var offsetX = 0;\n var offsetY = 0;\n for (var i = 0; i < chain.length; i++) {\n var frameRect = chain[i].getBoundingClientRect();\n var origin = frameViewportOriginOffset(chain[i]);\n offsetX += frameRect.left + origin.x;\n offsetY += frameRect.top + origin.y;\n }\n return { x: offsetX + localX, y: offsetY + localY, localX: localX, localY: localY };\n }\n\n function composedContains(ancestor, node) {\n // Node.contains() does not cross shadow boundaries. This walks the\n // COMPOSED tree: when climbing hits a ShadowRoot (no parentNode), jump\n // to its host and keep climbing.\n var cur = node;\n var guard = 0;\n while (cur && guard < 1000) {\n guard++;\n if (cur === ancestor) return true;\n if (cur.parentNode) {\n cur = cur.parentNode;\n continue;\n }\n if (cur.host) {\n cur = cur.host;\n continue;\n }\n break;\n }\n return false;\n }\n\n function describeElement(el) {\n if (!el || !el.tagName) return 'an unknown element';\n var desc = el.tagName.toLowerCase();\n if (el.id) desc += '#' + el.id;\n if (el.classList && el.classList.length) {\n var classes = [];\n for (var i = 0; i < el.classList.length && i < 2; i++) classes.push(el.classList[i]);\n if (classes.length) desc += '.' + classes.join('.');\n }\n return desc;\n }\n\n function hitTestDeep(root, x, y) {\n var node = null;\n try {\n node = root && root.elementFromPoint ? root.elementFromPoint(x, y) : null;\n } catch (_) {\n node = null;\n }\n // The hit element may itself host a nested open shadow root whose\n // content visually covers the point \u2014 descend until elementFromPoint\n // stops finding a deeper shadow tree.\n var guard = 0;\n while (node && node.shadowRoot && guard < 20) {\n guard++;\n var inner = null;\n try {\n inner = node.shadowRoot.elementFromPoint ? node.shadowRoot.elementFromPoint(x, y) : null;\n } catch (_) {\n inner = null;\n }\n if (!inner || inner === node) break;\n node = inner;\n }\n return node;\n }\n\n function computedPointerEvents(el) {\n // Read through the element's own window (cross-document\n // getComputedStyle is unreliable); any failure fails open (not 'none').\n try {\n var view = (el.ownerDocument && el.ownerDocument.defaultView) || window;\n return view.getComputedStyle(el).pointerEvents;\n } catch (_) {\n return '';\n }\n }\n\n function checkOcclusion(target, localX, localY) {\n // Hit-test starting from the element's OWN root (its closest shadow\n // root, or the document that owns it when not in shadow DOM) using\n // LOCAL coordinates \u2014 the same coordinate space that root's own\n // elementFromPoint expects (an iframe document's viewport starts at\n // its own top-left, not the top-level page's).\n var root = target.getRootNode ? target.getRootNode() : document;\n var hit = hitTestDeep(root, localX, localY);\n if (!hit) {\n // Nothing hit-tested at all (e.g. elementFromPoint unsupported in\n // this environment, or the point is outside the rendered area).\n // Fail open rather than block a click we cannot actually verify.\n return { allowed: true };\n }\n if (hit === target || composedContains(target, hit)) {\n return { allowed: true };\n }\n if (computedPointerEvents(target) === 'none') {\n // A pointer-events:none target can never be the hit-test result \u2014 a\n // real user click at this exact point reaches hit instead, by the\n // page's own design (an icon inside its button, or an invisible a11y\n // layer over the visual control's hit-target sibling). The hit is not\n // a blocker to dismiss; it IS what a real click activates, so allow\n // the dispatch and report the true recipient.\n return { allowed: true, hit: describeElement(hit) };\n }\n if (composedContains(hit, target)) {\n // ANCESTOR hit on a target that does take pointer events: the target\n // is painted but not at its own centre (clipped by an overflow\n // container, zero-size, covered by its own parent). The click would\n // land on the ancestor and never reach the element itself (events\n // bubble upward, not down), so refuse it as 'retargeted' and report\n // where it would land.\n return { allowed: false, retargeted: true, hit: describeElement(hit) };\n }\n return { allowed: false, blocker: describeElement(hit) };\n }\n";
|
|
@@ -313,21 +313,24 @@ export const DEEP_QUERY_JS = `
|
|
|
313
313
|
if (hit === target || composedContains(target, hit)) {
|
|
314
314
|
return { allowed: true };
|
|
315
315
|
}
|
|
316
|
-
if (composedContains(hit, target)) {
|
|
317
|
-
// ANCESTOR hit: the click targets the ancestor and never reaches
|
|
318
|
-
// the element itself (events bubble upward, not down). Allowed as
|
|
319
|
-
// before, but surfaced so click results can report hit_element.
|
|
320
|
-
return { allowed: true, hit: describeElement(hit) };
|
|
321
|
-
}
|
|
322
316
|
if (computedPointerEvents(target) === 'none') {
|
|
323
317
|
// A pointer-events:none target can never be the hit-test result — a
|
|
324
318
|
// real user click at this exact point reaches hit instead, by the
|
|
325
|
-
// page's own design (
|
|
326
|
-
// hit-target sibling). The hit is not
|
|
327
|
-
// blocker to dismiss; it IS what a real click activates, so allow
|
|
319
|
+
// page's own design (an icon inside its button, or an invisible a11y
|
|
320
|
+
// layer over the visual control's hit-target sibling). The hit is not
|
|
321
|
+
// a blocker to dismiss; it IS what a real click activates, so allow
|
|
328
322
|
// the dispatch and report the true recipient.
|
|
329
323
|
return { allowed: true, hit: describeElement(hit) };
|
|
330
324
|
}
|
|
325
|
+
if (composedContains(hit, target)) {
|
|
326
|
+
// ANCESTOR hit on a target that does take pointer events: the target
|
|
327
|
+
// is painted but not at its own centre (clipped by an overflow
|
|
328
|
+
// container, zero-size, covered by its own parent). The click would
|
|
329
|
+
// land on the ancestor and never reach the element itself (events
|
|
330
|
+
// bubble upward, not down), so refuse it as 'retargeted' and report
|
|
331
|
+
// where it would land.
|
|
332
|
+
return { allowed: false, retargeted: true, hit: describeElement(hit) };
|
|
333
|
+
}
|
|
331
334
|
return { allowed: false, blocker: describeElement(hit) };
|
|
332
335
|
}
|
|
333
336
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-query.js","sourceRoot":"","sources":["../../src/inpage/deep-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG
|
|
1
|
+
{"version":3,"file":"deep-query.js","sourceRoot":"","sources":["../../src/inpage/deep-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwS5B,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "browser-link",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.39.0",
|
|
5
5
|
"description": "Bridge between Chrome and an MCP client (Claude Code, OpenCode, GitHub Copilot CLI, …). Per-tab manual activation.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"debugger",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hand-written core of the agent manual: the part every agent must see
|
|
3
|
+
* before its first browser call. It is rendered first in the MCP
|
|
4
|
+
* `initialize.instructions` (hosts truncate that text; Claude Code cut it at
|
|
5
|
+
* ~4,090 characters) and first in the installed instructions block, so it is
|
|
6
|
+
* pinned at 3,800 characters by `manual.test.ts`.
|
|
7
|
+
*
|
|
8
|
+
* Rule for editing: every line must change what the agent does next. Depth
|
|
9
|
+
* lives in the per-tool `doc` blocks and in `topics.ts`, reachable through
|
|
10
|
+
* `browser.manual`.
|
|
11
|
+
*
|
|
12
|
+
* The bilingual phrases in the trigger list are DATA (what users actually
|
|
13
|
+
* type), not instructions; tests pin them verbatim.
|
|
14
|
+
*/
|
|
15
|
+
export interface CoreOptions {
|
|
16
|
+
/** One line placed before everything else, e.g. an outdated-block notice. */
|
|
17
|
+
notice?: string;
|
|
18
|
+
}
|
|
19
|
+
/** The core as one string; the optional notice is its first line. */
|
|
20
|
+
export declare function renderCore(opts?: CoreOptions): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hand-written core of the agent manual: the part every agent must see
|
|
3
|
+
* before its first browser call. It is rendered first in the MCP
|
|
4
|
+
* `initialize.instructions` (hosts truncate that text; Claude Code cut it at
|
|
5
|
+
* ~4,090 characters) and first in the installed instructions block, so it is
|
|
6
|
+
* pinned at 3,800 characters by `manual.test.ts`.
|
|
7
|
+
*
|
|
8
|
+
* Rule for editing: every line must change what the agent does next. Depth
|
|
9
|
+
* lives in the per-tool `doc` blocks and in `topics.ts`, reachable through
|
|
10
|
+
* `browser.manual`.
|
|
11
|
+
*
|
|
12
|
+
* The bilingual phrases in the trigger list are DATA (what users actually
|
|
13
|
+
* type), not instructions; tests pin them verbatim.
|
|
14
|
+
*/
|
|
15
|
+
import { TOPIC_NAMES } from './topics.js';
|
|
16
|
+
const CORE_LINES = [
|
|
17
|
+
'## browser-link — reflex protocol (ALWAYS ACTIVE)',
|
|
18
|
+
'',
|
|
19
|
+
'`browser.*` tools see only the Chrome tabs the user connected through the companion extension. Never reason about page state you have not seen.',
|
|
20
|
+
'',
|
|
21
|
+
'### TRIGGER LIST — call before responding when ANY apply',
|
|
22
|
+
'',
|
|
23
|
+
'User mentions any of these (English or Spanish):',
|
|
24
|
+
'- "the button doesn\'t work" / "no anda el botón" / "no funciona X"',
|
|
25
|
+
'- "broken layout" / "está roto" / "se ve mal" / "looks wrong"',
|
|
26
|
+
'- "check if X works" / "fíjate si anda X" / "¿anda X?" / "does X work?"',
|
|
27
|
+
'- "the page" / "la página" / "la web" / "the site"',
|
|
28
|
+
'- "open this in the browser" / "abrí esto en el navegador" / "navegá a"',
|
|
29
|
+
'- a UI element, web app name, URL, browser tab, layout, dialog, form',
|
|
30
|
+
'→ call `browser.list_tabs` FIRST. A tab with a `map` field: read its `inline` map when present, otherwise call `browser.map.recall` BEFORE `browser.snapshot`.',
|
|
31
|
+
'',
|
|
32
|
+
'- Before suggesting a UI code change or a click, `browser.snapshot` and verify the element exists.',
|
|
33
|
+
'- "Tab not connected" → `browser.events` for the renamed tab_id, then retry.',
|
|
34
|
+
'- SELF-CHECK after each user message: is this about something visible in a browser? Did I run `browser.list_tabs`?',
|
|
35
|
+
'',
|
|
36
|
+
'### Success is not proof',
|
|
37
|
+
'',
|
|
38
|
+
'An `ok` result can still mean the action missed. Check these fields:',
|
|
39
|
+
'- `hit_element` on `click` (and flow click steps) appears when a `pointer-events: none` target passes the click on, or on `force: true`; it names the real receiver: if it is not your target or inside it, the click missed → do not proceed; `find` the element that receives clicks by its text.',
|
|
40
|
+
'- `retargeted` error on `click`: the click would land on an ancestor of your target (clipped or covered by its own container) → scroll or expand the container, or `find` the element that receives clicks; `force: true` clicks there anyway.',
|
|
41
|
+
'- `settle.settled: false` or `mutation_count: 0`: quiet is not effect → `expect` or `wait_for` the effect you need.',
|
|
42
|
+
'- `since_status` on `snapshot`: you received a full snapshot → keep its new `snapshot_id`.',
|
|
43
|
+
'- `reresolved: true`: the node was replaced and re-found → fine.',
|
|
44
|
+
'- `ref-stale` → take a fresh `snapshot`/`find`; never retry the same ref.',
|
|
45
|
+
'- `wait_for` returning `matched: false` is not an error → decide.',
|
|
46
|
+
'- `extension_outdated: true` on `list_tabs` → extension older than server, fields may be missing: ask the user to update or reload the extension.',
|
|
47
|
+
'',
|
|
48
|
+
'### Habits',
|
|
49
|
+
'',
|
|
50
|
+
'- One full `snapshot` per view, then `snapshot({ since: snapshot_id })` for every later look.',
|
|
51
|
+
'- Act on the `ref` from the last `snapshot`/`find`, not a selector copied earlier; never inject attributes with `evaluate` to build selectors.',
|
|
52
|
+
'- Locate with `find` by text (`role` optional). `evaluate` only reads, never clicks or types: synthetic events are `isTrusted: false`, apps ignore them, and nothing records what happened.',
|
|
53
|
+
'- Downloads only through `browser.download` (`url` or `selector`), never a click on a download control.',
|
|
54
|
+
'- Pass `expect` to `click`/`type`/`press` when the effect matters.',
|
|
55
|
+
'',
|
|
56
|
+
'### Shared tabs and cdp-direct',
|
|
57
|
+
'',
|
|
58
|
+
'- Several agents can share the bridge: `browser.claim_tab` before a multi-step flow, `browser.release_tab` after.',
|
|
59
|
+
'- On a claim conflict do not spin-retry: use another tab or ask the user.',
|
|
60
|
+
'- `cdp:` tabs (`transport: "cdp"`) lack drag, console, network, network_body, canvas_screenshot, dialog_respond, set_permission, wait_for_tab and download by selector.',
|
|
61
|
+
'- "cdp-direct is disabled" / "requires an active grant" → relay `browser-link config set cdp-direct.enabled true` or `browser-link cdp allow` to the user; there is no workaround.',
|
|
62
|
+
'',
|
|
63
|
+
'### Depth on demand',
|
|
64
|
+
'',
|
|
65
|
+
`\`browser.manual()\` lists tools and topics; \`browser.manual({ tool: "browser.click" })\` gives one tool's full doc; \`browser.manual({ topic })\` gives one topic: ${TOPIC_NAMES.join(', ')}.`,
|
|
66
|
+
];
|
|
67
|
+
/** The core as one string; the optional notice is its first line. */
|
|
68
|
+
export function renderCore(opts = {}) {
|
|
69
|
+
const lines = opts.notice ? [opts.notice, '', ...CORE_LINES] : CORE_LINES;
|
|
70
|
+
return lines.join('\n');
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/manual/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,GAAsB;IACpC,mDAAmD;IACnD,EAAE;IACF,iJAAiJ;IACjJ,EAAE;IACF,0DAA0D;IAC1D,EAAE;IACF,kDAAkD;IAClD,qEAAqE;IACrE,+DAA+D;IAC/D,yEAAyE;IACzE,oDAAoD;IACpD,yEAAyE;IACzE,sEAAsE;IACtE,gKAAgK;IAChK,EAAE;IACF,oGAAoG;IACpG,8EAA8E;IAC9E,oHAAoH;IACpH,EAAE;IACF,0BAA0B;IAC1B,EAAE;IACF,sEAAsE;IACtE,qSAAqS;IACrS,gPAAgP;IAChP,qHAAqH;IACrH,4FAA4F;IAC5F,kEAAkE;IAClE,2EAA2E;IAC3E,mEAAmE;IACnE,mJAAmJ;IACnJ,EAAE;IACF,YAAY;IACZ,EAAE;IACF,+FAA+F;IAC/F,gJAAgJ;IAChJ,6LAA6L;IAC7L,yGAAyG;IACzG,oEAAoE;IACpE,EAAE;IACF,gCAAgC;IAChC,EAAE;IACF,mHAAmH;IACnH,2EAA2E;IAC3E,yKAAyK;IACzK,oLAAoL;IACpL,EAAE;IACF,qBAAqB;IACrB,EAAE;IACF,wKAAwK,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;CAClM,CAAC;AAOF,qEAAqE;AACrE,MAAM,UAAU,UAAU,CAAC,OAAoB,EAAE;IAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IAC1E,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start-time refresh of the installed instructions block. A client whose
|
|
3
|
+
* block exists but is older than the server (`installed-outdated`) is
|
|
4
|
+
* refreshed in place through its installer's `install()` — the same write
|
|
5
|
+
* path `browser-link instructions install` uses (`installAt`: atomic,
|
|
6
|
+
* home-guarded, refuses a corrupt file). The prior install is the opt-in: a
|
|
7
|
+
* client without a block (`not-installed`, `no-file`) or with a `corrupt`
|
|
8
|
+
* file is never touched.
|
|
9
|
+
*
|
|
10
|
+
* Pure apart from the installers it is handed, so tests drive it with stubs.
|
|
11
|
+
*/
|
|
12
|
+
import type { ClientId, InstructionsInstaller } from '../agent-instructions/index.js';
|
|
13
|
+
import { type ClientInstructionsStatus } from './status.js';
|
|
14
|
+
export interface RefreshFailure {
|
|
15
|
+
id: ClientId;
|
|
16
|
+
displayName: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
export interface RefreshResult {
|
|
20
|
+
/** Every client's status after the refresh (a failed client keeps its
|
|
21
|
+
* `installed-outdated` state). */
|
|
22
|
+
statuses: ClientInstructionsStatus[];
|
|
23
|
+
/** Clients whose block was rewritten, with their post-refresh status. */
|
|
24
|
+
refreshed: ClientInstructionsStatus[];
|
|
25
|
+
failed: RefreshFailure[];
|
|
26
|
+
}
|
|
27
|
+
export declare function refreshOutdatedBlocks(input: {
|
|
28
|
+
statuses: readonly ClientInstructionsStatus[];
|
|
29
|
+
installers: readonly InstructionsInstaller[];
|
|
30
|
+
enabled: boolean;
|
|
31
|
+
}): RefreshResult;
|
|
32
|
+
/** The one-line connect-time notice after a refresh: the update notice when
|
|
33
|
+
* at least one block was rewritten, then the `installed-outdated` notice for
|
|
34
|
+
* any block still outdated, with the failure reasons appended. `undefined`
|
|
35
|
+
* when there is nothing to say. */
|
|
36
|
+
export declare function refreshNotice(result: RefreshResult, version?: string): string | undefined;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start-time refresh of the installed instructions block. A client whose
|
|
3
|
+
* block exists but is older than the server (`installed-outdated`) is
|
|
4
|
+
* refreshed in place through its installer's `install()` — the same write
|
|
5
|
+
* path `browser-link instructions install` uses (`installAt`: atomic,
|
|
6
|
+
* home-guarded, refuses a corrupt file). The prior install is the opt-in: a
|
|
7
|
+
* client without a block (`not-installed`, `no-file`) or with a `corrupt`
|
|
8
|
+
* file is never touched.
|
|
9
|
+
*
|
|
10
|
+
* Pure apart from the installers it is handed, so tests drive it with stubs.
|
|
11
|
+
*/
|
|
12
|
+
import { VERSION } from '../version.js';
|
|
13
|
+
import { instructionsNotice } from './status.js';
|
|
14
|
+
export function refreshOutdatedBlocks(input) {
|
|
15
|
+
const { installers, enabled } = input;
|
|
16
|
+
const refreshed = [];
|
|
17
|
+
const failed = [];
|
|
18
|
+
const statuses = input.statuses.map((s) => {
|
|
19
|
+
if (!enabled || s.state.kind !== 'installed-outdated')
|
|
20
|
+
return s;
|
|
21
|
+
const installer = installers.find((i) => i.id === s.id);
|
|
22
|
+
if (installer === undefined)
|
|
23
|
+
return s;
|
|
24
|
+
try {
|
|
25
|
+
installer.install();
|
|
26
|
+
const after = { ...s, state: installer.detect().state };
|
|
27
|
+
refreshed.push(after);
|
|
28
|
+
return after;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
failed.push({
|
|
32
|
+
id: s.id,
|
|
33
|
+
displayName: s.displayName,
|
|
34
|
+
message: err instanceof Error ? err.message : String(err),
|
|
35
|
+
});
|
|
36
|
+
return s;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return { statuses, refreshed, failed };
|
|
40
|
+
}
|
|
41
|
+
/** The one-line connect-time notice after a refresh: the update notice when
|
|
42
|
+
* at least one block was rewritten, then the `installed-outdated` notice for
|
|
43
|
+
* any block still outdated, with the failure reasons appended. `undefined`
|
|
44
|
+
* when there is nothing to say. */
|
|
45
|
+
export function refreshNotice(result, version = VERSION) {
|
|
46
|
+
const parts = [];
|
|
47
|
+
if (result.refreshed.length > 0) {
|
|
48
|
+
const names = result.refreshed.map((s) => s.displayName).join(', ');
|
|
49
|
+
parts.push(`Your browser-link instructions block was updated to v${version} for ${names}: restart the client to load it.`);
|
|
50
|
+
}
|
|
51
|
+
const outdated = instructionsNotice(result.statuses, version);
|
|
52
|
+
if (outdated !== undefined)
|
|
53
|
+
parts.push(outdated);
|
|
54
|
+
if (result.failed.length > 0) {
|
|
55
|
+
const reasons = result.failed.map((f) => `${f.displayName}: ${f.message}`).join('; ');
|
|
56
|
+
parts.push(`Automatic refresh failed for ${reasons}`);
|
|
57
|
+
}
|
|
58
|
+
return parts.length > 0 ? parts.join(' ') : undefined;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/manual/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,kBAAkB,EAAiC,MAAM,aAAa,CAAC;AAiBhF,MAAM,UAAU,qBAAqB,CAAC,KAIrC;IACC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACtC,MAAM,SAAS,GAA+B,EAAE,CAAC;IACjD,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACxC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,oBAAoB;YAAE,OAAO,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC;YACH,SAAS,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,KAAK,GAA6B,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;YAClF,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC;gBACV,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D,CAAC,CAAC;YACH,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACzC,CAAC;AAED;;;mCAGmC;AACnC,MAAM,UAAU,aAAa,CAAC,MAAqB,EAAE,OAAO,GAAG,OAAO;IACpE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,KAAK,CAAC,IAAI,CACR,wDAAwD,OAAO,QAAQ,KAAK,kCAAkC,CAC/G,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9D,IAAI,QAAQ,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtF,KAAK,CAAC,IAAI,CAAC,gCAAgC,OAAO,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renderers for the agent manual. One source, three outputs:
|
|
3
|
+
*
|
|
4
|
+
* - `renderServerInstructions` — the MCP `initialize.instructions` text:
|
|
5
|
+
* the core first (hosts truncate this text, so what comes first is what
|
|
6
|
+
* arrives), then one section per tool rendered from its `doc` block.
|
|
7
|
+
* - `renderInstallBody` — the body of the block `browser-link instructions
|
|
8
|
+
* install` writes into each client's instructions file: the core, then
|
|
9
|
+
* the topics an agent needs every session.
|
|
10
|
+
* - `renderTopic` (in `topics.ts`) — one topic on demand.
|
|
11
|
+
*/
|
|
12
|
+
import type { ToolDefinition } from '../tools/types.js';
|
|
13
|
+
import { type CoreOptions } from './core.js';
|
|
14
|
+
/** Render a single tool's documentation as a markdown section. Skips
|
|
15
|
+
* tools without a `doc` block. */
|
|
16
|
+
export declare function renderTool(def: ToolDefinition): string | null;
|
|
17
|
+
/** The MCP `initialize.instructions` text: core, then `## Tools` and one
|
|
18
|
+
* section per documented tool, browser tools first, then map tools. */
|
|
19
|
+
export declare function renderServerInstructions(opts?: CoreOptions): string;
|
|
20
|
+
/** The installed block body as single lines: the core, then the install
|
|
21
|
+
* topics, each separated by one blank line. */
|
|
22
|
+
export declare function renderInstallBody(): string[];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renderers for the agent manual. One source, three outputs:
|
|
3
|
+
*
|
|
4
|
+
* - `renderServerInstructions` — the MCP `initialize.instructions` text:
|
|
5
|
+
* the core first (hosts truncate this text, so what comes first is what
|
|
6
|
+
* arrives), then one section per tool rendered from its `doc` block.
|
|
7
|
+
* - `renderInstallBody` — the body of the block `browser-link instructions
|
|
8
|
+
* install` writes into each client's instructions file: the core, then
|
|
9
|
+
* the topics an agent needs every session.
|
|
10
|
+
* - `renderTopic` (in `topics.ts`) — one topic on demand.
|
|
11
|
+
*/
|
|
12
|
+
import { BROWSER_TOOL_DEFINITIONS } from '../tools/browser-definitions.js';
|
|
13
|
+
import { MAP_TOOL_DEFINITIONS } from '../map/tools.js';
|
|
14
|
+
import { renderCore } from './core.js';
|
|
15
|
+
import { renderTopic } from './topics.js';
|
|
16
|
+
/** Topics embedded in the installed block, in order. The block is read in
|
|
17
|
+
* full every session, so these are the ones worth that cost. */
|
|
18
|
+
const INSTALL_TOPICS = ['token-efficiency', 'downloads', 'flows', 'map'];
|
|
19
|
+
/** Render a single tool's documentation as a markdown section. Skips
|
|
20
|
+
* tools without a `doc` block. */
|
|
21
|
+
export function renderTool(def) {
|
|
22
|
+
if (!def.doc)
|
|
23
|
+
return null;
|
|
24
|
+
const { purpose, when_to_use, gotchas, example } = def.doc;
|
|
25
|
+
const lines = [`### ${def.name}`, '', purpose, '', '**When to use:**'];
|
|
26
|
+
for (const w of when_to_use)
|
|
27
|
+
lines.push(`- ${w}`);
|
|
28
|
+
if (gotchas && gotchas.length > 0) {
|
|
29
|
+
lines.push('', '**Gotchas:**');
|
|
30
|
+
for (const g of gotchas)
|
|
31
|
+
lines.push(`- ${g}`);
|
|
32
|
+
}
|
|
33
|
+
if (example !== undefined && example !== '') {
|
|
34
|
+
lines.push('', '**Example:**', '```', example, '```');
|
|
35
|
+
}
|
|
36
|
+
return lines.join('\n');
|
|
37
|
+
}
|
|
38
|
+
/** The MCP `initialize.instructions` text: core, then `## Tools` and one
|
|
39
|
+
* section per documented tool, browser tools first, then map tools. */
|
|
40
|
+
export function renderServerInstructions(opts = {}) {
|
|
41
|
+
const all = [...BROWSER_TOOL_DEFINITIONS, ...MAP_TOOL_DEFINITIONS];
|
|
42
|
+
const sections = [renderCore(opts), '', '## Tools'];
|
|
43
|
+
for (const def of all) {
|
|
44
|
+
const rendered = renderTool(def);
|
|
45
|
+
if (rendered)
|
|
46
|
+
sections.push('', rendered);
|
|
47
|
+
}
|
|
48
|
+
return sections.join('\n');
|
|
49
|
+
}
|
|
50
|
+
/** The installed block body as single lines: the core, then the install
|
|
51
|
+
* topics, each separated by one blank line. */
|
|
52
|
+
export function renderInstallBody() {
|
|
53
|
+
const text = [renderCore({}), ...INSTALL_TOPICS.map(renderTopic)].join('\n\n');
|
|
54
|
+
return text.split('\n');
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/manual/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAoB,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAE1D;gEACgE;AAChE,MAAM,cAAc,GAAyB,CAAC,kBAAkB,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAE/F;kCACkC;AAClC,MAAM,UAAU,UAAU,CAAC,GAAmB;IAC5C,IAAI,CAAC,GAAG,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC;IAC3D,MAAM,KAAK,GAAa,CAAC,OAAO,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,kBAAkB,CAAC,CAAC;IACjF,KAAK,MAAM,CAAC,IAAI,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClD,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;uEACuE;AACvE,MAAM,UAAU,wBAAwB,CAAC,OAAoB,EAAE;IAC7D,MAAM,GAAG,GAAqB,CAAC,GAAG,wBAAwB,EAAE,GAAG,oBAAoB,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAa,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;IAC9D,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,QAAQ;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED;+CAC+C;AAC/C,MAAM,UAAU,iBAAiB;IAC/B,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only status of the installed instructions block, per client, shared
|
|
3
|
+
* by the primary's start-time notice (`server.ts`) and the `browser.manual`
|
|
4
|
+
* index. Detection only reads the clients' instructions files through each
|
|
5
|
+
* installer's `detect()` (home-guarded as the installers already are); this
|
|
6
|
+
* module never writes anything.
|
|
7
|
+
*/
|
|
8
|
+
import { type ClientId, type InstructionsInstaller, type InstructionsState } from '../agent-instructions/index.js';
|
|
9
|
+
export interface ClientInstructionsStatus {
|
|
10
|
+
id: ClientId;
|
|
11
|
+
displayName: string;
|
|
12
|
+
state: InstructionsState;
|
|
13
|
+
}
|
|
14
|
+
/** Detect every client's block. A client whose detection throws (permissions,
|
|
15
|
+
* EBADF) is skipped, never fatal. */
|
|
16
|
+
export declare function detectInstructionsStatus(installers?: readonly InstructionsInstaller[]): ClientInstructionsStatus[];
|
|
17
|
+
/** One status line for the `browser.manual` index. */
|
|
18
|
+
export declare function formatClientStatus(s: ClientInstructionsStatus, version?: string): string;
|
|
19
|
+
/** The one-line notice placed before the core when a client's block exists
|
|
20
|
+
* and is older than the server (`installed-outdated`); `undefined` otherwise.
|
|
21
|
+
* A client without a block (`not-installed`, `no-file`) is a choice and a
|
|
22
|
+
* `corrupt` file is the CLI's business: neither ever produces a notice. */
|
|
23
|
+
export declare function instructionsNotice(statuses: readonly ClientInstructionsStatus[], version?: string): string | undefined;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only status of the installed instructions block, per client, shared
|
|
3
|
+
* by the primary's start-time notice (`server.ts`) and the `browser.manual`
|
|
4
|
+
* index. Detection only reads the clients' instructions files through each
|
|
5
|
+
* installer's `detect()` (home-guarded as the installers already are); this
|
|
6
|
+
* module never writes anything.
|
|
7
|
+
*/
|
|
8
|
+
import { VERSION } from '../version.js';
|
|
9
|
+
import { INSTRUCTIONS_INSTALLERS, } from '../agent-instructions/index.js';
|
|
10
|
+
/** Detect every client's block. A client whose detection throws (permissions,
|
|
11
|
+
* EBADF) is skipped, never fatal. */
|
|
12
|
+
export function detectInstructionsStatus(installers = INSTRUCTIONS_INSTALLERS) {
|
|
13
|
+
const out = [];
|
|
14
|
+
for (const installer of installers) {
|
|
15
|
+
try {
|
|
16
|
+
const { state } = installer.detect();
|
|
17
|
+
out.push({ id: installer.id, displayName: installer.displayName, state });
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
/* best effort: an unreadable client is left out */
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
const v = (version) => (version === null ? 'unversioned' : `v${version}`);
|
|
26
|
+
/** One status line for the `browser.manual` index. */
|
|
27
|
+
export function formatClientStatus(s, version = VERSION) {
|
|
28
|
+
const { state } = s;
|
|
29
|
+
switch (state.kind) {
|
|
30
|
+
case 'installed':
|
|
31
|
+
return `${s.displayName}: installed ${v(state.version)}`;
|
|
32
|
+
case 'installed-outdated':
|
|
33
|
+
return `${s.displayName}: installed-outdated ${v(state.version)} → v${version}: run browser-link instructions install`;
|
|
34
|
+
case 'corrupt':
|
|
35
|
+
return `${s.displayName}: corrupt (${state.reason})`;
|
|
36
|
+
default:
|
|
37
|
+
return `${s.displayName}: ${state.kind}`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** The one-line notice placed before the core when a client's block exists
|
|
41
|
+
* and is older than the server (`installed-outdated`); `undefined` otherwise.
|
|
42
|
+
* A client without a block (`not-installed`, `no-file`) is a choice and a
|
|
43
|
+
* `corrupt` file is the CLI's business: neither ever produces a notice. */
|
|
44
|
+
export function instructionsNotice(statuses, version = VERSION) {
|
|
45
|
+
for (const s of statuses) {
|
|
46
|
+
if (s.state.kind === 'installed-outdated') {
|
|
47
|
+
return `Your browser-link instructions block is installed-outdated (installed ${v(s.state.version)}, server v${version}): run \`browser-link instructions install\` and restart the client.`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/manual/status.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,uBAAuB,GAIxB,MAAM,gCAAgC,CAAC;AAQxC;qCACqC;AACrC,MAAM,UAAU,wBAAwB,CACtC,aAA+C,uBAAuB;IAEtE,MAAM,GAAG,GAA+B,EAAE,CAAC;IAC3C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;YACrC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,WAAW,EAAE,SAAS,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,mDAAmD;QACrD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,GAAG,CAAC,OAAsB,EAAU,EAAE,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC;AAEjG,sDAAsD;AACtD,MAAM,UAAU,kBAAkB,CAAC,CAA2B,EAAE,OAAO,GAAG,OAAO;IAC/E,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IACpB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,WAAW;YACd,OAAO,GAAG,CAAC,CAAC,WAAW,eAAe,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3D,KAAK,oBAAoB;YACvB,OAAO,GAAG,CAAC,CAAC,WAAW,wBAAwB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,OAAO,yCAAyC,CAAC;QACzH,KAAK,SAAS;YACZ,OAAO,GAAG,CAAC,CAAC,WAAW,cAAc,KAAK,CAAC,MAAM,GAAG,CAAC;QACvD;YACE,OAAO,GAAG,CAAC,CAAC,WAAW,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7C,CAAC;AACH,CAAC;AAED;;;2EAG2E;AAC3E,MAAM,UAAU,kBAAkB,CAChC,QAA6C,EAC7C,OAAO,GAAG,OAAO;IAEjB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;YAC1C,OAAO,yEAAyE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,OAAO,sEAAsE,CAAC;QAC/L,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `browser.manual` — the agent manual on demand. Server-only: it reads the
|
|
3
|
+
* tool definitions, the topics and the instructions-block status, never a
|
|
4
|
+
* tab or the bridge.
|
|
5
|
+
*
|
|
6
|
+
* - no arguments → the index: version, block status per client, one line
|
|
7
|
+
* per tool, the topic list;
|
|
8
|
+
* - `{ tool }` → that tool's `doc` plus one line per `inputSchema` property;
|
|
9
|
+
* - `{ topic }` → one hand-written topic.
|
|
10
|
+
*/
|
|
11
|
+
import { type ClientInstructionsStatus } from './status.js';
|
|
12
|
+
export interface ManualOptions {
|
|
13
|
+
/** Current block status per client (read-only detection). */
|
|
14
|
+
statuses: () => readonly ClientInstructionsStatus[];
|
|
15
|
+
}
|
|
16
|
+
/** Text up to and including the first sentence end followed by a capital
|
|
17
|
+
* letter or the end of the text, so "e.g. `x`" does not cut it short. */
|
|
18
|
+
export declare function firstSentence(text: string): string;
|
|
19
|
+
/** Render `browser.manual`'s answer for `args`; throws on invalid input. */
|
|
20
|
+
export declare function renderManual(args: unknown, opts: ManualOptions): string;
|