mushroomdb 0.7.2 → 0.8.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
@@ -7,7 +7,7 @@ declare a relationship as a rule, once; every write after that derives the match
7
7
  them, and retracts them when they stop being true.
8
8
 
9
9
  This package is the launcher: it downloads the native `mushroomdb` binary for your platform from
10
- the project's GitHub Releases and runs it. macOS arm64, Linux x86_64 and Linux arm64 are built.
10
+ the project's GitHub Releases and runs it. macOS (Apple Silicon and Intel), Linux x86_64 (glibc and musl), Linux arm64 and Windows x86_64 are built.
11
11
 
12
12
  ```sh
13
13
  npx mushroomdb install --db ./memory # give Claude Code, Cursor or Codex a memory
package/bin/mushroomdb.js CHANGED
@@ -5,7 +5,14 @@ const { spawnSync } = require("child_process");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
7
 
8
- const bin = path.resolve(path.join(__dirname, "..", "vendor", "mushroomdb"));
8
+ const bin = path.resolve(
9
+ path.join(
10
+ __dirname,
11
+ "..",
12
+ "vendor",
13
+ process.platform === "win32" ? "mushroomdb.exe" : "mushroomdb",
14
+ ),
15
+ );
9
16
 
10
17
  // Two flags that answer "where is this package", so an installer can ask once
11
18
  // instead of making every invocation pay for `npx`.
package/install.js CHANGED
@@ -5,20 +5,39 @@ const crypto = require("crypto");
5
5
  const fs = require("fs");
6
6
  const http = require("http");
7
7
  const https = require("https");
8
- const os = require("os");
9
8
  const path = require("path");
10
- const { spawnSync } = require("child_process");
9
+ const zlib = require("zlib");
11
10
 
12
11
  const pkg = require("./package.json");
13
12
  const SUPPORTED = [
14
13
  "darwin-arm64 → aarch64-apple-darwin",
14
+ "darwin-x64 → x86_64-apple-darwin",
15
15
  "linux-x64 → x86_64-unknown-linux-gnu",
16
+ "linux-x64 with musl → x86_64-unknown-linux-musl",
16
17
  "linux-arm64 → aarch64-unknown-linux-gnu",
18
+ "win32-x64 → x86_64-pc-windows-msvc",
17
19
  ];
18
20
 
21
+ // Whether this Linux is a musl one (Alpine). Node reports the glibc it runs
22
+ // on; a Node built for musl reports none. MUSHROOMDB_FORCE_LIBC overrides.
23
+ function isMusl() {
24
+ const forced = process.env.MUSHROOMDB_FORCE_LIBC;
25
+ if (forced) return forced === "musl";
26
+ try {
27
+ const header = process.report.getReport().header;
28
+ return !header.glibcVersionRuntime;
29
+ } catch (_) {
30
+ return false;
31
+ }
32
+ }
33
+
19
34
  function rustTarget(platform, arch) {
20
35
  if (platform === "darwin" && arch === "arm64") return "aarch64-apple-darwin";
21
- if (platform === "linux" && arch === "x64") return "x86_64-unknown-linux-gnu";
36
+ if (platform === "darwin" && arch === "x64") return "x86_64-apple-darwin";
37
+ if (platform === "win32" && arch === "x64") return "x86_64-pc-windows-msvc";
38
+ if (platform === "linux" && arch === "x64") {
39
+ return isMusl() ? "x86_64-unknown-linux-musl" : "x86_64-unknown-linux-gnu";
40
+ }
22
41
  if (platform === "linux" && (arch === "arm64" || arch === "aarch64")) {
23
42
  return "aarch64-unknown-linux-gnu";
24
43
  }
@@ -53,6 +72,33 @@ function fetchBuffer(url) {
53
72
  });
54
73
  }
55
74
 
75
+ // The bytes of the top-level file named `name` in a gzipped tarball, or null.
76
+ //
77
+ // Read here and not by running `tar`: there is not always one, and where
78
+ // there are two — Git Bash on Windows puts GNU tar ahead of the system's —
79
+ // the one found first reads a path that starts `C:` as a remote host.
80
+ function fileFromTarball(tarball, name) {
81
+ // Bounded: a release binary is tens of megabytes, and the checksum that
82
+ // admitted this tarball came from the host that served it.
83
+ const tar = zlib.gunzipSync(tarball, { maxOutputLength: 512 * 1024 * 1024 });
84
+ for (let at = 0; at + 512 <= tar.length; ) {
85
+ const header = tar.subarray(at, at + 512);
86
+ if (header.every((b) => b === 0)) break;
87
+ const entry = header.subarray(0, 100).toString("utf8").replace(/\0.*$/, "");
88
+ const size = parseInt(header.subarray(124, 136).toString("utf8").trim(), 8) || 0;
89
+ const type = String.fromCharCode(header[156] || 48);
90
+ const body = at + 512;
91
+ // The top-level entry only: a file of the same name further down is not
92
+ // the binary.
93
+ if ((type === "0" || type === "\0") && (entry === name || entry === `./${name}`)) {
94
+ if (body + size > tar.length) return null;
95
+ return tar.subarray(body, body + size);
96
+ }
97
+ at = body + Math.ceil(size / 512) * 512;
98
+ }
99
+ return null;
100
+ }
101
+
56
102
  function checksumOf(buf) {
57
103
  return crypto.createHash("sha256").update(buf).digest("hex");
58
104
  }
@@ -60,7 +106,9 @@ function checksumOf(buf) {
60
106
  function expectedChecksum(sums, filename) {
61
107
  const lines = sums.split(/\r?\n/);
62
108
  for (const line of lines) {
63
- const m = line.match(/^([0-9a-fA-F]{64})\s+(\S+)$/);
109
+ // `sha256sum` marks a file read in binary mode with `*` before its
110
+ // name, and on Windows that is every file.
111
+ const m = line.match(/^([0-9a-fA-F]{64})\s+\*?(\S+)$/);
64
112
  if (m && path.basename(m[2]) === filename) return m[1].toLowerCase();
65
113
  }
66
114
  return null;
@@ -84,7 +132,8 @@ async function main() {
84
132
  process.env.MUSHROOMDB_RELEASE_BASE ||
85
133
  `https://github.com/${repo}/releases/download/${tag}`;
86
134
  const vendor = path.join(__dirname, "vendor");
87
- const dest = path.join(vendor, "mushroomdb");
135
+ const binName = platform === "win32" ? "mushroomdb.exe" : "mushroomdb";
136
+ const dest = path.join(vendor, binName);
88
137
  fs.mkdirSync(vendor, { recursive: true });
89
138
 
90
139
  const tarUrl = `${base.replace(/\/$/, "")}/${asset}`;
@@ -100,20 +149,14 @@ async function main() {
100
149
  fail(`checksum mismatch for ${asset}: got ${got} want ${want}`);
101
150
  }
102
151
 
103
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "mushroomdb-npm-"));
104
- const tarPath = path.join(tmp, asset);
105
- fs.writeFileSync(tarPath, tarball);
106
- const extracted = spawnSync("tar", ["-xzf", tarPath, "-C", tmp], {
107
- encoding: "utf8",
108
- });
109
- if (extracted.status !== 0) {
110
- fail(`tar extract failed: ${extracted.stderr || extracted.stdout || extracted.status}`);
152
+ const binary = fileFromTarball(tarball, binName);
153
+ if (!binary) {
154
+ fail(`tarball ${asset} did not contain ./${binName}`);
111
155
  }
112
- const extractedBin = path.join(tmp, "mushroomdb");
113
- if (!fs.existsSync(extractedBin)) {
114
- fail(`tarball ${asset} did not contain ./mushroomdb`);
156
+ if (binary.length === 0) {
157
+ fail(`tarball ${asset} holds an empty ${binName}`);
115
158
  }
116
- fs.copyFileSync(extractedBin, dest);
159
+ fs.writeFileSync(dest, binary);
117
160
  fs.chmodSync(dest, 0o755);
118
161
  process.stdout.write(`installed ${dest} (${target} ${tag})\n`);
119
162
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mushroomdb",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Embedded graph database for agents: declare a relationship as a rule, and the edges maintain themselves. This package launches the native binary.",
5
5
  "mcpName": "io.github.MatthewSherlin/mushroomdb",
6
6
  "homepage": "https://matthewsherlin.github.io/mushroomdb/",
@@ -27,7 +27,8 @@
27
27
  },
28
28
  "os": [
29
29
  "darwin",
30
- "linux"
30
+ "linux",
31
+ "win32"
31
32
  ],
32
33
  "cpu": [
33
34
  "x64",