berkelium-studio 0.0.0-stage → 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/README.md CHANGED
@@ -1,3 +1,24 @@
1
- # Temporary Holding Version
1
+ # berkelium-studio
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ One command to install and open **Berkelium Studio**:
4
+
5
+ ```
6
+ npx berkelium-studio
7
+ ```
8
+
9
+ It downloads the installer for your computer from the official GitHub release, checks it against the
10
+ release's `SHA256SUMS.txt`, and installs it:
11
+
12
+ | System | What happens |
13
+ |---|---|
14
+ | macOS (Apple Silicon) | Copies Berkelium.app into Applications and opens it |
15
+ | Windows 10/11 (64-bit) | Runs the Berkelium installer |
16
+ | Linux (64-bit) | Saves the AppImage to `~/Applications` and starts it |
17
+
18
+ Intel Macs are not supported. These builds are not notarized by Apple yet, so macOS may ask once on first
19
+ launch: open **System Settings > Privacy & Security** and click **Open Anyway**.
20
+
21
+ Options: `--download-only` (save the installer, do not install), `--version 0.2.0` (a specific release).
22
+
23
+ This package contains no app code. It always installs the same tested files as the
24
+ [Releases page](https://github.com/kmmohaimenulhaque-code/Berkelium-Neural-Rendering-Studio/releases).
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ // berkelium-studio: download, verify and install Berkelium Studio.
3
+ //
4
+ // This package holds no app code on purpose. It fetches the installer from the
5
+ // official GitHub release, checks it against SHA256SUMS.txt from the same
6
+ // release, and installs it. So npx users get exactly the files the release
7
+ // pipeline tested, and there is no second copy of the app to keep in sync.
8
+ //
9
+ // No dependencies: Node 18+ has fetch, and the rest is the standard library.
10
+
11
+ "use strict";
12
+
13
+ const crypto = require("crypto");
14
+ const fs = require("fs");
15
+ const os = require("os");
16
+ const path = require("path");
17
+ const { spawn, spawnSync } = require("child_process");
18
+ const { Readable } = require("stream");
19
+ const { pipeline } = require("stream/promises");
20
+
21
+ const REPO = "kmmohaimenulhaque-code/Berkelium-Neural-Rendering-Studio";
22
+ const API = `https://api.github.com/repos/${REPO}/releases`;
23
+
24
+ const args = process.argv.slice(2);
25
+ const downloadOnly = args.includes("--download-only");
26
+ const versionArg = (() => {
27
+ const i = args.indexOf("--version");
28
+ return i >= 0 ? args[i + 1] : null;
29
+ })();
30
+
31
+ function die(message) {
32
+ console.error(`\nberkelium-studio: ${message}`);
33
+ process.exit(1);
34
+ }
35
+
36
+ // Which installer fits this computer. Matches the release file names.
37
+ function target() {
38
+ const { platform, arch } = process;
39
+ if (platform === "darwin" && arch === "arm64") return { suffix: "-mac-arm64.dmg", kind: "mac" };
40
+ if (platform === "darwin") die("Intel Macs are not supported. Berkelium needs an Apple Silicon Mac (M1 or newer).");
41
+ if (platform === "win32" && arch === "x64") return { suffix: "-win-x64.exe", kind: "windows" };
42
+ if (platform === "linux" && arch === "x64") return { suffix: "-linux-x86_64.AppImage", kind: "linux" };
43
+ die(`no installer for ${platform} ${arch}. Supported: macOS Apple Silicon, Windows x64, Linux x64.`);
44
+ }
45
+
46
+ async function getJson(url) {
47
+ const r = await fetch(url, { headers: { "User-Agent": "berkelium-studio-installer", Accept: "application/vnd.github+json" } });
48
+ if (r.status === 404) die(versionArg ? `release v${versionArg} not found.` : "no published release found.");
49
+ if (r.status === 403) die("GitHub rate limit reached. Try again in a few minutes.");
50
+ if (!r.ok) die(`GitHub answered ${r.status} for ${url}`);
51
+ return r.json();
52
+ }
53
+
54
+ async function download(url, dest, label) {
55
+ const r = await fetch(url, { headers: { "User-Agent": "berkelium-studio-installer" }, redirect: "follow" });
56
+ if (!r.ok || !r.body) die(`download failed (${r.status}): ${url}`);
57
+ const total = Number(r.headers.get("content-length")) || 0;
58
+ let done = 0;
59
+ let lastShown = 0;
60
+ const counter = new (require("stream").Transform)({
61
+ transform(chunk, _enc, cb) {
62
+ done += chunk.length;
63
+ const now = Date.now();
64
+ if (process.stdout.isTTY && now - lastShown > 250) {
65
+ lastShown = now;
66
+ const pct = total ? ` ${Math.floor((done / total) * 100)}%` : "";
67
+ process.stdout.write(`\r ${label}: ${(done / 1e6).toFixed(0)} MB${pct} `);
68
+ }
69
+ cb(null, chunk);
70
+ },
71
+ });
72
+ await pipeline(Readable.fromWeb(r.body), counter, fs.createWriteStream(dest));
73
+ if (process.stdout.isTTY) process.stdout.write("\n");
74
+ }
75
+
76
+ function sha256(file) {
77
+ return new Promise((resolve, reject) => {
78
+ const h = crypto.createHash("sha256");
79
+ fs.createReadStream(file).on("data", (d) => h.update(d)).on("end", () => resolve(h.digest("hex"))).on("error", reject);
80
+ });
81
+ }
82
+
83
+ function run(cmd, cmdArgs, opts = {}) {
84
+ const r = spawnSync(cmd, cmdArgs, { stdio: "pipe", encoding: "utf-8", ...opts });
85
+ if (r.status !== 0) throw new Error(`${cmd} ${cmdArgs.join(" ")} failed: ${(r.stderr || r.stdout || "").trim()}`);
86
+ return r.stdout;
87
+ }
88
+
89
+ // macOS: mount the dmg, copy the app into Applications, unmount, open it.
90
+ // A dmg this tool downloads itself is usually not marked "quarantined" the way
91
+ // browser downloads are. A reused browser download keeps its mark, so macOS may
92
+ // still ask once. We never remove that mark: it is the user's Gatekeeper decision.
93
+ function installMac(dmg) {
94
+ const mount = fs.mkdtempSync(path.join(os.tmpdir(), "berkelium-dmg-"));
95
+ run("hdiutil", ["attach", dmg, "-nobrowse", "-readonly", "-mountpoint", mount]);
96
+ try {
97
+ const app = path.join(mount, "Berkelium.app");
98
+ if (!fs.existsSync(app)) throw new Error("Berkelium.app not found in the dmg");
99
+ let destDir = "/Applications";
100
+ try {
101
+ fs.accessSync(destDir, fs.constants.W_OK);
102
+ } catch {
103
+ destDir = path.join(os.homedir(), "Applications"); // no admin rights: install for this user only
104
+ fs.mkdirSync(destDir, { recursive: true });
105
+ }
106
+ const dest = path.join(destDir, "Berkelium.app");
107
+ spawnSync("osascript", ["-e", 'tell application "Berkelium" to quit'], { stdio: "ignore" });
108
+ fs.rmSync(dest, { recursive: true, force: true });
109
+ run("ditto", [app, dest]); // ditto keeps the code signature and permissions intact
110
+ console.log(` Installed: ${dest}`);
111
+ return dest;
112
+ } finally {
113
+ spawnSync("hdiutil", ["detach", mount, "-quiet"], { stdio: "ignore" });
114
+ }
115
+ }
116
+
117
+ async function main() {
118
+ const t = target();
119
+ console.log("Berkelium Studio installer");
120
+ const release = await getJson(versionArg ? `${API}/tags/v${versionArg.replace(/^v/, "")}` : `${API}/latest`);
121
+ const asset = release.assets.find((a) => a.name.endsWith(t.suffix));
122
+ const sums = release.assets.find((a) => a.name === "SHA256SUMS.txt");
123
+ if (!asset) die(`release ${release.tag_name} has no file ending in ${t.suffix}`);
124
+ if (!sums) die(`release ${release.tag_name} has no SHA256SUMS.txt, so the download cannot be verified`);
125
+ console.log(` Release: ${release.tag_name}`);
126
+
127
+ const dir = path.join(os.homedir(), "Downloads");
128
+ const destDir = fs.existsSync(dir) ? dir : os.tmpdir();
129
+ const file = path.join(destDir, asset.name);
130
+
131
+ const sumsText = await (await fetch(sums.browser_download_url, { headers: { "User-Agent": "berkelium-studio-installer" } })).text();
132
+ const expected = (sumsText.split("\n").map((l) => l.trim().split(/\s+/)).find(([, name]) => name === asset.name) || [])[0];
133
+ if (!expected) die(`${asset.name} is not listed in SHA256SUMS.txt`);
134
+
135
+ // Reuse a finished earlier download only if it still matches the checksum.
136
+ if (!(fs.existsSync(file) && (await sha256(file)) === expected)) {
137
+ await download(asset.browser_download_url, file + ".part", asset.name);
138
+ fs.renameSync(file + ".part", file);
139
+ }
140
+ const actual = await sha256(file);
141
+ if (actual !== expected) {
142
+ fs.rmSync(file, { force: true });
143
+ die(`checksum mismatch for ${asset.name}. The file was deleted. Try again.`);
144
+ }
145
+ console.log(` Verified: SHA-256 matches the release (${expected.slice(0, 12)}...)`);
146
+
147
+ if (downloadOnly) {
148
+ console.log(` Saved: ${file}`);
149
+ return;
150
+ }
151
+
152
+ if (t.kind === "mac") {
153
+ const app = installMac(file);
154
+ spawn("open", [app], { detached: true, stdio: "ignore" }).unref();
155
+ console.log(" Opening Berkelium...");
156
+ } else if (t.kind === "windows") {
157
+ console.log(" Starting the installer...");
158
+ spawn(file, [], { detached: true, stdio: "ignore" }).unref();
159
+ } else {
160
+ const appsDir = path.join(os.homedir(), "Applications");
161
+ fs.mkdirSync(appsDir, { recursive: true });
162
+ const dest = path.join(appsDir, "Berkelium.AppImage");
163
+ fs.copyFileSync(file, dest);
164
+ fs.chmodSync(dest, 0o755);
165
+ console.log(` Installed: ${dest}`);
166
+ spawn(dest, [], { detached: true, stdio: "ignore" }).unref();
167
+ console.log(" Starting Berkelium...");
168
+ }
169
+ console.log("Done.");
170
+ }
171
+
172
+ main().catch((err) => die(err.message));
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "berkelium-studio",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Installs and opens Berkelium Studio, the neural rendering studio (3D viewport, code editor, free AI chat).",
5
+ "bin": {
6
+ "berkelium-studio": "bin/berkelium-studio.js"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "README.md"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "os": [
16
+ "darwin",
17
+ "win32",
18
+ "linux"
19
+ ],
20
+ "keywords": [
21
+ "berkelium",
22
+ "3d",
23
+ "cad",
24
+ "neural-rendering",
25
+ "electron",
26
+ "installer"
27
+ ],
28
+ "author": "Team Berkelium",
29
+ "license": "SEE LICENSE IN https://github.com/kmmohaimenulhaque-code/Berkelium-Neural-Rendering-Studio/blob/main/LICENSE",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/kmmohaimenulhaque-code/Berkelium-Neural-Rendering-Studio.git"
33
+ },
34
+ "homepage": "https://github.com/kmmohaimenulhaque-code/Berkelium-Neural-Rendering-Studio"
35
+ }