@paulnsorensen/hallouminate-nightly 0.0.0-experimental.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.
Files changed (3) hide show
  1. package/install.js +157 -0
  2. package/package.json +27 -0
  3. package/run.js +54 -0
package/install.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const https = require("https");
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+ const crypto = require("crypto");
9
+ const { spawn } = require("child_process");
10
+
11
+ // cargo-dist artifact target triples. Hallouminate's dist-workspace.toml
12
+ // builds gnu (not musl) Linux and Apple Silicon macOS only — no Windows,
13
+ // no Intel macOS. Keep this map in lockstep with dist-workspace.toml's
14
+ // `targets` and the nightly.yml build matrix.
15
+ const PLATFORM_MAP = {
16
+ "linux-x64": "x86_64-unknown-linux-gnu",
17
+ "linux-arm64": "aarch64-unknown-linux-gnu",
18
+ "darwin-arm64": "aarch64-apple-darwin",
19
+ };
20
+
21
+ const key = `${process.platform}-${process.arch}`;
22
+ const target = PLATFORM_MAP[key];
23
+
24
+ if (!target) {
25
+ console.error(`hallouminate-nightly: unsupported platform ${key}`);
26
+ console.error(`Supported: ${Object.keys(PLATFORM_MAP).join(", ")}`);
27
+ console.error("Install manually: cargo install hallouminate");
28
+ process.exit(1);
29
+ }
30
+
31
+ const binName = "hallouminate";
32
+ // cargo-dist default unix archive format is .tar.xz; nightly.yml packages the
33
+ // binary under a top-level <app>-<target>/ directory to match.
34
+ const archive = `hallouminate-${target}.tar.xz`;
35
+ // Rolling build: assets live on the fixed `nightly` prerelease, not a
36
+ // per-version tag, so this URL is version-independent.
37
+ const url = `https://github.com/paulnsorensen/hallouminate/releases/download/nightly/${archive}`;
38
+
39
+ const binDir = path.join(__dirname, "bin");
40
+ const binPath = path.join(binDir, binName);
41
+
42
+ // Always refresh: each npm version maps to a fresh nightly build, so never
43
+ // short-circuit on an existing (stale) binary.
44
+ fs.mkdirSync(binDir, { recursive: true });
45
+
46
+ console.log(`hallouminate-nightly: downloading ${target} nightly binary...`);
47
+
48
+ const MAX_REDIRECTS = 5;
49
+ const REQUEST_TIMEOUT_MS = 30_000;
50
+
51
+ function follow(url, redirectsLeft, cb) {
52
+ // postinstall download: refuse anything but HTTPS, including redirect
53
+ // targets, so a hijacked Location header can't downgrade or jump host.
54
+ if (!url.startsWith("https:")) {
55
+ console.error(`hallouminate-nightly: refusing non-HTTPS download URL: ${url}`);
56
+ console.error("Install manually: cargo install hallouminate");
57
+ process.exit(1);
58
+ }
59
+ const req = https
60
+ .get(url, { headers: { "User-Agent": "hallouminate-npm" } }, (res) => {
61
+ if (
62
+ res.statusCode >= 300 &&
63
+ res.statusCode < 400 &&
64
+ res.headers.location
65
+ ) {
66
+ res.resume(); // drain the redirect body so the socket is freed
67
+ if (redirectsLeft <= 0) {
68
+ console.error(`hallouminate-nightly: too many redirects fetching ${url}`);
69
+ console.error("Install manually: cargo install hallouminate");
70
+ process.exit(1);
71
+ }
72
+ follow(res.headers.location, redirectsLeft - 1, cb);
73
+ } else if (res.statusCode !== 200) {
74
+ console.error(`hallouminate-nightly: download failed (HTTP ${res.statusCode})`);
75
+ console.error(`URL: ${url}`);
76
+ console.error("Install manually: cargo install hallouminate");
77
+ process.exit(1);
78
+ } else {
79
+ cb(res);
80
+ }
81
+ })
82
+ .on("error", (err) => {
83
+ console.error(`hallouminate-nightly: download failed: ${err.message}`);
84
+ console.error("Install manually: cargo install hallouminate");
85
+ process.exit(1);
86
+ });
87
+ req.setTimeout(REQUEST_TIMEOUT_MS, () => {
88
+ req.destroy(new Error(`timed out after ${REQUEST_TIMEOUT_MS}ms`));
89
+ });
90
+ }
91
+
92
+ function downloadToBuffer(url, cb) {
93
+ follow(url, MAX_REDIRECTS, (res) => {
94
+ const chunks = [];
95
+ res.on("data", (chunk) => chunks.push(chunk));
96
+ res.on("end", () => cb(Buffer.concat(chunks)));
97
+ res.on("error", (err) => {
98
+ console.error(`hallouminate-nightly: download failed: ${err.message}`);
99
+ console.error("Install manually: cargo install hallouminate");
100
+ process.exit(1);
101
+ });
102
+ });
103
+ }
104
+
105
+ const shaUrl = `${url}.sha256`;
106
+
107
+ downloadToBuffer(shaUrl, (shaBuf) => {
108
+ const expected = shaBuf.toString("utf8").trim().split(/\s+/)[0].toLowerCase();
109
+ if (!/^[0-9a-f]{64}$/.test(expected)) {
110
+ console.error(`hallouminate-nightly: malformed checksum file at ${shaUrl}`);
111
+ console.error("Install manually: cargo install hallouminate");
112
+ process.exit(1);
113
+ }
114
+
115
+ downloadToBuffer(url, (tarBuf) => {
116
+ const actual = crypto.createHash("sha256").update(tarBuf).digest("hex");
117
+ if (actual !== expected) {
118
+ console.error(`hallouminate-nightly: checksum mismatch for ${archive}`);
119
+ console.error(`expected: ${expected}`);
120
+ console.error(`actual: ${actual}`);
121
+ console.error("Install manually: cargo install hallouminate");
122
+ process.exit(1);
123
+ }
124
+
125
+ // tar -xJ understands xz on both macOS (BSD tar) and Linux (GNU tar).
126
+ // --strip-components=1 flattens the top-level <app>-<target>/ wrapper so
127
+ // the binary lands directly in bin/.
128
+ const tar = spawn(
129
+ "tar",
130
+ ["-xJ", "--strip-components=1", "-C", binDir],
131
+ { stdio: ["pipe", "inherit", "inherit"] },
132
+ );
133
+ tar.on("error", (err) => {
134
+ console.error(`hallouminate-nightly: failed to run tar: ${err.message}`);
135
+ console.error("Install manually: cargo install hallouminate");
136
+ process.exit(1);
137
+ });
138
+ tar.stdin.end(tarBuf);
139
+ tar.on("close", (code) => {
140
+ if (code !== 0) {
141
+ console.error(
142
+ "hallouminate-nightly: failed to extract. Install manually: cargo install hallouminate",
143
+ );
144
+ process.exit(1);
145
+ }
146
+ if (!fs.existsSync(binPath)) {
147
+ console.error(
148
+ `hallouminate-nightly: binary missing after extract (expected ${binPath}).`,
149
+ );
150
+ console.error("Install manually: cargo install hallouminate");
151
+ process.exit(1);
152
+ }
153
+ fs.chmodSync(binPath, 0o755);
154
+ console.log("hallouminate-nightly: installed successfully");
155
+ });
156
+ });
157
+ });
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@paulnsorensen/hallouminate-nightly",
3
+ "version": "0.0.0-experimental.0",
4
+ "description": "EXPERIMENTAL rolling build of paulnsorensen/hallouminate off main — NOT the official `hallouminate` package",
5
+ "bin": {
6
+ "hallouminate": "run.js"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node install.js"
10
+ },
11
+ "files": [
12
+ "install.js",
13
+ "run.js"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/paulnsorensen/hallouminate.git"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "markdown",
22
+ "wiki",
23
+ "rag",
24
+ "ai-agents"
25
+ ],
26
+ "license": "MIT"
27
+ }
package/run.js ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ // Thin launcher for the downloaded binary. Node has no execve, so we can't
6
+ // truly replace this process — instead we spawn the binary, forward the
7
+ // termination/job-control signals a caller might send to this wrapper, and
8
+ // exit reflecting the child's fate (same code, or re-raise the same signal).
9
+ // This matters for `hallouminate serve`: an MCP client that shuts the server
10
+ // down by signalling the launcher PID must have that signal reach the binary.
11
+ const { spawn } = require("child_process");
12
+ const path = require("path");
13
+
14
+ const bin = path.join(__dirname, "bin", "hallouminate");
15
+
16
+ const child = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
17
+
18
+ // Registering a listener overrides Node's default (which would kill the
19
+ // wrapper and orphan the child); forward to the child and let its exit drive
20
+ // ours. SIGKILL/SIGSTOP can't be trapped, so they're intentionally absent.
21
+ const SIGNALS = [
22
+ "SIGINT",
23
+ "SIGTERM",
24
+ "SIGHUP",
25
+ "SIGQUIT",
26
+ "SIGUSR1",
27
+ "SIGUSR2",
28
+ ];
29
+ for (const sig of SIGNALS) {
30
+ process.on(sig, () => {
31
+ if (child.exitCode === null && child.signalCode === null) {
32
+ try {
33
+ child.kill(sig);
34
+ } catch {
35
+ /* child already gone */
36
+ }
37
+ }
38
+ });
39
+ }
40
+
41
+ child.on("exit", (code, signal) => {
42
+ if (signal) {
43
+ // Re-raise so our parent observes signal-death, not a synthetic exit code.
44
+ process.kill(process.pid, signal);
45
+ } else {
46
+ process.exit(code === null ? 1 : code);
47
+ }
48
+ });
49
+
50
+ child.on("error", (err) => {
51
+ console.error(`hallouminate: failed to run binary at ${bin}`);
52
+ console.error(err.message);
53
+ process.exit(1);
54
+ });