fundamentum-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/README.md +96 -0
- package/install.js +205 -0
- package/package.json +66 -0
- package/run.js +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# fundamentum-cli
|
|
2
|
+
|
|
3
|
+
**One command to make your GitHub repo look (and behave) like a pro open-source project.**
|
|
4
|
+
|
|
5
|
+
[npm](https://www.npmjs.com/package/fundamentum-cli) · [GitHub](https://github.com/jpvelasco/fundamentum)
|
|
6
|
+
|
|
7
|
+
Fundamentum bootstraps and hardens GitHub repos for OSS collaboration — community health files, branch protection, secret scanning, and starter CI workflows. You bring the code, it brings the polish.
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://github.com/jpvelasco/fundamentum/actions/workflows/ci.yml"><img src="https://github.com/jpvelasco/fundamentum/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
11
|
+
<a href="https://github.com/jpvelasco/fundamentum/releases/latest"><img src="https://img.shields.io/github/v/release/jpvelasco/fundamentum" alt="Release"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/fundamentum-cli"><img src="https://img.shields.io/npm/v/fundamentum-cli" alt="npm"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/fundamentum-cli"><img src="https://img.shields.io/npm/dm/fundamentum-cli" alt="npm downloads"></a>
|
|
14
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g fundamentum-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Try without a global install:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx fundamentum-cli --version
|
|
27
|
+
npx fundamentum-cli apply OWNER/REPO
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Works on **macOS**, **Linux**, and **Windows** — `x64` and `arm64`. Postinstall downloads the matching prebuilt binary from GitHub Releases with embedded SHA-256 verification.
|
|
31
|
+
|
|
32
|
+
> **npm 9+ / Ubuntu 26+ note:** npm's `allow-scripts` security policy may block the postinstall
|
|
33
|
+
> script, so the binary won't be downloaded at install time. No worries — if `fundamentum` is invoked
|
|
34
|
+
> without a binary present it detects this and downloads automatically. You can also trigger it
|
|
35
|
+
> manually:
|
|
36
|
+
> ```bash
|
|
37
|
+
> node $(npm root -g)/fundamentum-cli/install.js
|
|
38
|
+
> ```
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Harden an existing repo
|
|
44
|
+
fundamentum apply OWNER/REPO
|
|
45
|
+
|
|
46
|
+
# Create a new repo + harden it in one shot
|
|
47
|
+
fundamentum init OWNER/REPO
|
|
48
|
+
|
|
49
|
+
# Preview everything before touching anything
|
|
50
|
+
fundamentum --dry-run apply OWNER/REPO
|
|
51
|
+
|
|
52
|
+
# Apply file changes via a pull request
|
|
53
|
+
fundamentum --pr apply OWNER/REPO
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
That's it. A repo that went from bare-bones to release-ready in under a minute.
|
|
57
|
+
|
|
58
|
+
## What you get
|
|
59
|
+
|
|
60
|
+
- **Community health files** — `CONTRIBUTING.md`, `CODEOWNERS`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, issue + PR templates, `dependabot.yml`
|
|
61
|
+
- **Branch protection** — modern rulesets on `main` (PR-only pushes, CODEOWNERS review, required checks, no force-push)
|
|
62
|
+
- **Security** — secret scanning + push protection, Dependabot alerts, CodeQL
|
|
63
|
+
- **Starter workflows** — CI, coverage, and CodeQL pipelines that work out of the box
|
|
64
|
+
- **Idempotent by design** — re-running is always safe
|
|
65
|
+
|
|
66
|
+
## Why fundamentum?
|
|
67
|
+
|
|
68
|
+
| Manual setup | fundamentum |
|
|
69
|
+
|---------------|-------------|
|
|
70
|
+
| 20+ files and settings across 6 GitHub pages | One command |
|
|
71
|
+
| "Which templates does everyone use?" | Opinionated, proven defaults |
|
|
72
|
+
| Copy-paste from another repo, hope it's current | Rendered fresh, version-controlled |
|
|
73
|
+
| "Did I forget CODEOWNERS?" | Full summary table before you apply |
|
|
74
|
+
| Private repo? Pro account? | Graceful fallbacks for free-tier |
|
|
75
|
+
|
|
76
|
+
Built for solo devs and small teams who want their repos to feel like they were made by someone who cared.
|
|
77
|
+
|
|
78
|
+
## Commands
|
|
79
|
+
|
|
80
|
+
| Command | Purpose |
|
|
81
|
+
|---------|---------|
|
|
82
|
+
| `apply OWNER/REPO` | Harden an existing repo |
|
|
83
|
+
| `init OWNER/REPO` | Create a new repo, then harden it |
|
|
84
|
+
| `--dry-run` | Preview the full plan without applying |
|
|
85
|
+
| `--pr` | Batch file changes into a pull request |
|
|
86
|
+
| `--no-overwrite` | Only add missing files, never touch existing ones |
|
|
87
|
+
|
|
88
|
+
## Prerequisites
|
|
89
|
+
|
|
90
|
+
A GitHub token with `repo` scope (classic) or Contents + Metadata + Administration (fine-grained) — via `GITHUB_TOKEN` or `--token`.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT — see [LICENSE](https://github.com/jpvelasco/fundamentum/blob/main/LICENSE).
|
|
95
|
+
|
|
96
|
+
fundamentum is an independent project, not affiliated with GitHub.
|
package/install.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// nosemgrep: all — npm install shim, not application code
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const https = require("https");
|
|
8
|
+
const crypto = require("crypto");
|
|
9
|
+
const { spawnSync } = require("child_process");
|
|
10
|
+
|
|
11
|
+
const REPO = "jpvelasco/fundamentum";
|
|
12
|
+
const MAX_REDIRECTS = 5;
|
|
13
|
+
const DOWNLOAD_TIMEOUT_MS = 30000;
|
|
14
|
+
|
|
15
|
+
const RELEASE_HOSTS = new Set([
|
|
16
|
+
"github.com",
|
|
17
|
+
"objects.githubusercontent.com",
|
|
18
|
+
"github-releases.githubusercontent.com",
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const PLATFORM_MAP = {
|
|
22
|
+
linux: "linux",
|
|
23
|
+
darwin: "darwin",
|
|
24
|
+
win32: "windows",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const ARCH_MAP = {
|
|
28
|
+
x64: "amd64",
|
|
29
|
+
arm64: "arm64",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function getArchiveName(version, platform, arch) {
|
|
33
|
+
const os = PLATFORM_MAP[platform];
|
|
34
|
+
const cpu = ARCH_MAP[arch];
|
|
35
|
+
if (!os || !cpu) {
|
|
36
|
+
throw new Error(`Unsupported platform: ${platform}/${arch}`);
|
|
37
|
+
}
|
|
38
|
+
const ext = platform === "win32" ? "zip" : "tar.gz";
|
|
39
|
+
return `fundamentum_${version}_${os}_${cpu}.${ext}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function download(url, redirectCount = 0) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
if (redirectCount > MAX_REDIRECTS) {
|
|
45
|
+
return reject(new Error(`Too many redirects (max ${MAX_REDIRECTS})`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const req = https.get(url, { timeout: DOWNLOAD_TIMEOUT_MS }, (res) => {
|
|
49
|
+
if (
|
|
50
|
+
res.statusCode >= 300 &&
|
|
51
|
+
res.statusCode < 400 &&
|
|
52
|
+
res.headers.location
|
|
53
|
+
) {
|
|
54
|
+
const nextUrl = new URL(res.headers.location, url);
|
|
55
|
+
if (
|
|
56
|
+
!RELEASE_HOSTS.has(nextUrl.hostname) &&
|
|
57
|
+
!nextUrl.hostname.endsWith(".githubusercontent.com")
|
|
58
|
+
) {
|
|
59
|
+
return reject(
|
|
60
|
+
new Error(`Refusing redirect to unexpected host: ${nextUrl.hostname}`)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
res.resume();
|
|
64
|
+
return download(nextUrl.href, redirectCount + 1).then(resolve, reject);
|
|
65
|
+
}
|
|
66
|
+
if (res.statusCode !== 200) {
|
|
67
|
+
return reject(
|
|
68
|
+
new Error(`Download failed: HTTP ${res.statusCode} for ${url}`)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const chunks = [];
|
|
72
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
73
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
74
|
+
res.on("error", reject);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
req.setTimeout(DOWNLOAD_TIMEOUT_MS, () => {
|
|
78
|
+
req.destroy(new Error(`Timed out downloading ${url}`));
|
|
79
|
+
});
|
|
80
|
+
req.on("error", reject);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function verifyChecksum(buffer, archiveName, pkg) {
|
|
85
|
+
const expected = pkg.binaryChecksums?.[archiveName] || null;
|
|
86
|
+
if (!expected) {
|
|
87
|
+
// A published package always has checksums embedded by the release
|
|
88
|
+
// workflow. A missing entry means asset-naming drift or tampering —
|
|
89
|
+
// fail closed rather than run an unverified binary.
|
|
90
|
+
if (pkg.version !== "0.0.0") {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`No checksum found for ${archiveName}. Refusing to install an unverified binary.`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
console.log("fundamentum: dev version, skipping checksum verification");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const actual = crypto.createHash("sha256").update(buffer).digest("hex");
|
|
100
|
+
if (actual !== expected) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Checksum mismatch for ${archiveName}\n` +
|
|
103
|
+
` Expected: ${expected}\n` +
|
|
104
|
+
` Actual: ${actual}`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
console.log("fundamentum: checksum verified (SHA-256)");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Escape a string for use inside PowerShell single quotes.
|
|
111
|
+
// PowerShell single-quoted strings only need '' to represent a literal '.
|
|
112
|
+
function psEscape(s) {
|
|
113
|
+
return s.replace(/'/g, "''");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function spawnOrFail(cmd, args, label) {
|
|
117
|
+
const result = spawnSync(cmd, args, { stdio: "pipe" });
|
|
118
|
+
if (result.error) {
|
|
119
|
+
throw new Error(`${label}: ${result.error.message}`);
|
|
120
|
+
}
|
|
121
|
+
if (result.status !== 0) {
|
|
122
|
+
const stderr = result.stderr ? result.stderr.toString().trim() : "";
|
|
123
|
+
throw new Error(
|
|
124
|
+
`${label} exited with code ${result.status}${stderr ? ": " + stderr : ""}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function extract(buffer, archiveName, binDir) {
|
|
130
|
+
const tmpDir = path.join(__dirname, ".tmp-install");
|
|
131
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
132
|
+
|
|
133
|
+
const archivePath = path.join(tmpDir, archiveName);
|
|
134
|
+
fs.writeFileSync(archivePath, buffer);
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
if (archiveName.endsWith(".zip")) {
|
|
138
|
+
if (process.platform === "win32") {
|
|
139
|
+
spawnOrFail(
|
|
140
|
+
"powershell",
|
|
141
|
+
[
|
|
142
|
+
"-NoProfile",
|
|
143
|
+
"-Command",
|
|
144
|
+
`Expand-Archive -Force -Path '${psEscape(
|
|
145
|
+
archivePath
|
|
146
|
+
)}' -DestinationPath '${psEscape(tmpDir)}'`,
|
|
147
|
+
],
|
|
148
|
+
"Expand-Archive"
|
|
149
|
+
);
|
|
150
|
+
} else {
|
|
151
|
+
spawnOrFail("unzip", ["-o", archivePath, "-d", tmpDir], "unzip");
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
spawnOrFail("tar", ["-xzf", archivePath, "-C", tmpDir], "tar");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Find the binary in the extracted files
|
|
158
|
+
const binaryName = process.platform === "win32" ? "fundamentum.exe" : "fundamentum";
|
|
159
|
+
const extractedBinary = path.join(tmpDir, binaryName);
|
|
160
|
+
|
|
161
|
+
if (!fs.existsSync(extractedBinary)) {
|
|
162
|
+
throw new Error(`Binary ${binaryName} not found in archive`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
166
|
+
const destBinary = path.join(binDir, binaryName);
|
|
167
|
+
fs.copyFileSync(extractedBinary, destBinary);
|
|
168
|
+
|
|
169
|
+
if (process.platform !== "win32") {
|
|
170
|
+
fs.chmodSync(destBinary, 0o755);
|
|
171
|
+
}
|
|
172
|
+
} finally {
|
|
173
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function main() {
|
|
178
|
+
const pkg = JSON.parse(
|
|
179
|
+
fs.readFileSync(path.join(__dirname, "package.json"), "utf8")
|
|
180
|
+
);
|
|
181
|
+
const version = pkg.version;
|
|
182
|
+
if (version === "0.0.0") {
|
|
183
|
+
console.error("fundamentum: skipping binary download for development version");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const archiveName = getArchiveName(version, process.platform, process.arch);
|
|
188
|
+
const url = `https://github.com/${REPO}/releases/download/v${version}/${archiveName}`;
|
|
189
|
+
const binDir = path.join(__dirname, "bin");
|
|
190
|
+
|
|
191
|
+
console.log(`fundamentum: downloading ${archiveName}...`);
|
|
192
|
+
const buffer = await download(url);
|
|
193
|
+
|
|
194
|
+
verifyChecksum(buffer, archiveName, pkg);
|
|
195
|
+
|
|
196
|
+
console.log("fundamentum: extracting binary...");
|
|
197
|
+
extract(buffer, archiveName, binDir);
|
|
198
|
+
|
|
199
|
+
console.log("fundamentum: installed successfully");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
main().catch((err) => {
|
|
203
|
+
console.error(`fundamentum: installation failed: ${err.message}`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fundamentum-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Bootstrap and harden GitHub repos for OSS collaboration — community files, branch protection, security features, and starter workflows in one command.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jpvelasco/fundamentum.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/jpvelasco/fundamentum/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/jpvelasco/fundamentum#readme",
|
|
14
|
+
"bin": {
|
|
15
|
+
"fundamentum": "run.js"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"postinstall": "node install.js"
|
|
19
|
+
},
|
|
20
|
+
"os": [
|
|
21
|
+
"linux",
|
|
22
|
+
"darwin",
|
|
23
|
+
"win32"
|
|
24
|
+
],
|
|
25
|
+
"cpu": [
|
|
26
|
+
"x64",
|
|
27
|
+
"arm64"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"github",
|
|
31
|
+
"github-repo",
|
|
32
|
+
"repo-hardening",
|
|
33
|
+
"repository-security",
|
|
34
|
+
"branch-protection",
|
|
35
|
+
"codeowners",
|
|
36
|
+
"code-of-conduct",
|
|
37
|
+
"contributing",
|
|
38
|
+
"community-files",
|
|
39
|
+
"open-source",
|
|
40
|
+
"oss",
|
|
41
|
+
"best-practices",
|
|
42
|
+
"security",
|
|
43
|
+
"dependabot",
|
|
44
|
+
"codeql",
|
|
45
|
+
"secret-scanning",
|
|
46
|
+
"templates",
|
|
47
|
+
"scaffolding",
|
|
48
|
+
"automation",
|
|
49
|
+
"devops",
|
|
50
|
+
"cli",
|
|
51
|
+
"github-api"
|
|
52
|
+
],
|
|
53
|
+
"files": [
|
|
54
|
+
"README.md",
|
|
55
|
+
"install.js",
|
|
56
|
+
"run.js",
|
|
57
|
+
"bin/"
|
|
58
|
+
],
|
|
59
|
+
"binaryChecksums": {
|
|
60
|
+
"fundamentum_0.1.0_darwin_amd64.tar.gz": "9d7be6fbfee6414005932547e1cc2cef54580739a5a265573942c80bb5fd28d9",
|
|
61
|
+
"fundamentum_0.1.0_darwin_arm64.tar.gz": "e50920a5522c9739899e30aa8f9414ac8a66bfcea4b91ef98e2c38f407d54b75",
|
|
62
|
+
"fundamentum_0.1.0_linux_amd64.tar.gz": "1772182f6c1d6a13b0e23bafb01ce89a8b9316895b778ce7a38ce2a9409fff16",
|
|
63
|
+
"fundamentum_0.1.0_linux_arm64.tar.gz": "5506f3002cdd4fad7ee641dfb023672c80d0e8934602906e9c8bb35a643da4ab",
|
|
64
|
+
"fundamentum_0.1.0_windows_amd64.zip": "ba5d12e8a0dbcae168adf7f68141910e0536c186f2921adba000a5b486f68853"
|
|
65
|
+
}
|
|
66
|
+
}
|
package/run.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// nosemgrep: all — npm run shim, not application code
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const { spawn, spawnSync } = require("child_process");
|
|
8
|
+
|
|
9
|
+
const binaryName = process.platform === "win32" ? "fundamentum.exe" : "fundamentum";
|
|
10
|
+
const binaryPath = path.join(__dirname, "bin", binaryName);
|
|
11
|
+
|
|
12
|
+
function runBinary() {
|
|
13
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
14
|
+
stdio: "inherit",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Forward signals so the Go binary shuts down cleanly
|
|
18
|
+
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
|
|
19
|
+
process.on(sig, () => child.kill(sig));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
child.on("error", (err) => {
|
|
23
|
+
if (err.code === "ENOENT") {
|
|
24
|
+
console.error(
|
|
25
|
+
`fundamentum: binary not found at ${binaryPath}\n` +
|
|
26
|
+
"Try running: node " +
|
|
27
|
+
path.join(__dirname, "install.js")
|
|
28
|
+
);
|
|
29
|
+
} else {
|
|
30
|
+
console.error(`fundamentum: failed to start: ${err.message}`);
|
|
31
|
+
}
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
child.on("exit", (code, signal) => {
|
|
36
|
+
process.exit(signal ? 1 : code || 0);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// If the binary is missing, postinstall was likely blocked by npm's allow-scripts
|
|
41
|
+
// security policy. Attempt a lazy download, but the package dir may be root-owned
|
|
42
|
+
// (e.g. sudo npm install -g). In that case, surface a clear recovery message.
|
|
43
|
+
if (!fs.existsSync(binaryPath)) {
|
|
44
|
+
const installScript = path.join(__dirname, "install.js");
|
|
45
|
+
|
|
46
|
+
// Check if we can write to the package dir before attempting the download.
|
|
47
|
+
let canWrite = false;
|
|
48
|
+
try {
|
|
49
|
+
fs.accessSync(__dirname, fs.constants.W_OK);
|
|
50
|
+
canWrite = true;
|
|
51
|
+
} catch (_) {
|
|
52
|
+
// directory is not writable by current user
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!canWrite) {
|
|
56
|
+
console.error(
|
|
57
|
+
"fundamentum: binary not found and the package directory is not writable.\n" +
|
|
58
|
+
"Run the installer with the same permissions used for npm install:\n" +
|
|
59
|
+
" sudo node " + installScript
|
|
60
|
+
);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.error(
|
|
65
|
+
"fundamentum: binary not found — postinstall was likely blocked by npm allow-scripts."
|
|
66
|
+
);
|
|
67
|
+
console.error("fundamentum: downloading binary now...");
|
|
68
|
+
const result = spawnSync(process.execPath, [installScript], {
|
|
69
|
+
stdio: "inherit",
|
|
70
|
+
});
|
|
71
|
+
if (result.error || result.status !== 0) {
|
|
72
|
+
console.error(
|
|
73
|
+
"fundamentum: download failed. Manual recovery:\n" +
|
|
74
|
+
" node " + installScript
|
|
75
|
+
);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
runBinary();
|