sphere-cli 0.1.41 → 0.2.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sphere-cli",
3
- "version": "0.1.41",
4
- "description": "SPHERE CLI — synthetic data generation, evaluation, and certification",
3
+ "version": "0.2.0",
4
+ "description": "SPHERE CLI — synthetic data generation, evaluation, and certification (sealed native binary)",
5
5
  "keywords": ["synthetic-data", "privacy", "cli", "data-science"],
6
6
  "homepage": "https://github.com/statzihuai/sphere-cli",
7
7
  "bugs": "https://github.com/statzihuai/sphere-cli/issues",
@@ -14,19 +14,12 @@
14
14
  "sphere": "bin/sphere.js"
15
15
  },
16
16
  "scripts": {
17
- "postinstall": "node scripts/postinstall.js"
18
- },
19
- "dependencies": {
20
- "bytenode": "^1.5.7"
17
+ "postinstall": "node scripts/postinstall.js",
18
+ "release": "bash scripts/release.sh"
21
19
  },
22
20
  "files": [
23
- "bin/",
24
- "examples/",
25
- "scripts/postinstall.js",
26
- "scripts/evaluate.py",
27
- "scripts/certify.py",
28
- "sphere_cli/",
29
- "sphere-node.js"
21
+ "bin/sphere.js",
22
+ "scripts/postinstall.js"
30
23
  ],
31
24
  "engines": {
32
25
  "node": ">=18.0.0"
@@ -1,93 +1,106 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * SPHERE CLI postinstall — compiles sphere-node.js to V8 bytecode.
4
+ * SPHERE CLI postinstall — download the platform-specific sealed binary.
5
5
  *
6
- * sphere-node.js in this package is an obfuscated JS bundle of the SPHERE
7
- * algorithm. Compiling it to V8 bytecode (.jsc) at install time ensures the
8
- * bytecode matches the Node.js version on this machine, preventing
9
- * decompilation. After compilation the source file is replaced by a tiny
10
- * loader stub so only bytecode remains on disk.
6
+ * The SPHERE engine ships as a signed + notarized native binary (one per
7
+ * platform) hosted on GitHub Releases. This script detects the platform,
8
+ * downloads the matching tarball, verifies its SHA-256 against the published
9
+ * SHA256SUMS.txt, and extracts it into ./vendor/. No algorithm source ships in
10
+ * the npm package only this downloader + a thin launcher.
11
11
  *
12
- * This mirrors exactly how the SPHERE desktop app protects its code.
12
+ * Env:
13
+ * SPHERE_SKIP_POSTINSTALL=1 skip download (CI / offline)
14
+ * SPHERE_BINARY_BASEURL=... override the release base URL (testing)
13
15
  */
14
16
 
17
+ const fs = require('fs');
15
18
  const path = require('path');
16
- const fs = require('fs');
19
+ const crypto = require('crypto');
20
+ const { execFileSync } = require('child_process');
17
21
 
18
- const PKG_DIR = path.join(__dirname, '..');
19
- const SRC = path.join(PKG_DIR, 'sphere-node.js');
20
- const OUT = path.join(PKG_DIR, 'sphere-node.jsc');
21
- const STUB = "'use strict';\nrequire('bytenode');\nrequire('./sphere-node.jsc');\n";
22
- const VERSION = require('../package.json').version;
23
- const MARKER = path.join(PKG_DIR, '.sphere-node-version');
22
+ const PKG = require('../package.json');
23
+ const VERSION = PKG.version;
24
+ const REPO = 'statzihuai/sphere-cli';
25
+
26
+ const PLATFORM = process.platform; // 'darwin' | 'linux'
27
+ const ARCH = process.arch; // 'arm64' | 'x64'
28
+ const KEY = `${PLATFORM}-${ARCH}`;
29
+ const SUPPORTED = new Set(['darwin-arm64', 'darwin-x64', 'linux-x64']);
30
+
31
+ const PKG_ROOT = path.join(__dirname, '..');
32
+ const VENDOR = path.join(PKG_ROOT, 'vendor');
33
+ const ASSET = `sphere-${KEY}.tar.gz`;
34
+ const BASE = process.env.SPHERE_BINARY_BASEURL
35
+ || `https://github.com/${REPO}/releases/download/v${VERSION}`;
36
+
37
+ function log(m) { process.stdout.write(`sphere-cli: ${m}\n`); }
38
+ function fail(m) { process.stderr.write(`sphere-cli: ${m}\n`); process.exit(1); }
24
39
 
25
- // ── Skip flag ─────────────────────────────────────────────────────────────────
26
40
  if (process.env.SPHERE_SKIP_POSTINSTALL === '1') {
27
- process.stdout.write('SPHERE_SKIP_POSTINSTALL=1 — skipping bytecode compilation.\n');
41
+ log('SPHERE_SKIP_POSTINSTALL=1 — skipping binary download.');
28
42
  process.exit(0);
29
43
  }
30
44
 
31
- // ── Already compiled for this version? ───────────────────────────────────────
32
- const installedVer = fs.existsSync(MARKER)
33
- ? fs.readFileSync(MARKER, 'utf8').trim()
34
- : null;
35
-
36
- if (fs.existsSync(OUT) && installedVer === VERSION) {
37
- process.stdout.write(`✓ SPHERE algorithm already compiled for v${VERSION}.\n`);
45
+ if (!SUPPORTED.has(KEY)) {
46
+ // Don't hard-fail the install; the launcher prints guidance if run.
47
+ process.stderr.write(
48
+ `sphere-cli: no prebuilt binary for ${KEY} yet ` +
49
+ `(supported: ${[...SUPPORTED].join(', ')}).\n`,
50
+ );
38
51
  process.exit(0);
39
52
  }
40
53
 
41
- // ── Source check ──────────────────────────────────────────────────────────────
42
- if (!fs.existsSync(SRC)) {
43
- process.stderr.write('sphere-node.js not found in package — reinstall sphere-cli.\n');
44
- process.exit(1);
54
+ // Already installed for this version?
55
+ const STAMP = path.join(VENDOR, '.version');
56
+ const BIN = path.join(VENDOR, 'sphere-cli', 'sphere');
57
+ if (fs.existsSync(BIN) && fs.existsSync(STAMP) &&
58
+ fs.readFileSync(STAMP, 'utf8').trim() === VERSION) {
59
+ log(`binary already present for v${VERSION}.`);
60
+ process.exit(0);
45
61
  }
46
62
 
47
- const srcText = fs.readFileSync(SRC, 'utf8');
48
- if (srcText.includes("require('bytenode')") && srcText.length < 200) {
49
- // Already stubbed from a previous install .jsc must exist
50
- if (fs.existsSync(OUT)) {
51
- process.stdout.write('✓ SPHERE algorithm already compiled.\n');
52
- process.exit(0);
53
- }
54
- process.stderr.write('sphere-node.js is a stub but sphere-node.jsc is missing. Reinstall sphere-cli.\n');
55
- process.exit(1);
63
+ function curl(url, dest) {
64
+ execFileSync('curl', ['-fL', '--retry', '3', '--retry-delay', '2',
65
+ '--connect-timeout', '30', '-o', dest, url], { stdio: ['ignore', 'ignore', 'inherit'] });
56
66
  }
57
67
 
58
- // ── Compile ───────────────────────────────────────────────────────────────────
59
- process.stdout.write('Compiling SPHERE algorithm for your Node.js version …\n');
68
+ function sha256(file) {
69
+ const h = crypto.createHash('sha256');
70
+ h.update(fs.readFileSync(file));
71
+ return h.digest('hex');
72
+ }
60
73
 
61
- let bytenode;
62
74
  try {
63
- bytenode = require('bytenode');
64
- } catch {
65
- process.stderr.write(
66
- 'bytenode is not installed — this is unexpected.\n' +
67
- 'Try: cd "$(npm root -g)/sphere-cli" && npm install bytenode\n',
68
- );
69
- process.exit(1);
70
- }
75
+ fs.mkdirSync(VENDOR, { recursive: true });
76
+ const tarball = path.join(VENDOR, ASSET);
77
+
78
+ log(`downloading ${ASSET} (v${VERSION}) …`);
79
+ curl(`${BASE}/${ASSET}`, tarball);
71
80
 
72
- Promise.resolve(bytenode.compileFile({ filename: SRC, output: OUT }))
73
- .then(() => {
74
- // Replace source with loader stub so no readable code remains on disk
75
- fs.writeFileSync(SRC, STUB, 'utf8');
76
- // Write version marker
77
- fs.writeFileSync(MARKER, VERSION, 'utf8');
78
-
79
- const sizeKB = (fs.statSync(OUT).size / 1024).toFixed(1);
80
- process.stdout.write(`✓ SPHERE algorithm compiled (${sizeKB} KB).\n\n`);
81
- process.stdout.write(' Quick start:\n');
82
- process.stdout.write(' sphere generate data.csv -o synthetic.csv\n');
83
- process.stdout.write(" Run 'sphere --help' for all options.\n\n");
84
- })
85
- .catch((err) => {
86
- process.stderr.write(`\n✗ SPHERE compilation failed: ${err.message}\n`);
87
- process.stderr.write(
88
- 'The package was installed but the algorithm could not be compiled.\n' +
89
- 'Try reinstalling: npm install -g sphere-cli\n\n',
90
- );
91
- // Exit 0 so npm install doesn't fail — will error on first use
92
- process.exit(0);
93
- });
81
+ // Verify checksum against the published SHA256SUMS.txt
82
+ const sumsFile = path.join(VENDOR, 'SHA256SUMS.txt');
83
+ curl(`${BASE}/SHA256SUMS.txt`, sumsFile);
84
+ const sums = fs.readFileSync(sumsFile, 'utf8');
85
+ const expected = sums.split('\n')
86
+ .map((l) => l.trim().split(/\s+/))
87
+ .find((p) => p[1] && p[1].endsWith(ASSET));
88
+ if (!expected) fail(`no checksum for ${ASSET} in SHA256SUMS.txt`);
89
+ const got = sha256(tarball);
90
+ if (got !== expected[0]) {
91
+ fail(`checksum mismatch for ${ASSET}\n expected ${expected[0]}\n got ${got}`);
92
+ }
93
+ log('checksum verified ✓');
94
+
95
+ // Extract
96
+ fs.rmSync(path.join(VENDOR, 'sphere-cli'), { recursive: true, force: true });
97
+ execFileSync('tar', ['-xzf', tarball, '-C', VENDOR], { stdio: 'inherit' });
98
+ fs.chmodSync(BIN, 0o755);
99
+ fs.unlinkSync(tarball);
100
+ fs.writeFileSync(STAMP, VERSION);
101
+
102
+ log(`installed sealed binary for ${KEY} ✓`);
103
+ } catch (e) {
104
+ fail(`failed to install binary: ${e.message}\n` +
105
+ `You can retry with: npm rebuild sphere-cli`);
106
+ }