@ceki/sdk 1.11.2 → 1.13.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/README.md +26 -9
- package/dist/cli.js +60 -46
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +31 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +31 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ Close all sessions and the connection.
|
|
|
73
73
|
```typescript
|
|
74
74
|
await browser.navigate(url) // Navigate to URL
|
|
75
75
|
await browser.click(x, y) // Click at coordinates
|
|
76
|
-
await browser.type(text) // Type text
|
|
76
|
+
await browser.type(text) // Type text — one Ceki.typeText command; extension does per-char keydown/keyUp + humanizer delays
|
|
77
77
|
await browser.scroll({ deltaY: -300 }) // Scroll
|
|
78
78
|
await browser.screenshot({ format: 'png' }) // Screenshot as Buffer
|
|
79
79
|
await browser.screenshot({ format: 'base64' }) // Screenshot as {data: string}
|
|
@@ -111,24 +111,41 @@ await browser.profile.import(saved);
|
|
|
111
111
|
|
|
112
112
|
## Human Mode
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
Behavioral humanization is **ON by default** in both `main` and `incognito` profile modes:
|
|
115
|
+
|
|
116
|
+
- **Typing** — per-character keystrokes with natural inter-key cadence + jitter (extension-side, `Ceki.typeText`).
|
|
117
|
+
- **Mouse** — clicks are preceded by a bezier mousemove trajectory (8–35 intermediate `mouseMoved` events), so the page sees a real pointer trail instead of a teleport.
|
|
118
|
+
|
|
119
|
+
Fingerprint Tier-2 (UA / timezone / WebGL overrides) stays OFF in `main` mode to preserve the provider's identity — separate from behavioral humanization.
|
|
115
120
|
|
|
116
121
|
```typescript
|
|
117
|
-
// Default:
|
|
122
|
+
// Default: behavioral humanizer ON (natural profile)
|
|
118
123
|
const browser = await client.rent(scheduleId);
|
|
119
124
|
|
|
120
125
|
// Explicit profile
|
|
121
126
|
const browser = await client.rent(scheduleId, { human: 'careful' });
|
|
122
127
|
|
|
123
|
-
// Disable humanization
|
|
128
|
+
// Disable session-wide humanization
|
|
124
129
|
const browser = await client.rent(scheduleId, { human: null });
|
|
125
130
|
```
|
|
126
131
|
|
|
132
|
+
### Per-call disable
|
|
133
|
+
|
|
134
|
+
Pass `{ human: false }` to flatten **just one call**, without leaking jitter to siblings:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
await browser.type('user@example.com', { human: false }); // flat keystrokes
|
|
138
|
+
await browser.click(120, 240, { human: false }); // straight pointer jump
|
|
139
|
+
await browser.scroll({ deltaY: -300, human: false });
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The CLI equivalent is `--no-human` / `--raw` on `type`, `click`, `scroll`, `navigate`. Both flags mean "this call only".
|
|
143
|
+
|
|
127
144
|
### Environment overrides
|
|
128
145
|
|
|
129
146
|
- `CEKI_HUMAN_PROFILE` — Override default profile name (`careful`)
|
|
130
147
|
- `CEKI_HUMAN_PROFILE_PATH` — Path to custom JSON profile file
|
|
131
|
-
- `CEKI_HUMAN_DISABLE=1` —
|
|
148
|
+
- `CEKI_HUMAN_DISABLE=1` — **Global kill-switch**: disable humanization for every call regardless of per-call `human:` arguments or CLI flags
|
|
132
149
|
|
|
133
150
|
## Error Classes
|
|
134
151
|
|
|
@@ -194,10 +211,10 @@ The CLI persists session state locally — after `rent` it saves the session ID
|
|
|
194
211
|
|
|
195
212
|
| Command | Description |
|
|
196
213
|
|---|---|
|
|
197
|
-
| `navigate SID URL` | Open URL |
|
|
198
|
-
| `click SID X Y` | Click at viewport coordinates |
|
|
199
|
-
| `type SID TEXT [--
|
|
200
|
-
| `scroll SID X Y DY` | Scroll from (X, Y) by `DY` pixels |
|
|
214
|
+
| `navigate SID URL [--no-human\|--raw]` | Open URL (humanized by default; `--no-human` skips pre/post delays) |
|
|
215
|
+
| `click SID X Y [--no-human\|--raw]` | Click at viewport coordinates (mousemove trail ON by default; `--no-human` for direct jump) |
|
|
216
|
+
| `type SID TEXT [--selector CSS] [--no-human\|--raw]` | Type text (humanized by default; `--no-human` for flat keystrokes) |
|
|
217
|
+
| `scroll SID X Y DY [--no-human\|--raw]` | Scroll from (X, Y) by `DY` pixels (eased by default; `--no-human` for raw CDP wheel) |
|
|
201
218
|
| `screenshot SID -o FILE [--format png\|jpeg] [--full]` | Save screenshot |
|
|
202
219
|
| `snapshot SID -o FILE` | Screenshot + new chat messages |
|
|
203
220
|
| `switch-tab SID` | Switch active tab |
|
package/dist/cli.js
CHANGED
|
@@ -654,27 +654,39 @@ var Browser = class _Browser {
|
|
|
654
654
|
this._sendRaw(msg);
|
|
655
655
|
});
|
|
656
656
|
}
|
|
657
|
-
|
|
658
|
-
|
|
657
|
+
// task 427 — per-call kill-switch. human=false bypasses humanizer timings
|
|
658
|
+
// AND tells the extension to skip mouse-jitter via the `_ceki_raw` marker
|
|
659
|
+
// (see cdp.ts). human=true forces humanizer; human undefined = session
|
|
660
|
+
// default. Global env CEKI_HUMAN_DISABLE=1 nulls this._humanizer in the
|
|
661
|
+
// constructor so all paths become raw.
|
|
662
|
+
_humanizeForCall(human) {
|
|
663
|
+
if (human === false) return null;
|
|
664
|
+
return this._humanizer;
|
|
665
|
+
}
|
|
666
|
+
async navigate(url, timeout = 3e4, opts) {
|
|
667
|
+
const h = this._humanizeForCall(opts?.human);
|
|
668
|
+
if (h) await h.before("navigate");
|
|
659
669
|
const result = await this.send({ method: "Page.navigate", params: { url } }, timeout);
|
|
660
|
-
if (
|
|
670
|
+
if (h) await h.after("navigate");
|
|
661
671
|
return {
|
|
662
672
|
url: String(result?.url ?? url),
|
|
663
673
|
frameId: result?.frameId ? String(result.frameId) : void 0
|
|
664
674
|
};
|
|
665
675
|
}
|
|
666
|
-
async click(x, y) {
|
|
667
|
-
|
|
676
|
+
async click(x, y, opts) {
|
|
677
|
+
const h = this._humanizeForCall(opts?.human);
|
|
678
|
+
if (h) await h.before("click");
|
|
679
|
+
const rawFlag = h === null ? { _ceki_raw: true } : {};
|
|
668
680
|
await this.send({
|
|
669
681
|
method: "Input.dispatchMouseEvent",
|
|
670
|
-
params: { type: "mousePressed", x, y, button: "left", clickCount: 1 }
|
|
682
|
+
params: { type: "mousePressed", x, y, button: "left", clickCount: 1, ...rawFlag }
|
|
671
683
|
});
|
|
672
684
|
await this.send({
|
|
673
685
|
method: "Input.dispatchMouseEvent",
|
|
674
686
|
params: { type: "mouseReleased", x, y, button: "left", clickCount: 1 }
|
|
675
687
|
});
|
|
676
688
|
this._lastPointer = [x, y];
|
|
677
|
-
if (
|
|
689
|
+
if (h) await h.after("click");
|
|
678
690
|
}
|
|
679
691
|
async _sendKeystroke(char) {
|
|
680
692
|
const mapping = keymapForChar(char);
|
|
@@ -720,9 +732,10 @@ var Browser = class _Browser {
|
|
|
720
732
|
});
|
|
721
733
|
}
|
|
722
734
|
}
|
|
723
|
-
async type(text) {
|
|
724
|
-
|
|
725
|
-
|
|
735
|
+
async type(text, opts) {
|
|
736
|
+
const h = this._humanizeForCall(opts?.human);
|
|
737
|
+
if (h) {
|
|
738
|
+
await h.before("type");
|
|
726
739
|
if (this._lastPointer) {
|
|
727
740
|
const [px, py] = this._lastPointer;
|
|
728
741
|
await this.send({
|
|
@@ -734,18 +747,11 @@ var Browser = class _Browser {
|
|
|
734
747
|
params: { type: "mouseReleased", x: px, y: py, button: "left", clickCount: 1 }
|
|
735
748
|
});
|
|
736
749
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
await this._humanizer.after("type");
|
|
745
|
-
} else {
|
|
746
|
-
for (const char of text) {
|
|
747
|
-
await this._sendKeystroke(char);
|
|
748
|
-
}
|
|
750
|
+
}
|
|
751
|
+
const human = h ? ["natural", "careful"].includes(h.profile.name) ? h.profile.name : "natural" : null;
|
|
752
|
+
await this.send({ method: "Ceki.typeText", params: { text, human } });
|
|
753
|
+
if (h) {
|
|
754
|
+
await h.after("type");
|
|
749
755
|
}
|
|
750
756
|
}
|
|
751
757
|
async scroll(opts) {
|
|
@@ -753,13 +759,14 @@ var Browser = class _Browser {
|
|
|
753
759
|
const y = opts?.y ?? 0;
|
|
754
760
|
const deltaX = opts?.deltaX ?? 0;
|
|
755
761
|
const deltaY = opts?.deltaY ?? -300;
|
|
756
|
-
|
|
762
|
+
const h = this._humanizeForCall(opts?.human);
|
|
763
|
+
if (h) await h.before("scroll");
|
|
757
764
|
await this.send({
|
|
758
765
|
method: "Input.dispatchMouseEvent",
|
|
759
766
|
params: { type: "mouseWheel", x, y, deltaX, deltaY }
|
|
760
767
|
});
|
|
761
768
|
this._lastPointer = [x, y];
|
|
762
|
-
if (
|
|
769
|
+
if (h) await h.after("scroll");
|
|
763
770
|
}
|
|
764
771
|
async screenshot(opts) {
|
|
765
772
|
const format = opts?.format ?? "base64";
|
|
@@ -2156,32 +2163,38 @@ async function cmdSnapshot(sid, args) {
|
|
|
2156
2163
|
await closeClient(client);
|
|
2157
2164
|
}
|
|
2158
2165
|
}
|
|
2166
|
+
function parseNoHuman(args) {
|
|
2167
|
+
return args.includes("--no-human") || args.includes("--raw");
|
|
2168
|
+
}
|
|
2159
2169
|
async function cmdNavigate(sid, args) {
|
|
2160
|
-
const url = args
|
|
2170
|
+
const url = args.find((a) => !a.startsWith("--"));
|
|
2161
2171
|
if (!url) {
|
|
2162
2172
|
err("URL is required", "args");
|
|
2163
2173
|
process.exit(1);
|
|
2164
2174
|
}
|
|
2175
|
+
const raw = parseNoHuman(args);
|
|
2165
2176
|
const apiKey = getApiKey();
|
|
2166
2177
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2167
2178
|
try {
|
|
2168
|
-
await browser.navigate(url);
|
|
2179
|
+
await browser.navigate(url, 3e4, raw ? { human: false } : void 0);
|
|
2169
2180
|
out({ ok: true });
|
|
2170
2181
|
} finally {
|
|
2171
2182
|
await closeClient(client);
|
|
2172
2183
|
}
|
|
2173
2184
|
}
|
|
2174
2185
|
async function cmdClick(sid, args) {
|
|
2175
|
-
const
|
|
2176
|
-
const
|
|
2186
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
2187
|
+
const x = parseInt(positional[0], 10);
|
|
2188
|
+
const y = parseInt(positional[1], 10);
|
|
2177
2189
|
if (isNaN(x) || isNaN(y)) {
|
|
2178
2190
|
err("x and y coordinates are required", "args");
|
|
2179
2191
|
process.exit(1);
|
|
2180
2192
|
}
|
|
2193
|
+
const raw = parseNoHuman(args);
|
|
2181
2194
|
const apiKey = getApiKey();
|
|
2182
2195
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2183
2196
|
try {
|
|
2184
|
-
await browser.click(x, y);
|
|
2197
|
+
await browser.click(x, y, raw ? { human: false } : void 0);
|
|
2185
2198
|
out({ ok: true, pointer: [x, y] });
|
|
2186
2199
|
} finally {
|
|
2187
2200
|
await closeClient(client);
|
|
@@ -2189,41 +2202,42 @@ async function cmdClick(sid, args) {
|
|
|
2189
2202
|
}
|
|
2190
2203
|
async function cmdType(sid, args) {
|
|
2191
2204
|
let text = "";
|
|
2192
|
-
let
|
|
2205
|
+
let raw = false;
|
|
2193
2206
|
for (let i = 0; i < args.length; i++) {
|
|
2194
|
-
if (args[i] === "--
|
|
2195
|
-
else if (
|
|
2207
|
+
if (args[i] === "--no-human" || args[i] === "--raw") raw = true;
|
|
2208
|
+
else if (args[i] === "--natural") {
|
|
2209
|
+
} else if (!text) text = args[i];
|
|
2196
2210
|
}
|
|
2197
2211
|
if (!text) {
|
|
2198
2212
|
err("text is required", "args");
|
|
2199
2213
|
process.exit(1);
|
|
2200
2214
|
}
|
|
2201
2215
|
const apiKey = getApiKey();
|
|
2202
|
-
const human = natural ? "natural" : null;
|
|
2203
2216
|
const client = await connect(apiKey, connectOptions());
|
|
2204
2217
|
try {
|
|
2205
|
-
const browser = await client.resume(sid
|
|
2206
|
-
|
|
2207
|
-
browser._humanizer = null;
|
|
2208
|
-
}
|
|
2209
|
-
await browser.type(text);
|
|
2218
|
+
const browser = await client.resume(sid);
|
|
2219
|
+
await browser.type(text, raw ? { human: false } : void 0);
|
|
2210
2220
|
out({ ok: true });
|
|
2211
2221
|
} finally {
|
|
2212
2222
|
await closeClient(client);
|
|
2213
2223
|
}
|
|
2214
2224
|
}
|
|
2215
2225
|
async function cmdScroll(sid, args) {
|
|
2216
|
-
const
|
|
2217
|
-
const
|
|
2218
|
-
const
|
|
2226
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
2227
|
+
const x = parseInt(positional[0], 10);
|
|
2228
|
+
const y = parseInt(positional[1], 10);
|
|
2229
|
+
const dy = parseInt(positional[2], 10);
|
|
2219
2230
|
if (isNaN(x) || isNaN(y) || isNaN(dy)) {
|
|
2220
2231
|
err("x, y, dy are required", "args");
|
|
2221
2232
|
process.exit(1);
|
|
2222
2233
|
}
|
|
2234
|
+
const raw = parseNoHuman(args);
|
|
2223
2235
|
const apiKey = getApiKey();
|
|
2224
2236
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2225
2237
|
try {
|
|
2226
|
-
|
|
2238
|
+
const scrollOpts = { x, y, deltaY: dy };
|
|
2239
|
+
if (raw) scrollOpts.human = false;
|
|
2240
|
+
await browser.scroll(scrollOpts);
|
|
2227
2241
|
out({ ok: true });
|
|
2228
2242
|
} finally {
|
|
2229
2243
|
await closeClient(client);
|
|
@@ -2536,10 +2550,10 @@ Commands:
|
|
|
2536
2550
|
search [--limit N] [--filter k=v]...
|
|
2537
2551
|
snapshot <sid> -o PATH
|
|
2538
2552
|
screenshot <sid> -o PATH [--full]
|
|
2539
|
-
navigate <sid> <url>
|
|
2540
|
-
click <sid> <x> <y>
|
|
2541
|
-
type <sid> "<text>" [--
|
|
2542
|
-
scroll <sid> <x> <y> <dy>
|
|
2553
|
+
navigate <sid> <url> [--no-human|--raw]
|
|
2554
|
+
click <sid> <x> <y> [--no-human|--raw]
|
|
2555
|
+
type <sid> "<text>" [--no-human|--raw] (humanized by default)
|
|
2556
|
+
scroll <sid> <x> <y> <dy> [--no-human|--raw]
|
|
2543
2557
|
switch-tab <sid>
|
|
2544
2558
|
configure <sid> [--masking-mode true|false] [--fingerprint true|false]
|
|
2545
2559
|
cdp <sid> --method <M> [--params JSON]
|