capiumbrowser 1.0.0 → 1.0.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.
package/README.md CHANGED
@@ -91,6 +91,28 @@ launch the SDK fetches the per-platform build from the license service (authenti
91
91
  checksum-verified) and caches it under `~/.capium` (`CAPIUM_HOME` overrides — it deliberately
92
92
  lives *outside* `node_modules`, so reinstalls never delete it).
93
93
 
94
+ ### Download the browser — just your license key
95
+
96
+ The browser binary is fetched with **only your Capium license key** — no separate token, no
97
+ manual download, no per-platform URL to figure out. Set the key once:
98
+
99
+ ```bash
100
+ export CAPIUM_LICENSE_KEY=cap_your_key_here
101
+ # or put a `KEY=cap_...` line in ~/.capium/license
102
+ ```
103
+
104
+ Then the right build for your OS/arch downloads automatically — either **on first launch**, or
105
+ ahead of time with the CLI:
106
+
107
+ ```bash
108
+ npx capiumbrowser install # ...or skip it: the first launch() downloads the binary for you.
109
+ ```
110
+
111
+ That same key both **authorizes the download** and **licenses the browser**, and it's passed to
112
+ the binary through the environment — never on argv. The download is checksum-verified before
113
+ anything runs and cached, so it's fetched once and reused. Run `npx capiumbrowser` (the `info`
114
+ check) to see your license, the installed build, and anything still missing.
115
+
94
116
  **Migrating?** It's a one-line change — the returned object is a standard Playwright /
95
117
  Puppeteer `Browser`:
96
118
 
package/lib/channels.json CHANGED
@@ -1,12 +1,12 @@
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 (so the server/website can resolve 'latest stable' without a new SDK release). `version` is the Chromium base of the immutable artifact folder; `revision` is the cap<N> re-spin counter. `published: false` means the tag is declared but no artifact exists yet -- the SDK raises a clear 'not published yet' error instead of a raw 404.",
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 Chromium base; `revision` is the re-spin counter -- the served R2 folder is chromium-v<version>/ for revision 1 (the base build) and chromium-v<version>-r<N>/ for a re-spin (N>=2), so publishing a re-spin never overwrites the base artifact older SDKs still fetch. `published: false` means the tag is declared but no artifact exists yet -- the SDK raises a clear 'not published yet' error instead of a raw 404.",
3
3
  "schema": 1,
4
- "updated": "2026-09-02",
4
+ "updated": "2026-09-08",
5
5
  "channels": {
6
6
  "stable": {
7
- "windows-x64": { "version": "152.0.7977.65", "revision": 2, "sha256": "eec6d63a8a1ae7371388a39d10506117261131768020f15aa2d281d9931d35c0", "published": true },
8
- "macos-arm64": { "version": "152.0.7977.65", "revision": 2, "sha256": "350e7f87b8f8e7e32feee26b8844e80b4650c9aa5fb6888fbcf4cc19fb08eda9", "published": true },
9
- "linux-x64": { "version": "152.0.7977.64", "revision": 2, "sha256": "248e735b3afec58a44e962cd866f704adfb15ee8b71cf976665dd109f512b7c5", "published": true },
7
+ "windows-x64": { "version": "152.0.7977.65", "revision": 2, "sha256": "1556ad4fd5fe555b3c427be5e868bcace705f271ec3bcd6be4674cae8163ffc6", "published": true },
8
+ "macos-arm64": { "version": "152.0.7977.65", "revision": 2, "sha256": "a71654da4b0e1b7ab2f8374e62ba5378decc5b7762953cdbe6946781d8e08c33", "published": true },
9
+ "linux-x64": { "version": "152.0.7977.64", "revision": 2, "sha256": "cf26c55e86038eb33e1de607ade5619f0494188624b30bb508fa9429c0e98a17", "published": true },
10
10
  "linux-arm64": { "version": "152.0.7977.64", "revision": 1, "sha256": null, "published": false }
11
11
  }
12
12
  }
package/lib/download.js CHANGED
@@ -41,7 +41,7 @@ const tar = require('tar');
41
41
  const config = require('./config');
42
42
  const license = require('./license');
43
43
  const { CapiumError, CapiumExpiredError, CapiumServerDownError } = require('./errors');
44
- const { SDK_VERSION, binaryVersionFor, isPublished } = require('./version');
44
+ const { SDK_VERSION, binaryVersionFor, binaryRevisionFor, buildId, isPublished } = require('./version');
45
45
 
46
46
  const NET_TIMEOUT_MS = 300000;
47
47
  const USER_AGENT = `capiumbrowser/${SDK_VERSION}`;
@@ -93,8 +93,10 @@ function downloadTag(system = null, machine = null) {
93
93
  * is .64). Key travels in the X-Capzy-License header, the path is HMAC-signed, and the bytes
94
94
  * are verified against the response's X-Capzy-SHA256.
95
95
  */
96
- function distroPath(version, tag) {
97
- return `/download/distro/chromium-v${version}/capiumbrowser-${tag}.tar.gz`;
96
+ function distroPath(version, tag, revision = 1) {
97
+ // A same-engine re-spin (revision>=2) lands in its own immutable folder
98
+ // chromium-v<version>-r<N>/, so publishing it never overwrites the base artifact older SDKs fetch.
99
+ return `/download/distro/chromium-v${buildId(version, revision)}/capiumbrowser-${tag}.tar.gz`;
98
100
  }
99
101
 
100
102
  // ---- extraction ------------------------------------------------------------------------------
@@ -289,14 +291,23 @@ async function ensureBinary({ version = null, licenseKey = null, server = null }
289
291
  'Set CAPIUM_VERSION to pin a specific build if one exists.');
290
292
  }
291
293
 
292
- version = version || process.env.CAPIUM_VERSION || binaryVersionFor(tag);
294
+ const envVer = process.env.CAPIUM_VERSION;
295
+ version = version || envVer || binaryVersionFor(tag);
296
+ // A same Chromium version can be re-spun (r2, r3, ...). CAPIUM_REVISION overrides; an explicit
297
+ // CAPIUM_VERSION pin defaults to the base (r1) unless a revision is given; otherwise the per-OS
298
+ // revision comes from channels.json.
299
+ let revision;
300
+ if (process.env.CAPIUM_REVISION) revision = parseInt(process.env.CAPIUM_REVISION, 10);
301
+ else if (envVer) revision = 1;
302
+ else revision = binaryRevisionFor(tag);
303
+ const bid = buildId(version, revision);
293
304
 
294
- // Reuse the cached binary ONLY if it's already the target version. A different (older)
295
- // version -- e.g. after `npm install capiumbrowser@latest` bumps this OS's pinned build --
296
- // falls through to fetch the new build and drop the old one.
305
+ // Reuse the cached binary ONLY if it's already this exact build (version + revision). A
306
+ // different build -- e.g. after `npm install capiumbrowser@latest` bumps this OS's pinned build
307
+ // or re-spin -- falls through to fetch the new one and drop the old.
297
308
  try {
298
309
  const existing = config.findBinary();
299
- if (readInstalledVersion(existing) === version) return existing;
310
+ if (readInstalledVersion(existing) === bid) return existing;
300
311
  } catch {}
301
312
 
302
313
  let url;
@@ -307,7 +318,7 @@ async function ensureBinary({ version = null, licenseKey = null, server = null }
307
318
  } else {
308
319
  // Signed path-based download: key in a header, PATH HMAC-signed, sha256-verified.
309
320
  const { key, server: srv } = license.resolve(licenseKey, server); // -> CapiumConfigError if no key
310
- const p = distroPath(version, tag);
321
+ const p = distroPath(version, tag, revision);
311
322
  url = srv + p;
312
323
  headers = license.getHeaders(key, p);
313
324
  }
@@ -327,7 +338,7 @@ async function ensureBinary({ version = null, licenseKey = null, server = null }
327
338
  // marker files, and findBinary only accepts a bare chrome.exe next to one -- the
328
339
  // downloader vouches for the directory it just extracted.
329
340
  try {
330
- fs.writeFileSync(path.join(distroDir, MARKER), String(version));
341
+ fs.writeFileSync(path.join(distroDir, MARKER), String(bid));
331
342
  } catch {}
332
343
  } finally {
333
344
  try {
@@ -343,7 +354,7 @@ async function ensureBinary({ version = null, licenseKey = null, server = null }
343
354
  `downloaded and extracted the ${tag} build but no launch target was found afterwards ` +
344
355
  `(unexpected archive layout under ${root}).`);
345
356
  }
346
- stampVersion(binPath, version); // so a later version bump knows to re-download
357
+ stampVersion(binPath, bid); // so a later version/revision bump knows to re-download
347
358
  // Re-apply exec bits the wrapper + engine binaries need (zip drops unix perms).
348
359
  if (process.platform !== 'win32') {
349
360
  for (const p of [binPath, path.join(path.dirname(binPath), 'chrome')]) {
package/lib/version.js CHANGED
@@ -62,6 +62,38 @@ function binaryVersionFor(tag) {
62
62
  return CAPIUM_BINARY_VERSIONS[tag] || DEFAULT_BINARY_VERSION;
63
63
  }
64
64
 
65
+ function revisionsFromManifest() {
66
+ if (!STABLE) return {};
67
+ const out = {};
68
+ for (const [tag, entry] of Object.entries(STABLE)) {
69
+ if (entry && typeof entry === 'object' && Number.isInteger(entry.revision)) out[tag] = entry.revision;
70
+ }
71
+ return out;
72
+ }
73
+
74
+ // Re-spin variant per tag (>=1). Same Chromium version can be rebuilt (a stealth/fingerprint
75
+ // change with no engine bump); each re-spin increments this. 1 = the base build.
76
+ const CAPIUM_BINARY_REVISIONS = revisionsFromManifest();
77
+
78
+ /**
79
+ * The re-spin variant (>=1) for a distro tag. 1 = the base build (suffix-less folder); 2,3,...
80
+ * are re-spins of the SAME Chromium version served from chromium-v<version>-r<N>/ (so a re-spin
81
+ * never overwrites the base artifact older SDKs still fetch). A version bump resets this to 1.
82
+ */
83
+ function binaryRevisionFor(tag) {
84
+ return CAPIUM_BINARY_REVISIONS[tag] || 1;
85
+ }
86
+
87
+ /**
88
+ * Stable identity of one specific build = the R2 folder discriminator AND the install-cache key.
89
+ * revision 1 -> the version itself (chromium-v<version>/, the base build); N>=2 -> '<version>-r<N>'
90
+ * (chromium-v<version>-r<N>/). So a revision bump on an unchanged engine still re-downloads.
91
+ */
92
+ function buildId(version, revision) {
93
+ const n = parseInt(revision, 10);
94
+ return !n || n < 2 ? version : `${version}-r${n}`;
95
+ }
96
+
65
97
  /**
66
98
  * False only when the manifest declares the tag but marks it not-yet-published. Unknown
67
99
  * tags / missing manifest return true so the download layer's own error/404 speaks instead.
@@ -93,7 +125,10 @@ module.exports = {
93
125
  SDK_VERSION,
94
126
  CAPIUM_BINARY_VERSION,
95
127
  CAPIUM_BINARY_VERSIONS,
128
+ CAPIUM_BINARY_REVISIONS,
96
129
  binaryVersionFor,
130
+ binaryRevisionFor,
131
+ buildId,
97
132
  isPublished,
98
133
  hostTag,
99
134
  _FALLBACK_VERSIONS: FALLBACK_VERSIONS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capiumbrowser",
3
- "version": "1.0.0",
3
+ "version": "1.0.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)",