klypix-mcp 1.40.0 → 1.40.2
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/bin/klypix-install.mjs +1 -1
- package/bin/klypix-worker.mjs +23 -0
- package/package.json +1 -1
- package/src/mcp-auto-update.mjs +59 -2
- package/src/mcp-supervisor.mjs +5 -25
package/bin/klypix-install.mjs
CHANGED
|
@@ -220,7 +220,7 @@ const flatten = (code) => code
|
|
|
220
220
|
.replace(/\.\.\/src\/klypix-(core|format)\.mjs/g, './klypix-$1.mjs')
|
|
221
221
|
// brain-doctor + agent-rules (the server's lazy `import('../src/brain-doctor.mjs')`
|
|
222
222
|
// for the brain_doctor tool) → flat sibling refs in the runtime layout.
|
|
223
|
-
.replace(/\.\.\/src\/(brain-doctor|agent-rules|mcp-presence|mcp-supervisor)\.mjs/g, './$1.mjs')
|
|
223
|
+
.replace(/\.\.\/src\/(brain-doctor|agent-rules|mcp-presence|mcp-supervisor|mcp-auto-update)\.mjs/g, './$1.mjs')
|
|
224
224
|
.replace(/klypix-worker\.mjs/g, 'klypix-mcp-worker.mjs')
|
|
225
225
|
.replace(/const PKG_VERSION = \(\(\) => \{[\s\S]*?\}\)\(\);/, `const PKG_VERSION = '${VERSION}'; // baked at install (flat layout has no package.json)`);
|
|
226
226
|
|
package/bin/klypix-worker.mjs
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from '../src/klypix-core.mjs';
|
|
33
33
|
import { mcpServerEntry } from '../src/agent-rules.mjs';
|
|
34
34
|
import { createMcpPresence, KLYPIX_MCP_INSTRUCTIONS } from '../src/mcp-presence.mjs';
|
|
35
|
+
import { spawnAutoUpdateHelper } from '../src/mcp-auto-update.mjs';
|
|
35
36
|
|
|
36
37
|
// Real package version for the MCP handshake (was hardcoded '1.0.0', which
|
|
37
38
|
// misled every client/version diagnosis — it could never reflect the true release).
|
|
@@ -494,12 +495,18 @@ if (!canvasViewAsApp) {
|
|
|
494
495
|
|
|
495
496
|
const transport = new StdioServerTransport();
|
|
496
497
|
let runningHeartbeat = null;
|
|
498
|
+
let autoUpdateStarter = null;
|
|
499
|
+
let autoUpdatePoller = null;
|
|
497
500
|
let runtimeStopped = false;
|
|
498
501
|
const stopRuntimePresence = () => {
|
|
499
502
|
if (runtimeStopped) return;
|
|
500
503
|
runtimeStopped = true;
|
|
501
504
|
if (runningHeartbeat) clearInterval(runningHeartbeat);
|
|
502
505
|
runningHeartbeat = null;
|
|
506
|
+
if (autoUpdateStarter) clearTimeout(autoUpdateStarter);
|
|
507
|
+
if (autoUpdatePoller) clearInterval(autoUpdatePoller);
|
|
508
|
+
autoUpdateStarter = null;
|
|
509
|
+
autoUpdatePoller = null;
|
|
503
510
|
recordRunningServer({ remove: true });
|
|
504
511
|
mcpPresence.stop();
|
|
505
512
|
};
|
|
@@ -508,6 +515,22 @@ server.server.oninitialized = () => {
|
|
|
508
515
|
recordRunningServer();
|
|
509
516
|
runningHeartbeat = setInterval(() => recordRunningServer(), 30_000);
|
|
510
517
|
runningHeartbeat.unref?.();
|
|
518
|
+
// The worker mirrors the supervisor's host-neutral scheduler. This lets an
|
|
519
|
+
// older stable supervisor acquire the updater immediately after hot-swapping
|
|
520
|
+
// to a compatible new worker; no extra host reconnect is needed for the
|
|
521
|
+
// scheduler itself. Stamp + lock make the duplicate trigger effectively free.
|
|
522
|
+
const autoUpdateDir = path.dirname(
|
|
523
|
+
process.env.KLYPIX_MCP_RUNTIME_MANIFEST
|
|
524
|
+
|| path.join(os.homedir(), '.claude', 'project-brain', '.mcp-runtime.json'),
|
|
525
|
+
);
|
|
526
|
+
const checkForCoreUpdate = () => spawnAutoUpdateHelper({
|
|
527
|
+
brainDir: autoUpdateDir,
|
|
528
|
+
currentVersion: PKG_VERSION,
|
|
529
|
+
});
|
|
530
|
+
autoUpdateStarter = setTimeout(checkForCoreUpdate, 2000);
|
|
531
|
+
autoUpdateStarter.unref?.();
|
|
532
|
+
autoUpdatePoller = setInterval(checkForCoreUpdate, 60 * 60 * 1000);
|
|
533
|
+
autoUpdatePoller.unref?.();
|
|
511
534
|
log(`ready · vault=${VAULT} · presence=mcp`);
|
|
512
535
|
};
|
|
513
536
|
server.server.onclose = stopRuntimePresence;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "klypix-mcp",
|
|
3
|
-
"version": "1.40.
|
|
3
|
+
"version": "1.40.2",
|
|
4
4
|
"description": "Every project gets a brain — one open .klypix file your AI agents read, write, and argue from, over MCP. Works with Claude, Codex, Cursor, Cline, any model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
package/src/mcp-auto-update.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import path from 'path';
|
|
|
21
21
|
import crypto from 'crypto';
|
|
22
22
|
import https from 'https';
|
|
23
23
|
import { spawn } from 'child_process';
|
|
24
|
+
import { fileURLToPath } from 'url';
|
|
24
25
|
|
|
25
26
|
export const AUTO_UPDATE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
26
27
|
export const AUTO_UPDATE_LOCK_STALE_MS = 30 * 60 * 1000;
|
|
@@ -106,6 +107,44 @@ export function inspectAutoUpdate(brainDir, { now = Date.now(), env = process.en
|
|
|
106
107
|
};
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Start the detached updater when this machine is due.
|
|
112
|
+
*
|
|
113
|
+
* Safe to call from both the stable supervisor and the replaceable worker:
|
|
114
|
+
* the shared stamp prevents unnecessary children and the helper lock collapses
|
|
115
|
+
* the remaining cross-process race.
|
|
116
|
+
*/
|
|
117
|
+
export function spawnAutoUpdateHelper({
|
|
118
|
+
brainDir = path.join(os.homedir(), '.claude', 'project-brain'),
|
|
119
|
+
currentVersion = null,
|
|
120
|
+
env = process.env,
|
|
121
|
+
spawnProcess = spawn,
|
|
122
|
+
} = {}) {
|
|
123
|
+
if (!autoUpdateEnabled(env) || env.KLYPIX_MCP_AUTO_UPDATE_CHILD === '1') return null;
|
|
124
|
+
if (!inspectAutoUpdate(brainDir, { env }).due) return null;
|
|
125
|
+
try {
|
|
126
|
+
const helper = fileURLToPath(import.meta.url);
|
|
127
|
+
const child = spawnProcess(process.execPath, [helper, AUTO_UPDATE_WORKER_ARG], {
|
|
128
|
+
// Do not hold the managed directory as this detached process's cwd.
|
|
129
|
+
// This matters for ephemeral/test homes on Windows and is cleaner for
|
|
130
|
+
// uninstallers; the installer receives its exact target through env.
|
|
131
|
+
cwd: os.tmpdir(),
|
|
132
|
+
env: {
|
|
133
|
+
...env,
|
|
134
|
+
KLYPIX_MCP_AUTO_UPDATE_DIR: path.resolve(brainDir),
|
|
135
|
+
KLYPIX_MCP_AUTO_UPDATE_CURRENT: String(currentVersion || ''),
|
|
136
|
+
KLYPIX_MCP_AUTO_UPDATE_CHILD: '1',
|
|
137
|
+
},
|
|
138
|
+
detached: true,
|
|
139
|
+
stdio: 'ignore',
|
|
140
|
+
windowsHide: true,
|
|
141
|
+
});
|
|
142
|
+
child.on('error', () => { /* fail-open: the MCP transport remains healthy */ });
|
|
143
|
+
child.unref();
|
|
144
|
+
return child;
|
|
145
|
+
} catch { return null; }
|
|
146
|
+
}
|
|
147
|
+
|
|
109
148
|
function acquireLock(lockFile, now, staleMs = AUTO_UPDATE_LOCK_STALE_MS) {
|
|
110
149
|
fs.mkdirSync(path.dirname(lockFile), { recursive: true });
|
|
111
150
|
const token = `${process.pid}-${crypto.randomBytes(8).toString('hex')}`;
|
|
@@ -190,7 +229,25 @@ export function installExactRuntime(version, {
|
|
|
190
229
|
};
|
|
191
230
|
let child;
|
|
192
231
|
try {
|
|
193
|
-
|
|
232
|
+
let command = 'npx';
|
|
233
|
+
let args = ['-y', `klypix-mcp@${version}`, 'install', '--runtime-only'];
|
|
234
|
+
if (process.platform === 'win32') {
|
|
235
|
+
// .cmd files require a shell on Windows, but Node 24 correctly warns
|
|
236
|
+
// that shell:true concatenates arguments. Invoke npm's JS entry with
|
|
237
|
+
// this exact Node binary instead: no quoting ambiguity, no shell, and
|
|
238
|
+
// the strict-semver gate above leaves no command-injection surface.
|
|
239
|
+
const npxCli = path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npx-cli.js');
|
|
240
|
+
if (fs.existsSync(npxCli)) {
|
|
241
|
+
command = process.execPath;
|
|
242
|
+
args = [npxCli, ...args];
|
|
243
|
+
} else {
|
|
244
|
+
// Portable fallback for unusual Windows Node layouts. The only
|
|
245
|
+
// interpolated value is strict x.y.z semver.
|
|
246
|
+
command = process.env.ComSpec || 'cmd.exe';
|
|
247
|
+
args = ['/d', '/s', '/c', `npx -y klypix-mcp@${version} install --runtime-only`];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
child = spawnProcess(command, args, {
|
|
194
251
|
cwd: brainDir,
|
|
195
252
|
env: {
|
|
196
253
|
...process.env,
|
|
@@ -198,7 +255,7 @@ export function installExactRuntime(version, {
|
|
|
198
255
|
KLYPIX_MCP_AUTO_UPDATE_CHILD: '1',
|
|
199
256
|
},
|
|
200
257
|
stdio: 'ignore',
|
|
201
|
-
shell:
|
|
258
|
+
shell: false,
|
|
202
259
|
windowsHide: true,
|
|
203
260
|
});
|
|
204
261
|
} catch (error) {
|
package/src/mcp-supervisor.mjs
CHANGED
|
@@ -21,11 +21,10 @@ import os from 'os';
|
|
|
21
21
|
import path from 'path';
|
|
22
22
|
import crypto from 'crypto';
|
|
23
23
|
import { spawn } from 'child_process';
|
|
24
|
-
import { fileURLToPath } from 'url';
|
|
25
24
|
import {
|
|
26
|
-
AUTO_UPDATE_WORKER_ARG,
|
|
27
25
|
autoUpdateEnabled,
|
|
28
26
|
inspectAutoUpdate,
|
|
27
|
+
spawnAutoUpdateHelper,
|
|
29
28
|
} from './mcp-auto-update.mjs';
|
|
30
29
|
|
|
31
30
|
const INTERNAL_PREFIX = '__klypix_supervisor__';
|
|
@@ -705,29 +704,10 @@ class Supervisor {
|
|
|
705
704
|
|
|
706
705
|
scheduleAutoUpdate() {
|
|
707
706
|
if (this.closed || !this.autoUpdate || process.env.KLYPIX_MCP_AUTO_UPDATE_CHILD === '1') return;
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const helper = fileURLToPath(new URL('./mcp-auto-update.mjs', import.meta.url));
|
|
713
|
-
const child = spawn(process.execPath, [helper, AUTO_UPDATE_WORKER_ARG], {
|
|
714
|
-
// Do not hold the managed directory as this detached process's cwd.
|
|
715
|
-
// This matters for ephemeral/test homes on Windows and is cleaner for
|
|
716
|
-
// uninstallers; the installer receives its exact target through env.
|
|
717
|
-
cwd: os.tmpdir(),
|
|
718
|
-
env: {
|
|
719
|
-
...process.env,
|
|
720
|
-
KLYPIX_MCP_AUTO_UPDATE_DIR: brainDir,
|
|
721
|
-
KLYPIX_MCP_AUTO_UPDATE_CURRENT: String(this.active?.version || this.fallbackTarget.version || ''),
|
|
722
|
-
KLYPIX_MCP_AUTO_UPDATE_CHILD: '1',
|
|
723
|
-
},
|
|
724
|
-
detached: true,
|
|
725
|
-
stdio: 'ignore',
|
|
726
|
-
windowsHide: true,
|
|
727
|
-
});
|
|
728
|
-
child.on('error', () => { /* fail-open: the MCP transport remains healthy */ });
|
|
729
|
-
child.unref();
|
|
730
|
-
} catch { /* update discovery must never affect MCP */ }
|
|
707
|
+
spawnAutoUpdateHelper({
|
|
708
|
+
brainDir: path.dirname(this.runtimeManifest),
|
|
709
|
+
currentVersion: this.active?.version || this.fallbackTarget.version,
|
|
710
|
+
});
|
|
731
711
|
}
|
|
732
712
|
|
|
733
713
|
flushHostQueue() {
|