@jobshimo/browser-link 0.37.0 → 0.38.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 (58) hide show
  1. package/dist/agent-instructions/content.js +5 -360
  2. package/dist/agent-instructions/content.js.map +1 -1
  3. package/dist/bridge/server.d.ts +5 -0
  4. package/dist/bridge/server.js +1 -1
  5. package/dist/bridge/server.js.map +1 -1
  6. package/dist/cdp/flow.d.ts +8 -0
  7. package/dist/cdp/flow.js +11 -0
  8. package/dist/cdp/flow.js.map +1 -1
  9. package/dist/cdp/inpage/builders.d.ts +7 -4
  10. package/dist/cdp/inpage/builders.js +4 -1
  11. package/dist/cdp/inpage/builders.js.map +1 -1
  12. package/dist/cdp/inpage/deep-query.d.ts +1 -1
  13. package/dist/cdp/inpage/deep-query.js +12 -9
  14. package/dist/cdp/inpage/deep-query.js.map +1 -1
  15. package/dist/cdp/transport.js +7 -1
  16. package/dist/cdp/transport.js.map +1 -1
  17. package/dist/commands/about.js +4 -2
  18. package/dist/commands/about.js.map +1 -1
  19. package/dist/extension/background.js +7 -1
  20. package/dist/extension/background.js.map +1 -1
  21. package/dist/extension/flow.d.ts +8 -0
  22. package/dist/extension/flow.js +11 -0
  23. package/dist/extension/flow.js.map +1 -1
  24. package/dist/extension/inpage/builders.d.ts +7 -4
  25. package/dist/extension/inpage/builders.js +4 -1
  26. package/dist/extension/inpage/builders.js.map +1 -1
  27. package/dist/extension/inpage/deep-query.d.ts +1 -1
  28. package/dist/extension/inpage/deep-query.js +12 -9
  29. package/dist/extension/inpage/deep-query.js.map +1 -1
  30. package/dist/extension/manifest.json +1 -1
  31. package/dist/manual/core.d.ts +20 -0
  32. package/dist/manual/core.js +71 -0
  33. package/dist/manual/core.js.map +1 -0
  34. package/dist/manual/render.d.ts +22 -0
  35. package/dist/manual/render.js +56 -0
  36. package/dist/manual/render.js.map +1 -0
  37. package/dist/manual/status.d.ts +23 -0
  38. package/dist/manual/status.js +52 -0
  39. package/dist/manual/status.js.map +1 -0
  40. package/dist/manual/tool.d.ts +20 -0
  41. package/dist/manual/tool.js +106 -0
  42. package/dist/manual/tool.js.map +1 -0
  43. package/dist/manual/topics.d.ts +18 -0
  44. package/dist/manual/topics.js +60 -0
  45. package/dist/manual/topics.js.map +1 -0
  46. package/dist/permissions.js +6 -0
  47. package/dist/permissions.js.map +1 -1
  48. package/dist/server.js +11 -14
  49. package/dist/server.js.map +1 -1
  50. package/dist/tools/browser-definitions.js +34 -6
  51. package/dist/tools/browser-definitions.js.map +1 -1
  52. package/dist/tools/browser-dispatch.d.ts +5 -0
  53. package/dist/tools/browser-dispatch.js +17 -0
  54. package/dist/tools/browser-dispatch.js.map +1 -1
  55. package/dist/tools/server-instructions.d.ts +24 -13
  56. package/dist/tools/server-instructions.js +25 -314
  57. package/dist/tools/server-instructions.js.map +1 -1
  58. 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 sibling
191
- * takes the click (allowed, not 'occluded' — see checkOcclusion), an
192
- * ancestor wrapper, or a `force` click landing on whatever covers the
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.allowed ? occlusion.hit : occlusion.blocker;
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 (invisible a11y layer over the visual control's
326
- // hit-target sibling). The hit is not a
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqS5B,CAAC"}
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.37.0",
4
+ "version": "0.38.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,71 @@
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
+ '',
47
+ '### Habits',
48
+ '',
49
+ '- One full `snapshot` per view, then `snapshot({ since: snapshot_id })` for every later look.',
50
+ '- Act on the `ref` from the last `snapshot`/`find`, not a selector copied earlier; never inject attributes with `evaluate` to build selectors.',
51
+ '- 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.',
52
+ '- Downloads only through `browser.download` (`url` or `selector`), never a click on a download control.',
53
+ '- Pass `expect` to `click`/`type`/`press` when the effect matters.',
54
+ '',
55
+ '### Shared tabs and cdp-direct',
56
+ '',
57
+ '- Several agents can share the bridge: `browser.claim_tab` before a multi-step flow, `browser.release_tab` after.',
58
+ '- On a claim conflict do not spin-retry: use another tab or ask the user.',
59
+ '- `cdp:` tabs (`transport: "cdp"`) lack drag, console, network, network_body, canvas_screenshot, dialog_respond, set_permission, wait_for_tab and download by selector.',
60
+ '- "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.',
61
+ '',
62
+ '### Depth on demand',
63
+ '',
64
+ `\`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(', ')}.`,
65
+ ];
66
+ /** The core as one string; the optional notice is its first line. */
67
+ export function renderCore(opts = {}) {
68
+ const lines = opts.notice ? [opts.notice, '', ...CORE_LINES] : CORE_LINES;
69
+ return lines.join('\n');
70
+ }
71
+ //# 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,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,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;
@@ -0,0 +1,106 @@
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 { VERSION } from '../version.js';
12
+ import { BROWSER_TOOL_DEFINITIONS } from '../tools/browser-definitions.js';
13
+ import { MAP_TOOL_DEFINITIONS } from '../map/tools.js';
14
+ import { renderTool } from './render.js';
15
+ import { TOPICS, TOPIC_NAMES, renderTopic } from './topics.js';
16
+ import { formatClientStatus } from './status.js';
17
+ /** Text up to and including the first sentence end followed by a capital
18
+ * letter or the end of the text, so "e.g. `x`" does not cut it short. */
19
+ export function firstSentence(text) {
20
+ const flat = text.replace(/\s+/g, ' ').trim();
21
+ const m = /^.*?[.!?](?=\s+[A-Z]|$)/.exec(flat);
22
+ return m ? m[0] : flat;
23
+ }
24
+ function allTools() {
25
+ return [...BROWSER_TOOL_DEFINITIONS, ...MAP_TOOL_DEFINITIONS];
26
+ }
27
+ function renderIndex(statuses) {
28
+ const lines = [`browser-link ${VERSION}`, '', 'Instructions block:'];
29
+ if (statuses.length === 0)
30
+ lines.push('- (no client detected)');
31
+ for (const s of statuses)
32
+ lines.push(`- ${formatClientStatus(s)}`);
33
+ lines.push('', 'Tools (`browser.manual({ tool })` for one):');
34
+ for (const def of allTools()) {
35
+ lines.push(`- ${def.name} — ${firstSentence(def.doc?.purpose ?? def.description)}`);
36
+ }
37
+ lines.push('', 'Topics (`browser.manual({ topic })` for one):');
38
+ for (const name of TOPIC_NAMES)
39
+ lines.push(`- ${name} — ${TOPICS[name].title}`);
40
+ return lines.join('\n');
41
+ }
42
+ function schemaType(schema) {
43
+ const t = schema.type;
44
+ if (Array.isArray(t))
45
+ return t.join('|');
46
+ if (t === 'array') {
47
+ const items = schema.items;
48
+ return items && typeof items.type === 'string' ? `${items.type}[]` : 'array';
49
+ }
50
+ if (typeof t === 'string')
51
+ return t;
52
+ const alts = (schema.oneOf ?? schema.anyOf);
53
+ if (Array.isArray(alts))
54
+ return alts.map(schemaType).join('|');
55
+ return 'any';
56
+ }
57
+ function renderParameters(def) {
58
+ const props = (def.inputSchema.properties ?? {});
59
+ const required = new Set(def.inputSchema.required ?? []);
60
+ const names = Object.keys(props);
61
+ if (names.length === 0)
62
+ return '**Parameters:** none';
63
+ const lines = ['**Parameters:**'];
64
+ for (const name of names) {
65
+ const p = props[name];
66
+ const head = `- ${name} (${schemaType(p)}, ${required.has(name) ? 'required' : 'optional'})`;
67
+ const desc = typeof p.description === 'string' ? firstSentence(p.description) : '';
68
+ lines.push(desc ? `${head}: ${desc}` : head);
69
+ }
70
+ return lines.join('\n');
71
+ }
72
+ function renderToolManual(name) {
73
+ const def = allTools().find((d) => d.name === name);
74
+ if (!def) {
75
+ throw new Error(`Unknown tool "${name}". Valid names: ${allTools()
76
+ .map((d) => d.name)
77
+ .join(', ')}`);
78
+ }
79
+ const doc = renderTool(def) ?? `### ${def.name}\n\n${def.description}`;
80
+ return `${doc}\n\n${renderParameters(def)}`;
81
+ }
82
+ function isTopicName(name) {
83
+ return TOPIC_NAMES.includes(name);
84
+ }
85
+ /** Render `browser.manual`'s answer for `args`; throws on invalid input. */
86
+ export function renderManual(args, opts) {
87
+ const { tool, topic } = (args ?? {});
88
+ if (tool !== undefined && topic !== undefined) {
89
+ throw new Error('browser.manual takes either `tool` or `topic`, not both.');
90
+ }
91
+ if (tool !== undefined) {
92
+ if (typeof tool !== 'string')
93
+ throw new Error('browser.manual: `tool` must be a string.');
94
+ return renderToolManual(tool);
95
+ }
96
+ if (topic !== undefined) {
97
+ if (typeof topic !== 'string')
98
+ throw new Error('browser.manual: `topic` must be a string.');
99
+ if (!isTopicName(topic)) {
100
+ throw new Error(`Unknown topic "${topic}". Valid topics: ${TOPIC_NAMES.join(', ')}`);
101
+ }
102
+ return renderTopic(topic);
103
+ }
104
+ return renderIndex(opts.statuses());
105
+ }
106
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/manual/tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Hand-written topic sections of the agent manual: cross-cutting depth that
3
+ * belongs to no single tool. Per-tool depth lives in each tool's `doc` block
4
+ * (`tools/browser-definitions.ts`, `map/tools.ts`); a topic only carries what
5
+ * those blocks do not. `browser.manual({ topic })` serves one topic, and the
6
+ * installed instructions block embeds a few of them after the core.
7
+ *
8
+ * A topic never repeats a sentence of the core (`manual.test.ts` pins it).
9
+ */
10
+ export declare const TOPIC_NAMES: readonly ["downloads", "canvas", "screenshot", "flows", "map", "token-efficiency", "multi-agent", "cdp-direct"];
11
+ export type TopicName = (typeof TOPIC_NAMES)[number];
12
+ export interface Topic {
13
+ title: string;
14
+ body: string;
15
+ }
16
+ export declare const TOPICS: Record<TopicName, Topic>;
17
+ /** One topic as a markdown section: `### <title>`, blank line, body. */
18
+ export declare function renderTopic(name: TopicName): string;