@particle-academy/agent-integrations 0.46.0 → 0.47.1

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 (96) hide show
  1. package/CHANGELOG.md +885 -0
  2. package/LICENSE +21 -0
  3. package/README.md +53 -3
  4. package/dist/bridges/artboard.d.cts +3 -3
  5. package/dist/bridges/artboard.d.ts +3 -3
  6. package/dist/bridges/catalog.d.cts +3 -3
  7. package/dist/bridges/catalog.d.ts +3 -3
  8. package/dist/bridges/charts.d.cts +3 -3
  9. package/dist/bridges/charts.d.ts +3 -3
  10. package/dist/bridges/cms.d.cts +3 -3
  11. package/dist/bridges/cms.d.ts +3 -3
  12. package/dist/bridges/code.d.cts +3 -3
  13. package/dist/bridges/code.d.ts +3 -3
  14. package/dist/bridges/doc.d.cts +3 -3
  15. package/dist/bridges/doc.d.ts +3 -3
  16. package/dist/bridges/features.d.cts +3 -3
  17. package/dist/bridges/features.d.ts +3 -3
  18. package/dist/bridges/files.d.cts +3 -3
  19. package/dist/bridges/files.d.ts +3 -3
  20. package/dist/bridges/flow.d.cts +3 -3
  21. package/dist/bridges/flow.d.ts +3 -3
  22. package/dist/bridges/forms.d.cts +3 -3
  23. package/dist/bridges/forms.d.ts +3 -3
  24. package/dist/bridges/git.d.cts +3 -3
  25. package/dist/bridges/git.d.ts +3 -3
  26. package/dist/bridges/grid.d.cts +3 -3
  27. package/dist/bridges/grid.d.ts +3 -3
  28. package/dist/bridges/map.d.cts +3 -3
  29. package/dist/bridges/map.d.ts +3 -3
  30. package/dist/bridges/navigation.d.cts +140 -0
  31. package/dist/bridges/navigation.d.ts +140 -0
  32. package/dist/bridges/passkeys.d.cts +4 -4
  33. package/dist/bridges/passkeys.d.ts +4 -4
  34. package/dist/bridges/scene.d.cts +3 -3
  35. package/dist/bridges/scene.d.ts +3 -3
  36. package/dist/bridges/screens.d.cts +3 -3
  37. package/dist/bridges/screens.d.ts +3 -3
  38. package/dist/bridges/sheets.d.cts +3 -3
  39. package/dist/bridges/sheets.d.ts +3 -3
  40. package/dist/bridges/slides.d.cts +3 -3
  41. package/dist/bridges/slides.d.ts +3 -3
  42. package/dist/bridges/terminal.d.cts +3 -3
  43. package/dist/bridges/terminal.d.ts +3 -3
  44. package/dist/bridges/tui.d.cts +2 -2
  45. package/dist/bridges/tui.d.ts +2 -2
  46. package/dist/bridges/whiteboard.d.cts +3 -3
  47. package/dist/bridges/whiteboard.d.ts +3 -3
  48. package/dist/bridges-navigation.cjs +437 -0
  49. package/dist/bridges-navigation.cjs.map +1 -0
  50. package/dist/bridges-navigation.js +7 -0
  51. package/dist/bridges-navigation.js.map +1 -0
  52. package/dist/bridges-passkeys.cjs.map +1 -1
  53. package/dist/bridges-passkeys.js +1 -1
  54. package/dist/{chunk-PWYEW6NL.js → chunk-G6CRHFWU.js} +2 -2
  55. package/dist/{chunk-PWYEW6NL.js.map → chunk-G6CRHFWU.js.map} +1 -1
  56. package/dist/chunk-OHNGURUJ.js +317 -0
  57. package/dist/chunk-OHNGURUJ.js.map +1 -0
  58. package/dist/dom-navigation/index.d.cts +175 -0
  59. package/dist/dom-navigation/index.d.ts +175 -0
  60. package/dist/dom-navigation/script.d.cts +3 -0
  61. package/dist/dom-navigation/script.d.ts +3 -0
  62. package/dist/dom-navigation-script.cjs +11 -0
  63. package/dist/dom-navigation-script.cjs.map +1 -0
  64. package/dist/dom-navigation-script.js +9 -0
  65. package/dist/dom-navigation-script.js.map +1 -0
  66. package/dist/dom-navigation.cjs +405 -0
  67. package/dist/dom-navigation.cjs.map +1 -0
  68. package/dist/dom-navigation.js +388 -0
  69. package/dist/dom-navigation.js.map +1 -0
  70. package/dist/index.cjs +94 -48
  71. package/dist/index.cjs.map +1 -1
  72. package/dist/index.d.cts +10 -125
  73. package/dist/index.d.ts +10 -125
  74. package/dist/index.js +12 -275
  75. package/dist/index.js.map +1 -1
  76. package/dist/mcp/index.d.cts +5 -5
  77. package/dist/mcp/index.d.ts +5 -5
  78. package/dist/mcp/stdio.d.cts +3 -3
  79. package/dist/mcp/stdio.d.ts +3 -3
  80. package/dist/presence/index.d.cts +1 -1
  81. package/dist/presence/index.d.ts +1 -1
  82. package/dist/{server-BIcA3ZtK.d.cts → server-1y0n61R5.d.cts} +2 -2
  83. package/dist/{server-DnUyeavY.d.ts → server-vq6qVl3T.d.ts} +2 -2
  84. package/dist/sharing/index.d.cts +3 -3
  85. package/dist/sharing/index.d.ts +3 -3
  86. package/dist/sheets-adapter.d.cts +3 -3
  87. package/dist/sheets-adapter.d.ts +3 -3
  88. package/dist/{tool-host-LVBgCN5m.d.cts → tool-host-D67N2a2h.d.cts} +1 -1
  89. package/dist/{tool-host-Wo0pkY83.d.ts → tool-host-LYfdPE8u.d.ts} +1 -1
  90. package/dist/{types-EcElKOlk.d.cts → types-DP5yQZQU.d.cts} +1 -1
  91. package/dist/{types-BxNRKSVd.d.ts → types-VX_9je7w.d.ts} +1 -1
  92. package/dist/{types-C2DtnHIn.d.cts → types-gzIpsUDE.d.cts} +1 -1
  93. package/dist/{types-C2DtnHIn.d.ts → types-gzIpsUDE.d.ts} +1 -1
  94. package/dist/undo/index.d.cts +2 -2
  95. package/dist/undo/index.d.ts +2 -2
  96. package/package.json +35 -3
@@ -0,0 +1,175 @@
1
+ import { PageSnapshot, PageReadout, NavigationConfirmRequest, NavigationBridgeAdapter } from '../bridges/navigation.js';
2
+ export { PageAction } from '../bridges/navigation.js';
3
+ import '../tool-host-LYfdPE8u.js';
4
+ import '../types-gzIpsUDE.js';
5
+ import '../types-VX_9je7w.js';
6
+
7
+ /**
8
+ * The in-page half of DOM co-browsing: a framework-free walker that turns a
9
+ * live document into a {@link PageSnapshot} of stable handles, and acts on
10
+ * those handles.
11
+ *
12
+ * Extracted from px-ui-sandbox's CoBrowseProvider so a host that does NOT own
13
+ * the page (Genie's browser, showing sites through an Electron
14
+ * WebContentsView) can run the same walker, injected into an isolated world.
15
+ *
16
+ * Constraints this file holds, because the injected IIFE is built from it:
17
+ *
18
+ * - NO runtime imports. Type-only imports are erased; anything else would be
19
+ * bundled into a script that must run with no module loader.
20
+ * - Nothing touches `document` / `window` until a method is called, so the
21
+ * script can be evaluated in a context with no DOM at all.
22
+ * - Every result is plain, structured-cloneable data.
23
+ *
24
+ * Trust boundary: the page is hostile input. It controls every attribute,
25
+ * label and text node read here. Two consequences:
26
+ * - Sensitive values (passwords, card fields, one-time codes, anything marked
27
+ * `data-private`) are masked HERE, before anything leaves the page — the host
28
+ * cannot mask what it has already received.
29
+ * - Handles are compared as plain strings, never spliced into a CSS selector,
30
+ * so a crafted handle cannot change what a lookup matches.
31
+ */
32
+
33
+ /** Elements the walker considers actionable. */
34
+ declare const INTERACTIVE = "a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]";
35
+ /** Every method the in-page agent answers. The wire protocol is exactly this list. */
36
+ declare const DOM_NAVIGATION_METHODS: readonly ["getLocation", "describe", "read", "visit", "back", "forward", "scrollTo", "scrollBy", "setField", "click", "submit", "rectFor"];
37
+ type DomNavigationMethod = (typeof DOM_NAVIGATION_METHODS)[number];
38
+ type ActionResult = {
39
+ ok: boolean;
40
+ error?: string;
41
+ };
42
+ type HandleRect = {
43
+ x: number;
44
+ y: number;
45
+ width: number;
46
+ height: number;
47
+ };
48
+ type FieldElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
49
+ declare function isVisible(el: Element): boolean;
50
+ /** Sensitive fields whose value is never read into the snapshot. */
51
+ declare function isSensitive(el: Element): boolean;
52
+ declare function roleOf(el: Element): string;
53
+ declare function accessibleName(el: Element): string;
54
+ declare function isDestructive(el: Element, label: string): boolean;
55
+ /**
56
+ * The element's own stable handle: data-co-handle → name → id, or "" when it
57
+ * has none. An EMPTY attribute counts as absent — `button.name` is `""`, not
58
+ * null, on every button, anchor and input without one, so a plain `??` chain
59
+ * never reaches the id.
60
+ */
61
+ declare function attributeHandle(el: Element): string;
62
+ /**
63
+ * Build a fresh snapshot + a handle→element registry. Handle preference:
64
+ * data-co-handle → name → id → `role#index` (stable within a snapshot).
65
+ */
66
+ declare function walk(doc?: Document): {
67
+ snapshot: PageSnapshot;
68
+ registry: Map<string, Element>;
69
+ };
70
+ /**
71
+ * Set a field's value so a React-controlled input sees the change.
72
+ *
73
+ * - Text, textarea, select: call the PROTOTYPE `value` setter (React shadows
74
+ * `value` on the instance with its value tracker), then dispatch bubbling
75
+ * `input` + `change`.
76
+ * - Checkbox, radio: React's onChange for these listens to `click`, not
77
+ * `input` / `change`, so the write is a real `click()` — the browser toggles
78
+ * the box and fires click, input and change itself. Only when the state
79
+ * actually differs, so setting the current value is a no-op rather than a
80
+ * toggle. A radio cannot be unchecked by clicking it; that one case falls
81
+ * back to the setter.
82
+ */
83
+ declare function setNativeValue(el: FieldElement, value: unknown): void;
84
+ /**
85
+ * Visible text / heading outline, with sensitive text removed. Structured, so
86
+ * the bridge can render each page-supplied string on its own single line.
87
+ */
88
+ declare function readPage(doc?: Document): PageReadout;
89
+ type DomNavigationAgentOptions = {
90
+ /** Document to drive. Default: the global `document`, read lazily. */
91
+ document?: Document;
92
+ /**
93
+ * Called just before the agent acts on the page — before resolving a field
94
+ * for `setField`, and after resolving (only when the element exists) for
95
+ * `click` / `submit`. The sandbox uses it to attribute the request an action
96
+ * causes to the agent.
97
+ */
98
+ beforeAction?: (action: "setField" | "click" | "submit") => void;
99
+ };
100
+ type DomNavigationAgent = {
101
+ getLocation(): {
102
+ url: string;
103
+ title: string;
104
+ };
105
+ describe(): PageSnapshot;
106
+ read(): PageReadout;
107
+ /** Navigate. Only http(s) or same-site paths; deferred a tick so a remote call returns first. */
108
+ visit(url: string): void;
109
+ back(): void;
110
+ forward(): void;
111
+ scrollTo(opts: {
112
+ x?: number;
113
+ y?: number;
114
+ handle?: string;
115
+ }): void;
116
+ scrollBy(dy: number): void;
117
+ setField(handle: string, value: unknown): ActionResult;
118
+ click(handle: string): ActionResult;
119
+ submit(handle: string): ActionResult;
120
+ rectFor(handle: string): HandleRect | null;
121
+ /** Element for a handle: this snapshot's registry, then a live attribute lookup. */
122
+ resolve(handle: string): Element | null;
123
+ /** Serialisable dispatch — what the injected script exposes to a host. */
124
+ call(method: DomNavigationMethod, args?: unknown[]): unknown;
125
+ };
126
+ declare function createDomNavigationAgent(options?: DomNavigationAgentOptions): DomNavigationAgent;
127
+
128
+ /**
129
+ * The host half: a {@link NavigationBridgeAdapter} that drives a page it does
130
+ * not run in, through any transport that can carry a method name and plain
131
+ * arguments into the page and a plain result back — Electron's
132
+ * `executeJavaScript` / `executeJavaScriptInIsolatedWorld`, a MessagePort, a
133
+ * WebSocket to a remote browser.
134
+ *
135
+ * Every result is treated as hostile: the page owns the DOM the in-page walker
136
+ * reads, and if the script was injected into the MAIN world the page can
137
+ * replace the walker outright. So results are coerced to the declared shapes
138
+ * here, and anything malformed is dropped rather than passed on to the agent.
139
+ */
140
+
141
+ /** The global the injected script installs in the world it runs in. */
142
+ declare const DOM_NAVIGATION_GLOBAL = "__fancyDomNavigation";
143
+ /** Bumped only if the wire protocol changes incompatibly. */
144
+ declare const DOM_NAVIGATION_PROTOCOL = 1;
145
+ /**
146
+ * Carries one call into the page. `args` and the resolved value are plain,
147
+ * structured-cloneable data. Reject (or throw) when the call could not be
148
+ * delivered — e.g. the page navigated and the script must be re-injected.
149
+ */
150
+ type DomNavigationTransport = {
151
+ call(method: DomNavigationMethod, args: unknown[]): Promise<unknown>;
152
+ };
153
+ type RemoteNavigationAdapterOptions = {
154
+ transport: DomNavigationTransport;
155
+ /** Optional fancy-screens screen id for presence targeting. */
156
+ screenId?: string;
157
+ /** Human-control revision, if the host tracks one. Omit to disable stale-intent protection. */
158
+ getRevision?: () => number | Promise<number>;
159
+ /**
160
+ * Trust-but-verify. Without it, `page_submit` and destructive `page_click`
161
+ * are NOT staged — they run as soon as the agent asks. A host showing pages
162
+ * it does not own should always supply one.
163
+ */
164
+ confirm?: (req: NavigationConfirmRequest) => Promise<boolean>;
165
+ };
166
+ /**
167
+ * A JavaScript expression that performs one call against the injected global —
168
+ * what a host passes to `executeJavaScript`. Arguments are embedded as JSON,
169
+ * which is a valid JS expression, so no argument can break out of its literal.
170
+ */
171
+ declare function domNavigationCallScript(method: DomNavigationMethod, args?: unknown[]): string;
172
+ /** Build a NavigationBridgeAdapter backed by a transport into the page. */
173
+ declare function createRemoteNavigationAdapter(options: RemoteNavigationAdapterOptions): NavigationBridgeAdapter;
174
+
175
+ export { type ActionResult, DOM_NAVIGATION_GLOBAL, DOM_NAVIGATION_METHODS, DOM_NAVIGATION_PROTOCOL, type DomNavigationAgent, type DomNavigationAgentOptions, type DomNavigationMethod, type DomNavigationTransport, type HandleRect, INTERACTIVE, NavigationBridgeAdapter, NavigationConfirmRequest, PageReadout, PageSnapshot, type RemoteNavigationAdapterOptions, accessibleName, attributeHandle, createDomNavigationAgent, createRemoteNavigationAdapter, domNavigationCallScript, isDestructive, isSensitive, isVisible, readPage, roleOf, setNativeValue, walk };
@@ -0,0 +1,3 @@
1
+ declare const DOM_NAVIGATION_SCRIPT: string;
2
+
3
+ export { DOM_NAVIGATION_SCRIPT };
@@ -0,0 +1,3 @@
1
+ declare const DOM_NAVIGATION_SCRIPT: string;
2
+
3
+ export { DOM_NAVIGATION_SCRIPT };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ // dom-navigation-inject:virtual:dom-navigation-inject
4
+ var virtual_dom_navigation_inject_default = '"use strict";\n(() => {\n // src/dom-navigation/agent.ts\n var INTERACTIVE = "a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]";\n var tag = (el) => el.tagName.toUpperCase();\n function isVisible(el) {\n const he = el;\n if (el.type === "hidden") return false;\n if (he.hidden || he.getAttribute("aria-hidden") === "true") return false;\n return he.offsetParent !== null || he.getClientRects().length > 0;\n }\n function isSensitive(el) {\n const input = el;\n const ac = (el.getAttribute("autocomplete") ?? "").toLowerCase();\n return input.type === "password" || ac.startsWith("cc-") || ac === "one-time-code" || el.hasAttribute("data-private");\n }\n function insideSensitive(node) {\n for (let el = node.parentElement; el; el = el.parentElement) {\n if (isSensitive(el)) return true;\n }\n return false;\n }\n function safeText(root) {\n const doc = root.ownerDocument ?? root;\n const walker = doc.createTreeWalker(root, 4);\n let out = "";\n for (let n = walker.nextNode(); n; n = walker.nextNode()) {\n if (!insideSensitive(n)) out += n.nodeValue ?? "";\n }\n return out;\n }\n function roleOf(el) {\n const explicit = el.getAttribute("role");\n if (explicit) return explicit;\n const t = tag(el);\n if (t === "A") return "link";\n if (t === "BUTTON") return "button";\n if (t === "SELECT") return "select";\n if (t === "TEXTAREA") return "textbox";\n if (t === "INPUT") {\n const type = el.type;\n if (type === "checkbox") return "checkbox";\n if (type === "radio") return "radio";\n if (type === "submit" || type === "button") return "button";\n return "textbox";\n }\n return "generic";\n }\n function labelFor(doc, id) {\n for (const lbl of Array.from(doc.querySelectorAll("label[for]"))) {\n if (lbl.getAttribute("for") === id) return lbl;\n }\n return null;\n }\n function accessibleName(el) {\n const doc = el.ownerDocument;\n const aria = el.getAttribute("aria-label");\n if (aria) return aria.trim();\n const labelledby = el.getAttribute("aria-labelledby");\n if (labelledby) {\n const txt = labelledby.split(/\\s+/).map((id2) => doc.getElementById(id2)?.textContent ?? "").join(" ").trim();\n if (txt) return txt;\n }\n const id = el.getAttribute("id");\n if (id) {\n const lbl = labelFor(doc, id);\n if (lbl?.textContent?.trim()) return lbl.textContent.trim();\n }\n const wrapping = el.closest("label");\n if (wrapping) {\n const txt = safeText(wrapping).trim();\n if (txt) return txt;\n }\n const placeholder = el.getAttribute("placeholder");\n if (placeholder) return placeholder.trim();\n const text = isSensitive(el) ? "" : el.textContent?.trim();\n if (text) return text.replace(/\\s+/g, " ").slice(0, 80);\n return el.getAttribute("name") ?? el.getAttribute("id") ?? roleOf(el);\n }\n function isDestructive(el, label) {\n if (el.hasAttribute("data-co-confirm")) return true;\n if (el.type === "submit") return true;\n if (tag(el) === "BUTTON" && el.type === "submit") return true;\n return /\\b(delete|remove|destroy|sign out|log ?out|cancel subscription|deactivate)\\b/i.test(label);\n }\n function attributeHandle(el) {\n return el.getAttribute("data-co-handle") || el.getAttribute("name") || el.getAttribute("id") || "";\n }\n function walk(doc = document) {\n const registry = /* @__PURE__ */ new Map();\n const actions = [];\n const seen = /* @__PURE__ */ new Set();\n let synthetic = 0;\n for (const el of Array.from(doc.querySelectorAll(INTERACTIVE))) {\n if (seen.has(el) || !isVisible(el)) continue;\n seen.add(el);\n const label = accessibleName(el);\n const role = roleOf(el);\n let handle = attributeHandle(el);\n if (!handle || registry.has(handle)) handle = `${role}#${synthetic++}`;\n registry.set(handle, el);\n let value;\n if (role === "textbox" || role === "select") {\n value = isSensitive(el) ? void 0 : el.value;\n } else if (role === "checkbox" || role === "radio") {\n value = el.checked;\n }\n const action = { handle, role, label, destructive: isDestructive(el, label) };\n if (value !== void 0) action.value = value;\n actions.push(action);\n }\n const loc = doc.defaultView?.location;\n return {\n snapshot: { url: loc ? loc.pathname + loc.search : "", title: doc.title, actions },\n registry\n };\n }\n function isField(el) {\n const t = tag(el);\n return t === "INPUT" || t === "TEXTAREA" || t === "SELECT";\n }\n function setNativeValue(el, value) {\n const view = el.ownerDocument.defaultView ?? globalThis;\n const fire = () => {\n el.dispatchEvent(new view.Event("input", { bubbles: true }));\n el.dispatchEvent(new view.Event("change", { bubbles: true }));\n };\n const t = tag(el);\n if (t === "INPUT" && (el.type === "checkbox" || el.type === "radio")) {\n const input = el;\n const want = Boolean(value);\n if (input.checked === want) return;\n if (input.type === "checkbox" || want) {\n input.click();\n return;\n }\n const setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, "checked")?.set;\n setter2?.call(el, false);\n fire();\n return;\n }\n const proto = t === "TEXTAREA" ? view.HTMLTextAreaElement.prototype : t === "SELECT" ? view.HTMLSelectElement.prototype : view.HTMLInputElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;\n setter?.call(el, String(value));\n fire();\n }\n function readPage(doc = document) {\n const main = doc.querySelector("main") ?? doc.body;\n if (!main) return { title: doc.title, headings: [], text: "" };\n const headings = Array.from(main.querySelectorAll("h1, h2, h3")).map((h) => ({\n level: Number(h.tagName.slice(1)),\n text: safeText(h).trim()\n }));\n const text = safeText(main).replace(/\\s+/g, " ").trim().slice(0, 4e3);\n return { title: doc.title, headings, text };\n }\n function findByAttribute(doc, attr, value) {\n for (const el of Array.from(doc.querySelectorAll(`[${attr}]`))) {\n if (el.getAttribute(attr) === value) return el;\n }\n return null;\n }\n var num = (v) => typeof v === "number" ? v : void 0;\n function createDomNavigationAgent(options = {}) {\n let registry = /* @__PURE__ */ new Map();\n const doc = () => options.document ?? document;\n const win = () => doc().defaultView ?? window;\n const before = (action) => options.beforeAction?.(action);\n const resolve = (handle) => {\n const hit = registry.get(handle);\n if (hit && hit.isConnected) return hit;\n const d = doc();\n return findByAttribute(d, "data-co-handle", handle) ?? findByAttribute(d, "name", handle) ?? d.getElementById(handle);\n };\n const agent = {\n getLocation: () => {\n const loc = win().location;\n return { url: loc.pathname + loc.search, title: doc().title };\n },\n describe: () => {\n const res = walk(doc());\n registry = res.registry;\n return res.snapshot;\n },\n read: () => readPage(doc()),\n visit: (url) => {\n const w = win();\n const target = new URL(String(url), w.location.href);\n if (target.protocol !== "http:" && target.protocol !== "https:") {\n throw new Error(`Refusing to navigate to a \'${target.protocol}\' URL.`);\n }\n w.setTimeout(() => w.location.assign(target.href), 0);\n },\n back: () => win().history.back(),\n forward: () => win().history.forward(),\n scrollTo: ({ x, y, handle }) => {\n const w = win();\n if (handle) {\n resolve(handle)?.scrollIntoView({ behavior: "smooth", block: "center" });\n return;\n }\n w.scrollTo({ left: x ?? w.scrollX, top: y ?? w.scrollY, behavior: "smooth" });\n },\n scrollBy: (dy) => win().scrollBy({ top: dy, behavior: "smooth" }),\n setField: (handle, value) => {\n before("setField");\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n if (isField(el)) {\n setNativeValue(el, value);\n return { ok: true };\n }\n return { ok: false, error: `Handle "${handle}" is not a form field` };\n },\n click: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n before("click");\n el.click();\n return { ok: true };\n },\n submit: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n const form = tag(el) === "FORM" ? el : el.closest("form");\n if (!form) return { ok: false, error: `Handle "${handle}" is not inside a form` };\n before("submit");\n if (typeof form.requestSubmit === "function") form.requestSubmit();\n else form.submit();\n return { ok: true };\n },\n rectFor: (handle) => {\n const el = resolve(handle);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, width: r.width, height: r.height };\n },\n resolve,\n call: (method, args = []) => {\n const a = Array.isArray(args) ? args : [];\n const handle = String(a[0] ?? "");\n switch (method) {\n case "getLocation":\n return agent.getLocation();\n case "describe":\n return agent.describe();\n case "read":\n return agent.read();\n case "visit":\n agent.visit(String(a[0] ?? ""));\n return void 0;\n case "back":\n agent.back();\n return void 0;\n case "forward":\n agent.forward();\n return void 0;\n case "scrollTo": {\n const o = a[0] && typeof a[0] === "object" ? a[0] : {};\n agent.scrollTo({\n x: num(o.x),\n y: num(o.y),\n handle: typeof o.handle === "string" ? o.handle : void 0\n });\n return void 0;\n }\n case "scrollBy":\n agent.scrollBy(Number(a[0] ?? 0));\n return void 0;\n case "setField":\n return agent.setField(handle, a[1]);\n case "click":\n return agent.click(handle);\n case "submit":\n return agent.submit(handle);\n case "rectFor":\n return agent.rectFor(handle);\n default:\n throw new Error(`Unknown dom-navigation method: ${String(method)}`);\n }\n }\n };\n return agent;\n }\n\n // src/dom-navigation/inject.ts\n var KEY = "__fancyDomNavigation";\n var PROTOCOL = 1;\n var g = globalThis;\n var existing = g[KEY];\n if (!existing || existing.protocol !== PROTOCOL) {\n const agent = createDomNavigationAgent();\n Object.defineProperty(globalThis, KEY, {\n configurable: true,\n enumerable: false,\n writable: false,\n value: Object.freeze({\n protocol: PROTOCOL,\n call: (method, args) => agent.call(method, args)\n })\n });\n }\n})();\n';
5
+
6
+ // src/dom-navigation/script.ts
7
+ var DOM_NAVIGATION_SCRIPT = virtual_dom_navigation_inject_default;
8
+
9
+ exports.DOM_NAVIGATION_SCRIPT = DOM_NAVIGATION_SCRIPT;
10
+ //# sourceMappingURL=dom-navigation-script.cjs.map
11
+ //# sourceMappingURL=dom-navigation-script.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["dom-navigation-inject:virtual:dom-navigation-inject","../src/dom-navigation/script.ts"],"names":[],"mappings":";;;AAAA,IAAA,qCAAA,GAAA,gxWAAA;;;ACcO,IAAM,qBAAA,GAAgC","file":"dom-navigation-script.cjs","sourcesContent":["\"use strict\";\n(() => {\n // src/dom-navigation/agent.ts\n var INTERACTIVE = \"a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]\";\n var tag = (el) => el.tagName.toUpperCase();\n function isVisible(el) {\n const he = el;\n if (el.type === \"hidden\") return false;\n if (he.hidden || he.getAttribute(\"aria-hidden\") === \"true\") return false;\n return he.offsetParent !== null || he.getClientRects().length > 0;\n }\n function isSensitive(el) {\n const input = el;\n const ac = (el.getAttribute(\"autocomplete\") ?? \"\").toLowerCase();\n return input.type === \"password\" || ac.startsWith(\"cc-\") || ac === \"one-time-code\" || el.hasAttribute(\"data-private\");\n }\n function insideSensitive(node) {\n for (let el = node.parentElement; el; el = el.parentElement) {\n if (isSensitive(el)) return true;\n }\n return false;\n }\n function safeText(root) {\n const doc = root.ownerDocument ?? root;\n const walker = doc.createTreeWalker(root, 4);\n let out = \"\";\n for (let n = walker.nextNode(); n; n = walker.nextNode()) {\n if (!insideSensitive(n)) out += n.nodeValue ?? \"\";\n }\n return out;\n }\n function roleOf(el) {\n const explicit = el.getAttribute(\"role\");\n if (explicit) return explicit;\n const t = tag(el);\n if (t === \"A\") return \"link\";\n if (t === \"BUTTON\") return \"button\";\n if (t === \"SELECT\") return \"select\";\n if (t === \"TEXTAREA\") return \"textbox\";\n if (t === \"INPUT\") {\n const type = el.type;\n if (type === \"checkbox\") return \"checkbox\";\n if (type === \"radio\") return \"radio\";\n if (type === \"submit\" || type === \"button\") return \"button\";\n return \"textbox\";\n }\n return \"generic\";\n }\n function labelFor(doc, id) {\n for (const lbl of Array.from(doc.querySelectorAll(\"label[for]\"))) {\n if (lbl.getAttribute(\"for\") === id) return lbl;\n }\n return null;\n }\n function accessibleName(el) {\n const doc = el.ownerDocument;\n const aria = el.getAttribute(\"aria-label\");\n if (aria) return aria.trim();\n const labelledby = el.getAttribute(\"aria-labelledby\");\n if (labelledby) {\n const txt = labelledby.split(/\\s+/).map((id2) => doc.getElementById(id2)?.textContent ?? \"\").join(\" \").trim();\n if (txt) return txt;\n }\n const id = el.getAttribute(\"id\");\n if (id) {\n const lbl = labelFor(doc, id);\n if (lbl?.textContent?.trim()) return lbl.textContent.trim();\n }\n const wrapping = el.closest(\"label\");\n if (wrapping) {\n const txt = safeText(wrapping).trim();\n if (txt) return txt;\n }\n const placeholder = el.getAttribute(\"placeholder\");\n if (placeholder) return placeholder.trim();\n const text = isSensitive(el) ? \"\" : el.textContent?.trim();\n if (text) return text.replace(/\\s+/g, \" \").slice(0, 80);\n return el.getAttribute(\"name\") ?? el.getAttribute(\"id\") ?? roleOf(el);\n }\n function isDestructive(el, label) {\n if (el.hasAttribute(\"data-co-confirm\")) return true;\n if (el.type === \"submit\") return true;\n if (tag(el) === \"BUTTON\" && el.type === \"submit\") return true;\n return /\\b(delete|remove|destroy|sign out|log ?out|cancel subscription|deactivate)\\b/i.test(label);\n }\n function attributeHandle(el) {\n return el.getAttribute(\"data-co-handle\") || el.getAttribute(\"name\") || el.getAttribute(\"id\") || \"\";\n }\n function walk(doc = document) {\n const registry = /* @__PURE__ */ new Map();\n const actions = [];\n const seen = /* @__PURE__ */ new Set();\n let synthetic = 0;\n for (const el of Array.from(doc.querySelectorAll(INTERACTIVE))) {\n if (seen.has(el) || !isVisible(el)) continue;\n seen.add(el);\n const label = accessibleName(el);\n const role = roleOf(el);\n let handle = attributeHandle(el);\n if (!handle || registry.has(handle)) handle = `${role}#${synthetic++}`;\n registry.set(handle, el);\n let value;\n if (role === \"textbox\" || role === \"select\") {\n value = isSensitive(el) ? void 0 : el.value;\n } else if (role === \"checkbox\" || role === \"radio\") {\n value = el.checked;\n }\n const action = { handle, role, label, destructive: isDestructive(el, label) };\n if (value !== void 0) action.value = value;\n actions.push(action);\n }\n const loc = doc.defaultView?.location;\n return {\n snapshot: { url: loc ? loc.pathname + loc.search : \"\", title: doc.title, actions },\n registry\n };\n }\n function isField(el) {\n const t = tag(el);\n return t === \"INPUT\" || t === \"TEXTAREA\" || t === \"SELECT\";\n }\n function setNativeValue(el, value) {\n const view = el.ownerDocument.defaultView ?? globalThis;\n const fire = () => {\n el.dispatchEvent(new view.Event(\"input\", { bubbles: true }));\n el.dispatchEvent(new view.Event(\"change\", { bubbles: true }));\n };\n const t = tag(el);\n if (t === \"INPUT\" && (el.type === \"checkbox\" || el.type === \"radio\")) {\n const input = el;\n const want = Boolean(value);\n if (input.checked === want) return;\n if (input.type === \"checkbox\" || want) {\n input.click();\n return;\n }\n const setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, \"checked\")?.set;\n setter2?.call(el, false);\n fire();\n return;\n }\n const proto = t === \"TEXTAREA\" ? view.HTMLTextAreaElement.prototype : t === \"SELECT\" ? view.HTMLSelectElement.prototype : view.HTMLInputElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(proto, \"value\")?.set;\n setter?.call(el, String(value));\n fire();\n }\n function readPage(doc = document) {\n const main = doc.querySelector(\"main\") ?? doc.body;\n if (!main) return { title: doc.title, headings: [], text: \"\" };\n const headings = Array.from(main.querySelectorAll(\"h1, h2, h3\")).map((h) => ({\n level: Number(h.tagName.slice(1)),\n text: safeText(h).trim()\n }));\n const text = safeText(main).replace(/\\s+/g, \" \").trim().slice(0, 4e3);\n return { title: doc.title, headings, text };\n }\n function findByAttribute(doc, attr, value) {\n for (const el of Array.from(doc.querySelectorAll(`[${attr}]`))) {\n if (el.getAttribute(attr) === value) return el;\n }\n return null;\n }\n var num = (v) => typeof v === \"number\" ? v : void 0;\n function createDomNavigationAgent(options = {}) {\n let registry = /* @__PURE__ */ new Map();\n const doc = () => options.document ?? document;\n const win = () => doc().defaultView ?? window;\n const before = (action) => options.beforeAction?.(action);\n const resolve = (handle) => {\n const hit = registry.get(handle);\n if (hit && hit.isConnected) return hit;\n const d = doc();\n return findByAttribute(d, \"data-co-handle\", handle) ?? findByAttribute(d, \"name\", handle) ?? d.getElementById(handle);\n };\n const agent = {\n getLocation: () => {\n const loc = win().location;\n return { url: loc.pathname + loc.search, title: doc().title };\n },\n describe: () => {\n const res = walk(doc());\n registry = res.registry;\n return res.snapshot;\n },\n read: () => readPage(doc()),\n visit: (url) => {\n const w = win();\n const target = new URL(String(url), w.location.href);\n if (target.protocol !== \"http:\" && target.protocol !== \"https:\") {\n throw new Error(`Refusing to navigate to a '${target.protocol}' URL.`);\n }\n w.setTimeout(() => w.location.assign(target.href), 0);\n },\n back: () => win().history.back(),\n forward: () => win().history.forward(),\n scrollTo: ({ x, y, handle }) => {\n const w = win();\n if (handle) {\n resolve(handle)?.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n return;\n }\n w.scrollTo({ left: x ?? w.scrollX, top: y ?? w.scrollY, behavior: \"smooth\" });\n },\n scrollBy: (dy) => win().scrollBy({ top: dy, behavior: \"smooth\" }),\n setField: (handle, value) => {\n before(\"setField\");\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle \"${handle}\"` };\n if (isField(el)) {\n setNativeValue(el, value);\n return { ok: true };\n }\n return { ok: false, error: `Handle \"${handle}\" is not a form field` };\n },\n click: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle \"${handle}\"` };\n before(\"click\");\n el.click();\n return { ok: true };\n },\n submit: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle \"${handle}\"` };\n const form = tag(el) === \"FORM\" ? el : el.closest(\"form\");\n if (!form) return { ok: false, error: `Handle \"${handle}\" is not inside a form` };\n before(\"submit\");\n if (typeof form.requestSubmit === \"function\") form.requestSubmit();\n else form.submit();\n return { ok: true };\n },\n rectFor: (handle) => {\n const el = resolve(handle);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, width: r.width, height: r.height };\n },\n resolve,\n call: (method, args = []) => {\n const a = Array.isArray(args) ? args : [];\n const handle = String(a[0] ?? \"\");\n switch (method) {\n case \"getLocation\":\n return agent.getLocation();\n case \"describe\":\n return agent.describe();\n case \"read\":\n return agent.read();\n case \"visit\":\n agent.visit(String(a[0] ?? \"\"));\n return void 0;\n case \"back\":\n agent.back();\n return void 0;\n case \"forward\":\n agent.forward();\n return void 0;\n case \"scrollTo\": {\n const o = a[0] && typeof a[0] === \"object\" ? a[0] : {};\n agent.scrollTo({\n x: num(o.x),\n y: num(o.y),\n handle: typeof o.handle === \"string\" ? o.handle : void 0\n });\n return void 0;\n }\n case \"scrollBy\":\n agent.scrollBy(Number(a[0] ?? 0));\n return void 0;\n case \"setField\":\n return agent.setField(handle, a[1]);\n case \"click\":\n return agent.click(handle);\n case \"submit\":\n return agent.submit(handle);\n case \"rectFor\":\n return agent.rectFor(handle);\n default:\n throw new Error(`Unknown dom-navigation method: ${String(method)}`);\n }\n }\n };\n return agent;\n }\n\n // src/dom-navigation/inject.ts\n var KEY = \"__fancyDomNavigation\";\n var PROTOCOL = 1;\n var g = globalThis;\n var existing = g[KEY];\n if (!existing || existing.protocol !== PROTOCOL) {\n const agent = createDomNavigationAgent();\n Object.defineProperty(globalThis, KEY, {\n configurable: true,\n enumerable: false,\n writable: false,\n value: Object.freeze({\n protocol: PROTOCOL,\n call: (method, args) => agent.call(method, args)\n })\n });\n }\n})();\n","/**\n * `@particle-academy/agent-integrations/dom-navigation/script`\n *\n * The in-page walker as one self-contained script string — no imports, no\n * module system — for a host to inject into a page it does not own:\n *\n * await webContents.executeJavaScriptInIsolatedWorld(worldId, [{ code: DOM_NAVIGATION_SCRIPT }]);\n *\n * Built at package build time from ./inject.ts (see\n * scripts/dom-navigation-inject-plugin.ts). Its own subpath so a host process\n * that only injects never loads the walker as a module, and vice versa.\n */\nimport source from \"virtual:dom-navigation-inject\";\n\nexport const DOM_NAVIGATION_SCRIPT: string = source;\n"]}
@@ -0,0 +1,9 @@
1
+ // dom-navigation-inject:virtual:dom-navigation-inject
2
+ var virtual_dom_navigation_inject_default = '"use strict";\n(() => {\n // src/dom-navigation/agent.ts\n var INTERACTIVE = "a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]";\n var tag = (el) => el.tagName.toUpperCase();\n function isVisible(el) {\n const he = el;\n if (el.type === "hidden") return false;\n if (he.hidden || he.getAttribute("aria-hidden") === "true") return false;\n return he.offsetParent !== null || he.getClientRects().length > 0;\n }\n function isSensitive(el) {\n const input = el;\n const ac = (el.getAttribute("autocomplete") ?? "").toLowerCase();\n return input.type === "password" || ac.startsWith("cc-") || ac === "one-time-code" || el.hasAttribute("data-private");\n }\n function insideSensitive(node) {\n for (let el = node.parentElement; el; el = el.parentElement) {\n if (isSensitive(el)) return true;\n }\n return false;\n }\n function safeText(root) {\n const doc = root.ownerDocument ?? root;\n const walker = doc.createTreeWalker(root, 4);\n let out = "";\n for (let n = walker.nextNode(); n; n = walker.nextNode()) {\n if (!insideSensitive(n)) out += n.nodeValue ?? "";\n }\n return out;\n }\n function roleOf(el) {\n const explicit = el.getAttribute("role");\n if (explicit) return explicit;\n const t = tag(el);\n if (t === "A") return "link";\n if (t === "BUTTON") return "button";\n if (t === "SELECT") return "select";\n if (t === "TEXTAREA") return "textbox";\n if (t === "INPUT") {\n const type = el.type;\n if (type === "checkbox") return "checkbox";\n if (type === "radio") return "radio";\n if (type === "submit" || type === "button") return "button";\n return "textbox";\n }\n return "generic";\n }\n function labelFor(doc, id) {\n for (const lbl of Array.from(doc.querySelectorAll("label[for]"))) {\n if (lbl.getAttribute("for") === id) return lbl;\n }\n return null;\n }\n function accessibleName(el) {\n const doc = el.ownerDocument;\n const aria = el.getAttribute("aria-label");\n if (aria) return aria.trim();\n const labelledby = el.getAttribute("aria-labelledby");\n if (labelledby) {\n const txt = labelledby.split(/\\s+/).map((id2) => doc.getElementById(id2)?.textContent ?? "").join(" ").trim();\n if (txt) return txt;\n }\n const id = el.getAttribute("id");\n if (id) {\n const lbl = labelFor(doc, id);\n if (lbl?.textContent?.trim()) return lbl.textContent.trim();\n }\n const wrapping = el.closest("label");\n if (wrapping) {\n const txt = safeText(wrapping).trim();\n if (txt) return txt;\n }\n const placeholder = el.getAttribute("placeholder");\n if (placeholder) return placeholder.trim();\n const text = isSensitive(el) ? "" : el.textContent?.trim();\n if (text) return text.replace(/\\s+/g, " ").slice(0, 80);\n return el.getAttribute("name") ?? el.getAttribute("id") ?? roleOf(el);\n }\n function isDestructive(el, label) {\n if (el.hasAttribute("data-co-confirm")) return true;\n if (el.type === "submit") return true;\n if (tag(el) === "BUTTON" && el.type === "submit") return true;\n return /\\b(delete|remove|destroy|sign out|log ?out|cancel subscription|deactivate)\\b/i.test(label);\n }\n function attributeHandle(el) {\n return el.getAttribute("data-co-handle") || el.getAttribute("name") || el.getAttribute("id") || "";\n }\n function walk(doc = document) {\n const registry = /* @__PURE__ */ new Map();\n const actions = [];\n const seen = /* @__PURE__ */ new Set();\n let synthetic = 0;\n for (const el of Array.from(doc.querySelectorAll(INTERACTIVE))) {\n if (seen.has(el) || !isVisible(el)) continue;\n seen.add(el);\n const label = accessibleName(el);\n const role = roleOf(el);\n let handle = attributeHandle(el);\n if (!handle || registry.has(handle)) handle = `${role}#${synthetic++}`;\n registry.set(handle, el);\n let value;\n if (role === "textbox" || role === "select") {\n value = isSensitive(el) ? void 0 : el.value;\n } else if (role === "checkbox" || role === "radio") {\n value = el.checked;\n }\n const action = { handle, role, label, destructive: isDestructive(el, label) };\n if (value !== void 0) action.value = value;\n actions.push(action);\n }\n const loc = doc.defaultView?.location;\n return {\n snapshot: { url: loc ? loc.pathname + loc.search : "", title: doc.title, actions },\n registry\n };\n }\n function isField(el) {\n const t = tag(el);\n return t === "INPUT" || t === "TEXTAREA" || t === "SELECT";\n }\n function setNativeValue(el, value) {\n const view = el.ownerDocument.defaultView ?? globalThis;\n const fire = () => {\n el.dispatchEvent(new view.Event("input", { bubbles: true }));\n el.dispatchEvent(new view.Event("change", { bubbles: true }));\n };\n const t = tag(el);\n if (t === "INPUT" && (el.type === "checkbox" || el.type === "radio")) {\n const input = el;\n const want = Boolean(value);\n if (input.checked === want) return;\n if (input.type === "checkbox" || want) {\n input.click();\n return;\n }\n const setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, "checked")?.set;\n setter2?.call(el, false);\n fire();\n return;\n }\n const proto = t === "TEXTAREA" ? view.HTMLTextAreaElement.prototype : t === "SELECT" ? view.HTMLSelectElement.prototype : view.HTMLInputElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;\n setter?.call(el, String(value));\n fire();\n }\n function readPage(doc = document) {\n const main = doc.querySelector("main") ?? doc.body;\n if (!main) return { title: doc.title, headings: [], text: "" };\n const headings = Array.from(main.querySelectorAll("h1, h2, h3")).map((h) => ({\n level: Number(h.tagName.slice(1)),\n text: safeText(h).trim()\n }));\n const text = safeText(main).replace(/\\s+/g, " ").trim().slice(0, 4e3);\n return { title: doc.title, headings, text };\n }\n function findByAttribute(doc, attr, value) {\n for (const el of Array.from(doc.querySelectorAll(`[${attr}]`))) {\n if (el.getAttribute(attr) === value) return el;\n }\n return null;\n }\n var num = (v) => typeof v === "number" ? v : void 0;\n function createDomNavigationAgent(options = {}) {\n let registry = /* @__PURE__ */ new Map();\n const doc = () => options.document ?? document;\n const win = () => doc().defaultView ?? window;\n const before = (action) => options.beforeAction?.(action);\n const resolve = (handle) => {\n const hit = registry.get(handle);\n if (hit && hit.isConnected) return hit;\n const d = doc();\n return findByAttribute(d, "data-co-handle", handle) ?? findByAttribute(d, "name", handle) ?? d.getElementById(handle);\n };\n const agent = {\n getLocation: () => {\n const loc = win().location;\n return { url: loc.pathname + loc.search, title: doc().title };\n },\n describe: () => {\n const res = walk(doc());\n registry = res.registry;\n return res.snapshot;\n },\n read: () => readPage(doc()),\n visit: (url) => {\n const w = win();\n const target = new URL(String(url), w.location.href);\n if (target.protocol !== "http:" && target.protocol !== "https:") {\n throw new Error(`Refusing to navigate to a \'${target.protocol}\' URL.`);\n }\n w.setTimeout(() => w.location.assign(target.href), 0);\n },\n back: () => win().history.back(),\n forward: () => win().history.forward(),\n scrollTo: ({ x, y, handle }) => {\n const w = win();\n if (handle) {\n resolve(handle)?.scrollIntoView({ behavior: "smooth", block: "center" });\n return;\n }\n w.scrollTo({ left: x ?? w.scrollX, top: y ?? w.scrollY, behavior: "smooth" });\n },\n scrollBy: (dy) => win().scrollBy({ top: dy, behavior: "smooth" }),\n setField: (handle, value) => {\n before("setField");\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n if (isField(el)) {\n setNativeValue(el, value);\n return { ok: true };\n }\n return { ok: false, error: `Handle "${handle}" is not a form field` };\n },\n click: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n before("click");\n el.click();\n return { ok: true };\n },\n submit: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n const form = tag(el) === "FORM" ? el : el.closest("form");\n if (!form) return { ok: false, error: `Handle "${handle}" is not inside a form` };\n before("submit");\n if (typeof form.requestSubmit === "function") form.requestSubmit();\n else form.submit();\n return { ok: true };\n },\n rectFor: (handle) => {\n const el = resolve(handle);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, width: r.width, height: r.height };\n },\n resolve,\n call: (method, args = []) => {\n const a = Array.isArray(args) ? args : [];\n const handle = String(a[0] ?? "");\n switch (method) {\n case "getLocation":\n return agent.getLocation();\n case "describe":\n return agent.describe();\n case "read":\n return agent.read();\n case "visit":\n agent.visit(String(a[0] ?? ""));\n return void 0;\n case "back":\n agent.back();\n return void 0;\n case "forward":\n agent.forward();\n return void 0;\n case "scrollTo": {\n const o = a[0] && typeof a[0] === "object" ? a[0] : {};\n agent.scrollTo({\n x: num(o.x),\n y: num(o.y),\n handle: typeof o.handle === "string" ? o.handle : void 0\n });\n return void 0;\n }\n case "scrollBy":\n agent.scrollBy(Number(a[0] ?? 0));\n return void 0;\n case "setField":\n return agent.setField(handle, a[1]);\n case "click":\n return agent.click(handle);\n case "submit":\n return agent.submit(handle);\n case "rectFor":\n return agent.rectFor(handle);\n default:\n throw new Error(`Unknown dom-navigation method: ${String(method)}`);\n }\n }\n };\n return agent;\n }\n\n // src/dom-navigation/inject.ts\n var KEY = "__fancyDomNavigation";\n var PROTOCOL = 1;\n var g = globalThis;\n var existing = g[KEY];\n if (!existing || existing.protocol !== PROTOCOL) {\n const agent = createDomNavigationAgent();\n Object.defineProperty(globalThis, KEY, {\n configurable: true,\n enumerable: false,\n writable: false,\n value: Object.freeze({\n protocol: PROTOCOL,\n call: (method, args) => agent.call(method, args)\n })\n });\n }\n})();\n';
3
+
4
+ // src/dom-navigation/script.ts
5
+ var DOM_NAVIGATION_SCRIPT = virtual_dom_navigation_inject_default;
6
+
7
+ export { DOM_NAVIGATION_SCRIPT };
8
+ //# sourceMappingURL=dom-navigation-script.js.map
9
+ //# sourceMappingURL=dom-navigation-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["dom-navigation-inject:virtual:dom-navigation-inject","../src/dom-navigation/script.ts"],"names":[],"mappings":";AAAA,IAAA,qCAAA,GAAA,gxWAAA;;;ACcO,IAAM,qBAAA,GAAgC","file":"dom-navigation-script.js","sourcesContent":["\"use strict\";\n(() => {\n // src/dom-navigation/agent.ts\n var INTERACTIVE = \"a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]\";\n var tag = (el) => el.tagName.toUpperCase();\n function isVisible(el) {\n const he = el;\n if (el.type === \"hidden\") return false;\n if (he.hidden || he.getAttribute(\"aria-hidden\") === \"true\") return false;\n return he.offsetParent !== null || he.getClientRects().length > 0;\n }\n function isSensitive(el) {\n const input = el;\n const ac = (el.getAttribute(\"autocomplete\") ?? \"\").toLowerCase();\n return input.type === \"password\" || ac.startsWith(\"cc-\") || ac === \"one-time-code\" || el.hasAttribute(\"data-private\");\n }\n function insideSensitive(node) {\n for (let el = node.parentElement; el; el = el.parentElement) {\n if (isSensitive(el)) return true;\n }\n return false;\n }\n function safeText(root) {\n const doc = root.ownerDocument ?? root;\n const walker = doc.createTreeWalker(root, 4);\n let out = \"\";\n for (let n = walker.nextNode(); n; n = walker.nextNode()) {\n if (!insideSensitive(n)) out += n.nodeValue ?? \"\";\n }\n return out;\n }\n function roleOf(el) {\n const explicit = el.getAttribute(\"role\");\n if (explicit) return explicit;\n const t = tag(el);\n if (t === \"A\") return \"link\";\n if (t === \"BUTTON\") return \"button\";\n if (t === \"SELECT\") return \"select\";\n if (t === \"TEXTAREA\") return \"textbox\";\n if (t === \"INPUT\") {\n const type = el.type;\n if (type === \"checkbox\") return \"checkbox\";\n if (type === \"radio\") return \"radio\";\n if (type === \"submit\" || type === \"button\") return \"button\";\n return \"textbox\";\n }\n return \"generic\";\n }\n function labelFor(doc, id) {\n for (const lbl of Array.from(doc.querySelectorAll(\"label[for]\"))) {\n if (lbl.getAttribute(\"for\") === id) return lbl;\n }\n return null;\n }\n function accessibleName(el) {\n const doc = el.ownerDocument;\n const aria = el.getAttribute(\"aria-label\");\n if (aria) return aria.trim();\n const labelledby = el.getAttribute(\"aria-labelledby\");\n if (labelledby) {\n const txt = labelledby.split(/\\s+/).map((id2) => doc.getElementById(id2)?.textContent ?? \"\").join(\" \").trim();\n if (txt) return txt;\n }\n const id = el.getAttribute(\"id\");\n if (id) {\n const lbl = labelFor(doc, id);\n if (lbl?.textContent?.trim()) return lbl.textContent.trim();\n }\n const wrapping = el.closest(\"label\");\n if (wrapping) {\n const txt = safeText(wrapping).trim();\n if (txt) return txt;\n }\n const placeholder = el.getAttribute(\"placeholder\");\n if (placeholder) return placeholder.trim();\n const text = isSensitive(el) ? \"\" : el.textContent?.trim();\n if (text) return text.replace(/\\s+/g, \" \").slice(0, 80);\n return el.getAttribute(\"name\") ?? el.getAttribute(\"id\") ?? roleOf(el);\n }\n function isDestructive(el, label) {\n if (el.hasAttribute(\"data-co-confirm\")) return true;\n if (el.type === \"submit\") return true;\n if (tag(el) === \"BUTTON\" && el.type === \"submit\") return true;\n return /\\b(delete|remove|destroy|sign out|log ?out|cancel subscription|deactivate)\\b/i.test(label);\n }\n function attributeHandle(el) {\n return el.getAttribute(\"data-co-handle\") || el.getAttribute(\"name\") || el.getAttribute(\"id\") || \"\";\n }\n function walk(doc = document) {\n const registry = /* @__PURE__ */ new Map();\n const actions = [];\n const seen = /* @__PURE__ */ new Set();\n let synthetic = 0;\n for (const el of Array.from(doc.querySelectorAll(INTERACTIVE))) {\n if (seen.has(el) || !isVisible(el)) continue;\n seen.add(el);\n const label = accessibleName(el);\n const role = roleOf(el);\n let handle = attributeHandle(el);\n if (!handle || registry.has(handle)) handle = `${role}#${synthetic++}`;\n registry.set(handle, el);\n let value;\n if (role === \"textbox\" || role === \"select\") {\n value = isSensitive(el) ? void 0 : el.value;\n } else if (role === \"checkbox\" || role === \"radio\") {\n value = el.checked;\n }\n const action = { handle, role, label, destructive: isDestructive(el, label) };\n if (value !== void 0) action.value = value;\n actions.push(action);\n }\n const loc = doc.defaultView?.location;\n return {\n snapshot: { url: loc ? loc.pathname + loc.search : \"\", title: doc.title, actions },\n registry\n };\n }\n function isField(el) {\n const t = tag(el);\n return t === \"INPUT\" || t === \"TEXTAREA\" || t === \"SELECT\";\n }\n function setNativeValue(el, value) {\n const view = el.ownerDocument.defaultView ?? globalThis;\n const fire = () => {\n el.dispatchEvent(new view.Event(\"input\", { bubbles: true }));\n el.dispatchEvent(new view.Event(\"change\", { bubbles: true }));\n };\n const t = tag(el);\n if (t === \"INPUT\" && (el.type === \"checkbox\" || el.type === \"radio\")) {\n const input = el;\n const want = Boolean(value);\n if (input.checked === want) return;\n if (input.type === \"checkbox\" || want) {\n input.click();\n return;\n }\n const setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, \"checked\")?.set;\n setter2?.call(el, false);\n fire();\n return;\n }\n const proto = t === \"TEXTAREA\" ? view.HTMLTextAreaElement.prototype : t === \"SELECT\" ? view.HTMLSelectElement.prototype : view.HTMLInputElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(proto, \"value\")?.set;\n setter?.call(el, String(value));\n fire();\n }\n function readPage(doc = document) {\n const main = doc.querySelector(\"main\") ?? doc.body;\n if (!main) return { title: doc.title, headings: [], text: \"\" };\n const headings = Array.from(main.querySelectorAll(\"h1, h2, h3\")).map((h) => ({\n level: Number(h.tagName.slice(1)),\n text: safeText(h).trim()\n }));\n const text = safeText(main).replace(/\\s+/g, \" \").trim().slice(0, 4e3);\n return { title: doc.title, headings, text };\n }\n function findByAttribute(doc, attr, value) {\n for (const el of Array.from(doc.querySelectorAll(`[${attr}]`))) {\n if (el.getAttribute(attr) === value) return el;\n }\n return null;\n }\n var num = (v) => typeof v === \"number\" ? v : void 0;\n function createDomNavigationAgent(options = {}) {\n let registry = /* @__PURE__ */ new Map();\n const doc = () => options.document ?? document;\n const win = () => doc().defaultView ?? window;\n const before = (action) => options.beforeAction?.(action);\n const resolve = (handle) => {\n const hit = registry.get(handle);\n if (hit && hit.isConnected) return hit;\n const d = doc();\n return findByAttribute(d, \"data-co-handle\", handle) ?? findByAttribute(d, \"name\", handle) ?? d.getElementById(handle);\n };\n const agent = {\n getLocation: () => {\n const loc = win().location;\n return { url: loc.pathname + loc.search, title: doc().title };\n },\n describe: () => {\n const res = walk(doc());\n registry = res.registry;\n return res.snapshot;\n },\n read: () => readPage(doc()),\n visit: (url) => {\n const w = win();\n const target = new URL(String(url), w.location.href);\n if (target.protocol !== \"http:\" && target.protocol !== \"https:\") {\n throw new Error(`Refusing to navigate to a '${target.protocol}' URL.`);\n }\n w.setTimeout(() => w.location.assign(target.href), 0);\n },\n back: () => win().history.back(),\n forward: () => win().history.forward(),\n scrollTo: ({ x, y, handle }) => {\n const w = win();\n if (handle) {\n resolve(handle)?.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n return;\n }\n w.scrollTo({ left: x ?? w.scrollX, top: y ?? w.scrollY, behavior: \"smooth\" });\n },\n scrollBy: (dy) => win().scrollBy({ top: dy, behavior: \"smooth\" }),\n setField: (handle, value) => {\n before(\"setField\");\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle \"${handle}\"` };\n if (isField(el)) {\n setNativeValue(el, value);\n return { ok: true };\n }\n return { ok: false, error: `Handle \"${handle}\" is not a form field` };\n },\n click: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle \"${handle}\"` };\n before(\"click\");\n el.click();\n return { ok: true };\n },\n submit: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle \"${handle}\"` };\n const form = tag(el) === \"FORM\" ? el : el.closest(\"form\");\n if (!form) return { ok: false, error: `Handle \"${handle}\" is not inside a form` };\n before(\"submit\");\n if (typeof form.requestSubmit === \"function\") form.requestSubmit();\n else form.submit();\n return { ok: true };\n },\n rectFor: (handle) => {\n const el = resolve(handle);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, width: r.width, height: r.height };\n },\n resolve,\n call: (method, args = []) => {\n const a = Array.isArray(args) ? args : [];\n const handle = String(a[0] ?? \"\");\n switch (method) {\n case \"getLocation\":\n return agent.getLocation();\n case \"describe\":\n return agent.describe();\n case \"read\":\n return agent.read();\n case \"visit\":\n agent.visit(String(a[0] ?? \"\"));\n return void 0;\n case \"back\":\n agent.back();\n return void 0;\n case \"forward\":\n agent.forward();\n return void 0;\n case \"scrollTo\": {\n const o = a[0] && typeof a[0] === \"object\" ? a[0] : {};\n agent.scrollTo({\n x: num(o.x),\n y: num(o.y),\n handle: typeof o.handle === \"string\" ? o.handle : void 0\n });\n return void 0;\n }\n case \"scrollBy\":\n agent.scrollBy(Number(a[0] ?? 0));\n return void 0;\n case \"setField\":\n return agent.setField(handle, a[1]);\n case \"click\":\n return agent.click(handle);\n case \"submit\":\n return agent.submit(handle);\n case \"rectFor\":\n return agent.rectFor(handle);\n default:\n throw new Error(`Unknown dom-navigation method: ${String(method)}`);\n }\n }\n };\n return agent;\n }\n\n // src/dom-navigation/inject.ts\n var KEY = \"__fancyDomNavigation\";\n var PROTOCOL = 1;\n var g = globalThis;\n var existing = g[KEY];\n if (!existing || existing.protocol !== PROTOCOL) {\n const agent = createDomNavigationAgent();\n Object.defineProperty(globalThis, KEY, {\n configurable: true,\n enumerable: false,\n writable: false,\n value: Object.freeze({\n protocol: PROTOCOL,\n call: (method, args) => agent.call(method, args)\n })\n });\n }\n})();\n","/**\n * `@particle-academy/agent-integrations/dom-navigation/script`\n *\n * The in-page walker as one self-contained script string — no imports, no\n * module system — for a host to inject into a page it does not own:\n *\n * await webContents.executeJavaScriptInIsolatedWorld(worldId, [{ code: DOM_NAVIGATION_SCRIPT }]);\n *\n * Built at package build time from ./inject.ts (see\n * scripts/dom-navigation-inject-plugin.ts). Its own subpath so a host process\n * that only injects never loads the walker as a module, and vice versa.\n */\nimport source from \"virtual:dom-navigation-inject\";\n\nexport const DOM_NAVIGATION_SCRIPT: string = source;\n"]}