nothing-browser 0.1.3 → 0.1.4
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/package.json +14 -26
- package/piggy/cache/{memory.ts → memory.js} +5 -10
- package/piggy/captcha/{index.ts → index.js} +19 -28
- package/piggy/capture/index.js +40 -0
- package/piggy/client/{index.ts → index.js} +133 -154
- package/piggy/dialog/{index.ts → index.js} +15 -31
- package/piggy/export/index.js +105 -0
- package/piggy/expose/index.js +24 -0
- package/piggy/find/{index.ts → index.js} +24 -39
- package/piggy/http/{index.ts → index.js} +7 -16
- package/piggy/human/{index.ts → index.js} +18 -49
- package/piggy/iframe/index.js +56 -0
- package/piggy/interactions/{index.ts → index.js} +20 -23
- package/piggy/intercept/{scripts.ts → scripts.js} +4 -13
- package/piggy/launch/{detect.ts → detect.js} +4 -6
- package/piggy/launch/{spawn.ts → spawn.js} +17 -22
- package/piggy/media/{index.ts → index.js} +13 -11
- package/piggy/navigation/{index.ts → index.js} +16 -14
- package/piggy/pool/index.js +76 -0
- package/piggy/provide/index.js +97 -0
- package/piggy/proxy/index.js +95 -0
- package/piggy/register/index.js +543 -0
- package/piggy/router/index.js +44 -0
- package/piggy/server/{index.ts → index.js} +16 -64
- package/piggy/session/index.js +69 -0
- package/piggy/store/{index.ts → index.js} +22 -62
- package/piggy/tabs/index.js +24 -0
- package/piggy/wait/index.js +77 -0
- package/{piggy.ts → piggy.js} +122 -146
- package/dist/cache/memory.js +0 -35
- package/dist/client/index.js +0 -1284
- package/dist/human/index.js +0 -82
- package/dist/launch/detect.js +0 -782
- package/dist/launch/spawn.js +0 -915
- package/dist/logger/index.js +0 -733
- package/dist/piggy/cache/memory.d.ts +0 -7
- package/dist/piggy/cache/memory.d.ts.map +0 -1
- package/dist/piggy/captcha/index.d.ts +0 -39
- package/dist/piggy/captcha/index.d.ts.map +0 -1
- package/dist/piggy/capture/index.d.ts +0 -48
- package/dist/piggy/capture/index.d.ts.map +0 -1
- package/dist/piggy/client/index.d.ts +0 -146
- package/dist/piggy/client/index.d.ts.map +0 -1
- package/dist/piggy/dialog/index.d.ts +0 -28
- package/dist/piggy/dialog/index.d.ts.map +0 -1
- package/dist/piggy/export/index.d.ts +0 -62
- package/dist/piggy/export/index.d.ts.map +0 -1
- package/dist/piggy/expose/index.d.ts +0 -6
- package/dist/piggy/expose/index.d.ts.map +0 -1
- package/dist/piggy/find/index.d.ts +0 -52
- package/dist/piggy/find/index.d.ts.map +0 -1
- package/dist/piggy/http/index.d.ts +0 -14
- package/dist/piggy/http/index.d.ts.map +0 -1
- package/dist/piggy/human/index.d.ts +0 -39
- package/dist/piggy/human/index.d.ts.map +0 -1
- package/dist/piggy/iframe/index.d.ts +0 -53
- package/dist/piggy/iframe/index.d.ts.map +0 -1
- package/dist/piggy/interactions/index.d.ts +0 -24
- package/dist/piggy/interactions/index.d.ts.map +0 -1
- package/dist/piggy/intercept/scripts.d.ts +0 -13
- package/dist/piggy/intercept/scripts.d.ts.map +0 -1
- package/dist/piggy/launch/detect.d.ts +0 -3
- package/dist/piggy/launch/detect.d.ts.map +0 -1
- package/dist/piggy/launch/spawn.d.ts +0 -6
- package/dist/piggy/launch/spawn.d.ts.map +0 -1
- package/dist/piggy/logger/index.d.ts +0 -3
- package/dist/piggy/logger/index.d.ts.map +0 -1
- package/dist/piggy/media/index.d.ts +0 -11
- package/dist/piggy/media/index.d.ts.map +0 -1
- package/dist/piggy/navigation/index.d.ts +0 -16
- package/dist/piggy/navigation/index.d.ts.map +0 -1
- package/dist/piggy/pool/index.d.ts +0 -23
- package/dist/piggy/pool/index.d.ts.map +0 -1
- package/dist/piggy/provide/index.d.ts +0 -98
- package/dist/piggy/provide/index.d.ts.map +0 -1
- package/dist/piggy/proxy/index.d.ts +0 -94
- package/dist/piggy/proxy/index.d.ts.map +0 -1
- package/dist/piggy/register/index.d.ts +0 -9
- package/dist/piggy/register/index.d.ts.map +0 -1
- package/dist/piggy/router/index.d.ts +0 -38
- package/dist/piggy/router/index.d.ts.map +0 -1
- package/dist/piggy/server/index.d.ts +0 -42
- package/dist/piggy/server/index.d.ts.map +0 -1
- package/dist/piggy/session/index.d.ts +0 -27
- package/dist/piggy/session/index.d.ts.map +0 -1
- package/dist/piggy/store/index.d.ts +0 -22
- package/dist/piggy/store/index.d.ts.map +0 -1
- package/dist/piggy/tabs/index.d.ts +0 -12
- package/dist/piggy/tabs/index.d.ts.map +0 -1
- package/dist/piggy/wait/index.d.ts +0 -28
- package/dist/piggy/wait/index.d.ts.map +0 -1
- package/dist/piggy.d.ts +0 -10
- package/dist/piggy.d.ts.map +0 -1
- package/dist/piggy.js +0 -30223
- package/dist/register/index.js +0 -23710
- package/dist/server/index.js +0 -26831
- package/piggy/capture/index.ts +0 -76
- package/piggy/export/index.d.ts +0 -117
- package/piggy/export/index.ts +0 -147
- package/piggy/expose/index.ts +0 -42
- package/piggy/iframe/index.ts +0 -79
- package/piggy/intercept/scripts.d.ts +0 -13
- package/piggy/intercept/scripts.d.ts.map +0 -1
- package/piggy/launch/detect.d.ts +0 -3
- package/piggy/launch/detect.d.ts.map +0 -1
- package/piggy/launch/spawn.d.ts +0 -6
- package/piggy/launch/spawn.d.ts.map +0 -1
- package/piggy/logger/index.d.ts +0 -3
- package/piggy/logger/index.d.ts.map +0 -1
- package/piggy/pool/index.d.ts +0 -12
- package/piggy/pool/index.ts +0 -75
- package/piggy/provide/index.ts +0 -170
- package/piggy/proxy/index.ts +0 -154
- package/piggy/register/index.ts +0 -575
- package/piggy/router/index.ts +0 -69
- package/piggy/session/index.ts +0 -76
- package/piggy/store/index.d.ts +0 -26
- package/piggy/tabs/index.ts +0 -23
- package/piggy/wait/index.ts +0 -90
- /package/piggy/logger/{index.ts → index.js} +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// piggy/export/index.js
|
|
2
|
+
import { PiggyClient } from "../client.js";
|
|
3
|
+
|
|
4
|
+
// ─── ExportClient ─────────────────────────────────────────────────────────────
|
|
5
|
+
export class ExportClient {
|
|
6
|
+
constructor(client) {
|
|
7
|
+
this.client = client;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// DOM fetch
|
|
11
|
+
searchCss(query, tabId = "default") {
|
|
12
|
+
return this.client.send("search.css", { query, tabId });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
searchId(query, tabId = "default") {
|
|
16
|
+
return this.client.send("search.id", { query, tabId });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Cookies
|
|
20
|
+
setCookie(opts, tabId = "default") {
|
|
21
|
+
return this.client.send("cookie.set", { ...opts, tabId });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
deleteCookie(opts, tabId = "default") {
|
|
25
|
+
return this.client.send("cookie.delete", { ...opts, tabId });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Session
|
|
29
|
+
sessionReload(tabId = "default") {
|
|
30
|
+
return this.client.send("session.reload", { tabId });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
cookiesPath() {
|
|
34
|
+
return this.client.send("session.cookies.path", {});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
profilePath() {
|
|
38
|
+
return this.client.send("session.profile.path", {});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
wsPath() {
|
|
42
|
+
return this.client.send("session.ws.path", {});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pingsPath() {
|
|
46
|
+
return this.client.send("session.pings.path", {});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
sessionPaths() {
|
|
50
|
+
return this.client.send("session.paths", {});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setWsSave(enabled) {
|
|
54
|
+
return this.client.send("session.ws.save", { enabled });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setPingsSave(enabled) {
|
|
58
|
+
return this.client.send("session.pings.save", { enabled });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Intercept rules
|
|
62
|
+
addInterceptRule(rule, tabId = "default") {
|
|
63
|
+
return this.client.send("intercept.rule.add", { ...rule, tabId });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
clearInterceptRules(tabId = "default") {
|
|
67
|
+
return this.client.send("intercept.rule.clear", { tabId });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Session export / import
|
|
71
|
+
async exportSession(tabId = "default") {
|
|
72
|
+
const raw = await this.client.send("session.export", { tabId });
|
|
73
|
+
return typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
importSession(data, tabId = "default") {
|
|
77
|
+
return this.client.send("session.import", {
|
|
78
|
+
data: JSON.stringify(data),
|
|
79
|
+
tabId,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Exposed functions
|
|
84
|
+
exposeFunction(name, tabId = "default") {
|
|
85
|
+
return this.client.send("expose.function", { name, tabId });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
resolveExposed(callId, result, isError = false, tabId = "default") {
|
|
89
|
+
return this.client.send("exposed.result", { callId, result, isError, tabId });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Init scripts
|
|
93
|
+
addInitScript(js, tabId = "default") {
|
|
94
|
+
return this.client.send("addInitScript", { js, tabId });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Events
|
|
98
|
+
onExposedFunctionCalled(tabId, handler) {
|
|
99
|
+
return this.client.onEvent("exposed_call", tabId, handler);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function createExportAPI(client) {
|
|
104
|
+
return new ExportClient(client);
|
|
105
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PiggyClient } from "../client.js";
|
|
2
|
+
import logger from "../logger.js";
|
|
3
|
+
|
|
4
|
+
export async function exposeFunction(client, fnName, handler, tabId) {
|
|
5
|
+
await client.exposeFunction(fnName, handler, tabId);
|
|
6
|
+
logger.success(`[${tabId}] exposed function: ${fnName}`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function unexposeFunction(client, fnName, tabId) {
|
|
10
|
+
await client.unexposeFunction(fnName, tabId);
|
|
11
|
+
logger.info(`[${tabId}] unexposed function: ${fnName}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function clearExposedFunctions(client, tabId) {
|
|
15
|
+
await client.clearExposedFunctions(tabId);
|
|
16
|
+
logger.info(`[${tabId}] cleared all exposed functions`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function exposeAndInject(client, fnName, handler, injectionJs, tabId) {
|
|
20
|
+
await client.exposeFunction(fnName, handler, tabId);
|
|
21
|
+
const js = typeof injectionJs === "function" ? injectionJs(fnName) : injectionJs;
|
|
22
|
+
await client.evaluate(js, tabId);
|
|
23
|
+
logger.success(`[${tabId}] exposed and injected: ${fnName}`);
|
|
24
|
+
}
|
|
@@ -1,22 +1,5 @@
|
|
|
1
|
-
// piggy/find/index.
|
|
2
|
-
import { PiggyClient } from "../client";
|
|
3
|
-
|
|
4
|
-
// ─── Element descriptor ───────────────────────────────────────────────────────
|
|
5
|
-
// Mirrors __nb_serialize() in PiggyFind.cpp
|
|
6
|
-
|
|
7
|
-
export interface ElementDescriptor {
|
|
8
|
-
tag: string;
|
|
9
|
-
id: string;
|
|
10
|
-
cls: string;
|
|
11
|
-
/** First 400 chars of innerText */
|
|
12
|
-
text: string;
|
|
13
|
-
/** First 800 chars of innerHTML */
|
|
14
|
-
html: string;
|
|
15
|
-
href: string;
|
|
16
|
-
src: string;
|
|
17
|
-
value: string;
|
|
18
|
-
attrs: Record<string, string>;
|
|
19
|
-
}
|
|
1
|
+
// piggy/find/index.js
|
|
2
|
+
import { PiggyClient } from "../client.js";
|
|
20
3
|
|
|
21
4
|
// ─── FindClient ───────────────────────────────────────────────────────────────
|
|
22
5
|
// find answers ONE question: "is this thing here, and where?"
|
|
@@ -24,95 +7,97 @@ export interface ElementDescriptor {
|
|
|
24
7
|
// If you need a value out of an element, use provide instead.
|
|
25
8
|
|
|
26
9
|
export class FindClient {
|
|
27
|
-
constructor(
|
|
10
|
+
constructor(client) {
|
|
11
|
+
this.client = client;
|
|
12
|
+
}
|
|
28
13
|
|
|
29
|
-
// ── Multi-result
|
|
14
|
+
// ── Multi-result ──────────────────────────────────────────────────────────
|
|
30
15
|
|
|
31
16
|
/** querySelectorAll — returns all matching elements. */
|
|
32
|
-
css(selector
|
|
17
|
+
css(selector, tabId = "default") {
|
|
33
18
|
return this.client.send("find.css", { selector, tabId });
|
|
34
19
|
}
|
|
35
20
|
|
|
36
21
|
/** Alias for css(). */
|
|
37
|
-
all(selector
|
|
22
|
+
all(selector, tabId = "default") {
|
|
38
23
|
return this.client.send("find.all", { selector, tabId });
|
|
39
24
|
}
|
|
40
25
|
|
|
41
26
|
/** querySelector — returns a single-element array or []. */
|
|
42
|
-
first(selector
|
|
27
|
+
first(selector, tabId = "default") {
|
|
43
28
|
return this.client.send("find.first", { selector, tabId });
|
|
44
29
|
}
|
|
45
30
|
|
|
46
31
|
/** Find elements whose innerText contains the given text. */
|
|
47
|
-
byText(text
|
|
32
|
+
byText(text, tabId = "default") {
|
|
48
33
|
return this.client.send("find.byText", { text, tabId });
|
|
49
34
|
}
|
|
50
35
|
|
|
51
36
|
/** Find elements by attribute name (and optional value). */
|
|
52
|
-
byAttr(attr
|
|
37
|
+
byAttr(attr, value, tabId = "default") {
|
|
53
38
|
return this.client.send("find.byAttr", { attr, value, tabId });
|
|
54
39
|
}
|
|
55
40
|
|
|
56
41
|
/** getElementsByTagName. */
|
|
57
|
-
byTag(tag
|
|
42
|
+
byTag(tag, tabId = "default") {
|
|
58
43
|
return this.client.send("find.byTag", { tag, tabId });
|
|
59
44
|
}
|
|
60
45
|
|
|
61
46
|
/** Find inputs/textareas whose placeholder contains the given text. */
|
|
62
|
-
byPlaceholder(text
|
|
47
|
+
byPlaceholder(text, tabId = "default") {
|
|
63
48
|
return this.client.send("find.byPlaceholder", { text, tabId });
|
|
64
49
|
}
|
|
65
50
|
|
|
66
51
|
/** Find elements by ARIA role, optionally filtered by accessible name. */
|
|
67
|
-
byRole(role
|
|
52
|
+
byRole(role, name, tabId = "default") {
|
|
68
53
|
return this.client.send("find.byRole", { role, name, tabId });
|
|
69
54
|
}
|
|
70
55
|
|
|
71
56
|
/** Direct children of the matched element. */
|
|
72
|
-
children(selector
|
|
57
|
+
children(selector, tabId = "default") {
|
|
73
58
|
return this.client.send("find.children", { selector, tabId });
|
|
74
59
|
}
|
|
75
60
|
|
|
76
61
|
/** parentElement of the matched element. */
|
|
77
|
-
parent(selector
|
|
62
|
+
parent(selector, tabId = "default") {
|
|
78
63
|
return this.client.send("find.parent", { selector, tabId });
|
|
79
64
|
}
|
|
80
65
|
|
|
81
66
|
/** Walk up the DOM from selector until ancestor matches. */
|
|
82
|
-
closest(selector
|
|
67
|
+
closest(selector, ancestor, tabId = "default") {
|
|
83
68
|
return this.client.send("find.closest", { selector, ancestor, tabId });
|
|
84
69
|
}
|
|
85
70
|
|
|
86
|
-
// ── Boolean / numeric
|
|
71
|
+
// ── Boolean / numeric ─────────────────────────────────────────────────────
|
|
87
72
|
|
|
88
73
|
/** Number of elements matching the selector. */
|
|
89
|
-
count(selector
|
|
74
|
+
count(selector, tabId = "default") {
|
|
90
75
|
return this.client.send("find.count", { selector, tabId });
|
|
91
76
|
}
|
|
92
77
|
|
|
93
78
|
/** True if at least one element matches. */
|
|
94
|
-
exists(selector
|
|
79
|
+
exists(selector, tabId = "default") {
|
|
95
80
|
return this.client.send("find.exists", { selector, tabId });
|
|
96
81
|
}
|
|
97
82
|
|
|
98
83
|
/** True if the first matched element is visible. */
|
|
99
|
-
visible(selector
|
|
84
|
+
visible(selector, tabId = "default") {
|
|
100
85
|
return this.client.send("find.visible", { selector, tabId });
|
|
101
86
|
}
|
|
102
87
|
|
|
103
88
|
/** True if the first matched element is not disabled. */
|
|
104
|
-
enabled(selector
|
|
89
|
+
enabled(selector, tabId = "default") {
|
|
105
90
|
return this.client.send("find.enabled", { selector, tabId });
|
|
106
91
|
}
|
|
107
92
|
|
|
108
93
|
/** True if the first matched checkbox/radio is checked. */
|
|
109
|
-
checked(selector
|
|
94
|
+
checked(selector, tabId = "default") {
|
|
110
95
|
return this.client.send("find.checked", { selector, tabId });
|
|
111
96
|
}
|
|
112
97
|
}
|
|
113
98
|
|
|
114
99
|
// ─── Factory helper ───────────────────────────────────────────────────────────
|
|
115
100
|
|
|
116
|
-
export function createFindAPI(client
|
|
101
|
+
export function createFindAPI(client) {
|
|
117
102
|
return new FindClient(client);
|
|
118
103
|
}
|
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
// piggy/http/index.
|
|
1
|
+
// piggy/http/index.js
|
|
2
2
|
// Mirrors PiggyHttp.cpp — connects to the HTTP server on port 2005.
|
|
3
3
|
// Use this when you want to talk to the browser over HTTP instead of the unix socket.
|
|
4
4
|
|
|
5
|
-
export interface HttpClientOptions {
|
|
6
|
-
host?: string;
|
|
7
|
-
port?: number;
|
|
8
|
-
key: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
5
|
export class PiggyHttpClient {
|
|
12
|
-
|
|
13
|
-
private key: string;
|
|
14
|
-
|
|
15
|
-
constructor(opts: HttpClientOptions) {
|
|
6
|
+
constructor(opts) {
|
|
16
7
|
const host = opts.host ?? "localhost";
|
|
17
8
|
const port = opts.port ?? 2005;
|
|
18
9
|
this.baseUrl = `http://${host}:${port}`;
|
|
@@ -21,7 +12,7 @@ export class PiggyHttpClient {
|
|
|
21
12
|
|
|
22
13
|
// ── Health check ──────────────────────────────────────────────────────────
|
|
23
14
|
|
|
24
|
-
async ping()
|
|
15
|
+
async ping() {
|
|
25
16
|
try {
|
|
26
17
|
const res = await fetch(this.baseUrl, {
|
|
27
18
|
method: "POST",
|
|
@@ -40,7 +31,7 @@ export class PiggyHttpClient {
|
|
|
40
31
|
|
|
41
32
|
// ── Send command ──────────────────────────────────────────────────────────
|
|
42
33
|
|
|
43
|
-
async send
|
|
34
|
+
async send(cmd, payload = {}) {
|
|
44
35
|
const res = await fetch(this.baseUrl, {
|
|
45
36
|
method: "POST",
|
|
46
37
|
headers: {
|
|
@@ -54,12 +45,12 @@ export class PiggyHttpClient {
|
|
|
54
45
|
if (res.status === 400) throw new Error("Bad request — invalid JSON");
|
|
55
46
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
56
47
|
|
|
57
|
-
const data = await res.json()
|
|
48
|
+
const data = await res.json();
|
|
58
49
|
if (!data.ok) throw new Error(String(data.data) ?? "command failed");
|
|
59
50
|
return data.data;
|
|
60
51
|
}
|
|
61
52
|
}
|
|
62
53
|
|
|
63
|
-
export function createHttpClient(opts
|
|
54
|
+
export function createHttpClient(opts) {
|
|
64
55
|
return new PiggyHttpClient(opts);
|
|
65
|
-
}
|
|
56
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
// piggy/human/index.
|
|
2
|
-
import { PiggyClient } from "../client";
|
|
1
|
+
// piggy/human/index.js
|
|
2
|
+
import { PiggyClient } from "../client.js";
|
|
3
3
|
|
|
4
4
|
// ─── Local utilities ──────────────────────────────────────────────────────────
|
|
5
5
|
|
|
6
|
-
export function randomDelay(min
|
|
6
|
+
export function randomDelay(min, max) {
|
|
7
7
|
return new Promise(r => setTimeout(r, Math.floor(Math.random() * (max - min + 1)) + min));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export function humanTypeSequence(text
|
|
11
|
-
const adjacent
|
|
10
|
+
export function humanTypeSequence(text) {
|
|
11
|
+
const adjacent = {
|
|
12
12
|
a: ["q","w","s","z"], b: ["v","g","h","n"], c: ["x","d","f","v"],
|
|
13
13
|
d: ["s","e","r","f","c","x"], e: ["w","r","d","s"],
|
|
14
14
|
f: ["d","r","t","g","v","c"], g: ["f","t","y","h","b","v"],
|
|
@@ -23,8 +23,8 @@ export function humanTypeSequence(text: string): string[] {
|
|
|
23
23
|
z: ["a","s","x"],
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const actions
|
|
27
|
-
const typoIndices = new Set
|
|
26
|
+
const actions = [];
|
|
27
|
+
const typoIndices = new Set();
|
|
28
28
|
|
|
29
29
|
if (text.length > 4) {
|
|
30
30
|
let tries = 0;
|
|
@@ -36,7 +36,7 @@ export function humanTypeSequence(text: string): string[] {
|
|
|
36
36
|
|
|
37
37
|
for (let i = 0; i < text.length; i++) {
|
|
38
38
|
if (typoIndices.has(i)) {
|
|
39
|
-
const ch = text[i]
|
|
39
|
+
const ch = text[i].toLowerCase();
|
|
40
40
|
const neighbors = adjacent[ch];
|
|
41
41
|
const typo = neighbors
|
|
42
42
|
? neighbors[Math.floor(Math.random() * neighbors.length)] ?? ch
|
|
@@ -44,72 +44,41 @@ export function humanTypeSequence(text: string): string[] {
|
|
|
44
44
|
actions.push(typo);
|
|
45
45
|
actions.push("BACKSPACE");
|
|
46
46
|
}
|
|
47
|
-
actions.push(text[i]
|
|
47
|
+
actions.push(text[i]);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
return actions;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
// ─── Profile types ────────────────────────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
export type TypingSpeed = "slow" | "normal" | "fast";
|
|
56
|
-
export type ClickDelay = "cautious" | "normal" | "fast";
|
|
57
|
-
export type ScrollSpeed = "slow" | "normal" | "fast";
|
|
58
|
-
|
|
59
|
-
export interface HumanProfile {
|
|
60
|
-
typingSpeed: TypingSpeed;
|
|
61
|
-
clickDelay: ClickDelay;
|
|
62
|
-
scrollSpeed: ScrollSpeed;
|
|
63
|
-
mouseWiggle: boolean;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface HumanSetOptions {
|
|
67
|
-
typingSpeed?: TypingSpeed;
|
|
68
|
-
clickDelay?: ClickDelay;
|
|
69
|
-
scrollSpeed?: ScrollSpeed;
|
|
70
|
-
mouseWiggle?: boolean;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export interface HumanTypeOptions {
|
|
74
|
-
selector: string;
|
|
75
|
-
text: string;
|
|
76
|
-
clear?: boolean;
|
|
77
|
-
speed?: number;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export interface HumanClickOptions {
|
|
81
|
-
selector: string;
|
|
82
|
-
force?: boolean;
|
|
83
|
-
delay?: number;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
53
|
// ─── HumanClient ─────────────────────────────────────────────────────────────
|
|
87
54
|
// Maps 1:1 to PiggyHuman.cpp commands
|
|
88
55
|
|
|
89
56
|
export class HumanClient {
|
|
90
|
-
constructor(
|
|
57
|
+
constructor(client) {
|
|
58
|
+
this.client = client;
|
|
59
|
+
}
|
|
91
60
|
|
|
92
61
|
// human.set — update global profile
|
|
93
|
-
set(opts
|
|
62
|
+
set(opts, tabId = "default") {
|
|
94
63
|
return this.client.send("human.set", { ...opts, tabId });
|
|
95
64
|
}
|
|
96
65
|
|
|
97
66
|
// human.get — read current profile
|
|
98
|
-
get(tabId = "default")
|
|
67
|
+
get(tabId = "default") {
|
|
99
68
|
return this.client.send("human.get", { tabId });
|
|
100
69
|
}
|
|
101
70
|
|
|
102
71
|
// human.type — char-by-char typing with realistic delays
|
|
103
|
-
type(opts
|
|
72
|
+
type(opts, tabId = "default") {
|
|
104
73
|
return this.client.send("human.type", { ...opts, tabId });
|
|
105
74
|
}
|
|
106
75
|
|
|
107
76
|
// human.click — delayed click with optional force dispatch
|
|
108
|
-
click(opts
|
|
77
|
+
click(opts, tabId = "default") {
|
|
109
78
|
return this.client.send("human.click", { ...opts, tabId });
|
|
110
79
|
}
|
|
111
80
|
}
|
|
112
81
|
|
|
113
|
-
export function createHumanAPI(client
|
|
82
|
+
export function createHumanAPI(client) {
|
|
114
83
|
return new HumanClient(client);
|
|
115
|
-
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// piggy/iframe/index.js
|
|
2
|
+
import { PiggyClient } from "../client.js";
|
|
3
|
+
|
|
4
|
+
// ─── IframeClient ─────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export class IframeClient {
|
|
7
|
+
constructor(client) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** List all iframes on the page: index, src, id, name. */
|
|
12
|
+
list(tabId = "default") {
|
|
13
|
+
return this.client.send("iframe.list", { tabId });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Run arbitrary JS inside the targeted iframe. Returns whatever the script returns. */
|
|
17
|
+
evaluate(opts, tabId = "default") {
|
|
18
|
+
return this.client.send("iframe.evaluate", { ...opts, tabId });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Click a selector inside the targeted iframe. */
|
|
22
|
+
click(opts, tabId = "default") {
|
|
23
|
+
return this.client.send("iframe.click", { ...opts, tabId });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Type text into a selector inside the targeted iframe. */
|
|
27
|
+
type(opts, tabId = "default") {
|
|
28
|
+
return this.client.send("iframe.type", { ...opts, tabId });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Get innerText of a selector inside the targeted iframe. */
|
|
32
|
+
text(opts, tabId = "default") {
|
|
33
|
+
return this.client.send("iframe.text", { ...opts, tabId });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Get the full HTML of the targeted iframe. */
|
|
37
|
+
html(opts, tabId = "default") {
|
|
38
|
+
return this.client.send("iframe.html", { ...opts, tabId });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Wait until a selector appears inside the targeted iframe. */
|
|
42
|
+
waitSel(opts, tabId = "default") {
|
|
43
|
+
return this.client.send("iframe.waitSel", { ...opts, tabId });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── Factory helper ───────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
export function createIframeAPI(client) {
|
|
50
|
+
return new IframeClient(client);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Usage:
|
|
54
|
+
// piggy.google.iframe.list()
|
|
55
|
+
// piggy.google.iframe.click({ index: 0, sel: "#btn" })
|
|
56
|
+
// piggy.google.iframe.evaluate({ src: "https://...", js: "document.title" })
|
|
@@ -1,79 +1,76 @@
|
|
|
1
|
-
// piggy/interactions/index.
|
|
2
|
-
import { PiggyClient } from "../client";
|
|
3
|
-
|
|
4
|
-
export interface MousePosition {
|
|
5
|
-
x: number;
|
|
6
|
-
y: number;
|
|
7
|
-
}
|
|
1
|
+
// piggy/interactions/index.js
|
|
2
|
+
import { PiggyClient } from "../client.js";
|
|
8
3
|
|
|
9
4
|
export class InteractionsClient {
|
|
10
|
-
constructor(
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
11
8
|
|
|
12
9
|
// ── Click ─────────────────────────────────────────────────────────────────
|
|
13
10
|
|
|
14
|
-
click(selector
|
|
11
|
+
click(selector, tabId = "default") {
|
|
15
12
|
return this.client.send("click", { selector, tabId });
|
|
16
13
|
}
|
|
17
14
|
|
|
18
|
-
dblclick(selector
|
|
15
|
+
dblclick(selector, tabId = "default") {
|
|
19
16
|
return this.client.send("dblclick", { selector, tabId });
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
hover(selector
|
|
19
|
+
hover(selector, tabId = "default") {
|
|
23
20
|
return this.client.send("hover", { selector, tabId });
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
// ── Input ─────────────────────────────────────────────────────────────────
|
|
27
24
|
|
|
28
|
-
type(selector
|
|
25
|
+
type(selector, text, tabId = "default") {
|
|
29
26
|
return this.client.send("type", { selector, text, tabId });
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
typeClear(selector
|
|
29
|
+
typeClear(selector, text, tabId = "default") {
|
|
33
30
|
return this.client.send("type", { selector, text, clear: true, tabId });
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
select(selector
|
|
33
|
+
select(selector, value, tabId = "default") {
|
|
37
34
|
return this.client.send("select", { selector, value, tabId });
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
// ── Scroll ────────────────────────────────────────────────────────────────
|
|
41
38
|
|
|
42
|
-
scrollTo(selector
|
|
39
|
+
scrollTo(selector, tabId = "default") {
|
|
43
40
|
return this.client.send("scroll.to", { selector, tabId });
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
scrollBy(px
|
|
43
|
+
scrollBy(px, tabId = "default") {
|
|
47
44
|
return this.client.send("scroll.by", { px, tabId });
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
// ── Keyboard ──────────────────────────────────────────────────────────────
|
|
51
48
|
|
|
52
|
-
keyPress(key
|
|
49
|
+
keyPress(key, tabId = "default") {
|
|
53
50
|
return this.client.send("keyboard.press", { key, tabId });
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
keyCombo(combo
|
|
53
|
+
keyCombo(combo, tabId = "default") {
|
|
57
54
|
return this.client.send("keyboard.combo", { combo, tabId });
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
// ── Mouse ─────────────────────────────────────────────────────────────────
|
|
61
58
|
|
|
62
|
-
mouseMove(x
|
|
59
|
+
mouseMove(x, y, tabId = "default") {
|
|
63
60
|
return this.client.send("mouse.move", { x, y, tabId });
|
|
64
61
|
}
|
|
65
62
|
|
|
66
|
-
mouseDrag(from
|
|
63
|
+
mouseDrag(from, to, tabId = "default") {
|
|
67
64
|
return this.client.send("mouse.drag", { from, to, tabId });
|
|
68
65
|
}
|
|
69
66
|
|
|
70
67
|
// ── Evaluate ──────────────────────────────────────────────────────────────
|
|
71
68
|
|
|
72
|
-
evaluate(js
|
|
69
|
+
evaluate(js, tabId = "default") {
|
|
73
70
|
return this.client.send("evaluate", { js, tabId });
|
|
74
71
|
}
|
|
75
72
|
}
|
|
76
73
|
|
|
77
|
-
export function createInteractionsAPI(client
|
|
74
|
+
export function createInteractionsAPI(client) {
|
|
78
75
|
return new InteractionsClient(client);
|
|
79
|
-
}
|
|
76
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// piggy/intercept/scripts.
|
|
1
|
+
// piggy/intercept/scripts.js
|
|
2
2
|
// JS injection helpers for intercept.respond and intercept.modifyResponse.
|
|
3
3
|
// Both work purely in the browser's JS layer — no C++ changes needed.
|
|
4
4
|
|
|
@@ -6,12 +6,7 @@
|
|
|
6
6
|
* Generates a script that short-circuits matching fetch/XHR requests
|
|
7
7
|
* and returns a static fake response — the request never hits the network.
|
|
8
8
|
*/
|
|
9
|
-
export function buildRespondScript(
|
|
10
|
-
pattern: string,
|
|
11
|
-
status: number,
|
|
12
|
-
contentType: string,
|
|
13
|
-
body: string
|
|
14
|
-
): string {
|
|
9
|
+
export function buildRespondScript(pattern, status, contentType, body) {
|
|
15
10
|
const safePattern = pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
16
11
|
const safeBody = body.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
17
12
|
const safeContentType = contentType.replace(/'/g, "\\'");
|
|
@@ -32,7 +27,6 @@ export function buildRespondScript(
|
|
|
32
27
|
catch { return url.includes(pattern); }
|
|
33
28
|
}
|
|
34
29
|
|
|
35
|
-
// Only install wrappers once per page
|
|
36
30
|
if (window.__PIGGY_RESPOND_INSTALLED__) return;
|
|
37
31
|
window.__PIGGY_RESPOND_INSTALLED__ = true;
|
|
38
32
|
|
|
@@ -92,7 +86,7 @@ export function buildRespondScript(
|
|
|
92
86
|
* The exposed function returns { body?, status?, headers? } modifications
|
|
93
87
|
* or an empty object {} to pass through unchanged.
|
|
94
88
|
*/
|
|
95
|
-
export function buildModifyResponseScript(pattern
|
|
89
|
+
export function buildModifyResponseScript(pattern, exposedFnName) {
|
|
96
90
|
const safePattern = pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
97
91
|
const safeFnName = exposedFnName.replace(/'/g, "\\'");
|
|
98
92
|
|
|
@@ -107,7 +101,6 @@ export function buildModifyResponseScript(pattern: string, exposedFnName: string
|
|
|
107
101
|
catch { return url.includes(pattern); }
|
|
108
102
|
}
|
|
109
103
|
|
|
110
|
-
// Only install wrappers once per page
|
|
111
104
|
if (window.__PIGGY_MODIFY_INSTALLED__) return;
|
|
112
105
|
window.__PIGGY_MODIFY_INSTALLED__ = true;
|
|
113
106
|
|
|
@@ -121,7 +114,6 @@ export function buildModifyResponseScript(pattern: string, exposedFnName: string
|
|
|
121
114
|
if (_piggyMatchUrl(url, rule.pattern)) { matchedFn = rule.fn; break; }
|
|
122
115
|
}
|
|
123
116
|
|
|
124
|
-
// No match — pass through untouched
|
|
125
117
|
const resp = await _origFetch.apply(this, arguments);
|
|
126
118
|
if (!matchedFn) return resp;
|
|
127
119
|
|
|
@@ -133,7 +125,6 @@ export function buildModifyResponseScript(pattern: string, exposedFnName: string
|
|
|
133
125
|
const handlerFn = window[matchedFn];
|
|
134
126
|
if (typeof handlerFn !== 'function') return resp;
|
|
135
127
|
|
|
136
|
-
// Call Node.js handler via exposeFunction bridge
|
|
137
128
|
const mod = await handlerFn({ body: bodyText, status: resp.status, headers });
|
|
138
129
|
if (!mod || typeof mod !== 'object' || Object.keys(mod).length === 0) return resp;
|
|
139
130
|
|
|
@@ -145,7 +136,7 @@ export function buildModifyResponseScript(pattern: string, exposedFnName: string
|
|
|
145
136
|
}
|
|
146
137
|
);
|
|
147
138
|
} catch {
|
|
148
|
-
return resp;
|
|
139
|
+
return resp;
|
|
149
140
|
}
|
|
150
141
|
};
|
|
151
142
|
})();
|