@intentic/browser 1.176.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/LICENSE +21 -0
- package/README.md +56 -0
- package/dist/cdp.d.ts +17 -0
- package/dist/cdp.d.ts.map +1 -0
- package/dist/cdp.js +82 -0
- package/dist/cdp.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +112 -0
- package/dist/index.js.map +1 -0
- package/dist/launch.d.ts +7 -0
- package/dist/launch.d.ts.map +1 -0
- package/dist/launch.js +55 -0
- package/dist/launch.js.map +1 -0
- package/dist/snapshot.d.ts +12 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +100 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Artur Kurowski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @intentic/browser
|
|
2
|
+
|
|
3
|
+
Drive a Chromium-family browser from Node over CDP: open pages, read them as **structured text**, and click and
|
|
4
|
+
type by element reference. No dependencies.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { browser } from "@intentic/browser";
|
|
8
|
+
|
|
9
|
+
const web = browser();
|
|
10
|
+
const page = await web.open("https://example.com/login");
|
|
11
|
+
// page.elements → [{ ref: "e0", role: "textbox", name: "Email" }, …]
|
|
12
|
+
|
|
13
|
+
await web.fill("e0", "someone@example.com");
|
|
14
|
+
await web.fill("e1", "…", true); // true = submit
|
|
15
|
+
const after = await web.snapshot();
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Why references instead of coordinates
|
|
19
|
+
|
|
20
|
+
A browser can be operated by clicking pixels, and it is miserable: the coordinates move when the window moves, a
|
|
21
|
+
scroll invalidates every one of them, and "the Submit button" is a guess about which grey rectangle is which.
|
|
22
|
+
|
|
23
|
+
A browser will simply tell you what it is showing. So this asks it — one snapshot returns every visible element
|
|
24
|
+
with its role (`link`, `button`, `textbox`), its accessible name, and what it currently holds — and every action
|
|
25
|
+
names an element rather than a position. The same instruction then works at any window size, on any machine,
|
|
26
|
+
after any re-render.
|
|
27
|
+
|
|
28
|
+
**Refs are deliberately short-lived.** They index an array parked on the page, and the next snapshot replaces it,
|
|
29
|
+
so a ref taken before a click that navigated cannot silently address whatever now occupies that slot. A stale ref
|
|
30
|
+
fails loudly, which is the behaviour worth having.
|
|
31
|
+
|
|
32
|
+
## Which browser it drives
|
|
33
|
+
|
|
34
|
+
**Not the user's own.** A browser only speaks CDP if it was started with `--remote-debugging-port`, and nobody's
|
|
35
|
+
everyday browser was; restarting theirs to add the flag would close every tab they had open. So: if a debugging
|
|
36
|
+
endpoint is already there, it is used; otherwise a separate instance starts with its own profile directory under
|
|
37
|
+
`~/.intentic/host/browser`.
|
|
38
|
+
|
|
39
|
+
That separate profile is a feature rather than a compromise. It is empty the first time, so the user signs into
|
|
40
|
+
whatever is needed once, in a window they can watch, and it persists afterwards. Their own session is never
|
|
41
|
+
automated and never at risk from a misfired click.
|
|
42
|
+
|
|
43
|
+
## Why hand-rolled CDP rather than Puppeteer or Playwright
|
|
44
|
+
|
|
45
|
+
This ships inside a `bun build --compile` binary, and that constraint has already been tested rather than
|
|
46
|
+
assumed: the last native-dependency-shaped library that looked reasonable (nut.js, for input injection) turned
|
|
47
|
+
out to be unloadable from a compiled binary at all — its addon is found through `bindings`, which walks up from
|
|
48
|
+
`__dirname` looking for a `package.json`, and inside a standalone binary there isn't one.
|
|
49
|
+
|
|
50
|
+
CDP needs no dependency. The protocol is JSON, `fetch` and `WebSocket` are globals, and the ~200 lines here are
|
|
51
|
+
the subset that driving a page actually uses. Nothing in it can fail to load on somebody's laptop.
|
|
52
|
+
|
|
53
|
+
## What is testable without a browser
|
|
54
|
+
|
|
55
|
+
`snapshot.ts`'s renderer and ref parsing, and the per-platform browser search — all pure. The CDP calls end in a
|
|
56
|
+
real Chrome painting a real page; those need a machine, not a test.
|
package/dist/cdp.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface CdpTarget {
|
|
2
|
+
readonly id: string;
|
|
3
|
+
readonly title: string;
|
|
4
|
+
readonly url: string;
|
|
5
|
+
readonly type: string;
|
|
6
|
+
readonly webSocketDebuggerUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CdpSession {
|
|
9
|
+
readonly send: <T = Record<string, unknown>>(method: string, params?: Record<string, unknown>) => Promise<T>;
|
|
10
|
+
readonly close: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const probe: (port: number) => Promise<boolean>;
|
|
13
|
+
export declare const waitForPort: (port: number, timeoutMs: number) => Promise<void>;
|
|
14
|
+
export declare const listTargets: (port: number) => Promise<CdpTarget[]>;
|
|
15
|
+
export declare const newTab: (port: number, url: string) => Promise<CdpTarget>;
|
|
16
|
+
export declare const attach: (wsUrl: string) => Promise<CdpSession>;
|
|
17
|
+
//# sourceMappingURL=cdp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdp.d.ts","sourceRoot":"","sources":["../src/cdp.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,SAAS;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC1C;AAED,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7G,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;CAC9B;AAMD,eAAO,MAAM,KAAK,SAAgB,MAAM,KAAG,OAAO,CAAC,OAAO,CAOzD,CAAC;AAEF,eAAO,MAAM,WAAW,SAAgB,MAAM,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAS/E,CAAC;AAEF,eAAO,MAAM,WAAW,SAAgB,MAAM,KAAG,OAAO,CAAC,SAAS,EAAE,CAQnE,CAAC;AAEF,eAAO,MAAM,MAAM,SAAgB,MAAM,OAAO,MAAM,KAAG,OAAO,CAAC,SAAS,CAMzE,CAAC;AAEF,eAAO,MAAM,MAAM,UAAiB,MAAM,KAAG,OAAO,CAAC,UAAU,CAkD9D,CAAC"}
|
package/dist/cdp.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { BrowserError } from "./types.js";
|
|
2
|
+
const CALL_TIMEOUT_MS = 30_000;
|
|
3
|
+
const endpoint = (port, path) => `http://127.0.0.1:${port}${path}`;
|
|
4
|
+
export const probe = async (port) => {
|
|
5
|
+
try {
|
|
6
|
+
const response = await fetch(endpoint(port, "/json/version"), { signal: AbortSignal.timeout(1000) });
|
|
7
|
+
return response.ok;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export const waitForPort = async (port, timeoutMs) => {
|
|
14
|
+
const deadline = Date.now() + timeoutMs;
|
|
15
|
+
while (Date.now() < deadline) {
|
|
16
|
+
if (await probe(port)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 150));
|
|
20
|
+
}
|
|
21
|
+
throw new BrowserError(`The browser did not open its debugging port (${port}) in time.`);
|
|
22
|
+
};
|
|
23
|
+
export const listTargets = async (port) => {
|
|
24
|
+
const response = await fetch(endpoint(port, "/json/list"), { signal: AbortSignal.timeout(5000) }).catch(() => undefined);
|
|
25
|
+
if (response === undefined || !response.ok) {
|
|
26
|
+
throw new BrowserError(`No browser is answering on the debugging port (${port}).`);
|
|
27
|
+
}
|
|
28
|
+
return (await response.json()).filter((target) => target.type === "page" && !target.url.startsWith("devtools://"));
|
|
29
|
+
};
|
|
30
|
+
export const newTab = async (port, url) => {
|
|
31
|
+
const response = await fetch(endpoint(port, `/json/new?${encodeURIComponent(url)}`), { method: "PUT" }).catch(() => undefined);
|
|
32
|
+
if (response === undefined || !response.ok) {
|
|
33
|
+
throw new BrowserError("The browser refused to open a new tab.");
|
|
34
|
+
}
|
|
35
|
+
return (await response.json());
|
|
36
|
+
};
|
|
37
|
+
export const attach = async (wsUrl) => {
|
|
38
|
+
const socket = new WebSocket(wsUrl);
|
|
39
|
+
const pending = new Map();
|
|
40
|
+
let nextId = 1;
|
|
41
|
+
await new Promise((resolvePromise, reject) => {
|
|
42
|
+
socket.addEventListener("open", () => resolvePromise(), { once: true });
|
|
43
|
+
socket.addEventListener("error", () => reject(new BrowserError("Could not connect to the browser's debugging socket.")), { once: true });
|
|
44
|
+
});
|
|
45
|
+
socket.addEventListener("message", (event) => {
|
|
46
|
+
const message = JSON.parse(String(event.data));
|
|
47
|
+
if (message.id === undefined) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const waiter = pending.get(message.id);
|
|
51
|
+
if (waiter === undefined) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
pending.delete(message.id);
|
|
55
|
+
clearTimeout(waiter.timer);
|
|
56
|
+
if (message.error !== undefined) {
|
|
57
|
+
waiter.reject(new BrowserError(message.error.message ?? "The browser refused that."));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
waiter.resolve(message.result);
|
|
61
|
+
});
|
|
62
|
+
socket.addEventListener("close", () => {
|
|
63
|
+
for (const [, waiter] of pending) {
|
|
64
|
+
clearTimeout(waiter.timer);
|
|
65
|
+
waiter.reject(new BrowserError("The browser closed the connection — the tab was probably closed."));
|
|
66
|
+
}
|
|
67
|
+
pending.clear();
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
send: (method, params = {}) => new Promise((resolvePromise, reject) => {
|
|
71
|
+
const id = nextId++;
|
|
72
|
+
const timer = setTimeout(() => {
|
|
73
|
+
pending.delete(id);
|
|
74
|
+
reject(new BrowserError(`The page did not answer "${method}" within ${CALL_TIMEOUT_MS / 1000}s.`));
|
|
75
|
+
}, CALL_TIMEOUT_MS);
|
|
76
|
+
pending.set(id, { resolve: resolvePromise, reject, timer });
|
|
77
|
+
socket.send(JSON.stringify({ id, method, params }));
|
|
78
|
+
}),
|
|
79
|
+
close: () => socket.close(),
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=cdp.js.map
|
package/dist/cdp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdp.js","sourceRoot":"","sources":["../src/cdp.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Browser } from "./types.js";
|
|
2
|
+
export { DEFAULT_PORT, browserCandidates, ensureBrowser, profileDir } from "./launch.js";
|
|
3
|
+
export { renderPage, refIndex, toPageState, SNAPSHOT_SCRIPT, type RawSnapshot } from "./snapshot.js";
|
|
4
|
+
export { BrowserError, type Browser, type PageElement, type PageState } from "./types.js";
|
|
5
|
+
export declare const browser: (port?: number) => Browser;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAgC,MAAM,YAAY,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACrG,OAAO,EAAE,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAqC1F,eAAO,MAAM,OAAO,UAAU,MAAM,KAAkB,OA6GrD,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { attach, listTargets, newTab } from "./cdp.js";
|
|
2
|
+
import { DEFAULT_PORT, ensureBrowser } from "./launch.js";
|
|
3
|
+
import { refIndex, SNAPSHOT_SCRIPT, toPageState } from "./snapshot.js";
|
|
4
|
+
import { BrowserError } from "./types.js";
|
|
5
|
+
export { DEFAULT_PORT, browserCandidates, ensureBrowser, profileDir } from "./launch.js";
|
|
6
|
+
export { renderPage, refIndex, toPageState, SNAPSHOT_SCRIPT } from "./snapshot.js";
|
|
7
|
+
export { BrowserError } from "./types.js";
|
|
8
|
+
const evaluate = async (session, expression) => {
|
|
9
|
+
const result = await session.send("Runtime.evaluate", {
|
|
10
|
+
expression,
|
|
11
|
+
returnByValue: true,
|
|
12
|
+
awaitPromise: true,
|
|
13
|
+
});
|
|
14
|
+
if (result.exceptionDetails !== undefined) {
|
|
15
|
+
throw new BrowserError(`The page rejected that: ${result.exceptionDetails.text ?? "script error"}`);
|
|
16
|
+
}
|
|
17
|
+
return result.result?.value;
|
|
18
|
+
};
|
|
19
|
+
const withElement = (ref, body) => {
|
|
20
|
+
const index = refIndex(ref);
|
|
21
|
+
return `(function () {
|
|
22
|
+
var refs = window.__intenticRefs || [];
|
|
23
|
+
var el = refs[${index}];
|
|
24
|
+
if (!el) throw new Error('${ref} is not on this page any more — take a new snapshot; the page has changed since the last one.');
|
|
25
|
+
${body}
|
|
26
|
+
})()`;
|
|
27
|
+
};
|
|
28
|
+
export const browser = (port = DEFAULT_PORT) => {
|
|
29
|
+
let session;
|
|
30
|
+
let targetId;
|
|
31
|
+
const connect = async (preferred) => {
|
|
32
|
+
if (session !== undefined && (preferred === undefined || preferred === targetId)) {
|
|
33
|
+
return session;
|
|
34
|
+
}
|
|
35
|
+
const targets = await listTargets(port);
|
|
36
|
+
const target = preferred === undefined ? targets[0] : targets.find((candidate) => candidate.id === preferred);
|
|
37
|
+
if (target?.webSocketDebuggerUrl === undefined) {
|
|
38
|
+
throw new BrowserError(preferred === undefined ? "The browser has no open page." : `There is no tab "${preferred}" any more.`);
|
|
39
|
+
}
|
|
40
|
+
session?.close();
|
|
41
|
+
session = await attach(target.webSocketDebuggerUrl);
|
|
42
|
+
targetId = target.id;
|
|
43
|
+
return session;
|
|
44
|
+
};
|
|
45
|
+
const snapshot = async () => {
|
|
46
|
+
const raw = await evaluate(await connect(targetId), SNAPSHOT_SCRIPT);
|
|
47
|
+
return toPageState(raw ?? {});
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
open: async (url) => {
|
|
51
|
+
await ensureBrowser(port, url);
|
|
52
|
+
if (url !== undefined) {
|
|
53
|
+
const target = await newTab(port, url);
|
|
54
|
+
targetId = target.id;
|
|
55
|
+
session?.close();
|
|
56
|
+
session = undefined;
|
|
57
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 600));
|
|
58
|
+
}
|
|
59
|
+
return await snapshot();
|
|
60
|
+
},
|
|
61
|
+
snapshot,
|
|
62
|
+
click: async (ref) => {
|
|
63
|
+
await evaluate(await connect(targetId), withElement(ref, "el.scrollIntoView({block: 'center'}); el.click();"));
|
|
64
|
+
},
|
|
65
|
+
fill: async (ref, text, submit) => {
|
|
66
|
+
const literal = JSON.stringify(text);
|
|
67
|
+
await evaluate(await connect(targetId), withElement(ref, `el.focus();
|
|
68
|
+
if (el.isContentEditable) { el.textContent = ${literal}; }
|
|
69
|
+
else { el.value = ${literal}; }
|
|
70
|
+
// The events a page's own JavaScript listens for. Setting .value alone updates the DOM and leaves every
|
|
71
|
+
// framework's state untouched, which is how a filled form submits empty.
|
|
72
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
73
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
74
|
+
${submit === true ? "if (el.form) { el.form.requestSubmit ? el.form.requestSubmit() : el.form.submit(); }" : ""}`));
|
|
75
|
+
},
|
|
76
|
+
press: async (combo) => {
|
|
77
|
+
const live = await connect(targetId);
|
|
78
|
+
const key = combo.split("+").pop() ?? combo;
|
|
79
|
+
const named = {
|
|
80
|
+
Return: { key: "Enter", code: "Enter", keyCode: 13 },
|
|
81
|
+
Enter: { key: "Enter", code: "Enter", keyCode: 13 },
|
|
82
|
+
Escape: { key: "Escape", code: "Escape", keyCode: 27 },
|
|
83
|
+
Tab: { key: "Tab", code: "Tab", keyCode: 9 },
|
|
84
|
+
};
|
|
85
|
+
const descriptor = named[key] ?? { key, code: `Key${key.toUpperCase()}`, keyCode: key.toUpperCase().charCodeAt(0) };
|
|
86
|
+
const modifiers = (combo.includes("ctrl") ? 2 : 0) | (combo.includes("shift") ? 8 : 0) | (combo.includes("alt") ? 1 : 0);
|
|
87
|
+
for (const type of ["keyDown", "keyUp"]) {
|
|
88
|
+
await live.send("Input.dispatchKeyEvent", { type, ...descriptor, windowsVirtualKeyCode: descriptor.keyCode, modifiers });
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
text: async () => {
|
|
92
|
+
const body = await evaluate(await connect(targetId), "document.body ? document.body.innerText : ''");
|
|
93
|
+
return (body ?? "").replace(/\n{3,}/g, "\n\n").slice(0, 20_000);
|
|
94
|
+
},
|
|
95
|
+
screenshot: async () => {
|
|
96
|
+
const shot = await (await connect(targetId)).send("Page.captureScreenshot", { format: "png" });
|
|
97
|
+
return Buffer.from(shot.data, "base64");
|
|
98
|
+
},
|
|
99
|
+
tabs: async () => (await listTargets(port)).map((target) => ({ id: target.id, title: target.title, url: target.url, active: target.id === targetId })),
|
|
100
|
+
selectTab: async (id) => {
|
|
101
|
+
await connect(id);
|
|
102
|
+
await session?.send("Page.bringToFront").catch(() => undefined);
|
|
103
|
+
return await snapshot();
|
|
104
|
+
},
|
|
105
|
+
disconnect: async () => {
|
|
106
|
+
session?.close();
|
|
107
|
+
session = undefined;
|
|
108
|
+
targetId = undefined;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAmB,WAAW,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAoB,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACzF,OAAO,EAAgB,YAAY,EAAkB,MAAM,YAAY,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAoB,MAAM,eAAe,CAAC;AACrG,OAAO,EAAE,YAAY,EAAkD,MAAM,YAAY,CAAC;AAa1F,MAAM,QAAQ,GAAG,KAAK,EAAK,OAAmB,EAAE,UAAkB,EAAc,EAAE;IAC9E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAmE,kBAAkB,EAAE;QACpH,UAAU;QACV,aAAa,EAAE,IAAI;QACnB,YAAY,EAAE,IAAI;KACrB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACxC,MAAM,IAAI,YAAY,CAAC,2BAA2B,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI,cAAc,EAAE,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,EAAE,KAAU,CAAC;AACrC,CAAC,CAAC;AAIF,MAAM,WAAW,GAAG,CAAC,GAAW,EAAE,IAAY,EAAU,EAAE;IACtD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO;;kBAEO,KAAK;8BACO,GAAG;IAC7B,IAAI;KACH,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAI,GAAW,YAAY,EAAW,EAAE;IAC5D,IAAI,OAA+B,CAAC;IACpC,IAAI,QAA4B,CAAC;IAEjC,MAAM,OAAO,GAAG,KAAK,EAAE,SAAkB,EAAuB,EAAE;QAC9D,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,QAAQ,CAAC,EAAE,CAAC;YAC/E,OAAO,OAAO,CAAC;QACnB,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QAC9G,IAAI,MAAM,EAAE,oBAAoB,KAAK,SAAS,EAAE,CAAC;YAC7C,MAAM,IAAI,YAAY,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,oBAAoB,SAAS,aAAa,CAAC,CAAC;QACnI,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,CAAC;QACjB,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACpD,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,KAAK,IAAwB,EAAE;QAC5C,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAc,MAAM,OAAO,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC;QAClF,OAAO,WAAW,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC;IAEF,OAAO;QACH,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAChB,MAAM,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC/B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBAGpB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBACvC,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;gBACrB,OAAO,EAAE,KAAK,EAAE,CAAC;gBACjB,OAAO,GAAG,SAAS,CAAC;gBAEpB,MAAM,IAAI,OAAO,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,UAAU,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3E,CAAC;YACD,OAAO,MAAM,QAAQ,EAAE,CAAC;QAC5B,CAAC;QAED,QAAQ;QAER,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACjB,MAAM,QAAQ,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC,GAAG,EAAE,mDAAmD,CAAC,CAAC,CAAC;QACnH,CAAC;QAED,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;YAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACrC,MAAM,QAAQ,CACV,MAAM,OAAO,CAAC,QAAQ,CAAC,EACvB,WAAW,CACP,GAAG,EACH;iDAC6B,OAAO;sBAClC,OAAO;;;;;IAKzB,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,sFAAsF,CAAC,CAAC,CAAC,EAAE,EAAE,CAClG,CACJ,CAAC;QACN,CAAC;QAID,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACnB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;YACrC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC;YAC5C,MAAM,KAAK,GAAmE;gBAC1E,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;gBACpD,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;gBACnD,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;gBACtD,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE;aAC/C,CAAC;YACF,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,WAAW,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACpH,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACzH,KAAK,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,OAAO,CAAU,EAAE,CAAC;gBAC/C,MAAM,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,qBAAqB,EAAE,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;YAC7H,CAAC;QACL,CAAC;QAED,IAAI,EAAE,KAAK,IAAI,EAAE;YACb,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAS,MAAM,OAAO,CAAC,QAAQ,CAAC,EAAE,8CAA8C,CAAC,CAAC;YAE7G,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACpE,CAAC;QAED,UAAU,EAAE,KAAK,IAAI,EAAE;YACnB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAmB,wBAAwB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YACjH,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,EAAE,KAAK,IAAI,EAAE,CACb,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC,CAAC;QAExI,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;YACpB,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;YAElB,MAAM,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAChE,OAAO,MAAM,QAAQ,EAAE,CAAC;QAC5B,CAAC;QAED,UAAU,EAAE,KAAK,IAAI,EAAE;YACnB,OAAO,EAAE,KAAK,EAAE,CAAC;YACjB,OAAO,GAAG,SAAS,CAAC;YACpB,QAAQ,GAAG,SAAS,CAAC;QACzB,CAAC;KACJ,CAAC;AACN,CAAC,CAAC"}
|
package/dist/launch.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const DEFAULT_PORT = 9222;
|
|
2
|
+
export declare const profileDir: () => string;
|
|
3
|
+
export declare const browserCandidates: (platform: NodeJS.Platform) => string[];
|
|
4
|
+
export declare const ensureBrowser: (port?: number, url?: string) => Promise<{
|
|
5
|
+
started: boolean;
|
|
6
|
+
}>;
|
|
7
|
+
//# sourceMappingURL=launch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC,eAAO,MAAM,UAAU,QAAO,MAAyD,CAAC;AAKxF,eAAO,MAAM,iBAAiB,aAAc,MAAM,CAAC,QAAQ,KAAG,MAAM,EAqBnE,CAAC;AAsBF,eAAO,MAAM,aAAa,UAAgB,MAAM,QAAuB,MAAM,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAiB3G,CAAC"}
|
package/dist/launch.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { probe, waitForPort } from "./cdp.js";
|
|
6
|
+
import { BrowserError } from "./types.js";
|
|
7
|
+
export const DEFAULT_PORT = 9222;
|
|
8
|
+
export const profileDir = () => join(homedir(), ".intentic", "host", "browser");
|
|
9
|
+
export const browserCandidates = (platform) => {
|
|
10
|
+
if (platform === "win32") {
|
|
11
|
+
const programFiles = process.env["ProgramFiles"] ?? "C:\\Program Files";
|
|
12
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
|
|
13
|
+
const local = process.env["LOCALAPPDATA"] ?? join(homedir(), "AppData", "Local");
|
|
14
|
+
return [
|
|
15
|
+
join(programFiles, "Google\\Chrome\\Application\\chrome.exe"),
|
|
16
|
+
join(programFilesX86, "Google\\Chrome\\Application\\chrome.exe"),
|
|
17
|
+
join(local, "Google\\Chrome\\Application\\chrome.exe"),
|
|
18
|
+
join(programFiles, "Microsoft\\Edge\\Application\\msedge.exe"),
|
|
19
|
+
join(programFilesX86, "Microsoft\\Edge\\Application\\msedge.exe"),
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
return [
|
|
23
|
+
"/usr/bin/google-chrome",
|
|
24
|
+
"/usr/bin/google-chrome-stable",
|
|
25
|
+
"/usr/bin/chromium",
|
|
26
|
+
"/usr/bin/chromium-browser",
|
|
27
|
+
"/usr/bin/microsoft-edge",
|
|
28
|
+
"/snap/bin/chromium",
|
|
29
|
+
];
|
|
30
|
+
};
|
|
31
|
+
const findBrowser = () => browserCandidates(process.platform).find((path) => existsSync(path));
|
|
32
|
+
const flags = (port, url) => [
|
|
33
|
+
`--remote-debugging-port=${port}`,
|
|
34
|
+
`--user-data-dir=${profileDir()}`,
|
|
35
|
+
"--no-first-run",
|
|
36
|
+
"--no-default-browser-check",
|
|
37
|
+
"--disable-session-crashed-bubble",
|
|
38
|
+
"--restore-last-session=false",
|
|
39
|
+
...(url === undefined ? [] : [url]),
|
|
40
|
+
];
|
|
41
|
+
const START_TIMEOUT_MS = 20_000;
|
|
42
|
+
export const ensureBrowser = async (port = DEFAULT_PORT, url) => {
|
|
43
|
+
if (await probe(port)) {
|
|
44
|
+
return { started: false };
|
|
45
|
+
}
|
|
46
|
+
const binary = findBrowser();
|
|
47
|
+
if (binary === undefined) {
|
|
48
|
+
throw new BrowserError("This computer has no Chrome, Chromium or Edge, and browser control needs one of them.", "Install Google Chrome (or Chromium) and try again.");
|
|
49
|
+
}
|
|
50
|
+
const child = spawn(binary, flags(port, url), { detached: true, stdio: "ignore" });
|
|
51
|
+
child.unref();
|
|
52
|
+
await waitForPort(port, START_TIMEOUT_MS);
|
|
53
|
+
return { started: true };
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PageElement, PageState } from "./types.js";
|
|
2
|
+
export declare const SNAPSHOT_SCRIPT = "(function () {\n var MAX = 150;\n var refs = [];\n window.__intenticRefs = refs;\n\n function visible(el) {\n var rect = el.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return false;\n var style = window.getComputedStyle(el);\n return style.visibility !== 'hidden' && style.display !== 'none' && style.opacity !== '0';\n }\n\n function roleOf(el) {\n var explicit = el.getAttribute('role');\n if (explicit) return explicit;\n var tag = el.tagName.toLowerCase();\n if (tag === 'a') return 'link';\n if (tag === 'button') return 'button';\n if (tag === 'select') return 'combobox';\n if (tag === 'textarea') return 'textbox';\n if (/^h[1-6]$/.test(tag)) return 'heading';\n if (tag === 'input') {\n var type = (el.getAttribute('type') || 'text').toLowerCase();\n if (type === 'submit' || type === 'button' || type === 'reset') return 'button';\n if (type === 'checkbox') return 'checkbox';\n if (type === 'radio') return 'radio';\n if (type === 'file') return 'file';\n return 'textbox';\n }\n if (el.isContentEditable) return 'textbox';\n return 'element';\n }\n\n function nameOf(el) {\n var candidates = [\n el.getAttribute('aria-label'),\n el.getAttribute('alt'),\n el.getAttribute('placeholder'),\n el.getAttribute('title'),\n el.getAttribute('name'),\n (el.innerText || '').trim(),\n el.value\n ];\n for (var i = 0; i < candidates.length; i++) {\n var candidate = candidates[i];\n if (typeof candidate === 'string' && candidate.trim() !== '') {\n return candidate.trim().replace(/\\s+/g, ' ').slice(0, 120);\n }\n }\n return '';\n }\n\n var selector = 'a[href], button, input, textarea, select, summary, [role], [onclick], [contenteditable=\"\"], [contenteditable=\"true\"], h1, h2, h3';\n var found = document.querySelectorAll(selector);\n var elements = [];\n for (var i = 0; i < found.length && elements.length < MAX; i++) {\n var el = found[i];\n if (!visible(el)) continue;\n var role = roleOf(el);\n var name = nameOf(el);\n // A nameless non-input is something a caller could never ask for by name, so it is noise.\n if (name === '' && role !== 'textbox' && role !== 'checkbox' && role !== 'file') continue;\n var ref = 'e' + refs.length;\n refs.push(el);\n var entry = { ref: ref, role: role, name: name };\n if (typeof el.value === 'string' && el.value !== '' && role !== 'button') entry.value = el.value.slice(0, 120);\n if (role === 'checkbox' || role === 'radio') entry.value = el.checked ? 'checked' : 'unchecked';\n elements.push(entry);\n }\n return {\n url: location.href,\n title: document.title,\n truncated: found.length > 0 && elements.length >= MAX,\n elements: elements\n };\n})()";
|
|
3
|
+
export declare const renderPage: (page: PageState, truncated?: boolean) => string;
|
|
4
|
+
export interface RawSnapshot {
|
|
5
|
+
readonly url?: string;
|
|
6
|
+
readonly title?: string;
|
|
7
|
+
readonly truncated?: boolean;
|
|
8
|
+
readonly elements?: readonly PageElement[];
|
|
9
|
+
}
|
|
10
|
+
export declare const toPageState: (raw: RawSnapshot) => PageState;
|
|
11
|
+
export declare const refIndex: (ref: string) => number;
|
|
12
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAqBzD,eAAO,MAAM,eAAe,gyFA2EvB,CAAC;AAMN,eAAO,MAAM,UAAU,SAAU,SAAS,0BAAsB,MAY/D,CAAC;AAGF,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CAC9C;AAED,eAAO,MAAM,WAAW,QAAS,WAAW,KAAG,SAI7C,CAAC;AAIH,eAAO,MAAM,QAAQ,QAAS,MAAM,KAAG,MAGtC,CAAC"}
|
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const MAX_ELEMENTS = 150;
|
|
2
|
+
export const SNAPSHOT_SCRIPT = `(function () {
|
|
3
|
+
var MAX = ${MAX_ELEMENTS};
|
|
4
|
+
var refs = [];
|
|
5
|
+
window.__intenticRefs = refs;
|
|
6
|
+
|
|
7
|
+
function visible(el) {
|
|
8
|
+
var rect = el.getBoundingClientRect();
|
|
9
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
10
|
+
var style = window.getComputedStyle(el);
|
|
11
|
+
return style.visibility !== 'hidden' && style.display !== 'none' && style.opacity !== '0';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function roleOf(el) {
|
|
15
|
+
var explicit = el.getAttribute('role');
|
|
16
|
+
if (explicit) return explicit;
|
|
17
|
+
var tag = el.tagName.toLowerCase();
|
|
18
|
+
if (tag === 'a') return 'link';
|
|
19
|
+
if (tag === 'button') return 'button';
|
|
20
|
+
if (tag === 'select') return 'combobox';
|
|
21
|
+
if (tag === 'textarea') return 'textbox';
|
|
22
|
+
if (/^h[1-6]$/.test(tag)) return 'heading';
|
|
23
|
+
if (tag === 'input') {
|
|
24
|
+
var type = (el.getAttribute('type') || 'text').toLowerCase();
|
|
25
|
+
if (type === 'submit' || type === 'button' || type === 'reset') return 'button';
|
|
26
|
+
if (type === 'checkbox') return 'checkbox';
|
|
27
|
+
if (type === 'radio') return 'radio';
|
|
28
|
+
if (type === 'file') return 'file';
|
|
29
|
+
return 'textbox';
|
|
30
|
+
}
|
|
31
|
+
if (el.isContentEditable) return 'textbox';
|
|
32
|
+
return 'element';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function nameOf(el) {
|
|
36
|
+
var candidates = [
|
|
37
|
+
el.getAttribute('aria-label'),
|
|
38
|
+
el.getAttribute('alt'),
|
|
39
|
+
el.getAttribute('placeholder'),
|
|
40
|
+
el.getAttribute('title'),
|
|
41
|
+
el.getAttribute('name'),
|
|
42
|
+
(el.innerText || '').trim(),
|
|
43
|
+
el.value
|
|
44
|
+
];
|
|
45
|
+
for (var i = 0; i < candidates.length; i++) {
|
|
46
|
+
var candidate = candidates[i];
|
|
47
|
+
if (typeof candidate === 'string' && candidate.trim() !== '') {
|
|
48
|
+
return candidate.trim().replace(/\\s+/g, ' ').slice(0, 120);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
var selector = 'a[href], button, input, textarea, select, summary, [role], [onclick], [contenteditable=""], [contenteditable="true"], h1, h2, h3';
|
|
55
|
+
var found = document.querySelectorAll(selector);
|
|
56
|
+
var elements = [];
|
|
57
|
+
for (var i = 0; i < found.length && elements.length < MAX; i++) {
|
|
58
|
+
var el = found[i];
|
|
59
|
+
if (!visible(el)) continue;
|
|
60
|
+
var role = roleOf(el);
|
|
61
|
+
var name = nameOf(el);
|
|
62
|
+
// A nameless non-input is something a caller could never ask for by name, so it is noise.
|
|
63
|
+
if (name === '' && role !== 'textbox' && role !== 'checkbox' && role !== 'file') continue;
|
|
64
|
+
var ref = 'e' + refs.length;
|
|
65
|
+
refs.push(el);
|
|
66
|
+
var entry = { ref: ref, role: role, name: name };
|
|
67
|
+
if (typeof el.value === 'string' && el.value !== '' && role !== 'button') entry.value = el.value.slice(0, 120);
|
|
68
|
+
if (role === 'checkbox' || role === 'radio') entry.value = el.checked ? 'checked' : 'unchecked';
|
|
69
|
+
elements.push(entry);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
url: location.href,
|
|
73
|
+
title: document.title,
|
|
74
|
+
truncated: found.length > 0 && elements.length >= MAX,
|
|
75
|
+
elements: elements
|
|
76
|
+
};
|
|
77
|
+
})()`;
|
|
78
|
+
export const renderPage = (page, truncated = false) => {
|
|
79
|
+
const header = [`Page: ${page.title === "" ? "(untitled)" : page.title}`, page.url];
|
|
80
|
+
if (page.elements.length === 0) {
|
|
81
|
+
return [...header, "", "Nothing on this page can be clicked or typed into — try reading its text instead."].join("\n");
|
|
82
|
+
}
|
|
83
|
+
const rows = page.elements.map((element) => {
|
|
84
|
+
const said = element.name === "" ? "" : ` "${element.name}"`;
|
|
85
|
+
const holds = element.value === undefined || element.value === "" ? "" : ` = "${element.value}"`;
|
|
86
|
+
return `[${element.ref}] ${element.role}${said}${holds}`;
|
|
87
|
+
});
|
|
88
|
+
const note = truncated ? [`(only the first ${MAX_ELEMENTS} are listed — scroll or narrow the page to see more)`] : [];
|
|
89
|
+
return [...header, "", ...rows, ...note].join("\n");
|
|
90
|
+
};
|
|
91
|
+
export const toPageState = (raw) => ({
|
|
92
|
+
url: raw.url ?? "",
|
|
93
|
+
title: raw.title ?? "",
|
|
94
|
+
elements: raw.elements ?? [],
|
|
95
|
+
});
|
|
96
|
+
export const refIndex = (ref) => {
|
|
97
|
+
const match = /^e(\d+)$/.exec(ref.trim());
|
|
98
|
+
return match?.[1] === undefined ? -1 : Number(match[1]);
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAmBA,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB,MAAM,CAAC,MAAM,eAAe,GAAG;cACjB,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0ErB,CAAC;AAMN,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAe,EAAE,SAAS,GAAG,KAAK,EAAU,EAAE;IACrE,MAAM,MAAM,GAAG,CAAC,SAAS,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACpF,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,MAAM,EAAE,EAAE,EAAE,mFAAmF,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3H,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACvC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,GAAG,CAAC;QAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,KAAK,GAAG,CAAC;QACjG,OAAO,IAAI,OAAO,CAAC,GAAG,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,mBAAmB,YAAY,sDAAsD,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtH,OAAO,CAAC,GAAG,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxD,CAAC,CAAC;AAUF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAgB,EAAa,EAAE,CAAC,CAAC;IACzD,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE;IAClB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;IACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE;CAC/B,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAU,EAAE;IAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface PageElement {
|
|
2
|
+
readonly ref: string;
|
|
3
|
+
readonly role: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly value?: string | undefined;
|
|
6
|
+
}
|
|
7
|
+
export interface PageState {
|
|
8
|
+
readonly url: string;
|
|
9
|
+
readonly title: string;
|
|
10
|
+
readonly elements: readonly PageElement[];
|
|
11
|
+
}
|
|
12
|
+
export interface Browser {
|
|
13
|
+
readonly open: (url?: string) => Promise<PageState>;
|
|
14
|
+
readonly snapshot: () => Promise<PageState>;
|
|
15
|
+
readonly click: (ref: string) => Promise<void>;
|
|
16
|
+
readonly fill: (ref: string, text: string, submit?: boolean) => Promise<void>;
|
|
17
|
+
readonly press: (combo: string) => Promise<void>;
|
|
18
|
+
readonly text: () => Promise<string>;
|
|
19
|
+
readonly screenshot: () => Promise<Buffer>;
|
|
20
|
+
readonly tabs: () => Promise<{
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly title: string;
|
|
23
|
+
readonly url: string;
|
|
24
|
+
readonly active: boolean;
|
|
25
|
+
}[]>;
|
|
26
|
+
readonly selectTab: (id: string) => Promise<PageState>;
|
|
27
|
+
readonly disconnect: () => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export declare class BrowserError extends Error {
|
|
30
|
+
readonly hint: string | undefined;
|
|
31
|
+
constructor(message: string, hint?: string);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,SAAS;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,OAAO;IAGpB,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAEpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9E,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC,CAAC;IAChI,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAGvD,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C;AAKD,qBAAa,YAAa,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,YAAY,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAIzC;CACJ"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAoDA,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC1B,IAAI,CAAqB;IAClC,YAAY,OAAe,EAAE,IAAa;QACtC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACrB,CAAC;CACJ"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@intentic/browser",
|
|
3
|
+
"version": "1.176.0",
|
|
4
|
+
"description": "Drive a Chromium browser from Node over CDP — open pages, read them as structured text, click and type by element reference. No dependencies.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://gitlab.com/radarsu/intentic.git",
|
|
10
|
+
"directory": "_libs/browser"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public",
|
|
17
|
+
"registry": "https://registry.npmjs.org/"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": {
|
|
23
|
+
"@intentic/src": "./src/index.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"@intentic/src": "./src/index.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"tslib": "2.8.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "24.13.2",
|
|
37
|
+
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
38
|
+
"vitest": "4.1.10",
|
|
39
|
+
"@intentic/tsconfig": "0.0.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsgo",
|
|
43
|
+
"typecheck": "tsgo --noEmit -p tsconfig.test.json",
|
|
44
|
+
"watch": "tsgo --build --watch",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest"
|
|
47
|
+
}
|
|
48
|
+
}
|