capiumbrowser 0.1.1 → 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 +22 -0
- package/lib/channels.json +5 -5
- package/lib/download.js +33 -14
- package/lib/license.js +3 -0
- package/lib/version.js +35 -0
- package/package.json +1 -1
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
|
|
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-
|
|
4
|
+
"updated": "2026-09-08",
|
|
5
5
|
"channels": {
|
|
6
6
|
"stable": {
|
|
7
|
-
"windows-x64": { "version": "152.0.7977.65", "revision":
|
|
8
|
-
"macos-arm64": { "version": "152.0.7977.65", "revision":
|
|
9
|
-
"linux-x64": { "version": "152.0.7977.64", "revision":
|
|
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,9 +41,10 @@ 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 { binaryVersionFor, isPublished } = require('./version');
|
|
44
|
+
const { SDK_VERSION, binaryVersionFor, binaryRevisionFor, buildId, isPublished } = require('./version');
|
|
45
45
|
|
|
46
46
|
const NET_TIMEOUT_MS = 300000;
|
|
47
|
+
const USER_AGENT = `capiumbrowser/${SDK_VERSION}`;
|
|
47
48
|
|
|
48
49
|
// (normalized os, normalized arch) -> distro tag used in the filename/URL.
|
|
49
50
|
const SUPPORTED = {
|
|
@@ -86,11 +87,16 @@ function downloadTag(system = null, machine = null) {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
/**
|
|
89
|
-
* The signed request path for a build
|
|
90
|
-
*
|
|
90
|
+
* The signed request path for a build: version in the folder, tag in the filename
|
|
91
|
+
* (`/download/distro/chromium-v<version>/capiumbrowser-<tag>.tar.gz`). Per-OS versions work
|
|
92
|
+
* because each tag lives under its own chromium-v<version>/ folder (win/mac .65 while linux
|
|
93
|
+
* is .64). Key travels in the X-Capzy-License header, the path is HMAC-signed, and the bytes
|
|
94
|
+
* are verified against the response's X-Capzy-SHA256.
|
|
91
95
|
*/
|
|
92
|
-
function distroPath(version, tag) {
|
|
93
|
-
|
|
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`;
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
// ---- extraction ------------------------------------------------------------------------------
|
|
@@ -216,8 +222,12 @@ function removeInstalls(root) {
|
|
|
216
222
|
*/
|
|
217
223
|
async function download(url, headers, dest, version, tag) {
|
|
218
224
|
let res;
|
|
225
|
+
// Identify the SDK on every request. undici otherwise sends a default UA that Cloudflare's
|
|
226
|
+
// Browser Integrity Check rejects as a bot (403) before the request reaches the origin. A
|
|
227
|
+
// stable product UA is both correct hygiene and what the edge allows.
|
|
228
|
+
const h = Object.assign({ 'User-Agent': USER_AGENT }, headers || {});
|
|
219
229
|
try {
|
|
220
|
-
res = await fetch(url, { headers:
|
|
230
|
+
res = await fetch(url, { headers: h, signal: AbortSignal.timeout(NET_TIMEOUT_MS) });
|
|
221
231
|
} catch (e) {
|
|
222
232
|
throw new CapiumServerDownError(
|
|
223
233
|
`could not reach the download server ${url}: ${e.cause?.message || e.message || e}`);
|
|
@@ -281,14 +291,23 @@ async function ensureBinary({ version = null, licenseKey = null, server = null }
|
|
|
281
291
|
'Set CAPIUM_VERSION to pin a specific build if one exists.');
|
|
282
292
|
}
|
|
283
293
|
|
|
284
|
-
|
|
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);
|
|
285
304
|
|
|
286
|
-
// Reuse the cached binary ONLY if it's already
|
|
287
|
-
//
|
|
288
|
-
// falls through to fetch the new
|
|
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.
|
|
289
308
|
try {
|
|
290
309
|
const existing = config.findBinary();
|
|
291
|
-
if (readInstalledVersion(existing) ===
|
|
310
|
+
if (readInstalledVersion(existing) === bid) return existing;
|
|
292
311
|
} catch {}
|
|
293
312
|
|
|
294
313
|
let url;
|
|
@@ -299,7 +318,7 @@ async function ensureBinary({ version = null, licenseKey = null, server = null }
|
|
|
299
318
|
} else {
|
|
300
319
|
// Signed path-based download: key in a header, PATH HMAC-signed, sha256-verified.
|
|
301
320
|
const { key, server: srv } = license.resolve(licenseKey, server); // -> CapiumConfigError if no key
|
|
302
|
-
const p = distroPath(version, tag);
|
|
321
|
+
const p = distroPath(version, tag, revision);
|
|
303
322
|
url = srv + p;
|
|
304
323
|
headers = license.getHeaders(key, p);
|
|
305
324
|
}
|
|
@@ -319,7 +338,7 @@ async function ensureBinary({ version = null, licenseKey = null, server = null }
|
|
|
319
338
|
// marker files, and findBinary only accepts a bare chrome.exe next to one -- the
|
|
320
339
|
// downloader vouches for the directory it just extracted.
|
|
321
340
|
try {
|
|
322
|
-
fs.writeFileSync(path.join(distroDir, MARKER), String(
|
|
341
|
+
fs.writeFileSync(path.join(distroDir, MARKER), String(bid));
|
|
323
342
|
} catch {}
|
|
324
343
|
} finally {
|
|
325
344
|
try {
|
|
@@ -335,7 +354,7 @@ async function ensureBinary({ version = null, licenseKey = null, server = null }
|
|
|
335
354
|
`downloaded and extracted the ${tag} build but no launch target was found afterwards ` +
|
|
336
355
|
`(unexpected archive layout under ${root}).`);
|
|
337
356
|
}
|
|
338
|
-
stampVersion(binPath,
|
|
357
|
+
stampVersion(binPath, bid); // so a later version/revision bump knows to re-download
|
|
339
358
|
// Re-apply exec bits the wrapper + engine binaries need (zip drops unix perms).
|
|
340
359
|
if (process.platform !== 'win32') {
|
|
341
360
|
for (const p of [binPath, path.join(path.dirname(binPath), 'chrome')]) {
|
package/lib/license.js
CHANGED
|
@@ -14,8 +14,10 @@ const os = require('os');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
|
|
16
16
|
const { CapiumConfigError, CapiumExpiredError, CapiumServerDownError } = require('./errors');
|
|
17
|
+
const { SDK_VERSION } = require('./version');
|
|
17
18
|
|
|
18
19
|
const DEFAULT_SERVER = 'https://license.capzy.ai';
|
|
20
|
+
const USER_AGENT = `capiumbrowser/${SDK_VERSION}`;
|
|
19
21
|
|
|
20
22
|
function licenseFile() {
|
|
21
23
|
// CAPIUM_LICENSE_FILE is a test/deploy override; the contract path is ~/.capium/license.
|
|
@@ -167,6 +169,7 @@ async function status(key = null, server = null) {
|
|
|
167
169
|
'X-Capzy-Device-Fp': fp,
|
|
168
170
|
'X-Capzy-Signature': sig,
|
|
169
171
|
'Content-Type': 'application/json',
|
|
172
|
+
'User-Agent': USER_AGENT,
|
|
170
173
|
},
|
|
171
174
|
signal: AbortSignal.timeout(10000),
|
|
172
175
|
});
|
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