@tokenade/cli 0.8.12 → 0.8.13

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/bin/tokenade.js CHANGED
@@ -3,16 +3,35 @@
3
3
  const { spawnSync } = require("node:child_process");
4
4
  const path = require("node:path");
5
5
  const fs = require("node:fs");
6
+ const { platformPackageName, binaryName } = require("../platform.js");
6
7
 
7
- const bin = path.join(
8
- __dirname,
9
- "..",
10
- "vendor",
11
- process.platform === "win32" ? "tokenade.exe" : "tokenade",
12
- );
13
- if (!fs.existsSync(bin)) {
8
+ // Resolve the binary, first hit wins (see platform.js for the two delivery
9
+ // paths): (1) the matching per-platform optionalDependency from the registry,
10
+ // (2) ./vendor, where install.js's download fallback drops it.
11
+ function resolveBinary() {
12
+ const name = binaryName();
13
+ // 1. Platform sub-package. A skipped optional dep throws MODULE_NOT_FOUND;
14
+ // resolve its package.json so we don't depend on a "main" entry.
15
+ try {
16
+ const pkgJson = require.resolve(`${platformPackageName()}/package.json`);
17
+ const bin = path.join(path.dirname(pkgJson), name);
18
+ if (fs.existsSync(bin)) return bin;
19
+ } catch {
20
+ // optional dep absent — fall through to the vendored copy.
21
+ }
22
+ // 2. Downloaded fallback.
23
+ const vendored = path.join(__dirname, "..", "vendor", name);
24
+ if (fs.existsSync(vendored)) return vendored;
25
+ return null;
26
+ }
27
+
28
+ const bin = resolveBinary();
29
+ if (!bin) {
14
30
  console.error(
15
- "tokenade: binary not found. Re-run `npm install -g @tokenade/cli` (the postinstall downloads it).",
31
+ "tokenade: binary not found. Re-run `npm install -g @tokenade/cli` " +
32
+ "(the platform binary ships as an optional dependency; a postinstall " +
33
+ "download is the fallback). If you installed with --no-optional or " +
34
+ "--ignore-scripts, reinstall without them.",
16
35
  );
17
36
  process.exit(1);
18
37
  }
package/install.js CHANGED
@@ -27,6 +27,11 @@ const http = require("node:http");
27
27
  const https = require("node:https");
28
28
  const crypto = require("node:crypto");
29
29
  const { URL } = require("node:url");
30
+ const {
31
+ rustTarget,
32
+ platformPackageName,
33
+ binaryName,
34
+ } = require("./platform.js");
30
35
 
31
36
  const DOWNLOADS_BASE =
32
37
  process.env.TOKENADE_DOWNLOADS_BASE || "https://downloads.tokenade.net";
@@ -38,52 +43,6 @@ const VENDOR = path.join(__dirname, "vendor");
38
43
  const REQUEST_TIMEOUT_MS = Number(process.env.TOKENADE_HTTP_TIMEOUT_MS) || 30000;
39
44
  const MAX_ATTEMPTS = 4;
40
45
 
41
- // Is this Linux userland musl (Alpine, Void-musl, …) rather than glibc?
42
- // A glibc node reports its runtime glibc version in the process report header;
43
- // a musl node does not. We fall back to looking for the musl loader on disk.
44
- // When genuinely unsure we return false (glibc) — that preserves the historical
45
- // x86_64-gnu mapping for the overwhelming glibc majority, so a detection miss is
46
- // never worse than the status quo.
47
- function isMuslLinux() {
48
- if (process.platform !== "linux") return false;
49
- try {
50
- const header = process.report && process.report.getReport().header;
51
- if (header && typeof header.glibcVersionRuntime === "string") return false;
52
- if (header && "glibcVersionRuntime" in header) {
53
- // key present but not a string ⇒ no glibc runtime ⇒ musl
54
- return true;
55
- }
56
- } catch {
57
- // fall through to the on-disk probe
58
- }
59
- try {
60
- return fs
61
- .readdirSync("/lib")
62
- .some((f) => f.startsWith("ld-musl-"));
63
- } catch {
64
- return false;
65
- }
66
- }
67
-
68
- function rustTarget() {
69
- const p = process.platform;
70
- const a = process.arch;
71
- if (p === "darwin")
72
- return a === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin";
73
- if (p === "linux") {
74
- if (a === "arm64") return "aarch64-unknown-linux-musl";
75
- // x86_64: a fully-static musl build runs on glibc too, but musl's DNS
76
- // resolver ignores /etc/nsswitch.conf — so we only hand musl to machines
77
- // whose libc is already musl (Alpine et al.), and keep the proven glibc
78
- // build as the default everywhere else.
79
- return isMuslLinux()
80
- ? "x86_64-unknown-linux-musl"
81
- : "x86_64-unknown-linux-gnu";
82
- }
83
- if (p === "win32") return "x86_64-pc-windows-gnu";
84
- throw new Error(`unsupported platform: ${p}/${a}`);
85
- }
86
-
87
46
  // Resolve the proxy that applies to `targetUrl`, honouring the standard
88
47
  // HTTPS_PROXY / HTTP_PROXY / ALL_PROXY and NO_PROXY env vars (both cases).
89
48
  // Returns null when no proxy applies — including when NO_PROXY matches.
@@ -260,6 +219,28 @@ async function downloadTo(url, dest) {
260
219
 
261
220
  async function main() {
262
221
  const target = rustTarget();
222
+
223
+ // Fast path: the binary is already here, so the download is pure waste.
224
+ // It arrives one of two ways — the matching per-platform optionalDependency
225
+ // npm just installed from the registry, or a ./vendor copy from a previous
226
+ // run. Either way postinstall is a clean no-op. Crucially this returns
227
+ // BEFORE any network call, so an unreachable downloads.tokenade.net (blocked
228
+ // corporate proxy, offline sandbox) can never fail an install whose binary
229
+ // the registry already delivered.
230
+ try {
231
+ require.resolve(`${platformPackageName(target)}/package.json`);
232
+ console.log(
233
+ `✓ tokenade provided by ${platformPackageName(target)} (${target}) — no download needed.`,
234
+ );
235
+ return;
236
+ } catch {
237
+ // optional dep absent (skipped or no registry build) — try ./vendor.
238
+ }
239
+ if (fs.existsSync(path.join(VENDOR, binaryName()))) {
240
+ console.log(`✓ tokenade already vendored (${target}) — no download needed.`);
241
+ return;
242
+ }
243
+
263
244
  const manifest = await fetchJson(MANIFEST_URL);
264
245
  const entry = manifest.targets && manifest.targets[target];
265
246
  if (!entry || !entry.url || !entry.sha256) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tokenade/cli",
3
- "version": "0.8.12",
4
- "description": "Tokenade \u2014 cut your AI coding agent's token bill. Installs the Tokenade CLI (a local, paid token-reduction tool; activate via your browser).",
3
+ "version": "0.8.13",
4
+ "description": "Tokenade cut your AI coding agent's token bill. Installs the Tokenade CLI (a local, paid token-reduction tool; activate via your browser).",
5
5
  "homepage": "https://tokenade.net",
6
6
  "license": "UNLICENSED",
7
7
  "bin": {
@@ -14,8 +14,17 @@
14
14
  "dependencies": {
15
15
  "tar": "^7.5.16"
16
16
  },
17
+ "optionalDependencies": {
18
+ "@tokenade/cli-linux-x64": "0.8.13",
19
+ "@tokenade/cli-linux-x64-musl": "0.8.13",
20
+ "@tokenade/cli-linux-arm64-musl": "0.8.13",
21
+ "@tokenade/cli-darwin-arm64": "0.8.13",
22
+ "@tokenade/cli-darwin-x64": "0.8.13",
23
+ "@tokenade/cli-win32-x64": "0.8.13"
24
+ },
17
25
  "files": [
18
26
  "bin/",
27
+ "platform.js",
19
28
  "install.js",
20
29
  "uninstall.js",
21
30
  "README.md"
package/platform.js ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * platform.js — the ONE place that maps this machine to a Rust target and to
4
+ * the matching per-platform npm package. Both bin/tokenade.js (runtime resolve)
5
+ * and install.js (download fallback) require it, so the platform detection —
6
+ * the musl probe especially — never drifts between the two.
7
+ *
8
+ * The per-platform packages (@tokenade/cli-<os>-<cpu>[-musl]) carry the actual
9
+ * binary and are listed as optionalDependencies of @tokenade/cli. npm installs
10
+ * ONLY the one whose os/cpu/libc match the host, straight from the registry —
11
+ * the esbuild/swc/biome pattern. install.js's download is a fallback for when
12
+ * those optional deps were skipped (--no-optional / --ignore-scripts) or the
13
+ * registry didn't carry a matching build.
14
+ */
15
+
16
+ "use strict";
17
+
18
+ const fs = require("node:fs");
19
+
20
+ // Is this Linux userland musl (Alpine, Void-musl, …) rather than glibc?
21
+ // A glibc node reports its runtime glibc version in the process report header;
22
+ // a musl node does not. We fall back to looking for the musl loader on disk.
23
+ // When genuinely unsure we return false (glibc) — that preserves the historical
24
+ // x86_64-gnu mapping for the overwhelming glibc majority, so a detection miss is
25
+ // never worse than the status quo.
26
+ function isMuslLinux() {
27
+ if (process.platform !== "linux") return false;
28
+ try {
29
+ const header = process.report && process.report.getReport().header;
30
+ if (header && typeof header.glibcVersionRuntime === "string") return false;
31
+ if (header && "glibcVersionRuntime" in header) {
32
+ // key present but not a string ⇒ no glibc runtime ⇒ musl
33
+ return true;
34
+ }
35
+ } catch {
36
+ // fall through to the on-disk probe
37
+ }
38
+ try {
39
+ return fs
40
+ .readdirSync("/lib")
41
+ .some((f) => f.startsWith("ld-musl-"));
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ function rustTarget() {
48
+ const p = process.platform;
49
+ const a = process.arch;
50
+ if (p === "darwin")
51
+ return a === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin";
52
+ if (p === "linux") {
53
+ if (a === "arm64") return "aarch64-unknown-linux-musl";
54
+ // x86_64: a fully-static musl build runs on glibc too, but musl's DNS
55
+ // resolver ignores /etc/nsswitch.conf — so we only hand musl to machines
56
+ // whose libc is already musl (Alpine et al.), and keep the proven glibc
57
+ // build as the default everywhere else.
58
+ return isMuslLinux()
59
+ ? "x86_64-unknown-linux-musl"
60
+ : "x86_64-unknown-linux-gnu";
61
+ }
62
+ if (p === "win32") return "x86_64-pc-windows-gnu";
63
+ throw new Error(`unsupported platform: ${p}/${a}`);
64
+ }
65
+
66
+ // Rust target dir (in dist/) → the npm sub-package that carries its binary.
67
+ // Kept in lockstep with the six packages the release script publishes and the
68
+ // optionalDependencies in package.json — one source of truth for the mapping.
69
+ const PACKAGE_BY_TARGET = {
70
+ "x86_64-unknown-linux-gnu": "@tokenade/cli-linux-x64",
71
+ "x86_64-unknown-linux-musl": "@tokenade/cli-linux-x64-musl",
72
+ "aarch64-unknown-linux-musl": "@tokenade/cli-linux-arm64-musl",
73
+ "aarch64-apple-darwin": "@tokenade/cli-darwin-arm64",
74
+ "x86_64-apple-darwin": "@tokenade/cli-darwin-x64",
75
+ "x86_64-pc-windows-gnu": "@tokenade/cli-win32-x64",
76
+ };
77
+
78
+ // The per-platform sub-package name for the current host, or the given target.
79
+ function platformPackageName(target) {
80
+ const t = target || rustTarget();
81
+ const pkg = PACKAGE_BY_TARGET[t];
82
+ if (!pkg) throw new Error(`no npm package mapped for target: ${t}`);
83
+ return pkg;
84
+ }
85
+
86
+ // The binary filename inside a sub-package (and in ./vendor).
87
+ function binaryName() {
88
+ return process.platform === "win32" ? "tokenade.exe" : "tokenade";
89
+ }
90
+
91
+ module.exports = {
92
+ isMuslLinux,
93
+ rustTarget,
94
+ platformPackageName,
95
+ binaryName,
96
+ PACKAGE_BY_TARGET,
97
+ };