context-mode 1.0.122 → 1.0.123

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.
@@ -128,19 +128,35 @@ function probeNativeInProcess(pluginRoot) {
128
128
  }
129
129
 
130
130
  export function ensureNativeCompat(pluginRoot) {
131
- // Bun ships bun:sqliteno native addon needed
132
- if (typeof globalThis.Bun !== "undefined") return;
131
+ // Pre-compute paths regardless of runtime the Bun branch below uses
132
+ // them to seed the ABI cache (#543) so the next /ctx-upgrade boot (under
133
+ // Node) finds the success marker file. Bun spoofs
134
+ // process.versions.modules to match the Node ABI level (e.g. 137 on
135
+ // Darwin matching Node 24), so a plain file-copy produces the correct
136
+ // filename for any subsequent Node boot at the same ABI.
137
+ const abi = process.versions.modules;
138
+ const nativeDir = resolve(pluginRoot, "node_modules", "better-sqlite3", "build", "Release");
139
+ const binaryPath = resolve(nativeDir, "better_sqlite3.node");
140
+ const abiCachePath = resolve(nativeDir, `better_sqlite3.abi${abi}.node`);
141
+
142
+ // Bun ships bun:sqlite — no native addon needed at RUNTIME. But
143
+ // /ctx-upgrade still verifies the ABI cache file as the success marker,
144
+ // so we seed it from the active binary if it exists. Best-effort:
145
+ // any failure here is silent because Bun never loads better-sqlite3.
146
+ if (typeof globalThis.Bun !== "undefined") {
147
+ try {
148
+ if (existsSync(nativeDir) && existsSync(binaryPath) && !existsSync(abiCachePath)) {
149
+ copyFileSync(binaryPath, abiCachePath);
150
+ }
151
+ } catch { /* best effort — Bun never dlopens this file */ }
152
+ return;
153
+ }
133
154
 
134
155
  // On Node >= 22.5, skip the child-process probe that can cause SIGSEGV (#331).
135
156
  // The binary install/rebuild still runs — only the dlopen probe is skipped.
136
157
  const skipProbe = hasModernSqlite();
137
158
 
138
159
  try {
139
- const abi = process.versions.modules;
140
- const nativeDir = resolve(pluginRoot, "node_modules", "better-sqlite3", "build", "Release");
141
- const binaryPath = resolve(nativeDir, "better_sqlite3.node");
142
- const abiCachePath = resolve(nativeDir, `better_sqlite3.abi${abi}.node`);
143
-
144
160
  if (!existsSync(nativeDir)) return;
145
161
 
146
162
  // Fast path: cached binary for this ABI already exists — swap in
@@ -3,7 +3,7 @@
3
3
  "name": "Context Mode",
4
4
  "kind": "tool",
5
5
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
6
- "version": "1.0.122",
6
+ "version": "1.0.123",
7
7
  "sandbox": {
8
8
  "mode": "permissive",
9
9
  "filesystem_access": "full",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.122",
3
+ "version": "1.0.123",
4
4
  "type": "module",
5
5
  "description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
6
6
  "author": "Mert Koseoğlu",
@@ -288,15 +288,25 @@ try { healBetterSqlite3Binding(pkgRoot); } catch { /* best effort — don't bloc
288
288
  // here too closes the gap for the very first hook fire after a fresh install
289
289
  // (before any MCP server has run).
290
290
  //
291
- // Guard: /ctx-upgrade clones the repo to `<tmpdir>/context-mode-upgrade-<epoch>/`
291
+ // Guard 1: only run on REAL `npm install -g context-mode`. A contributor's
292
+ // `npm install` from a git clone (or CI checkout) must NOT mutate the
293
+ // source-tracked `.claude-plugin/plugin.json` — doing so substitutes the
294
+ // literal `${CLAUDE_PLUGIN_ROOT}` with an absolute path and trips
295
+ // `scripts/assert-asymmetric-drift.mjs` (Issue #531) in the build chain.
296
+ // Reuses `isGlobalInstall()` (section -1 already gates that way); the
297
+ // `.git` walk inside it is what keeps contributor / CI installs untouched.
298
+ //
299
+ // Guard 2: /ctx-upgrade clones the repo to `<tmpdir>/context-mode-upgrade-<epoch>/`
292
300
  // and runs `npm install` there before `cpSync`-ing files into the real pluginRoot
293
- // (src/cli.ts). If we normalize here, pkgRoot is the tmpdir → hooks.json gets
294
- // the tmpdir's absolute paths baked in cpSync copies that poisoned hooks.json
295
- // into the real plugin dirtmpdir is later cleaned every hook fires with
296
- // `MODULE_NOT_FOUND`. Detect the upgrade staging path and skip; start.mjs will
297
- // normalize correctly on the next MCP boot from the real pluginRoot.
301
+ // (src/cli.ts). The tmpdir has no `.git`, so `isGlobalInstall()` returns
302
+ // true there we need this second check to skip the staging dir. Without
303
+ // it, pkgRoot is the tmpdirhooks.json gets the tmpdir's absolute paths
304
+ // baked in cpSync copies that poisoned hooks.json into the real plugin
305
+ // dir tmpdir is later cleaned every hook fires with MODULE_NOT_FOUND.
306
+ // start.mjs normalizes correctly on the next MCP boot from the real
307
+ // pluginRoot anyway.
298
308
  const TMPDIR_UPGRADE_RE = /[/\\]context-mode-upgrade-\d+[/\\]?$/;
299
- if (!TMPDIR_UPGRADE_RE.test(pkgRoot)) {
309
+ if (isGlobalInstall() && !TMPDIR_UPGRADE_RE.test(pkgRoot)) {
300
310
  try {
301
311
  const { normalizeHooksOnStartup } = await import("../hooks/normalize-hooks.mjs");
302
312
  normalizeHooksOnStartup({