capiumbrowser 1.2.4 → 1.3.1

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.
@@ -2,9 +2,9 @@
2
2
  "schema_version": 2,
3
3
  "minimum_release_year": 2022,
4
4
  "browser": "chrome",
5
- "status": "experimental-emulation",
6
- "gpu_policy": "host",
7
- "hardware_policy": "host",
5
+ "status": "catalog-emulation",
6
+ "gpu_policy": "catalog",
7
+ "hardware_policy": "catalog",
8
8
  "physical_device_validation": "pending",
9
9
  "default_profile_ids": [
10
10
  "pixel-7",
@@ -13,6 +13,20 @@
13
13
  "pixel-8a",
14
14
  "pixel-9"
15
15
  ],
16
+ "font_core": [
17
+ "Roboto",
18
+ "Noto Sans",
19
+ "Noto Serif",
20
+ "Noto Sans Mono",
21
+ "Noto Color Emoji",
22
+ "Noto Sans JP",
23
+ "Noto Sans KR",
24
+ "Noto Sans SC",
25
+ "Noto Sans TC",
26
+ "Noto Naskh Arabic",
27
+ "Noto Sans Devanagari",
28
+ "Noto Sans Thai"
29
+ ],
16
30
  "sources": [
17
31
  "https://support.google.com/pixelphone/answer/7158570?hl=en",
18
32
  "https://news.samsung.com/global/samsung-galaxy-a55-5g-and-galaxy-a35-5g-awesome-innovations-and-security-engineered-for-everyone",
@@ -27,7 +41,12 @@
27
41
  "platform_version": "13",
28
42
  "width": 412,
29
43
  "height": 915,
30
- "dpr": 2.625
44
+ "dpr": 2.625,
45
+ "soc": "Tensor G2",
46
+ "cores": 8,
47
+ "memory_gb": 8,
48
+ "gpu_vendor": "Google Inc. (ARM)",
49
+ "gpu_renderer": "ANGLE (ARM, Mali-G710, OpenGL ES 3.2)"
31
50
  },
32
51
  {
33
52
  "id": "pixel-8",
@@ -36,7 +55,12 @@
36
55
  "platform_version": "14",
37
56
  "width": 412,
38
57
  "height": 915,
39
- "dpr": 2.625
58
+ "dpr": 2.625,
59
+ "soc": "Tensor G3",
60
+ "cores": 9,
61
+ "memory_gb": 8,
62
+ "gpu_vendor": "Google Inc. (ARM)",
63
+ "gpu_renderer": "ANGLE (ARM, Mali-G715, OpenGL ES 3.2)"
40
64
  },
41
65
  {
42
66
  "id": "pixel-8-pro",
@@ -45,7 +69,12 @@
45
69
  "platform_version": "14",
46
70
  "width": 448,
47
71
  "height": 997,
48
- "dpr": 3.0
72
+ "dpr": 3.0,
73
+ "soc": "Tensor G3",
74
+ "cores": 9,
75
+ "memory_gb": 12,
76
+ "gpu_vendor": "Google Inc. (ARM)",
77
+ "gpu_renderer": "ANGLE (ARM, Mali-G715, OpenGL ES 3.2)"
49
78
  },
50
79
  {
51
80
  "id": "pixel-8a",
@@ -54,7 +83,12 @@
54
83
  "platform_version": "14",
55
84
  "width": 412,
56
85
  "height": 915,
57
- "dpr": 2.625
86
+ "dpr": 2.625,
87
+ "soc": "Tensor G3",
88
+ "cores": 9,
89
+ "memory_gb": 8,
90
+ "gpu_vendor": "Google Inc. (ARM)",
91
+ "gpu_renderer": "ANGLE (ARM, Mali-G715, OpenGL ES 3.2)"
58
92
  },
59
93
  {
60
94
  "id": "pixel-9",
@@ -63,7 +97,12 @@
63
97
  "platform_version": "14",
64
98
  "width": 412,
65
99
  "height": 924,
66
- "dpr": 2.625
100
+ "dpr": 2.625,
101
+ "soc": "Tensor G4",
102
+ "cores": 8,
103
+ "memory_gb": 12,
104
+ "gpu_vendor": "Google Inc. (ARM)",
105
+ "gpu_renderer": "ANGLE (ARM, Mali-G715, OpenGL ES 3.2)"
67
106
  },
68
107
  {
69
108
  "id": "samsung-galaxy-a55",
@@ -74,6 +113,11 @@
74
113
  "width": 360,
75
114
  "height": 800,
76
115
  "dpr": 2.25,
116
+ "soc": "Exynos 1480",
117
+ "cores": 8,
118
+ "memory_gb": 8,
119
+ "gpu_vendor": "Google Inc. (Samsung Electronics Co., Ltd.)",
120
+ "gpu_renderer": "ANGLE (Samsung Electronics Co., Ltd., Xclipse 530, OpenGL ES 3.2)",
77
121
  "geometry_status": "devtools-emulation-only",
78
122
  "geometry_note": "These Chromium preset dimensions/DPR do not directly map to the published 1080x2340 panel; physical Chrome capture required."
79
123
  },
@@ -84,7 +128,12 @@
84
128
  "platform_version": "14",
85
129
  "width": 427,
86
130
  "height": 952,
87
- "dpr": 3.0
131
+ "dpr": 3.0,
132
+ "soc": "Tensor G4",
133
+ "cores": 8,
134
+ "memory_gb": 16,
135
+ "gpu_vendor": "Google Inc. (ARM)",
136
+ "gpu_renderer": "ANGLE (ARM, Mali-G715, OpenGL ES 3.2)"
88
137
  },
89
138
  {
90
139
  "id": "pixel-9-pro-xl",
@@ -93,7 +142,12 @@
93
142
  "platform_version": "14",
94
143
  "width": 448,
95
144
  "height": 997,
96
- "dpr": 3.0
145
+ "dpr": 3.0,
146
+ "soc": "Tensor G4",
147
+ "cores": 8,
148
+ "memory_gb": 16,
149
+ "gpu_vendor": "Google Inc. (ARM)",
150
+ "gpu_renderer": "ANGLE (ARM, Mali-G715, OpenGL ES 3.2)"
97
151
  }
98
152
  ]
99
153
  }
package/lib/channels.json CHANGED
@@ -1,36 +1,36 @@
1
1
  {
2
- "_comment": "SINGLE SOURCE OF TRUTH for the current stable Capium browser build per OS/arch. Each OS reaches stable independently -- edit one entry without touching the others. This file ships inside the wheel (the SDK reads it to pick each host's stable build) AND is published to the download server by the release pipeline. `version` is the reported Chromium version (what the browser reports to the customer, names the R2 folder); `engine_version` is the actual built Chromium engine (box_package verifies the binary against it) -- it differs from `version` when a platform reports a spoofed patch (Windows reports .85 on the .84 engine). `revision` is our packaging counter -- the served R2 folder is chromium-v<version>.<revision>/, ALWAYS dotted-suffixed starting at 1. A Chromium version bump resets revision to 1. `published: false` means the tag is declared but no artifact exists yet.",
2
+ "_comment": "SINGLE SOURCE OF TRUTH for the current stable Capium browser build per OS/arch. Each OS reaches stable independently -- edit one entry without touching the others. This file ships inside the wheel (the SDK reads it to pick each host's stable build) AND is published to the download server by the release pipeline. `version` is the reported Chromium version (what the browser reports to the customer, names the R2 folder); `engine_version` is the actual built Chromium engine (box_package verifies the binary against it) -- it differs from `version` when a platform reports a spoofed patch (Windows/macOS report .48 on the .47 engine). `revision` is our packaging counter -- the served R2 folder is chromium-v<version>.<revision>/, ALWAYS dotted-suffixed starting at 1. A Chromium version bump resets revision to 1. `published: false` means the tag is declared but no artifact exists yet.",
3
3
  "schema": 1,
4
- "updated": "2026-10-21",
4
+ "updated": "2026-10-24",
5
5
  "channels": {
6
6
  "stable": {
7
7
  "windows-x64": {
8
- "version": "155.0.8059.85",
9
- "engine_version": "155.0.8059.84",
8
+ "version": "156.0.8078.48",
9
+ "engine_version": "156.0.8078.47",
10
10
  "revision": 2,
11
- "sha256": "905bb081d5234338237b6a4c3df7ef3fbb8d20d0049adde982027494b87e4ed2",
11
+ "sha256": "76fa0d609f3d3fcac05436681884b3150405aaa470730d9a9ad1453ff6ad8276",
12
12
  "published": true,
13
13
  "license_status_protocol": 1
14
14
  },
15
15
  "macos-arm64": {
16
- "version": "155.0.8059.84",
17
- "engine_version": "155.0.8059.84",
16
+ "version": "156.0.8078.48",
17
+ "engine_version": "156.0.8078.47",
18
18
  "revision": 2,
19
- "sha256": "5c4ab089d7925a3b86fd891cfbe9de95777c46fc7616990a94b15b891c89775f",
19
+ "sha256": "3b883146c0f41fcb409113d3f43764b7e7061bb6c64d58cddb26b2619887275f",
20
20
  "published": true,
21
21
  "license_status_protocol": 1
22
22
  },
23
23
  "linux-x64": {
24
- "version": "155.0.8059.84",
25
- "engine_version": "155.0.8059.84",
24
+ "version": "156.0.8078.47",
25
+ "engine_version": "156.0.8078.47",
26
26
  "revision": 2,
27
- "sha256": "e9e18dd9f23baa500e1be0a7075096f6e6c8fb3a37150c96bd96fd4e58518880",
27
+ "sha256": "5fc9954366d8052e09d8f528d1cdbcedcf6d0cb1ae43c36441ce975a1e60c749",
28
28
  "published": true,
29
29
  "license_status_protocol": 1
30
30
  },
31
31
  "linux-arm64": {
32
- "version": "155.0.8059.84",
33
- "engine_version": "155.0.8059.84",
32
+ "version": "156.0.8078.47",
33
+ "engine_version": "156.0.8078.47",
34
34
  "revision": 1,
35
35
  "sha256": null,
36
36
  "published": false
@@ -15,13 +15,14 @@ const proxy = require('./proxy');
15
15
  /**
16
16
  * Build [--proxy-server, --geoip] flags for geo coherence.
17
17
  *
18
- * The binary's CapiumGeoipResolver runs at PreBrowserStart ONLY when --geoip is present. A
19
- * proxy on its own does NOT trigger it: the lookup is a synchronous, blocking round-trip that
20
- * would otherwise add up to 2.5s to every proxied launch. When it does run it resolves the
21
- * egress IP (proxy exit, else the host's real public IP) and pins WebRTC / timezone /
22
- * geolocation / language -- so the SDK adds no probe of its own. geoipOn is tri-state:
23
- * * null -> default: add no flag, so NO geo lookup runs (no launch latency) even with a
24
- * proxy. Pass geoip: true to opt into proxy-exit coherence.
18
+ * The binary's CapiumGeoipResolver runs at PreBrowserStart ONLY when --geoip is present.
19
+ * When it runs it resolves the egress IP (proxy exit, else the host's real public IP) and
20
+ * pins WebRTC / timezone / geolocation / language -- so the SDK adds no probe of its own.
21
+ * geoipOn is tri-state:
22
+ * * null -> default: ON whenever a proxy is set, OFF otherwise. An uncorrected host
23
+ * timezone under a proxied identity (UTC on a server, or the operator's home
24
+ * zone against a foreign exit) is a far stronger detection signal than the
25
+ * lookup's one-off launch latency. Pass geoip: false to opt out.
25
26
  * * true -> ON with --geoip (resolves the egress -- proxy exit, or the host IP if proxyless).
26
27
  * * false -> OFF with --geoip=false, which the binary honors FIRST (explicit opt-out).
27
28
  * Returns {launchOptions, args}.
@@ -29,11 +30,11 @@ const proxy = require('./proxy');
29
30
  function proxyAndGeoArgs(spec, geoipOn) {
30
31
  const { launchOptions, args } = proxy.resolveProxyConfig(spec);
31
32
  const out = [...args];
32
- if (geoipOn === null || geoipOn === undefined) {
33
- // default: no flag, no lookup
34
- } else if (geoipOn) {
33
+ const explicitOff = geoipOn === false;
34
+ if (geoipOn === null || geoipOn === undefined) geoipOn = Boolean(spec);
35
+ if (geoipOn) {
35
36
  out.push('--geoip');
36
- } else {
37
+ } else if (explicitOff) {
37
38
  out.push('--geoip=false');
38
39
  }
39
40
  return { launchOptions, args: out };
@@ -87,4 +88,25 @@ function clearStatusFile(p) {
87
88
  } catch {}
88
89
  }
89
90
 
90
- module.exports = { proxyAndGeoArgs, buildArgs, newStatusFile, clearStatusFile };
91
+ /**
92
+ * Deterministic per-seed window geometry inside the seed's available screen area.
93
+ *
94
+ * Every profile previously got the identical maximized-like window (exactly availWidth x
95
+ * availHeight at 0,0), so all profiles sharing a pool screen were geometry-identical -- a
96
+ * clusterable signature. Real users are a mix: most keep the browser maximized, a minority
97
+ * run a windowed browser at an arbitrary size/position. Mirror that mix deterministically
98
+ * per seed (the geometry is part of the profile's stable identity, like its screen).
99
+ * Matches python browser._window_bounds_for_seed.
100
+ */
101
+ function windowBoundsForSeed(seed, aw, ah) {
102
+ const d = crypto.createHash('sha256')
103
+ .update(`capium-window:${Math.trunc(seed) || 0}`).digest();
104
+ if (d[0] % 10 < 6) return { left: 0, top: 0, width: aw, height: ah };
105
+ const w = Math.max(800, Math.trunc(aw * (0.78 + (d[1] / 255) * 0.16)));
106
+ const h = Math.max(600, Math.trunc(ah * (0.80 + (d[2] / 255) * 0.15)));
107
+ const left = Math.trunc(((aw - w) * d[3]) / 255);
108
+ const top = Math.trunc(((ah - h) * d[4]) / 255);
109
+ return { left, top, width: w, height: h };
110
+ }
111
+
112
+ module.exports = { proxyAndGeoArgs, buildArgs, newStatusFile, clearStatusFile, windowBoundsForSeed };
package/lib/mobile.js CHANGED
@@ -84,7 +84,7 @@ async function engineProbe(expected) {
84
84
  gl.getParameter(ext.UNMASKED_RENDERER_WEBGL) === 'Apple GPU';
85
85
  }
86
86
  const match = navigator.userAgent.match(/Chrome\/(\d+)\.0\.0\.0 Mobile Safari\//);
87
- if (!expected || navigator.platform !== 'Linux armv81' || !match ||
87
+ if (!expected || navigator.platform !== 'Linux armv8l' || !match ||
88
88
  !navigator.userAgent.includes('(Linux; Android 10; K)') ||
89
89
  navigator.userAgent.includes('Version/4.0') || !navigator.userAgentData) return false;
90
90
  const ch = await navigator.userAgentData.getHighEntropyValues([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capiumbrowser",
3
- "version": "1.2.4",
3
+ "version": "1.3.1",
4
4
  "description": "Drop-in stealth browser SDK on top of Playwright or Puppeteer (Capium Chromium).",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Capzy (https://capzy.ai)",
package/playwright.js CHANGED
@@ -27,7 +27,7 @@ const human = require('./lib/human');
27
27
  const license = require('./lib/license');
28
28
  const readiness = require('./lib/license-readiness');
29
29
  const { CapiumError, translateLaunchError, readLaunchStatus } = require('./lib/errors');
30
- const { proxyAndGeoArgs, buildArgs, newStatusFile, clearStatusFile } =
30
+ const { proxyAndGeoArgs, buildArgs, newStatusFile, clearStatusFile, windowBoundsForSeed } =
31
31
  require('./lib/launch-common');
32
32
 
33
33
  function requireDriver() {
@@ -161,9 +161,9 @@ function applyViewportDefaults(browser, mobileSeed = null, mobileDevice = null,
161
161
  *
162
162
  * seed/platform : identity. stealthArgs: false drops the default fingerprint flags.
163
163
  * proxy : "http://user:pass@host:port" | "socks5://host:port" | {server, username, ...}.
164
- * geoip : geo coherence, resolved inside the binary. null (default) = OFF: no lookup
165
- * runs, even with a proxy. true = ON: pins timezone/geo/WebRTC/language to the
166
- * egress (proxy exit, or the host IP if proxyless). false = explicit opt-out.
164
+ * geoip : geo coherence, resolved inside the binary. null (default) = ON whenever a
165
+ * proxy is set (pins timezone/geo/WebRTC/language to the exit), OFF otherwise.
166
+ * true = ON even proxyless (host IP). false = explicit opt-out.
167
167
  * timezone/locale: explicit override (IANA tz + BCP-47 locale; in-binary spoof).
168
168
  * licenseKey/licenseServer: pass the license programmatically (else read from
169
169
  * CAPIUM_LICENSE_KEY / ~/.capium/license). The binary self-licenses; the key
@@ -298,12 +298,11 @@ async function fitWindow(browser, context, page) {
298
298
  if (!scr || !scr.w || !scr.h) return;
299
299
  const aw = Math.trunc(scr.w);
300
300
  const ah = Math.trunc(scr.h);
301
- // Size the window to the FULL per-seed screen (match python _fit_window_to_screen) so
302
- // screen ~= viewport -- avoids the browserscan "screen != viewport" VM tell. NOT a forced 1440x900.
303
- const w = aw;
304
- const h = ah;
305
- const left = 0;
306
- const top = 0;
301
+ // Per-seed geometry within the seed's screen: maximized-like for most seeds,
302
+ // realistically windowed for the rest (match python _window_bounds_for_seed).
303
+ const seed = (context && context._capiumSeed) ||
304
+ (browser && browser._capiumSeed) || 0;
305
+ const { left, top, width: w, height: h } = windowBoundsForSeed(seed, aw, ah);
307
306
  pcdp = await context.newCDPSession(page);
308
307
  const { targetInfo } = await pcdp.send('Target.getTargetInfo');
309
308
  // Persistent contexts may have no Browser handle; their page session still
package/puppeteer.js CHANGED
@@ -31,7 +31,7 @@ const human = require('./lib/human');
31
31
  const license = require('./lib/license');
32
32
  const readiness = require('./lib/license-readiness');
33
33
  const { CapiumError, translateLaunchError, readLaunchStatus } = require('./lib/errors');
34
- const { proxyAndGeoArgs, buildArgs, newStatusFile, clearStatusFile } =
34
+ const { proxyAndGeoArgs, buildArgs, newStatusFile, clearStatusFile, windowBoundsForSeed } =
35
35
  require('./lib/launch-common');
36
36
 
37
37
  function requireDriver() {
@@ -267,12 +267,10 @@ async function fitWindow(browser, page) {
267
267
  if (!scr || !scr.w || !scr.h) return;
268
268
  const aw = Math.trunc(scr.w);
269
269
  const ah = Math.trunc(scr.h);
270
- // Size the window to the FULL per-seed screen (match python _fit_window_to_screen) so
271
- // screen ~= viewport -- avoids the browserscan "screen != viewport" VM tell. NOT a forced 1440x900.
272
- const w = aw;
273
- const h = ah;
274
- const left = 0;
275
- const top = 0;
270
+ // Per-seed geometry within the seed's screen: maximized-like for most seeds,
271
+ // realistically windowed for the rest (match python _window_bounds_for_seed).
272
+ const seed = (browser && browser._capiumSeed) || 0;
273
+ const { left, top, width: w, height: h } = windowBoundsForSeed(seed, aw, ah);
276
274
  cdp = await page.createCDPSession();
277
275
  const { targetInfo } = await cdp.send('Target.getTargetInfo');
278
276
  const { windowId } = await cdp.send('Browser.getWindowForTarget',