@massu/core 2.1.0 → 2.2.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/CHANGELOG.md +19 -0
  2. package/dist/cli.js +71 -11
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
10
10
  > redistributed under the **Apache License 2.0**. Full license + NOTICE:
11
11
  > `packages/core/assets/embedder/MODEL-LICENSE`.
12
12
 
13
+ ## [2.2.0] - 2026-07-22
14
+
15
+ ### Added
16
+
17
+ - **Windows Node bootstrap — CR-70 Layer 2 auto-recovery now covers Windows.** `2.1.0` shipped
18
+ the self-bootstrapping launcher that re-execs massu under a compatible installed Node when the
19
+ running Node is below the floor — but discovery was Unix-only (a slash-rooted allowlist, a POSIX
20
+ ownership gate). On Windows it degraded to a correct-but-manual winget/nodejs.org remedy. `2.2.0`
21
+ extends the SAME chokepoint, floor SoT, and re-exec to Windows: discovery now enumerates the
22
+ Windows install layouts (nvm-windows, fnm, volta, scoop, the MSI / winget / Chocolatey
23
+ `C:\Program Files\nodejs`, and `%LOCALAPPDATA%\Programs\nodejs`) from their env pointers only —
24
+ never `%PATH%` / `where.exe` — with a strict absolute-path allowlist-root containment trust gate
25
+ (the `getuid` analogue). A Windows customer with a compatible Node already installed now gets the
26
+ identical zero-onus auto-fix on `npm i -g @massu/core@2.2.0`; otherwise the already-shipped
27
+ correct Windows loud remedy. The POSIX path is byte-for-byte unchanged (regression-guarded). A
28
+ real `windows-latest` CI leg (`Windows Bootstrap Gate`) proves re-exec fidelity, installer
29
+ symmetry, and the doctor canary at runtime on Windows. No new Canonical Rule — Windows parity of
30
+ CR-70, folded into `canonical-rules-detail.md#cr-70`.
31
+
13
32
  ## [2.1.0] - 2026-07-22
14
33
 
15
34
  ### Added
package/dist/cli.js CHANGED
@@ -6376,7 +6376,7 @@ import {
6376
6376
  fsyncSync as fsyncSync3,
6377
6377
  closeSync as closeSync3,
6378
6378
  existsSync as existsSync14,
6379
- realpathSync,
6379
+ realpathSync as realpathSync2,
6380
6380
  unlinkSync as unlinkSync2,
6381
6381
  accessSync as accessSync2,
6382
6382
  statSync as statSync5,
@@ -6423,8 +6423,8 @@ function isContainedIn(rootDir, candidate, opts = {}) {
6423
6423
  let realRoot;
6424
6424
  let realProbe;
6425
6425
  try {
6426
- realRoot = realpathSync(root);
6427
- realProbe = realpathSync(probe);
6426
+ realRoot = realpathSync2(root);
6427
+ realProbe = realpathSync2(probe);
6428
6428
  } catch {
6429
6429
  return true;
6430
6430
  }
@@ -38300,13 +38300,14 @@ import { fileURLToPath as fileURLToPath7 } from "url";
38300
38300
 
38301
38301
  // src/lib/node-bootstrap.ts
38302
38302
  init_node_floor();
38303
- import { existsSync, readdirSync, statSync } from "fs";
38303
+ import { existsSync, readdirSync, statSync, realpathSync } from "fs";
38304
38304
  import { spawnSync, execFileSync } from "child_process";
38305
38305
  import { homedir } from "os";
38306
- import { isAbsolute, resolve } from "path";
38306
+ import { isAbsolute, resolve, win32 as pathWin32 } from "path";
38307
38307
  var REEXEC_SENTINEL_ENV = "MASSU_BOOTSTRAP_REEXEC";
38308
38308
  var OPT_OUT_ENV = "MASSU_NO_NODE_BOOTSTRAP";
38309
- function allowlistPatterns(env) {
38309
+ function allowlistPatterns(env, platform = process.platform) {
38310
+ if (platform === "win32") return windowsAllowlistPatterns(env);
38310
38311
  const patterns = [];
38311
38312
  const rawHome = env.HOME || homedir();
38312
38313
  const home = rawHome && isAbsolute(rawHome) ? rawHome : null;
@@ -38326,6 +38327,44 @@ function allowlistPatterns(env) {
38326
38327
  );
38327
38328
  return patterns;
38328
38329
  }
38330
+ function winManagerDirs(env) {
38331
+ const abs = (d2) => d2 && pathWin32.isAbsolute(d2) ? d2 : null;
38332
+ const appdata = abs(env.APPDATA);
38333
+ const localappdata = abs(env.LOCALAPPDATA);
38334
+ return {
38335
+ userprofile: abs(env.USERPROFILE),
38336
+ nvmHome: abs(env.NVM_HOME),
38337
+ nvmSymlink: abs(env.NVM_SYMLINK),
38338
+ fnmDir: abs(env.FNM_DIR) ?? (appdata ? `${appdata}\\fnm` : null),
38339
+ voltaHome: abs(env.VOLTA_HOME) ?? (localappdata ? `${localappdata}\\Volta` : null),
38340
+ localappdata
38341
+ };
38342
+ }
38343
+ function windowsAllowlistRoots(env) {
38344
+ const d2 = winManagerDirs(env);
38345
+ const roots = [];
38346
+ for (const r2 of [d2.userprofile, d2.nvmHome, d2.nvmSymlink, d2.fnmDir, d2.voltaHome, d2.localappdata]) {
38347
+ if (r2) roots.push(r2);
38348
+ }
38349
+ roots.push("C:\\Program Files\\nodejs", "C:\\Program Files (x86)\\nodejs");
38350
+ return roots;
38351
+ }
38352
+ function windowsAllowlistPatterns(env) {
38353
+ const d2 = winManagerDirs(env);
38354
+ const patterns = [];
38355
+ const add = (dir, suffix) => {
38356
+ if (dir) patterns.push(`${dir}\\${suffix}`);
38357
+ };
38358
+ add(d2.nvmHome, "v*\\node.exe");
38359
+ add(d2.nvmSymlink, "node.exe");
38360
+ add(d2.fnmDir, "node-versions\\v*\\installation\\node.exe");
38361
+ add(d2.voltaHome, "tools\\image\\node\\*\\node.exe");
38362
+ add(d2.userprofile, "scoop\\apps\\nodejs\\current\\node.exe");
38363
+ add(d2.userprofile, "scoop\\apps\\nodejs-lts\\current\\node.exe");
38364
+ add(d2.localappdata, "Programs\\nodejs\\node.exe");
38365
+ patterns.push("C:\\Program Files\\nodejs\\node.exe", "C:\\Program Files (x86)\\nodejs\\node.exe");
38366
+ return patterns;
38367
+ }
38329
38368
  function isOwnershipTrusted(candidate) {
38330
38369
  const getuid = process.getuid;
38331
38370
  if (typeof getuid !== "function") return true;
@@ -38336,8 +38375,22 @@ function isOwnershipTrusted(candidate) {
38336
38375
  return false;
38337
38376
  }
38338
38377
  }
38378
+ function isWindowsTrusted(candidate, env) {
38379
+ let resolved;
38380
+ try {
38381
+ resolved = pathWin32.resolve(realpathSync(candidate));
38382
+ } catch {
38383
+ resolved = pathWin32.resolve(candidate);
38384
+ }
38385
+ const target = resolved.toLowerCase();
38386
+ for (const root of windowsAllowlistRoots(env)) {
38387
+ const base = pathWin32.resolve(root).toLowerCase();
38388
+ if (target === base || target.startsWith(base + pathWin32.sep)) return true;
38389
+ }
38390
+ return false;
38391
+ }
38339
38392
  function expandSingleStarGlob(pattern) {
38340
- const parts = pattern.split("/");
38393
+ const parts = pattern.split(/[\\/]/);
38341
38394
  const starIdx = parts.findIndex((p19) => p19.includes("*"));
38342
38395
  if (starIdx === -1) {
38343
38396
  return existsSync(pattern) ? [pattern] : [];
@@ -38371,16 +38424,23 @@ function probeNodeVersion(candidate) {
38371
38424
  return null;
38372
38425
  }
38373
38426
  }
38374
- function discoverCompatibleNode(env = process.env) {
38427
+ function isAcceptedCandidatePath(candidate, platform = process.platform) {
38428
+ if (platform === "win32") {
38429
+ return pathWin32.isAbsolute(candidate) && (/^[A-Za-z]:[\\/]/.test(candidate) || candidate.startsWith("\\\\"));
38430
+ }
38431
+ return candidate.startsWith("/");
38432
+ }
38433
+ function discoverCompatibleNode(env = process.env, platform = process.platform) {
38375
38434
  const seen = /* @__PURE__ */ new Set();
38376
38435
  let best = null;
38377
- for (const pattern of allowlistPatterns(env)) {
38436
+ for (const pattern of allowlistPatterns(env, platform)) {
38378
38437
  for (const candidate of expandSingleStarGlob(pattern)) {
38379
- if (!candidate.startsWith("/")) continue;
38438
+ if (!isAcceptedCandidatePath(candidate, platform)) continue;
38380
38439
  if (seen.has(candidate)) continue;
38381
38440
  seen.add(candidate);
38382
38441
  if (!existsSync(candidate)) continue;
38383
- if (!isOwnershipTrusted(candidate)) continue;
38442
+ const trusted = platform === "win32" ? isWindowsTrusted(candidate, env) : isOwnershipTrusted(candidate);
38443
+ if (!trusted) continue;
38384
38444
  const v3 = probeNodeVersion(candidate);
38385
38445
  if (!v3) continue;
38386
38446
  if (!checkNodeVersion(`v${v3}`).ok) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massu/core",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, tiered tooling (12 free / 73 total), 56 workflow commands, 11 agents, 20+ patterns",
6
6
  "main": "./dist/cli.js",