@particle-academy/agent-integrations 0.45.0 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +835 -0
- package/LICENSE +21 -0
- package/README.md +54 -6
- package/dist/bridges/artboard.d.cts +3 -3
- package/dist/bridges/artboard.d.ts +3 -3
- package/dist/bridges/catalog.d.cts +3 -3
- package/dist/bridges/catalog.d.ts +3 -3
- package/dist/bridges/charts.d.cts +3 -3
- package/dist/bridges/charts.d.ts +3 -3
- package/dist/bridges/cms.d.cts +3 -3
- package/dist/bridges/cms.d.ts +3 -3
- package/dist/bridges/code.d.cts +3 -3
- package/dist/bridges/code.d.ts +3 -3
- package/dist/bridges/doc.d.cts +3 -3
- package/dist/bridges/doc.d.ts +3 -3
- package/dist/bridges/features.d.cts +3 -3
- package/dist/bridges/features.d.ts +3 -3
- package/dist/bridges/files.d.cts +3 -3
- package/dist/bridges/files.d.ts +3 -3
- package/dist/bridges/flow.d.cts +3 -3
- package/dist/bridges/flow.d.ts +3 -3
- package/dist/bridges/forms.d.cts +3 -3
- package/dist/bridges/forms.d.ts +3 -3
- package/dist/bridges/git.d.cts +3 -3
- package/dist/bridges/git.d.ts +3 -3
- package/dist/bridges/grid.d.cts +3 -3
- package/dist/bridges/grid.d.ts +3 -3
- package/dist/bridges/map.d.cts +3 -3
- package/dist/bridges/map.d.ts +3 -3
- package/dist/bridges/navigation.d.cts +127 -0
- package/dist/bridges/navigation.d.ts +127 -0
- package/dist/bridges/passkeys.d.cts +4 -4
- package/dist/bridges/passkeys.d.ts +4 -4
- package/dist/bridges/scene.d.cts +3 -3
- package/dist/bridges/scene.d.ts +3 -3
- package/dist/bridges/screens.d.cts +3 -3
- package/dist/bridges/screens.d.ts +3 -3
- package/dist/bridges/sheets.d.cts +3 -3
- package/dist/bridges/sheets.d.ts +3 -3
- package/dist/bridges/slides.d.cts +3 -3
- package/dist/bridges/slides.d.ts +3 -3
- package/dist/bridges/terminal.d.cts +3 -3
- package/dist/bridges/terminal.d.ts +3 -3
- package/dist/bridges/tui.d.cts +2 -2
- package/dist/bridges/tui.d.ts +2 -2
- package/dist/bridges/whiteboard.d.cts +3 -3
- package/dist/bridges/whiteboard.d.ts +3 -3
- package/dist/bridges-navigation.cjs +391 -0
- package/dist/bridges-navigation.cjs.map +1 -0
- package/dist/bridges-navigation.js +7 -0
- package/dist/bridges-navigation.js.map +1 -0
- package/dist/bridges-passkeys.cjs.map +1 -1
- package/dist/bridges-passkeys.js +1 -1
- package/dist/{chunk-OZBUPO4K.js → chunk-5W34X4EY.js} +263 -58
- package/dist/chunk-5W34X4EY.js.map +1 -0
- package/dist/{chunk-PWYEW6NL.js → chunk-G6CRHFWU.js} +2 -2
- package/dist/{chunk-PWYEW6NL.js.map → chunk-G6CRHFWU.js.map} +1 -1
- package/dist/chunk-JGJHPH65.js +271 -0
- package/dist/chunk-JGJHPH65.js.map +1 -0
- package/dist/dom-navigation/index.d.cts +164 -0
- package/dist/dom-navigation/index.d.ts +164 -0
- package/dist/dom-navigation/script.d.cts +3 -0
- package/dist/dom-navigation/script.d.ts +3 -0
- package/dist/dom-navigation-script.cjs +11 -0
- package/dist/dom-navigation-script.cjs.map +1 -0
- package/dist/dom-navigation-script.js +9 -0
- package/dist/dom-navigation-script.js.map +1 -0
- package/dist/dom-navigation.cjs +387 -0
- package/dist/dom-navigation.cjs.map +1 -0
- package/dist/dom-navigation.js +370 -0
- package/dist/dom-navigation.js.map +1 -0
- package/dist/index.cjs +31 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -125
- package/dist/index.d.ts +10 -125
- package/dist/index.js +12 -275
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.cts +5 -5
- package/dist/mcp/index.d.ts +5 -5
- package/dist/mcp/stdio.d.cts +3 -3
- package/dist/mcp/stdio.d.ts +3 -3
- package/dist/presence/index.d.cts +1 -1
- package/dist/presence/index.d.ts +1 -1
- package/dist/relay-server/index.d.cts +110 -26
- package/dist/relay-server/index.d.ts +110 -26
- package/dist/relay-server-cli.cjs +374 -89
- package/dist/relay-server-cli.cjs.map +1 -1
- package/dist/relay-server-cli.js +120 -38
- package/dist/relay-server-cli.js.map +1 -1
- package/dist/relay-server.cjs +255 -51
- package/dist/relay-server.cjs.map +1 -1
- package/dist/relay-server.js +1 -1
- package/dist/{server-BIcA3ZtK.d.cts → server-1y0n61R5.d.cts} +2 -2
- package/dist/{server-DnUyeavY.d.ts → server-vq6qVl3T.d.ts} +2 -2
- package/dist/sharing/index.d.cts +3 -3
- package/dist/sharing/index.d.ts +3 -3
- package/dist/sheets-adapter.d.cts +3 -3
- package/dist/sheets-adapter.d.ts +3 -3
- package/dist/{tool-host-LVBgCN5m.d.cts → tool-host-D67N2a2h.d.cts} +1 -1
- package/dist/{tool-host-Wo0pkY83.d.ts → tool-host-LYfdPE8u.d.ts} +1 -1
- package/dist/{types-EcElKOlk.d.cts → types-DP5yQZQU.d.cts} +1 -1
- package/dist/{types-BxNRKSVd.d.ts → types-VX_9je7w.d.ts} +1 -1
- package/dist/{types-C2DtnHIn.d.cts → types-gzIpsUDE.d.cts} +1 -1
- package/dist/{types-C2DtnHIn.d.ts → types-gzIpsUDE.d.ts} +1 -1
- package/dist/undo/index.d.cts +2 -2
- package/dist/undo/index.d.ts +2 -2
- package/docs/relay-protocol.md +41 -5
- package/docs/relay-server.md +102 -15
- package/package.json +41 -3
- package/dist/chunk-OZBUPO4K.js.map +0 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { PageSnapshot, 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: call the
|
|
72
|
+
* PROTOTYPE setter (React shadows `value` on the instance with its value
|
|
73
|
+
* tracker), then dispatch bubbling `input` + `change`.
|
|
74
|
+
*/
|
|
75
|
+
declare function setNativeValue(el: FieldElement, value: unknown): void;
|
|
76
|
+
/** Visible text / heading outline, with sensitive text removed. */
|
|
77
|
+
declare function readPage(doc?: Document): string;
|
|
78
|
+
type DomNavigationAgentOptions = {
|
|
79
|
+
/** Document to drive. Default: the global `document`, read lazily. */
|
|
80
|
+
document?: Document;
|
|
81
|
+
/**
|
|
82
|
+
* Called just before the agent acts on the page — before resolving a field
|
|
83
|
+
* for `setField`, and after resolving (only when the element exists) for
|
|
84
|
+
* `click` / `submit`. The sandbox uses it to attribute the request an action
|
|
85
|
+
* causes to the agent.
|
|
86
|
+
*/
|
|
87
|
+
beforeAction?: (action: "setField" | "click" | "submit") => void;
|
|
88
|
+
};
|
|
89
|
+
type DomNavigationAgent = {
|
|
90
|
+
getLocation(): {
|
|
91
|
+
url: string;
|
|
92
|
+
title: string;
|
|
93
|
+
};
|
|
94
|
+
describe(): PageSnapshot;
|
|
95
|
+
read(): string;
|
|
96
|
+
/** Navigate. Only http(s) or same-site paths; deferred a tick so a remote call returns first. */
|
|
97
|
+
visit(url: string): void;
|
|
98
|
+
back(): void;
|
|
99
|
+
forward(): void;
|
|
100
|
+
scrollTo(opts: {
|
|
101
|
+
x?: number;
|
|
102
|
+
y?: number;
|
|
103
|
+
handle?: string;
|
|
104
|
+
}): void;
|
|
105
|
+
scrollBy(dy: number): void;
|
|
106
|
+
setField(handle: string, value: unknown): ActionResult;
|
|
107
|
+
click(handle: string): ActionResult;
|
|
108
|
+
submit(handle: string): ActionResult;
|
|
109
|
+
rectFor(handle: string): HandleRect | null;
|
|
110
|
+
/** Element for a handle: this snapshot's registry, then a live attribute lookup. */
|
|
111
|
+
resolve(handle: string): Element | null;
|
|
112
|
+
/** Serialisable dispatch — what the injected script exposes to a host. */
|
|
113
|
+
call(method: DomNavigationMethod, args?: unknown[]): unknown;
|
|
114
|
+
};
|
|
115
|
+
declare function createDomNavigationAgent(options?: DomNavigationAgentOptions): DomNavigationAgent;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The host half: a {@link NavigationBridgeAdapter} that drives a page it does
|
|
119
|
+
* not run in, through any transport that can carry a method name and plain
|
|
120
|
+
* arguments into the page and a plain result back — Electron's
|
|
121
|
+
* `executeJavaScript` / `executeJavaScriptInIsolatedWorld`, a MessagePort, a
|
|
122
|
+
* WebSocket to a remote browser.
|
|
123
|
+
*
|
|
124
|
+
* Every result is treated as hostile: the page owns the DOM the in-page walker
|
|
125
|
+
* reads, and if the script was injected into the MAIN world the page can
|
|
126
|
+
* replace the walker outright. So results are coerced to the declared shapes
|
|
127
|
+
* here, and anything malformed is dropped rather than passed on to the agent.
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/** The global the injected script installs in the world it runs in. */
|
|
131
|
+
declare const DOM_NAVIGATION_GLOBAL = "__fancyDomNavigation";
|
|
132
|
+
/** Bumped only if the wire protocol changes incompatibly. */
|
|
133
|
+
declare const DOM_NAVIGATION_PROTOCOL = 1;
|
|
134
|
+
/**
|
|
135
|
+
* Carries one call into the page. `args` and the resolved value are plain,
|
|
136
|
+
* structured-cloneable data. Reject (or throw) when the call could not be
|
|
137
|
+
* delivered — e.g. the page navigated and the script must be re-injected.
|
|
138
|
+
*/
|
|
139
|
+
type DomNavigationTransport = {
|
|
140
|
+
call(method: DomNavigationMethod, args: unknown[]): Promise<unknown>;
|
|
141
|
+
};
|
|
142
|
+
type RemoteNavigationAdapterOptions = {
|
|
143
|
+
transport: DomNavigationTransport;
|
|
144
|
+
/** Optional fancy-screens screen id for presence targeting. */
|
|
145
|
+
screenId?: string;
|
|
146
|
+
/** Human-control revision, if the host tracks one. Omit to disable stale-intent protection. */
|
|
147
|
+
getRevision?: () => number | Promise<number>;
|
|
148
|
+
/**
|
|
149
|
+
* Trust-but-verify. Without it, `page_submit` and destructive `page_click`
|
|
150
|
+
* are NOT staged — they run as soon as the agent asks. A host showing pages
|
|
151
|
+
* it does not own should always supply one.
|
|
152
|
+
*/
|
|
153
|
+
confirm?: (req: NavigationConfirmRequest) => Promise<boolean>;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* A JavaScript expression that performs one call against the injected global —
|
|
157
|
+
* what a host passes to `executeJavaScript`. Arguments are embedded as JSON,
|
|
158
|
+
* which is a valid JS expression, so no argument can break out of its literal.
|
|
159
|
+
*/
|
|
160
|
+
declare function domNavigationCallScript(method: DomNavigationMethod, args?: unknown[]): string;
|
|
161
|
+
/** Build a NavigationBridgeAdapter backed by a transport into the page. */
|
|
162
|
+
declare function createRemoteNavigationAdapter(options: RemoteNavigationAdapterOptions): NavigationBridgeAdapter;
|
|
163
|
+
|
|
164
|
+
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, PageSnapshot, type RemoteNavigationAdapterOptions, accessibleName, attributeHandle, createDomNavigationAgent, createRemoteNavigationAdapter, domNavigationCallScript, isDestructive, isSensitive, isVisible, readPage, roleOf, setNativeValue, walk };
|
|
@@ -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 setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, "checked")?.set;\n setter2?.call(el, Boolean(value));\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 doc.title;\n const heads = Array.from(main.querySelectorAll("h1, h2, h3")).map((h) => `${h.tagName}: ${safeText(h).trim()}`).filter(Boolean);\n const text = safeText(main).replace(/\\s+/g, " ").trim().slice(0, 4e3);\n return [doc.title, ...heads, "", text].join("\\n");\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,ihWAAA;;;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 setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, \"checked\")?.set;\n setter2?.call(el, Boolean(value));\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 doc.title;\n const heads = Array.from(main.querySelectorAll(\"h1, h2, h3\")).map((h) => `${h.tagName}: ${safeText(h).trim()}`).filter(Boolean);\n const text = safeText(main).replace(/\\s+/g, \" \").trim().slice(0, 4e3);\n return [doc.title, ...heads, \"\", text].join(\"\\n\");\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 setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, "checked")?.set;\n setter2?.call(el, Boolean(value));\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 doc.title;\n const heads = Array.from(main.querySelectorAll("h1, h2, h3")).map((h) => `${h.tagName}: ${safeText(h).trim()}`).filter(Boolean);\n const text = safeText(main).replace(/\\s+/g, " ").trim().slice(0, 4e3);\n return [doc.title, ...heads, "", text].join("\\n");\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,ihWAAA;;;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 setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, \"checked\")?.set;\n setter2?.call(el, Boolean(value));\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 doc.title;\n const heads = Array.from(main.querySelectorAll(\"h1, h2, h3\")).map((h) => `${h.tagName}: ${safeText(h).trim()}`).filter(Boolean);\n const text = safeText(main).replace(/\\s+/g, \" \").trim().slice(0, 4e3);\n return [doc.title, ...heads, \"\", text].join(\"\\n\");\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"]}
|