@boxes-dev/cli 1.0.573

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/dvb ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+ DEVBOX_DIR="${DEVBOX_HOME:-$HOME/.devbox}"
3
+ STANDALONE_DIR="$DEVBOX_DIR/standalone/current"
4
+ if [ ! -x "$STANDALONE_DIR/dvb" ]; then
5
+ echo "dvb: standalone runtime not installed." >&2
6
+ echo "Run: npm install -g @boxes-dev/cli" >&2
7
+ exit 1
8
+ fi
9
+ exec "$STANDALONE_DIR/dvb" "$@"
package/bin/dvbd ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+ DEVBOX_DIR="${DEVBOX_HOME:-$HOME/.devbox}"
3
+ STANDALONE_DIR="$DEVBOX_DIR/standalone/current"
4
+ if [ ! -x "$STANDALONE_DIR/dvbd" ]; then
5
+ echo "dvbd: standalone runtime not installed." >&2
6
+ echo "Run: npm install -g @boxes-dev/cli" >&2
7
+ exit 1
8
+ fi
9
+ exec "$STANDALONE_DIR/dvbd" "$@"
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@boxes-dev/cli",
3
+ "version": "1.0.573",
4
+ "description": "Boxes.dev CLI (standalone, includes bundled Node runtime)",
5
+ "license": "UNLICENSED",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "bin": {
10
+ "dvb": "./bin/dvb",
11
+ "dvbd": "./bin/dvbd"
12
+ },
13
+ "files": [
14
+ "bin/dvb",
15
+ "bin/dvbd",
16
+ "scripts/postinstall.cjs",
17
+ "scripts/postinstall.mjs"
18
+ ],
19
+ "scripts": {
20
+ "postinstall": "node scripts/postinstall.cjs"
21
+ },
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "os": [
26
+ "darwin",
27
+ "linux"
28
+ ],
29
+ "cpu": [
30
+ "arm64",
31
+ "x64"
32
+ ]
33
+ }
@@ -0,0 +1,19 @@
1
+ // CJS wrapper for postinstall — spawns the ESM postinstall script.
2
+ // npm postinstall runs with the package's Node, which may be 18+ without
3
+ // native ESM support in all contexts. This wrapper ensures compatibility.
4
+ const { execFileSync } = require("node:child_process");
5
+ const path = require("node:path");
6
+
7
+ try {
8
+ execFileSync(
9
+ process.execPath,
10
+ [path.join(__dirname, "postinstall.mjs")],
11
+ { stdio: "inherit", env: { ...process.env } },
12
+ );
13
+ } catch (err) {
14
+ // Non-zero exit from the ESM script.
15
+ if (err.status != null) {
16
+ process.exit(err.status);
17
+ }
18
+ throw err;
19
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * @boxes-dev/cli postinstall script.
3
+ *
4
+ * Downloads the platform-specific standalone archive from deploy.boxes.dev,
5
+ * verifies its SHA256 checksum, extracts it, validates it, and promotes it
6
+ * to ~/.devbox/standalone/current/.
7
+ */
8
+
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ import os from "node:os";
12
+ import { createHash } from "node:crypto";
13
+ import { pipeline } from "node:stream/promises";
14
+ import { execFileSync } from "node:child_process";
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Configuration
18
+ // ---------------------------------------------------------------------------
19
+
20
+ // Build-time profile: if set to "dev" at build time, use the dev deploy host.
21
+ // For the published npm package this is baked in; for local dev testing,
22
+ // override via DEVBOX_STANDALONE_MANIFEST_URL env var.
23
+ const DEFAULT_MANIFEST_URL = "https://deploy.boxes.dev/cli/latest.json";
24
+ const DEV_MANIFEST_URL = "https://deploy-dev.boxes.dev/cli/latest.json";
25
+
26
+ function getManifestUrl() {
27
+ // Explicit override for testing.
28
+ if (process.env.DEVBOX_STANDALONE_MANIFEST_URL) {
29
+ return process.env.DEVBOX_STANDALONE_MANIFEST_URL;
30
+ }
31
+ // Build-time profile detection: the published package always uses prod.
32
+ // Local dev builds set DEVBOX_BUILD_PROFILE=dev.
33
+ if (process.env.DEVBOX_BUILD_PROFILE === "dev") {
34
+ return DEV_MANIFEST_URL;
35
+ }
36
+ return DEFAULT_MANIFEST_URL;
37
+ }
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Helpers
41
+ // ---------------------------------------------------------------------------
42
+
43
+ function getDevboxDir() {
44
+ return process.env.DEVBOX_HOME || path.join(os.homedir(), ".devbox");
45
+ }
46
+
47
+ function getPlatformKey() {
48
+ const platform = process.platform; // "darwin" or "linux"
49
+ const arch = process.arch; // "arm64" or "x64"
50
+ return `${platform}-${arch}`;
51
+ }
52
+
53
+ /** SHA-256 hex digest of a file. */
54
+ function sha256File(filePath) {
55
+ const hash = createHash("sha256");
56
+ hash.update(fs.readFileSync(filePath));
57
+ return hash.digest("hex");
58
+ }
59
+
60
+ /** Fetch JSON from a URL. */
61
+ async function fetchJson(url) {
62
+ const res = await fetch(url);
63
+ if (!res.ok) {
64
+ throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
65
+ }
66
+ return res.json();
67
+ }
68
+
69
+ /** Download a URL to a file. */
70
+ async function downloadFile(url, dest) {
71
+ const res = await fetch(url);
72
+ if (!res.ok) {
73
+ throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
74
+ }
75
+ const fileStream = fs.createWriteStream(dest);
76
+ await pipeline(res.body, fileStream);
77
+ }
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // Main
81
+ // ---------------------------------------------------------------------------
82
+
83
+ async function main() {
84
+ const platformKey = getPlatformKey();
85
+ const devboxDir = getDevboxDir();
86
+ const standaloneDir = path.join(devboxDir, "standalone");
87
+
88
+ console.log(`@boxes-dev/cli: installing standalone dvb for ${platformKey}...`);
89
+
90
+ // 1. Fetch manifest
91
+ const manifestUrl = getManifestUrl();
92
+ let manifest;
93
+ try {
94
+ manifest = await fetchJson(manifestUrl);
95
+ } catch (err) {
96
+ console.error(
97
+ `@boxes-dev/cli: failed to fetch manifest from ${manifestUrl}`,
98
+ );
99
+ console.error(` ${err.message}`);
100
+ console.error(
101
+ `\nIf you are offline, you can retry later with: npm install -g @boxes-dev/cli`,
102
+ );
103
+ process.exit(1);
104
+ }
105
+
106
+ const archiveInfo = manifest.archives?.[platformKey];
107
+ if (!archiveInfo) {
108
+ console.error(
109
+ `@boxes-dev/cli: no archive available for platform ${platformKey}`,
110
+ );
111
+ console.error(`Available platforms: ${Object.keys(manifest.archives || {}).join(", ")}`);
112
+ process.exit(1);
113
+ }
114
+
115
+ const version = manifest.version;
116
+ const versionDir = path.join(
117
+ standaloneDir,
118
+ `${version}-${platformKey}`,
119
+ );
120
+ const currentLink = path.join(standaloneDir, "current");
121
+
122
+ // 2. Check if already installed at this version
123
+ if (fs.existsSync(versionDir) && fs.existsSync(path.join(versionDir, "dvb"))) {
124
+ // Check if current symlink already points here.
125
+ try {
126
+ const currentTarget = fs.readlinkSync(currentLink);
127
+ if (path.resolve(standaloneDir, currentTarget) === versionDir ||
128
+ currentTarget === versionDir) {
129
+ console.log(
130
+ `@boxes-dev/cli: standalone dvb v${version} already installed, skipping.`,
131
+ );
132
+ return;
133
+ }
134
+ } catch {
135
+ // Symlink doesn't exist or can't be read — continue to update it.
136
+ }
137
+ // Version dir exists but symlink is stale/missing — just update symlink.
138
+ updateCurrentSymlink(standaloneDir, currentLink, versionDir, version);
139
+ return;
140
+ }
141
+
142
+ // 3. Download archive
143
+ fs.mkdirSync(standaloneDir, { recursive: true });
144
+ const tmpSuffix = `${version}-${platformKey}-${Date.now()}`;
145
+ const tmpDir = path.join(standaloneDir, `.tmp-${tmpSuffix}`);
146
+ const tarballPath = path.join(standaloneDir, `.download-${tmpSuffix}.tar.gz`);
147
+
148
+ try {
149
+ console.log(`@boxes-dev/cli: downloading dvb v${version}...`);
150
+ await downloadFile(archiveInfo.url, tarballPath);
151
+
152
+ // 4. Verify SHA256
153
+ const actualHash = sha256File(tarballPath);
154
+ if (actualHash !== archiveInfo.sha256) {
155
+ throw new Error(
156
+ `SHA256 mismatch:\n expected: ${archiveInfo.sha256}\n actual: ${actualHash}`,
157
+ );
158
+ }
159
+ console.log(`@boxes-dev/cli: SHA256 verified.`);
160
+
161
+ // 5. Extract to tmp directory
162
+ fs.mkdirSync(tmpDir, { recursive: true });
163
+ execFileSync("tar", [
164
+ "xzf",
165
+ tarballPath,
166
+ "-C",
167
+ tmpDir,
168
+ "--strip-components=1",
169
+ ]);
170
+
171
+ // 6. Validate: run dvb --help
172
+ try {
173
+ execFileSync(path.join(tmpDir, "dvb"), ["--help"], {
174
+ stdio: "pipe",
175
+ timeout: 15_000,
176
+ });
177
+ } catch (err) {
178
+ throw new Error(
179
+ `Validation failed: ./dvb --help exited with code ${err.status}`,
180
+ );
181
+ }
182
+
183
+ // 7. Promote: rename tmp to version dir
184
+ if (fs.existsSync(versionDir)) {
185
+ fs.rmSync(versionDir, { recursive: true });
186
+ }
187
+ fs.renameSync(tmpDir, versionDir);
188
+
189
+ // 8. Update current symlink
190
+ updateCurrentSymlink(standaloneDir, currentLink, versionDir, version);
191
+
192
+ console.log(`@boxes-dev/cli: successfully installed dvb v${version}`);
193
+ } catch (err) {
194
+ console.error(`@boxes-dev/cli: installation failed: ${err.message}`);
195
+ // Clean up
196
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
197
+ process.exit(1);
198
+ } finally {
199
+ // Always clean up the tarball
200
+ try { fs.unlinkSync(tarballPath); } catch {}
201
+ }
202
+ }
203
+
204
+ function updateCurrentSymlink(standaloneDir, currentLink, versionDir, version) {
205
+ // Atomic symlink update: create a temp symlink and rename it over the old one.
206
+ const tmpLink = path.join(standaloneDir, `.current-tmp-${Date.now()}`);
207
+ try {
208
+ // Use relative path for the symlink so it works if devbox dir is moved.
209
+ const relTarget = path.relative(standaloneDir, versionDir);
210
+ fs.symlinkSync(relTarget, tmpLink);
211
+ fs.renameSync(tmpLink, currentLink);
212
+ } catch {
213
+ // Fallback: remove and recreate.
214
+ try { fs.unlinkSync(tmpLink); } catch {}
215
+ try { fs.unlinkSync(currentLink); } catch {}
216
+ const relTarget = path.relative(standaloneDir, versionDir);
217
+ fs.symlinkSync(relTarget, currentLink);
218
+ }
219
+ }
220
+
221
+ main();