nothing-browser 0.0.21 → 0.1.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 +1 -1
- package/dist/human/index.js +25 -1
- package/dist/piggy/captcha/index.d.ts +39 -0
- package/dist/piggy/captcha/index.d.ts.map +1 -0
- package/dist/piggy/capture/index.d.ts +48 -0
- package/dist/piggy/capture/index.d.ts.map +1 -0
- package/dist/piggy/dialog/index.d.ts +28 -0
- package/dist/piggy/dialog/index.d.ts.map +1 -0
- package/dist/piggy/export/index.d.ts +62 -0
- package/dist/piggy/export/index.d.ts.map +1 -0
- package/dist/piggy/find/index.d.ts +90 -0
- package/dist/piggy/find/index.d.ts.map +1 -0
- package/dist/piggy/http/index.d.ts +14 -0
- package/dist/piggy/http/index.d.ts.map +1 -0
- package/dist/piggy/human/index.d.ts +36 -4
- package/dist/piggy/human/index.d.ts.map +1 -1
- package/dist/piggy/iframe/index.d.ts +53 -0
- package/dist/piggy/iframe/index.d.ts.map +1 -0
- package/dist/piggy/interactions/index.d.ts +24 -0
- package/dist/piggy/interactions/index.d.ts.map +1 -0
- package/dist/piggy/media/index.d.ts +11 -0
- package/dist/piggy/media/index.d.ts.map +1 -0
- package/dist/piggy/navigation/index.d.ts +16 -0
- package/dist/piggy/navigation/index.d.ts.map +1 -0
- package/dist/piggy/provide/index.d.ts +81 -0
- package/dist/piggy/provide/index.d.ts.map +1 -0
- package/dist/piggy/proxy/index.d.ts +94 -0
- package/dist/piggy/proxy/index.d.ts.map +1 -0
- package/dist/piggy/register/index.d.ts.map +1 -1
- package/dist/piggy/router/index.d.ts +38 -0
- package/dist/piggy/router/index.d.ts.map +1 -0
- package/dist/piggy/session/index.d.ts +27 -0
- package/dist/piggy/session/index.d.ts.map +1 -0
- package/dist/piggy/tabs/index.d.ts +10 -0
- package/dist/piggy/tabs/index.d.ts.map +1 -0
- package/dist/piggy/wait/index.d.ts +28 -0
- package/dist/piggy/wait/index.d.ts.map +1 -0
- package/dist/piggy.d.ts.map +1 -1
- package/dist/piggy.js +901 -181
- package/dist/register/index.js +39 -86
- package/package.json +1 -1
- package/piggy/captcha/index.d.ts +35 -0
- package/piggy/captcha/index.ts +93 -0
- package/piggy/capture/index.ts +76 -0
- package/piggy/dialog/index.d.ts +29 -0
- package/piggy/dialog/index.ts +85 -0
- package/piggy/export/index.d.ts +117 -0
- package/piggy/export/index.ts +147 -0
- package/piggy/find/index.d.ts +79 -0
- package/piggy/find/index.ts +165 -0
- package/piggy/http/index.ts +65 -0
- package/piggy/human/index.ts +115 -53
- package/piggy/iframe/index.ts +79 -0
- package/piggy/interactions/index.ts +79 -0
- package/piggy/media/index.ts +46 -0
- package/piggy/navigation/index.ts +52 -0
- package/piggy/provide/index.ts +144 -0
- package/piggy/proxy/index.ts +154 -0
- package/piggy/register/index.ts +41 -59
- package/piggy/router/index.ts +69 -0
- package/piggy/session/index.ts +76 -0
- package/piggy/tabs/index.ts +22 -0
- package/piggy/wait/index.ts +90 -0
- package/piggy.ts +94 -39
- package/dist/piggy/open/index.d.ts +0 -4
- package/dist/piggy/open/index.d.ts.map +0 -1
- package/piggy/open/index.d.ts +0 -4
- package/piggy/open/index.d.ts.map +0 -1
- package/piggy/open/index.ts +0 -5
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// piggy/export/index.ts
|
|
2
|
+
import { PiggyClient } from "../client";
|
|
3
|
+
import type { CapturedRequest, WebSocketFrame, CapturedCookie } from "../capture";
|
|
4
|
+
import type { SessionPaths } from "../session";
|
|
5
|
+
|
|
6
|
+
// ─── Intercept ────────────────────────────────────────────────────────────────
|
|
7
|
+
export interface InterceptRule {
|
|
8
|
+
pattern: string;
|
|
9
|
+
block?: boolean;
|
|
10
|
+
redirect?: string;
|
|
11
|
+
setHeaders?: Record<string, string>;
|
|
12
|
+
removeHeaders?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ─── Cookie types (defined here, imported by session) ────────────────────────
|
|
16
|
+
export interface CookieSetOptions {
|
|
17
|
+
name: string;
|
|
18
|
+
value: string;
|
|
19
|
+
domain: string;
|
|
20
|
+
path?: string;
|
|
21
|
+
httpOnly?: boolean;
|
|
22
|
+
secure?: boolean;
|
|
23
|
+
expiry?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CookieDeleteOptions {
|
|
27
|
+
name: string;
|
|
28
|
+
domain: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ─── Session export types (re-exported from capture) ────────────────────────
|
|
32
|
+
export type { CapturedRequest, WebSocketFrame, CapturedCookie, SessionPaths };
|
|
33
|
+
|
|
34
|
+
export interface SessionExport {
|
|
35
|
+
url: string;
|
|
36
|
+
requests: CapturedRequest[];
|
|
37
|
+
ws: WebSocketFrame[];
|
|
38
|
+
cookies: CapturedCookie[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ExposedFunctionCall {
|
|
42
|
+
name: string;
|
|
43
|
+
callId: string;
|
|
44
|
+
data: string;
|
|
45
|
+
tabId: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─── ExportClient ─────────────────────────────────────────────────────────────
|
|
49
|
+
export class ExportClient {
|
|
50
|
+
constructor(private client: PiggyClient) {}
|
|
51
|
+
|
|
52
|
+
// DOM fetch
|
|
53
|
+
searchCss(query: string, tabId = "default"): Promise<any> {
|
|
54
|
+
return this.client.send("search.css", { query, tabId });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
searchId(query: string, tabId = "default"): Promise<any> {
|
|
58
|
+
return this.client.send("search.id", { query, tabId });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Cookies
|
|
62
|
+
setCookie(opts: CookieSetOptions, tabId = "default"): Promise<void> {
|
|
63
|
+
return this.client.send("cookie.set", { ...opts, tabId });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
deleteCookie(opts: CookieDeleteOptions, tabId = "default"): Promise<void> {
|
|
67
|
+
return this.client.send("cookie.delete", { ...opts, tabId });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Session
|
|
71
|
+
sessionReload(tabId = "default"): Promise<void> {
|
|
72
|
+
return this.client.send("session.reload", { tabId });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
cookiesPath(): Promise<string> {
|
|
76
|
+
return this.client.send("session.cookies.path", {});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
profilePath(): Promise<string> {
|
|
80
|
+
return this.client.send("session.profile.path", {});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
wsPath(): Promise<string> {
|
|
84
|
+
return this.client.send("session.ws.path", {});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pingsPath(): Promise<string> {
|
|
88
|
+
return this.client.send("session.pings.path", {});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
sessionPaths(): Promise<SessionPaths> {
|
|
92
|
+
return this.client.send("session.paths", {});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
setWsSave(enabled: boolean): Promise<void> {
|
|
96
|
+
return this.client.send("session.ws.save", { enabled });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setPingsSave(enabled: boolean): Promise<void> {
|
|
100
|
+
return this.client.send("session.pings.save", { enabled });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Intercept rules
|
|
104
|
+
addInterceptRule(rule: InterceptRule, tabId = "default"): Promise<void> {
|
|
105
|
+
return this.client.send("intercept.rule.add", { ...rule, tabId });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
clearInterceptRules(tabId = "default"): Promise<void> {
|
|
109
|
+
return this.client.send("intercept.rule.clear", { tabId });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Session export / import
|
|
113
|
+
async exportSession(tabId = "default"): Promise<SessionExport> {
|
|
114
|
+
const raw = await this.client.send<string>("session.export", { tabId });
|
|
115
|
+
return typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
importSession(data: SessionExport, tabId = "default"): Promise<void> {
|
|
119
|
+
return this.client.send("session.import", {
|
|
120
|
+
data: JSON.stringify(data),
|
|
121
|
+
tabId,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Exposed functions
|
|
126
|
+
exposeFunction(name: string, tabId = "default"): Promise<void> {
|
|
127
|
+
return this.client.send("expose.function", { name, tabId });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
resolveExposed(callId: string, result: string, isError = false, tabId = "default"): Promise<void> {
|
|
131
|
+
return this.client.send("exposed.result", { callId, result, isError, tabId });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Init scripts
|
|
135
|
+
addInitScript(js: string, tabId = "default"): Promise<void> {
|
|
136
|
+
return this.client.send("addInitScript", { js, tabId });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Events
|
|
140
|
+
onExposedFunctionCalled(tabId: string, handler: (call: ExposedFunctionCall) => void): () => void {
|
|
141
|
+
return this.client.onEvent("exposed_call", tabId, handler);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function createExportAPI(client: PiggyClient): ExportClient {
|
|
146
|
+
return new ExportClient(client);
|
|
147
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// piggy/find/index.d.ts
|
|
2
|
+
import { PiggyClient } from "../client";
|
|
3
|
+
|
|
4
|
+
// ─── Element descriptor ───────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface ElementDescriptor {
|
|
7
|
+
tag: string;
|
|
8
|
+
id: string;
|
|
9
|
+
cls: string;
|
|
10
|
+
/** First 400 chars of innerText */
|
|
11
|
+
text: string;
|
|
12
|
+
/** First 800 chars of innerHTML */
|
|
13
|
+
html: string;
|
|
14
|
+
href: string;
|
|
15
|
+
src: string;
|
|
16
|
+
value: string;
|
|
17
|
+
attrs: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ─── Option types ─────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
export interface FindByTextOptions {
|
|
23
|
+
text: string;
|
|
24
|
+
selector?: string;
|
|
25
|
+
exact?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FindByAttrOptions {
|
|
29
|
+
attr: string;
|
|
30
|
+
value?: string;
|
|
31
|
+
selector?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface FindByRoleOptions {
|
|
35
|
+
role: string;
|
|
36
|
+
name?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FindClosestOptions {
|
|
40
|
+
selector: string;
|
|
41
|
+
ancestor: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface FindFilterOptions {
|
|
45
|
+
selector: string;
|
|
46
|
+
attr: string;
|
|
47
|
+
value: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── FindClient ───────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
export declare class FindClient {
|
|
53
|
+
constructor(client: PiggyClient);
|
|
54
|
+
|
|
55
|
+
// Multi-result
|
|
56
|
+
css(selector: string, tabId?: string): Promise<ElementDescriptor[]>;
|
|
57
|
+
all(selector: string, tabId?: string): Promise<ElementDescriptor[]>;
|
|
58
|
+
first(selector: string, tabId?: string): Promise<ElementDescriptor[]>;
|
|
59
|
+
byText(opts: FindByTextOptions, tabId?: string): Promise<ElementDescriptor[]>;
|
|
60
|
+
byAttr(opts: FindByAttrOptions, tabId?: string): Promise<ElementDescriptor[]>;
|
|
61
|
+
byTag(tag: string, tabId?: string): Promise<ElementDescriptor[]>;
|
|
62
|
+
byPlaceholder(text: string, tabId?: string): Promise<ElementDescriptor[]>;
|
|
63
|
+
byRole(opts: FindByRoleOptions, tabId?: string): Promise<ElementDescriptor[]>;
|
|
64
|
+
children(selector: string, tabId?: string): Promise<ElementDescriptor[]>;
|
|
65
|
+
filter(opts: FindFilterOptions, tabId?: string): Promise<ElementDescriptor[]>;
|
|
66
|
+
|
|
67
|
+
// Traversal
|
|
68
|
+
closest(opts: FindClosestOptions, tabId?: string): Promise<ElementDescriptor[]>;
|
|
69
|
+
parent(selector: string, tabId?: string): Promise<ElementDescriptor[]>;
|
|
70
|
+
|
|
71
|
+
// Boolean / numeric
|
|
72
|
+
count(selector: string, tabId?: string): Promise<number>;
|
|
73
|
+
exists(selector: string, tabId?: string): Promise<boolean>;
|
|
74
|
+
visible(selector: string, tabId?: string): Promise<boolean>;
|
|
75
|
+
enabled(selector: string, tabId?: string): Promise<boolean>;
|
|
76
|
+
checked(selector: string, tabId?: string): Promise<boolean>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export declare function createFindAPI(client: PiggyClient): FindClient;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// piggy/find/index.ts
|
|
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
|
+
}
|
|
20
|
+
|
|
21
|
+
// ─── Option types ─────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
export interface FindByTextOptions {
|
|
24
|
+
text: string;
|
|
25
|
+
/** Narrow the search to descendants of this CSS selector. */
|
|
26
|
+
selector?: string;
|
|
27
|
+
/** If true, innerText must match exactly (trimmed). Default: false. */
|
|
28
|
+
exact?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface FindByAttrOptions {
|
|
32
|
+
attr: string;
|
|
33
|
+
/** If omitted, matches any element that has the attribute at all. */
|
|
34
|
+
value?: string;
|
|
35
|
+
/** Optionally scope to a parent selector. */
|
|
36
|
+
selector?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FindByRoleOptions {
|
|
40
|
+
role: string;
|
|
41
|
+
/** Filter by aria-label or innerText containing this string. */
|
|
42
|
+
name?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface FindClosestOptions {
|
|
46
|
+
/** CSS selector for the starting element. */
|
|
47
|
+
selector: string;
|
|
48
|
+
/** CSS selector for the ancestor to climb to. */
|
|
49
|
+
ancestor: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface FindFilterOptions {
|
|
53
|
+
selector: string;
|
|
54
|
+
attr: string;
|
|
55
|
+
value: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── FindClient ───────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
export class FindClient {
|
|
61
|
+
constructor(private client: PiggyClient) {}
|
|
62
|
+
|
|
63
|
+
// ── Multi-result queries ─────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/** querySelectorAll — returns all matching elements. */
|
|
66
|
+
css(selector: string, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
67
|
+
return this.client.send("find.css", { selector, tabId });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Alias for css() — querySelectorAll. */
|
|
71
|
+
all(selector: string, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
72
|
+
return this.client.send("find.all", { selector, tabId });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** querySelector — returns a single-element array or []. */
|
|
76
|
+
first(selector: string, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
77
|
+
return this.client.send("find.first", { selector, tabId });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Find elements whose innerText contains (or exactly matches) the given text. */
|
|
81
|
+
byText(opts: FindByTextOptions, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
82
|
+
return this.client.send("find.byText", { ...opts, tabId });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Find elements by attribute name and optional value. */
|
|
86
|
+
byAttr(opts: FindByAttrOptions, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
87
|
+
return this.client.send("find.byAttr", { ...opts, tabId });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** getElementsByTagName. */
|
|
91
|
+
byTag(tag: string, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
92
|
+
return this.client.send("find.byTag", { tag, tabId });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Find inputs/textareas whose placeholder contains the given text. */
|
|
96
|
+
byPlaceholder(text: string, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
97
|
+
return this.client.send("find.byPlaceholder", { text, tabId });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Find elements by ARIA role, optionally filtered by aria-label / innerText. */
|
|
101
|
+
byRole(opts: FindByRoleOptions, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
102
|
+
return this.client.send("find.byRole", { ...opts, tabId });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Direct children of the matched element. */
|
|
106
|
+
children(selector: string, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
107
|
+
return this.client.send("find.children", { selector, tabId });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Filter querySelectorAll results by attribute value substring.
|
|
112
|
+
* Equivalent to: querySelectorAll(selector).filter(el => el.attr.includes(value))
|
|
113
|
+
*/
|
|
114
|
+
filter(opts: FindFilterOptions, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
115
|
+
return this.client.send("find.filter", { ...opts, tabId });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Single-element traversal ──────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/** Walk up the DOM from selector until ancestor matches. */
|
|
121
|
+
closest(opts: FindClosestOptions, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
122
|
+
return this.client.send("find.closest", { ...opts, tabId });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** parentElement of the matched element. */
|
|
126
|
+
parent(selector: string, tabId = "default"): Promise<ElementDescriptor[]> {
|
|
127
|
+
return this.client.send("find.parent", { selector, tabId });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Boolean / numeric queries ─────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
/** Number of elements matching the selector. */
|
|
133
|
+
count(selector: string, tabId = "default"): Promise<number> {
|
|
134
|
+
return this.client.send("find.count", { selector, tabId });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** True if at least one element matches the selector. */
|
|
138
|
+
exists(selector: string, tabId = "default"): Promise<boolean> {
|
|
139
|
+
return this.client.send("find.exists", { selector, tabId });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* True if the first matched element is visible
|
|
144
|
+
* (display !== none, visibility !== hidden, opacity !== 0).
|
|
145
|
+
*/
|
|
146
|
+
visible(selector: string, tabId = "default"): Promise<boolean> {
|
|
147
|
+
return this.client.send("find.visible", { selector, tabId });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** True if the first matched element is not disabled. */
|
|
151
|
+
enabled(selector: string, tabId = "default"): Promise<boolean> {
|
|
152
|
+
return this.client.send("find.enabled", { selector, tabId });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** True if the first matched checkbox/radio is checked. */
|
|
156
|
+
checked(selector: string, tabId = "default"): Promise<boolean> {
|
|
157
|
+
return this.client.send("find.checked", { selector, tabId });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ── Factory helper ────────────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
export function createFindAPI(client: PiggyClient): FindClient {
|
|
164
|
+
return new FindClient(client);
|
|
165
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// piggy/http/index.ts
|
|
2
|
+
// Mirrors PiggyHttp.cpp — connects to the HTTP server on port 2005.
|
|
3
|
+
// Use this when you want to talk to the browser over HTTP instead of the unix socket.
|
|
4
|
+
|
|
5
|
+
export interface HttpClientOptions {
|
|
6
|
+
host?: string;
|
|
7
|
+
port?: number;
|
|
8
|
+
key: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class PiggyHttpClient {
|
|
12
|
+
private baseUrl: string;
|
|
13
|
+
private key: string;
|
|
14
|
+
|
|
15
|
+
constructor(opts: HttpClientOptions) {
|
|
16
|
+
const host = opts.host ?? "localhost";
|
|
17
|
+
const port = opts.port ?? 2005;
|
|
18
|
+
this.baseUrl = `http://${host}:${port}`;
|
|
19
|
+
this.key = opts.key;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ── Health check ──────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
async ping(): Promise<boolean> {
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(this.baseUrl, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
"X-Piggy-Key": this.key,
|
|
31
|
+
},
|
|
32
|
+
body: "hello",
|
|
33
|
+
});
|
|
34
|
+
const text = await res.text();
|
|
35
|
+
return text.includes("active");
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Send command ──────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
async send<T = any>(cmd: string, payload: Record<string, any> = {}): Promise<T> {
|
|
44
|
+
const res = await fetch(this.baseUrl, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers: {
|
|
47
|
+
"Content-Type": "application/json",
|
|
48
|
+
"X-Piggy-Key": this.key,
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({ id: String(Date.now()), cmd, payload }),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (res.status === 401) throw new Error("Unauthorized — invalid X-Piggy-Key");
|
|
54
|
+
if (res.status === 400) throw new Error("Bad request — invalid JSON");
|
|
55
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
56
|
+
|
|
57
|
+
const data = await res.json() as { ok: boolean; data: T };
|
|
58
|
+
if (!data.ok) throw new Error(String(data.data) ?? "command failed");
|
|
59
|
+
return data.data;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createHttpClient(opts: HttpClientOptions): PiggyHttpClient {
|
|
64
|
+
return new PiggyHttpClient(opts);
|
|
65
|
+
}
|
package/piggy/human/index.ts
CHANGED
|
@@ -1,53 +1,115 @@
|
|
|
1
|
-
// piggy/human/index.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
// piggy/human/index.ts
|
|
2
|
+
import { PiggyClient } from "../client";
|
|
3
|
+
|
|
4
|
+
// ─── Local utilities ──────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export function randomDelay(min: number, max: number): Promise<void> {
|
|
7
|
+
return new Promise(r => setTimeout(r, Math.floor(Math.random() * (max - min + 1)) + min));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function humanTypeSequence(text: string): string[] {
|
|
11
|
+
const adjacent: Record<string, string[]> = {
|
|
12
|
+
a: ["q","w","s","z"], b: ["v","g","h","n"], c: ["x","d","f","v"],
|
|
13
|
+
d: ["s","e","r","f","c","x"], e: ["w","r","d","s"],
|
|
14
|
+
f: ["d","r","t","g","v","c"], g: ["f","t","y","h","b","v"],
|
|
15
|
+
h: ["g","y","u","j","n","b"], i: ["u","o","k","j"],
|
|
16
|
+
j: ["h","u","i","k","m","n"], k: ["j","i","o","l","m"],
|
|
17
|
+
l: ["k","o","p"], m: ["n","j","k"], n: ["b","h","j","m"],
|
|
18
|
+
o: ["i","p","l","k"], p: ["o","l"], q: ["w","a"],
|
|
19
|
+
r: ["e","t","f","d"], s: ["a","w","e","d","x","z"],
|
|
20
|
+
t: ["r","y","g","f"], u: ["y","i","h","j"],
|
|
21
|
+
v: ["c","f","g","b"], w: ["q","e","a","s"],
|
|
22
|
+
x: ["z","s","d","c"], y: ["t","u","g","h"],
|
|
23
|
+
z: ["a","s","x"],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const actions: string[] = [];
|
|
27
|
+
const typoIndices = new Set<number>();
|
|
28
|
+
|
|
29
|
+
if (text.length > 4) {
|
|
30
|
+
let tries = 0;
|
|
31
|
+
while (typoIndices.size < 2 && tries < 20) {
|
|
32
|
+
typoIndices.add(Math.floor(Math.random() * text.length));
|
|
33
|
+
tries++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < text.length; i++) {
|
|
38
|
+
if (typoIndices.has(i)) {
|
|
39
|
+
const ch = text[i]!.toLowerCase();
|
|
40
|
+
const neighbors = adjacent[ch];
|
|
41
|
+
const typo = neighbors
|
|
42
|
+
? neighbors[Math.floor(Math.random() * neighbors.length)] ?? ch
|
|
43
|
+
: ch;
|
|
44
|
+
actions.push(typo);
|
|
45
|
+
actions.push("BACKSPACE");
|
|
46
|
+
}
|
|
47
|
+
actions.push(text[i]!);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return actions;
|
|
51
|
+
}
|
|
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
|
+
// ─── HumanClient ─────────────────────────────────────────────────────────────
|
|
87
|
+
// Maps 1:1 to PiggyHuman.cpp commands
|
|
88
|
+
|
|
89
|
+
export class HumanClient {
|
|
90
|
+
constructor(private client: PiggyClient) {}
|
|
91
|
+
|
|
92
|
+
// human.set — update global profile
|
|
93
|
+
set(opts: HumanSetOptions, tabId = "default"): Promise<HumanProfile> {
|
|
94
|
+
return this.client.send("human.set", { ...opts, tabId });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// human.get — read current profile
|
|
98
|
+
get(tabId = "default"): Promise<HumanProfile> {
|
|
99
|
+
return this.client.send("human.get", { tabId });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// human.type — char-by-char typing with realistic delays
|
|
103
|
+
type(opts: HumanTypeOptions, tabId = "default"): Promise<void> {
|
|
104
|
+
return this.client.send("human.type", { ...opts, tabId });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// human.click — delayed click with optional force dispatch
|
|
108
|
+
click(opts: HumanClickOptions, tabId = "default"): Promise<void> {
|
|
109
|
+
return this.client.send("human.click", { ...opts, tabId });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function createHumanAPI(client: PiggyClient): HumanClient {
|
|
114
|
+
return new HumanClient(client);
|
|
115
|
+
}
|