@ceki/sdk 1.12.0 → 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 CHANGED
@@ -111,24 +111,41 @@ await browser.profile.import(saved);
111
111
 
112
112
  ## Human Mode
113
113
 
114
- Browser actions include human-like timing by default delays before/after actions and per-character typing with jitter.
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: natural profile (enabled by 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` — Disable humanization entirely
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 [--natural]` | Type text into focused element |
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
- async navigate(url, timeout = 3e4) {
658
- if (this._humanizer) await this._humanizer.before("navigate");
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 (this._humanizer) await this._humanizer.after("navigate");
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
- if (this._humanizer) await this._humanizer.before("click");
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 (this._humanizer) await this._humanizer.after("click");
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
- if (this._humanizer) {
725
- await this._humanizer.before("type");
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({
@@ -735,10 +748,10 @@ var Browser = class _Browser {
735
748
  });
736
749
  }
737
750
  }
738
- const human = this._humanizer ? ["natural", "careful"].includes(this._humanizer.profile.name) ? this._humanizer.profile.name : "natural" : null;
751
+ const human = h ? ["natural", "careful"].includes(h.profile.name) ? h.profile.name : "natural" : null;
739
752
  await this.send({ method: "Ceki.typeText", params: { text, human } });
740
- if (this._humanizer) {
741
- await this._humanizer.after("type");
753
+ if (h) {
754
+ await h.after("type");
742
755
  }
743
756
  }
744
757
  async scroll(opts) {
@@ -746,13 +759,14 @@ var Browser = class _Browser {
746
759
  const y = opts?.y ?? 0;
747
760
  const deltaX = opts?.deltaX ?? 0;
748
761
  const deltaY = opts?.deltaY ?? -300;
749
- if (this._humanizer) await this._humanizer.before("scroll");
762
+ const h = this._humanizeForCall(opts?.human);
763
+ if (h) await h.before("scroll");
750
764
  await this.send({
751
765
  method: "Input.dispatchMouseEvent",
752
766
  params: { type: "mouseWheel", x, y, deltaX, deltaY }
753
767
  });
754
768
  this._lastPointer = [x, y];
755
- if (this._humanizer) await this._humanizer.after("scroll");
769
+ if (h) await h.after("scroll");
756
770
  }
757
771
  async screenshot(opts) {
758
772
  const format = opts?.format ?? "base64";
@@ -2149,32 +2163,38 @@ async function cmdSnapshot(sid, args) {
2149
2163
  await closeClient(client);
2150
2164
  }
2151
2165
  }
2166
+ function parseNoHuman(args) {
2167
+ return args.includes("--no-human") || args.includes("--raw");
2168
+ }
2152
2169
  async function cmdNavigate(sid, args) {
2153
- const url = args[0];
2170
+ const url = args.find((a) => !a.startsWith("--"));
2154
2171
  if (!url) {
2155
2172
  err("URL is required", "args");
2156
2173
  process.exit(1);
2157
2174
  }
2175
+ const raw = parseNoHuman(args);
2158
2176
  const apiKey = getApiKey();
2159
2177
  const [client, browser] = await resumeBrowser(apiKey, sid);
2160
2178
  try {
2161
- await browser.navigate(url);
2179
+ await browser.navigate(url, 3e4, raw ? { human: false } : void 0);
2162
2180
  out({ ok: true });
2163
2181
  } finally {
2164
2182
  await closeClient(client);
2165
2183
  }
2166
2184
  }
2167
2185
  async function cmdClick(sid, args) {
2168
- const x = parseInt(args[0], 10);
2169
- const y = parseInt(args[1], 10);
2186
+ const positional = args.filter((a) => !a.startsWith("--"));
2187
+ const x = parseInt(positional[0], 10);
2188
+ const y = parseInt(positional[1], 10);
2170
2189
  if (isNaN(x) || isNaN(y)) {
2171
2190
  err("x and y coordinates are required", "args");
2172
2191
  process.exit(1);
2173
2192
  }
2193
+ const raw = parseNoHuman(args);
2174
2194
  const apiKey = getApiKey();
2175
2195
  const [client, browser] = await resumeBrowser(apiKey, sid);
2176
2196
  try {
2177
- await browser.click(x, y);
2197
+ await browser.click(x, y, raw ? { human: false } : void 0);
2178
2198
  out({ ok: true, pointer: [x, y] });
2179
2199
  } finally {
2180
2200
  await closeClient(client);
@@ -2182,41 +2202,42 @@ async function cmdClick(sid, args) {
2182
2202
  }
2183
2203
  async function cmdType(sid, args) {
2184
2204
  let text = "";
2185
- let natural = false;
2205
+ let raw = false;
2186
2206
  for (let i = 0; i < args.length; i++) {
2187
- if (args[i] === "--natural") natural = true;
2188
- else if (!text) text = args[i];
2207
+ if (args[i] === "--no-human" || args[i] === "--raw") raw = true;
2208
+ else if (args[i] === "--natural") {
2209
+ } else if (!text) text = args[i];
2189
2210
  }
2190
2211
  if (!text) {
2191
2212
  err("text is required", "args");
2192
2213
  process.exit(1);
2193
2214
  }
2194
2215
  const apiKey = getApiKey();
2195
- const human = natural ? "natural" : null;
2196
2216
  const client = await connect(apiKey, connectOptions());
2197
2217
  try {
2198
- const browser = await client.resume(sid, { human });
2199
- if (!natural) {
2200
- browser._humanizer = null;
2201
- }
2202
- await browser.type(text);
2218
+ const browser = await client.resume(sid);
2219
+ await browser.type(text, raw ? { human: false } : void 0);
2203
2220
  out({ ok: true });
2204
2221
  } finally {
2205
2222
  await closeClient(client);
2206
2223
  }
2207
2224
  }
2208
2225
  async function cmdScroll(sid, args) {
2209
- const x = parseInt(args[0], 10);
2210
- const y = parseInt(args[1], 10);
2211
- const dy = parseInt(args[2], 10);
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);
2212
2230
  if (isNaN(x) || isNaN(y) || isNaN(dy)) {
2213
2231
  err("x, y, dy are required", "args");
2214
2232
  process.exit(1);
2215
2233
  }
2234
+ const raw = parseNoHuman(args);
2216
2235
  const apiKey = getApiKey();
2217
2236
  const [client, browser] = await resumeBrowser(apiKey, sid);
2218
2237
  try {
2219
- await browser.scroll({ x, y, deltaY: dy });
2238
+ const scrollOpts = { x, y, deltaY: dy };
2239
+ if (raw) scrollOpts.human = false;
2240
+ await browser.scroll(scrollOpts);
2220
2241
  out({ ok: true });
2221
2242
  } finally {
2222
2243
  await closeClient(client);
@@ -2529,10 +2550,10 @@ Commands:
2529
2550
  search [--limit N] [--filter k=v]...
2530
2551
  snapshot <sid> -o PATH
2531
2552
  screenshot <sid> -o PATH [--full]
2532
- navigate <sid> <url>
2533
- click <sid> <x> <y>
2534
- type <sid> "<text>" [--natural]
2535
- 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]
2536
2557
  switch-tab <sid>
2537
2558
  configure <sid> [--masking-mode true|false] [--fingerprint true|false]
2538
2559
  cdp <sid> --method <M> [--params JSON]