memtrace 0.3.55 → 0.3.67

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 (2) hide show
  1. package/install.js +136 -9
  2. package/package.json +6 -4
package/install.js CHANGED
@@ -9,6 +9,16 @@ const { platformBinary, spawnOptionsForPlatform } = require("./lib/spawn-helper"
9
9
  const { installToFs } = require("./lib/claude-integration");
10
10
 
11
11
  // ── Platform binary resolution (preserved from legacy) ───────────────────────
12
+ //
13
+ // Two parallel package families on x86_64:
14
+ // • default — bundles MS prebuilt libonnxruntime (AVX2 baseline)
15
+ // • `*-noavx2` — bundles a from-source build with AVX/AVX2/AVX-512
16
+ // codegen disabled, for Ivy Bridge / Xeon E5 v2 etc.
17
+ // Apple Silicon and Linux ARM don't need the split (no AVX in the ISA).
18
+ //
19
+ // The choice is made at install time by `hasAvx2()`. Users can force the
20
+ // no-AVX2 variant via `MEMTRACE_FORCE_NOAVX2=1` (handy for testing or
21
+ // when CPU detection is unreliable in containerised envs).
12
22
 
13
23
  const PLATFORM_MAP = {
14
24
  "darwin-arm64": "@memtrace/darwin-arm64",
@@ -18,13 +28,94 @@ const PLATFORM_MAP = {
18
28
  "win32-x64": "@memtrace/win32-x64",
19
29
  };
20
30
 
31
+ const NOAVX2_PLATFORM_MAP = {
32
+ "linux-x64": "@memtrace/linux-x64-noavx2",
33
+ "win32-x64": "@memtrace/win32-x64-noavx2",
34
+ };
35
+
21
36
  function getPlatformKey() {
22
37
  return `${os.platform()}-${os.arch()}`;
23
38
  }
24
39
 
40
+ // Best-effort runtime AVX2 check. Returns `true` (AVX2 present) when we
41
+ // can't tell — the AVX2 path is the common case and the runtime gate in
42
+ // the binary catches the wrong choice with a clean error if we guess
43
+ // wrong. This is install-time best-effort; the actual bullet-proof check
44
+ // happens at process start.
45
+ function hasAvx2() {
46
+ if (process.env.MEMTRACE_FORCE_NOAVX2 === "1") {
47
+ return false;
48
+ }
49
+ if (process.env.MEMTRACE_FORCE_AVX2 === "1") {
50
+ return true;
51
+ }
52
+ const arch = os.arch();
53
+ // Only x86_64 CPUs have AVX as a concept. ARM hosts skip the check.
54
+ if (arch !== "x64") {
55
+ return true;
56
+ }
57
+ const platform = os.platform();
58
+ try {
59
+ if (platform === "linux") {
60
+ const cpuinfo = fs.readFileSync("/proc/cpuinfo", "utf8");
61
+ // `flags` line on Intel, `Features` on some emulators.
62
+ return /\b(avx2)\b/.test(cpuinfo);
63
+ }
64
+ if (platform === "win32") {
65
+ // PowerShell exposes ProcessorFeaturePresent via the kernel32 API
66
+ // through the PROCESSOR_FEATURE enum. PF_AVX2_INSTRUCTIONS_AVAILABLE
67
+ // is feature ID 40. A fresh PS process is ~300 ms — fine for an
68
+ // install-time hook that only runs once.
69
+ const result = spawnSync(
70
+ "powershell",
71
+ [
72
+ "-NoProfile",
73
+ "-NonInteractive",
74
+ "-Command",
75
+ "[System.Reflection.Assembly]::Load('mscorlib') | Out-Null; " +
76
+ "Add-Type -MemberDefinition '[DllImport(\"kernel32\")] " +
77
+ "public static extern bool IsProcessorFeaturePresent(uint f);' " +
78
+ "-Name PF -Namespace Memtrace; " +
79
+ "[Memtrace.PF]::IsProcessorFeaturePresent(40)",
80
+ ],
81
+ { encoding: "utf8", timeout: 5000 }
82
+ );
83
+ const out = (result.stdout || "").trim().toLowerCase();
84
+ if (out === "true") return true;
85
+ if (out === "false") return false;
86
+ return true; // ambiguous → assume AVX2
87
+ }
88
+ if (platform === "darwin") {
89
+ // sysctl exposes CPU feature leaves directly. macOS x86_64 was only
90
+ // ever shipped on Sandy Bridge → Coffee Lake; AVX2 lands in Haswell
91
+ // (mid-2013), which lines up with the 2013+ Mac Pro and every Mac
92
+ // Apple sold from late 2013 onward. We probe explicitly anyway in
93
+ // case someone is running on a Hackintosh with an older CPU.
94
+ const result = spawnSync("sysctl", ["-n", "machdep.cpu.leaf7_features"], {
95
+ encoding: "utf8",
96
+ timeout: 2000,
97
+ });
98
+ const out = (result.stdout || "").toUpperCase();
99
+ return /AVX2/.test(out);
100
+ }
101
+ } catch (_) {
102
+ // Detection failed (locked-down container, missing /proc, sysctl
103
+ // gone, etc.). Default to the AVX2 path — the runtime gate will
104
+ // catch a wrong guess with a clean error.
105
+ }
106
+ return true;
107
+ }
108
+
109
+ function platformPackageName(key) {
110
+ if (!hasAvx2() && Object.prototype.hasOwnProperty.call(NOAVX2_PLATFORM_MAP, key)) {
111
+ return NOAVX2_PLATFORM_MAP[key];
112
+ }
113
+ return PLATFORM_MAP[key];
114
+ }
115
+
25
116
  function getBinaryPath() {
26
117
  const key = getPlatformKey();
27
- const pkg = PLATFORM_MAP[key];
118
+ const pkg = platformPackageName(key);
28
119
  if (!pkg) {
29
120
  throw new Error(
30
121
  `Memtrace does not support platform: ${key}\n` +
@@ -33,16 +124,42 @@ function getBinaryPath() {
33
124
  }
34
125
  const ext = os.platform() === "win32" ? ".exe" : "";
35
126
  const binaryName = `memtrace${ext}`;
127
+ try {
128
+ return require.resolve(`${pkg}/bin/${binaryName}`);
129
+ } catch {
130
+ selfHealPlatformPackage();
131
+ }
36
132
  try {
37
133
  return require.resolve(`${pkg}/bin/${binaryName}`);
38
134
  } catch {
39
135
  throw new Error(
40
- `Could not find memtrace binary for ${key}.\n` +
136
+ `Could not find memtrace binary for ${key} (package ${pkg}).\n` +
41
137
  `Try reinstalling: npm install -g memtrace`
42
138
  );
43
139
  }
44
140
  }
45
141
 
142
+ function buildSelfHealInstallArgs(packageDir, versioned) {
143
+ return ["install", "--prefix", packageDir, "--no-save", "--include=optional", versioned];
144
+ }
145
+
146
+ function buildSelfHealEnv(env) {
147
+ const next = { ...env };
148
+
149
+ // During `npm install -g memtrace`, npm exposes global-mode config through
150
+ // the environment. A nested `npm install` can inherit that and install the
151
+ // platform package into the global prefix instead of this package directory.
152
+ delete next.npm_config_global;
153
+ delete next.NPM_CONFIG_GLOBAL;
154
+ next.npm_config_global = "false";
155
+
156
+ return next;
157
+ }
158
+
159
+ function getPinnedPlatformVersion(packageJson, platformPackage) {
160
+ return packageJson.optionalDependencies?.[platformPackage] ?? packageJson.version;
161
+ }
162
+
46
163
  // ── Optional-dep self-heal ───────────────────────────────────────────────────
47
164
  //
48
165
  // `optionalDependencies` is supposed to fan out platform binaries
@@ -56,16 +173,16 @@ function getBinaryPath() {
56
173
  // versioned name comes from the same map the runtime resolver uses.
57
174
  function selfHealPlatformPackage() {
58
175
  const key = getPlatformKey();
59
- const pkg = PLATFORM_MAP[key];
60
- if (!pkg) return; // unsupported platform — getBinaryPath will surface it later
176
+ const pkg = platformPackageName(key);
177
+ if (!pkg) return false; // unsupported platform — getBinaryPath will surface it later
61
178
  try {
62
179
  require.resolve(`${pkg}/package.json`);
63
- return; // already installed via optionalDependencies
180
+ return true; // already installed via optionalDependencies
64
181
  } catch (_) {
65
182
  // not installed — fall through to install
66
183
  }
67
184
  const ourPkg = require("./package.json");
68
- const versioned = `${pkg}@${ourPkg.version}`;
185
+ const versioned = `${pkg}@${getPinnedPlatformVersion(ourPkg, pkg)}`;
69
186
  console.log(
70
187
  `memtrace: optional platform dep ${pkg} was not installed; ` +
71
188
  `running 'npm install ${versioned}' to fetch it…`
@@ -78,11 +195,11 @@ function selfHealPlatformPackage() {
78
195
  // `test/spawn-helper.test.js` for the full regression coverage.
79
196
  const result = spawnSync(
80
197
  platformBinary("npm", process.platform),
81
- ["install", "--no-save", versioned],
198
+ buildSelfHealInstallArgs(__dirname, versioned),
82
199
  spawnOptionsForPlatform(process.platform, {
83
200
  cwd: __dirname,
84
201
  stdio: "inherit",
85
- env: process.env,
202
+ env: buildSelfHealEnv(process.env),
86
203
  })
87
204
  );
88
205
  if (result.status !== 0) {
@@ -90,7 +207,9 @@ function selfHealPlatformPackage() {
90
207
  `memtrace: self-heal install of ${versioned} failed (exit ${result.status}). ` +
91
208
  `Run manually: npm install -g memtrace --include=optional`
92
209
  );
210
+ return false;
93
211
  }
212
+ return true;
94
213
  }
95
214
 
96
215
  // ── Main postinstall ─────────────────────────────────────────────────────────
@@ -176,4 +295,12 @@ if (require.main === module) {
176
295
  }
177
296
  }
178
297
 
179
- module.exports = { getBinaryPath };
298
+ module.exports = {
299
+ getBinaryPath,
300
+ selfHealPlatformPackage,
301
+ buildSelfHealEnv,
302
+ buildSelfHealInstallArgs,
303
+ getPinnedPlatformVersion,
304
+ hasAvx2,
305
+ platformPackageName,
306
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memtrace",
3
- "version": "0.3.55",
3
+ "version": "0.3.67",
4
4
  "description": "Code intelligence graph — MCP server + AI agent skills + visualization UI",
5
5
  "keywords": [
6
6
  "mcp",
@@ -39,9 +39,11 @@
39
39
  "fs-extra": "^11.0.0"
40
40
  },
41
41
  "optionalDependencies": {
42
- "@memtrace/darwin-arm64": "0.3.55",
43
- "@memtrace/linux-x64": "0.3.55",
44
- "@memtrace/win32-x64": "0.3.55"
42
+ "@memtrace/darwin-arm64": "0.3.67",
43
+ "@memtrace/linux-x64": "0.3.67",
44
+ "@memtrace/win32-x64": "0.3.67",
45
+ "@memtrace/linux-x64-noavx2": "0.3.67",
46
+ "@memtrace/win32-x64-noavx2": "0.3.67"
45
47
  },
46
48
  "engines": {
47
49
  "node": ">=18"