context-mode 1.0.94 → 1.0.96
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/cache-heal.d.ts +48 -0
- package/build/cache-heal.js +150 -0
- package/build/server.js +36 -1
- package/build/session/analytics.d.ts +11 -2
- package/build/session/analytics.js +38 -32
- package/cli.bundle.mjs +107 -107
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +47 -1
- package/server.bundle.mjs +82 -82
- package/start.mjs +66 -14
package/start.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
|
-
import { existsSync, chmodSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
|
|
4
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { existsSync, chmodSync, readFileSync, writeFileSync, readdirSync, symlinkSync, mkdirSync, lstatSync } from "node:fs";
|
|
4
|
+
import { dirname, resolve, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
|
|
@@ -29,7 +29,9 @@ if (!process.env.CONTEXT_MODE_PROJECT_DIR) {
|
|
|
29
29
|
// - Non-hook platforms: server.ts writeRoutingInstructions() on MCP connect
|
|
30
30
|
// - Future: explicit `context-mode init` command
|
|
31
31
|
|
|
32
|
-
// Self-heal
|
|
32
|
+
// ── Self-heal Layer 1: Fix registry → symlink mismatches (anthropics/claude-code#46915) ──
|
|
33
|
+
// Claude Code auto-update can leave installed_plugins.json pointing to a non-existent
|
|
34
|
+
// directory. We detect this and create symlinks so hooks find the right path.
|
|
33
35
|
const cacheMatch = __dirname.match(
|
|
34
36
|
/^(.*[\/\\]plugins[\/\\]cache[\/\\][^\/\\]+[\/\\][^\/\\]+[\/\\])([^\/\\]+)$/,
|
|
35
37
|
);
|
|
@@ -37,6 +39,9 @@ if (cacheMatch) {
|
|
|
37
39
|
try {
|
|
38
40
|
const cacheParent = cacheMatch[1];
|
|
39
41
|
const myVersion = cacheMatch[2];
|
|
42
|
+
const ipPath = resolve(homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
43
|
+
|
|
44
|
+
// Forward heal: if a newer version dir exists, update registry
|
|
40
45
|
const dirs = readdirSync(cacheParent).filter((d) =>
|
|
41
46
|
/^\d+\.\d+\.\d+/.test(d),
|
|
42
47
|
);
|
|
@@ -52,12 +57,6 @@ if (cacheMatch) {
|
|
|
52
57
|
});
|
|
53
58
|
const newest = dirs[dirs.length - 1];
|
|
54
59
|
if (newest && newest !== myVersion) {
|
|
55
|
-
const ipPath = resolve(
|
|
56
|
-
homedir(),
|
|
57
|
-
".claude",
|
|
58
|
-
"plugins",
|
|
59
|
-
"installed_plugins.json",
|
|
60
|
-
);
|
|
61
60
|
const ip = JSON.parse(readFileSync(ipPath, "utf-8"));
|
|
62
61
|
for (const [key, entries] of Object.entries(ip.plugins || {})) {
|
|
63
62
|
if (!key.toLowerCase().includes("context-mode")) continue;
|
|
@@ -67,11 +66,25 @@ if (cacheMatch) {
|
|
|
67
66
|
entry.lastUpdated = new Date().toISOString();
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
|
-
writeFileSync(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
writeFileSync(ipPath, JSON.stringify(ip, null, 2) + "\n", "utf-8");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Reverse heal: if registry points to non-existent dir, create symlink to us
|
|
74
|
+
if (existsSync(ipPath)) {
|
|
75
|
+
const ip = JSON.parse(readFileSync(ipPath, "utf-8"));
|
|
76
|
+
for (const [key, entries] of Object.entries(ip.plugins || {})) {
|
|
77
|
+
if (!key.toLowerCase().includes("context-mode")) continue;
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
const rp = entry.installPath;
|
|
80
|
+
if (rp && !existsSync(rp) && rp !== __dirname) {
|
|
81
|
+
try {
|
|
82
|
+
const rpParent = dirname(rp);
|
|
83
|
+
if (!existsSync(rpParent)) mkdirSync(rpParent, { recursive: true });
|
|
84
|
+
symlinkSync(__dirname, rp, process.platform === "win32" ? "junction" : undefined);
|
|
85
|
+
} catch { /* best effort */ }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
90
|
} catch {
|
|
@@ -79,6 +92,45 @@ if (cacheMatch) {
|
|
|
79
92
|
}
|
|
80
93
|
}
|
|
81
94
|
|
|
95
|
+
// ── Self-heal Layer 4: Deploy global SessionStart hook ──
|
|
96
|
+
// This hook lives outside the plugin directory (~/.claude/hooks/) so it works
|
|
97
|
+
// even when the plugin cache is completely broken. It creates symlinks for any
|
|
98
|
+
// missing plugin cache directories on every session start.
|
|
99
|
+
try {
|
|
100
|
+
const globalHooksDir = resolve(homedir(), ".claude", "hooks");
|
|
101
|
+
const healHookPath = resolve(globalHooksDir, "context-mode-cache-heal.sh");
|
|
102
|
+
if (!existsSync(healHookPath)) {
|
|
103
|
+
if (!existsSync(globalHooksDir)) mkdirSync(globalHooksDir, { recursive: true });
|
|
104
|
+
const healScript = `#!/usr/bin/env bash
|
|
105
|
+
# context-mode plugin cache self-heal (auto-deployed)
|
|
106
|
+
# Fixes anthropics/claude-code#46915: auto-update breaks CLAUDE_PLUGIN_ROOT
|
|
107
|
+
set -euo pipefail
|
|
108
|
+
PLUGINS_FILE="$HOME/.claude/plugins/installed_plugins.json"
|
|
109
|
+
[[ -f "$PLUGINS_FILE" ]] || exit 0
|
|
110
|
+
node -e '
|
|
111
|
+
const fs=require("fs"),path=require("path");
|
|
112
|
+
try{
|
|
113
|
+
const ip=JSON.parse(fs.readFileSync(process.argv[1],"utf-8"));
|
|
114
|
+
for(const[k,es]of Object.entries(ip.plugins||{})){
|
|
115
|
+
if(!k.toLowerCase().includes("context-mode"))continue;
|
|
116
|
+
for(const e of es){
|
|
117
|
+
const p=e.installPath;
|
|
118
|
+
if(!p||fs.existsSync(p))continue;
|
|
119
|
+
const parent=path.dirname(p);
|
|
120
|
+
if(!fs.existsSync(parent))continue;
|
|
121
|
+
const dirs=fs.readdirSync(parent).filter(d=>/^\\d+\\.\\d+/.test(d)&&fs.statSync(path.join(parent,d)).isDirectory());
|
|
122
|
+
if(!dirs.length)continue;
|
|
123
|
+
dirs.sort((a,b)=>{const pa=a.split(".").map(Number),pb=b.split(".").map(Number);for(let i=0;i<3;i++){if((pa[i]||0)!==(pb[i]||0))return(pa[i]||0)-(pb[i]||0)}return 0});
|
|
124
|
+
try{fs.symlinkSync(path.join(parent,dirs[dirs.length-1]),p)}catch{}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}catch{}
|
|
128
|
+
' "$PLUGINS_FILE" 2>/dev/null || true
|
|
129
|
+
`;
|
|
130
|
+
writeFileSync(healHookPath, healScript, { mode: 0o755 });
|
|
131
|
+
}
|
|
132
|
+
} catch { /* best effort */ }
|
|
133
|
+
|
|
82
134
|
// Ensure native dependencies + ABI compatibility (shared with hooks via ensure-deps.mjs)
|
|
83
135
|
// ensure-deps handles better-sqlite3 install + ABI cache/rebuild automatically (#148, #203)
|
|
84
136
|
import "./hooks/ensure-deps.mjs";
|