-
-
Notifications
You must be signed in to change notification settings - Fork 478
Expand file tree
/
Copy pathstart.mjs
More file actions
121 lines (113 loc) · 4.47 KB
/
start.mjs
File metadata and controls
121 lines (113 loc) · 4.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/env node
import { execSync } from "node:child_process";
import { existsSync, chmodSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { homedir } from "node:os";
const __dirname = dirname(fileURLToPath(import.meta.url));
const originalCwd = process.cwd();
process.chdir(__dirname);
if (!process.env.CLAUDE_PROJECT_DIR) {
process.env.CLAUDE_PROJECT_DIR = originalCwd;
}
// Platform-agnostic project dir — guaranteed to be set for ALL platforms.
// Adapters may set their own env var (GEMINI_PROJECT_DIR, etc.) but this
// is the universal fallback so server.ts getProjectDir() never relies on cwd().
if (!process.env.CONTEXT_MODE_PROJECT_DIR) {
process.env.CONTEXT_MODE_PROJECT_DIR = originalCwd;
}
// Routing instructions file auto-write DISABLED for all platforms (#158, #164).
// Env vars like CLAUDE_SESSION_ID may not be set at MCP startup time, making
// the hook-capability guard unreliable. Writing to project dirs dirties git trees
// and causes double context injection on hook-capable platforms.
// Routing is handled by:
// - Hook-capable platforms: SessionStart hook injects ROUTING_BLOCK
// - Non-hook platforms: server.ts writeRoutingInstructions() on MCP connect
// - Future: explicit `context-mode init` command
// Self-heal: if a newer version dir exists, update registry so next session uses it
const cacheMatch = __dirname.match(
/^(.*[\/\\]plugins[\/\\]cache[\/\\][^\/\\]+[\/\\][^\/\\]+[\/\\])([^\/\\]+)$/,
);
if (cacheMatch) {
try {
const cacheParent = cacheMatch[1];
const myVersion = cacheMatch[2];
const dirs = readdirSync(cacheParent).filter((d) =>
/^\d+\.\d+\.\d+/.test(d),
);
if (dirs.length > 1) {
dirs.sort((a, b) => {
const pa = a.split(".").map(Number);
const 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;
});
const newest = dirs[dirs.length - 1];
if (newest && newest !== myVersion) {
const ipPath = resolve(
homedir(),
".claude",
"plugins",
"installed_plugins.json",
);
const ip = JSON.parse(readFileSync(ipPath, "utf-8"));
for (const [key, entries] of Object.entries(ip.plugins || {})) {
if (!key.toLowerCase().includes("context-mode")) continue;
for (const entry of entries) {
entry.installPath = resolve(cacheParent, newest);
entry.version = newest;
entry.lastUpdated = new Date().toISOString();
}
}
writeFileSync(
ipPath,
JSON.stringify(ip, null, 2) + "\n",
"utf-8",
);
}
}
} catch {
/* best effort — don't block server startup */
}
}
// Ensure native dependencies + ABI compatibility (shared with hooks via ensure-deps.mjs)
// ensure-deps handles better-sqlite3 install + ABI cache/rebuild automatically (#148, #203)
import "./hooks/ensure-deps.mjs";
// Also install pure-JS deps used by server
for (const pkg of ["turndown", "turndown-plugin-gfm", "@mixmark-io/domino"]) {
if (!existsSync(resolve(__dirname, "node_modules", pkg))) {
try {
execSync(`npm install ${pkg} --no-package-lock --no-save --silent`, {
cwd: __dirname,
stdio: "pipe",
timeout: 120000,
});
} catch { /* best effort */ }
}
}
// Self-heal: create CLI shim if cli.bundle.mjs is missing (marketplace installs)
if (!existsSync(resolve(__dirname, "cli.bundle.mjs")) && existsSync(resolve(__dirname, "build", "cli.js"))) {
const shimPath = resolve(__dirname, "cli.bundle.mjs");
writeFileSync(shimPath, '#!/usr/bin/env node\nawait import("./build/cli.js");\n');
if (process.platform !== "win32") chmodSync(shimPath, 0o755);
}
// Bundle exists (CI-built) — start instantly
if (existsSync(resolve(__dirname, "server.bundle.mjs"))) {
await import("./server.bundle.mjs");
} else {
// Dev or npm install — full build
if (!existsSync(resolve(__dirname, "node_modules"))) {
try {
execSync("npm install --silent", { cwd: __dirname, stdio: "pipe", timeout: 60000 });
} catch { /* best effort */ }
}
if (!existsSync(resolve(__dirname, "build", "server.js"))) {
try {
execSync("npx tsc --silent", { cwd: __dirname, stdio: "pipe", timeout: 30000 });
} catch { /* best effort */ }
}
await import("./build/server.js");
}