@maravilla-labs/cli 0.1.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/LICENSE ADDED
@@ -0,0 +1,12 @@
1
+ Copyright (c) 2019 - 2025 SOLUTAS GmbH, Switzerland
2
+
3
+ All Rights Reserved.
4
+ -------------------------------------------------------
5
+ NOTICE:
6
+ All information contained herein is, and remains
7
+ the property of SOLUTAS GmbH. The intellectual and
8
+ technical concepts contained herein are confidential
9
+ and proprietary to SOLUTAS GmbH. Dissemination of this
10
+ information or reproduction of this material is strictly
11
+ forbidden without the express written permission of
12
+ SOLUTAS GmbH.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Maravilla CLI
2
+
3
+ The official Maravilla command‑line tool.
4
+
5
+ > Preview (early access)
6
+ >
7
+ > This package is being published for testing and early feedback. It’s not yet generally available and may change without notice. Not for production use. We welcome feedback and bug reports.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install -g @maravilla-labs/cli
13
+ # or
14
+ pnpm add -g @maravilla-labs/cli
15
+ # or
16
+ yarn global add @maravilla-labs/cli
17
+ ```
18
+
19
+ Verify:
20
+
21
+ ```bash
22
+ maravilla --version
23
+ ```
24
+
25
+ ## Update
26
+
27
+ ```bash
28
+ maravilla update
29
+ ```
30
+
31
+ ## What it does
32
+
33
+ - Develop: start a local dev server with hot reload.
34
+ - Build: produce optimized production artifacts for your app.
35
+ - Preview: run a local server to preview your production build.
36
+ - Platform tools: inspect status, browse KV and database collections.
37
+ - Self‑update: fetch and install the latest CLI release.
38
+
39
+ ## Supported platforms
40
+
41
+ - Linux x64 (GNU)
42
+ - macOS x64 and arm64
43
+ - Windows x64
44
+
45
+ For other platforms and release notes, visit the Maravilla CLI releases page.
package/bin/maravilla ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ # Launcher that executes the downloaded Maravilla CLI binary in this package
3
+ set -euo pipefail
4
+ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ BIN="$DIR/maravilla"
6
+ if [ "$(uname -s)" = "MINGW64_NT" ] || [ "$(uname -s)" = "MSYS_NT" ] || [ "$(uname -s)" = "CYGWIN_NT" ]; then
7
+ BIN="$DIR/maravilla.exe"
8
+ fi
9
+ exec "$BIN" "$@"
@@ -0,0 +1,4 @@
1
+ @ECHO OFF
2
+ SETLOCAL
3
+ SET SCRIPT_DIR=%~dp0
4
+ "%SCRIPT_DIR%maravilla.exe" %*
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@maravilla-labs/cli",
3
+ "version": "0.1.0",
4
+ "description": "NPM wrapper for the Maravilla CLI binary; downloads the right release for your platform.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "author": "Maravilla Labs",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/solutas/maravilla-runtime.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/solutas/maravilla-runtime/issues"
14
+ },
15
+ "homepage": "https://github.com/solutas/maravilla-runtime#readme",
16
+ "bin": {
17
+ "maravilla": "bin/maravilla"
18
+ },
19
+ "type": "module",
20
+ "scripts": {
21
+ "postinstall": "node ./scripts/postinstall.js",
22
+ "download": "node ./scripts/postinstall.js",
23
+ "update": "node ./scripts/update.js",
24
+ "prepack": "node ./scripts/generate-readme-snippet.js || true"
25
+ },
26
+ "files": [
27
+ "bin",
28
+ "scripts",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "os": [
33
+ "darwin",
34
+ "linux",
35
+ "win32"
36
+ ],
37
+ "cpu": [
38
+ "x64",
39
+ "arm64"
40
+ ],
41
+ "engines": {
42
+ "node": ">=16"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "dependencies": {
48
+ "node-fetch": "^3.3.2",
49
+ "tar": "^6.2.1",
50
+ "unzipper": "^0.10.14",
51
+ "progress": "^2.0.3",
52
+ "chalk": "^5.3.0"
53
+ }
54
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ // Placeholder for future automation; not required for functionality.
3
+ console.log('');
@@ -0,0 +1,15 @@
1
+ // Map Node's process.platform/process.arch to GitHub release target triple and file ext
2
+ export function resolveTarget() {
3
+ const p = process.platform;
4
+ const a = process.arch;
5
+ // Supported targets per release matrix
6
+ if (p === 'linux' && a === 'x64') return { target: 'x86_64-unknown-linux-gnu', ext: 'tar.gz' };
7
+ if (p === 'darwin' && a === 'x64') return { target: 'x86_64-apple-darwin', ext: 'tar.gz' };
8
+ if (p === 'darwin' && a === 'arm64') return { target: 'aarch64-apple-darwin', ext: 'tar.gz' };
9
+ if (p === 'win32' && a === 'x64') return { target: 'x86_64-pc-windows-msvc', ext: 'zip' };
10
+ throw new Error(`Unsupported platform/arch: ${p}/${a}. See https://github.com/maravilla-labs/maravilla-cli/releases for supported binaries.`);
11
+ }
12
+
13
+ export function artifactName(binName, tag, target) {
14
+ return `${binName}-${tag}-${target}`;
15
+ }
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import { fileURLToPath } from 'url';
6
+ import { pipeline } from 'stream/promises';
7
+ import fetch from 'node-fetch';
8
+ import tar from 'tar';
9
+ import unzipper from 'unzipper';
10
+ import ProgressBar from 'progress';
11
+ import chalk from 'chalk';
12
+ import { resolveTarget, artifactName } from './platform.js';
13
+ import crypto from 'crypto';
14
+
15
+ // Allow CI to skip downloading the binary
16
+ if (process.env.MARAVILLA_CLI_SKIP_INSTALL === '1' || process.env.CI) {
17
+ console.log('✿ Skipping Maravilla CLI binary install (CI or skip flag set)');
18
+ process.exit(0);
19
+ }
20
+ import { spawn } from 'child_process';
21
+
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = path.dirname(__filename);
24
+
25
+ const BIN_NAME = 'maravilla';
26
+ const REPO = process.env.MARAVILLA_CLI_REPO || 'maravilla-labs/maravilla-cli';
27
+ const VERSION = process.env.MARAVILLA_VERSION || 'latest'; // cli-vX.Y.Z or 'latest'
28
+ const BIN_DIR = path.join(__dirname, '..', 'bin');
29
+ const EXECUTABLE = process.platform === 'win32' ? `${BIN_NAME}.exe` : BIN_NAME;
30
+ const INSTALL_PATH = path.join(BIN_DIR, EXECUTABLE);
31
+
32
+ function flower() {
33
+ return chalk.magenta('✿');
34
+ }
35
+
36
+ function log(msg) {
37
+ console.log(`${flower()} ${msg}`);
38
+ }
39
+
40
+ async function getReleaseTag() {
41
+ if (VERSION && VERSION !== 'latest') return VERSION;
42
+ const api = `https://api.github.com/repos/${REPO}/releases/latest`;
43
+ const res = await fetch(api, { headers: { 'User-Agent': 'maravilla-cli-installer' } });
44
+ if (!res.ok) throw new Error(`Failed to fetch latest release: ${res.status} ${res.statusText}`);
45
+ const json = await res.json();
46
+ return json.tag_name;
47
+ }
48
+
49
+ async function downloadWithProgress(url, dest) {
50
+ const res = await fetch(url);
51
+ if (!res.ok) throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
52
+ const total = Number(res.headers.get('content-length')) || 0;
53
+ const bar = total ? new ProgressBar(`${flower()} downloading [:bar] :percent :etas`, {
54
+ complete: '=', incomplete: ' ', width: 24, total
55
+ }) : null;
56
+
57
+ await pipeline(
58
+ res.body,
59
+ new (class extends fs.WriteStream {
60
+ constructor(dest) { super(dest); }
61
+ write(chunk, enc, cb) { if (bar) bar.tick(chunk.length); super.write(chunk, enc, cb); }
62
+ })(dest)
63
+ );
64
+ }
65
+
66
+ async function extractArchive(archivePath, targetDir, ext) {
67
+ if (ext === 'tar.gz') {
68
+ await tar.x({ file: archivePath, cwd: targetDir });
69
+ } else if (ext === 'zip') {
70
+ await fs.createReadStream(archivePath).pipe(unzipper.Extract({ path: targetDir })).promise();
71
+ } else {
72
+ throw new Error(`Unknown archive type: ${ext}`);
73
+ }
74
+ }
75
+
76
+ async function install() {
77
+ const { target, ext } = resolveTarget();
78
+ const tag = await getReleaseTag();
79
+ const art = artifactName(BIN_NAME, tag, target);
80
+ const assetFile = `${art}.${ext}`;
81
+ const url = `https://github.com/${REPO}/releases/download/${tag}/${assetFile}`;
82
+ const sumsUrl = `https://github.com/${REPO}/releases/download/${tag}/SHA256SUMS`;
83
+
84
+ await fs.promises.mkdir(BIN_DIR, { recursive: true });
85
+ const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'maravilla-cli-'));
86
+ const archive = path.join(tmpDir, assetFile);
87
+
88
+ log(chalk.bold(`Installing ${BIN_NAME} ${chalk.cyan(tag)} for ${chalk.cyan(target)}`));
89
+ log(`Downloading from ${url}`);
90
+ await downloadWithProgress(url, archive);
91
+
92
+ // Verify checksum if SHA256SUMS is available
93
+ try {
94
+ log('Verifying checksum');
95
+ const sumsRes = await fetch(sumsUrl, { headers: { 'User-Agent': 'maravilla-cli-installer' } });
96
+ if (sumsRes.ok) {
97
+ const sumsText = await sumsRes.text();
98
+ const expected = sumsText
99
+ .split(/\r?\n/)
100
+ .map((l) => l.trim())
101
+ .filter(Boolean)
102
+ .map((l) => l.split(/\s+/)) // [hash, filename]
103
+ .find(([, fname]) => fname === assetFile);
104
+ if (!expected) throw new Error('Checksum entry not found for asset');
105
+ const [expectedHash] = expected;
106
+ const hash = await sha256File(archive);
107
+ if (hash !== expectedHash) {
108
+ throw new Error(`Checksum mismatch for ${assetFile}: expected ${expectedHash}, got ${hash}`);
109
+ }
110
+ log(chalk.green('Checksum OK'));
111
+ } else {
112
+ log(chalk.yellow('SHA256SUMS not found; skipping verification'));
113
+ }
114
+ } catch (e) {
115
+ console.error(chalk.red(`${flower()} checksum verification failed: ${e.message}`));
116
+ throw e;
117
+ }
118
+
119
+ log('Extracting archive');
120
+ await extractArchive(archive, tmpDir, ext);
121
+
122
+ const innerDir = path.join(tmpDir, art);
123
+ const srcPath = path.join(innerDir, EXECUTABLE);
124
+ await fs.promises.copyFile(srcPath, INSTALL_PATH);
125
+ await fs.promises.chmod(INSTALL_PATH, 0o755);
126
+ log(chalk.green(`Installed to ${INSTALL_PATH}`));
127
+
128
+ // Self-check: run --version
129
+ await new Promise((resolve, reject) => {
130
+ log('Verifying install by running --version');
131
+ const child = spawn(INSTALL_PATH, ['--version'], { stdio: ['ignore', 'pipe', 'pipe'] });
132
+ let out = '';
133
+ child.stdout.on('data', (d) => (out += d.toString()))
134
+ child.on('error', reject);
135
+ child.on('exit', (code) => {
136
+ if (code === 0) {
137
+ log(chalk.green(`Version: ${out.trim()}`));
138
+ resolve();
139
+ } else {
140
+ reject(new Error(`Version check failed with code ${code}`));
141
+ }
142
+ });
143
+ });
144
+ }
145
+
146
+ async function sha256File(filePath) {
147
+ return new Promise((resolve, reject) => {
148
+ const hash = crypto.createHash('sha256');
149
+ const rs = fs.createReadStream(filePath);
150
+ rs.on('error', reject);
151
+ rs.on('data', (chunk) => hash.update(chunk));
152
+ rs.on('end', () => resolve(hash.digest('hex')));
153
+ });
154
+ }
155
+
156
+ install().catch(err => {
157
+ console.error(chalk.red(`${flower()} install failed: ${err.message}`));
158
+ process.exitCode = 1;
159
+ });
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'url';
3
+ import path from 'path';
4
+ import { spawn } from 'child_process';
5
+
6
+ // Simply rerun the postinstall script to fetch latest/specified version
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const postinstall = path.join(__dirname, 'postinstall.js');
10
+
11
+ const child = spawn(process.execPath, [postinstall], { stdio: 'inherit' });
12
+ child.on('exit', (code) => process.exit(code ?? 0));