@cachly-dev/mcp-server 0.10.97 → 0.10.99
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/README.md +3 -1
- package/dist/src/handlers/brain.js +141 -2
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +86 -35
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools.d.ts +13 -0
- package/dist/src/tools.js +16 -0
- package/dist/src/tools.js.map +1 -1
- package/dist/src/toolspecs.d.ts +1 -1
- package/dist/src/toolspecs.js +1 -1
- package/package.json +2 -2
- package/server.json +3 -3
package/dist/src/index.d.ts
CHANGED
|
@@ -11,3 +11,11 @@ export declare function mergeMcpConfig(configPath: string, apiKey: string, insta
|
|
|
11
11
|
existsSync: (p: string) => boolean;
|
|
12
12
|
}): Promise<string>;
|
|
13
13
|
export declare function buildClaudeMdBlock(instanceId: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Schreibt das Brain-Protokoll in alle relevanten Instruction-Dateien:
|
|
16
|
+
* - CLAUDE.md
|
|
17
|
+
* - AGENTS.md
|
|
18
|
+
* - .github/copilot-instructions.md
|
|
19
|
+
* Idempotent, Marker-basiert.
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeInstructions(projectDir: string, instanceId: string): Promise<Record<string, 'written' | 'updated' | 'appended'>>;
|
package/dist/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { jwtExpiryMs, checkJwt, handleApiError, diagnoseAuth, planAuthHeal, read
|
|
|
3
3
|
import { handleTcoTool } from './handlers/tco.js';
|
|
4
4
|
import { notify } from './notifier.js';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
6
7
|
// True only when this file is the entry point (not imported by tests or other modules).
|
|
7
8
|
const _isMain = process.argv[1] != null &&
|
|
8
9
|
(process.argv[1] === fileURLToPath(import.meta.url) ||
|
|
@@ -65,7 +66,26 @@ import { Redis } from 'ioredis';
|
|
|
65
66
|
let API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
|
|
66
67
|
let JWT = process.env.CACHLY_JWT ?? '';
|
|
67
68
|
const _EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
|
|
68
|
-
|
|
69
|
+
// Resolve the package version at runtime from package.json so the telemetry
|
|
70
|
+
// `version` field always matches the published npm version. A hardcoded constant
|
|
71
|
+
// silently drifted (0.10.88 vs published 0.10.98), making release adoption
|
|
72
|
+
// impossible to track. Paths cover both prod (dist/src/index.js → ../../) and
|
|
73
|
+
// dev (src/index.ts → ../). Falls back to a literal if resolution fails.
|
|
74
|
+
const CURRENT_VERSION = (() => {
|
|
75
|
+
try {
|
|
76
|
+
const req = createRequire(import.meta.url);
|
|
77
|
+
for (const rel of ['../../package.json', '../package.json']) {
|
|
78
|
+
try {
|
|
79
|
+
const pkg = req(rel);
|
|
80
|
+
if (pkg.name === '@cachly-dev/mcp-server' && pkg.version)
|
|
81
|
+
return pkg.version;
|
|
82
|
+
}
|
|
83
|
+
catch { /* try next candidate */ }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { /* fall through to literal */ }
|
|
87
|
+
return '0.10.99';
|
|
88
|
+
})();
|
|
69
89
|
// Max time to wait for a freshly-provisioned instance to become "running" before
|
|
70
90
|
// giving up. Free-tier provisioning in high-latency regions can take 45–90s, so the
|
|
71
91
|
// old hard-coded 25s caused spurious "instance_not_reachable" on first run.
|
|
@@ -336,20 +356,26 @@ async function selfHealAuth() {
|
|
|
336
356
|
}
|
|
337
357
|
let _deviceFlow = null;
|
|
338
358
|
async function startDeviceFlow() {
|
|
339
|
-
// Try the API
|
|
359
|
+
// Try the cachly API device flow first. It returns a long-lived API key directly
|
|
360
|
+
// on poll (no separate JWT→key exchange needed) and is served at the ROOT paths
|
|
361
|
+
// /auth/device + /auth/device/token (NOT under /api/v1). Requires VALKEY_L1_URL on
|
|
362
|
+
// the API; when unset the API returns 503 and we fall through to Keycloak below.
|
|
340
363
|
try {
|
|
341
|
-
const res = await fetch(`${API_URL}/
|
|
364
|
+
const res = await fetch(`${API_URL}/auth/device`, {
|
|
342
365
|
method: 'POST',
|
|
343
366
|
headers: { 'Content-Type': 'application/json' },
|
|
344
|
-
body: JSON.stringify({
|
|
367
|
+
body: JSON.stringify({}),
|
|
345
368
|
signal: AbortSignal.timeout(8000),
|
|
346
369
|
});
|
|
347
370
|
if (res.ok) {
|
|
348
371
|
const data = await res.json();
|
|
372
|
+
// Append the user_code so the web page (cachly.dev/device?code=…) auto-submits.
|
|
373
|
+
const base = data.verification_uri || 'https://cachly.dev/device';
|
|
374
|
+
const verifyUrl = `${base}${base.includes('?') ? '&' : '?'}code=${encodeURIComponent(data.user_code)}`;
|
|
349
375
|
return {
|
|
350
376
|
deviceCode: data.device_code,
|
|
351
377
|
userCode: data.user_code,
|
|
352
|
-
verifyUrl
|
|
378
|
+
verifyUrl,
|
|
353
379
|
pollInterval: (data.interval ?? 5) * 1000,
|
|
354
380
|
deadline: Date.now() + 10 * 60 * 1000,
|
|
355
381
|
polling: false,
|
|
@@ -385,12 +411,12 @@ async function startDeviceFlow() {
|
|
|
385
411
|
async function pollDeviceFlow(flow) {
|
|
386
412
|
if (Date.now() > flow.deadline)
|
|
387
413
|
return 'expired';
|
|
388
|
-
// Try API
|
|
414
|
+
// Try the cachly API device flow first, then direct Keycloak as fallback.
|
|
389
415
|
const tryProxy = async () => {
|
|
390
|
-
const res = await fetch(`${API_URL}/
|
|
416
|
+
const res = await fetch(`${API_URL}/auth/device/token`, {
|
|
391
417
|
method: 'POST',
|
|
392
418
|
headers: { 'Content-Type': 'application/json' },
|
|
393
|
-
body: JSON.stringify({ device_code: flow.deviceCode
|
|
419
|
+
body: JSON.stringify({ device_code: flow.deviceCode }),
|
|
394
420
|
signal: AbortSignal.timeout(8000),
|
|
395
421
|
});
|
|
396
422
|
if (!res.ok)
|
|
@@ -783,7 +809,7 @@ async function handleTool(name, args) {
|
|
|
783
809
|
'',
|
|
784
810
|
'After sign-in: call **any tool again** — your Brain activates instantly.',
|
|
785
811
|
'',
|
|
786
|
-
'✨ Free forever · No credit card ·
|
|
812
|
+
'✨ Free forever · No credit card · 121 MCP tools · GDPR · EU servers',
|
|
787
813
|
].join('\n');
|
|
788
814
|
}
|
|
789
815
|
// Device flow unavailable (network issue) — fall back to manual setup
|
|
@@ -1378,25 +1404,43 @@ Report predicted failure risks to the user before proceeding.
|
|
|
1378
1404
|
Never commit code that does not compile. Run \`tsc --noEmit\` / \`go build ./...\` before every commit.
|
|
1379
1405
|
${CLAUDE_MD_MARKER_END}`;
|
|
1380
1406
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1407
|
+
/**
|
|
1408
|
+
* Schreibt das Brain-Protokoll in alle relevanten Instruction-Dateien:
|
|
1409
|
+
* - CLAUDE.md
|
|
1410
|
+
* - AGENTS.md
|
|
1411
|
+
* - .github/copilot-instructions.md
|
|
1412
|
+
* Idempotent, Marker-basiert.
|
|
1413
|
+
*/
|
|
1414
|
+
export async function writeInstructions(projectDir, instanceId) {
|
|
1415
|
+
const { writeFile, appendFile, readFile, mkdir } = await import('node:fs/promises');
|
|
1383
1416
|
const { existsSync } = await import('node:fs');
|
|
1384
|
-
const { resolve } = await import('node:path');
|
|
1385
|
-
const
|
|
1417
|
+
const { resolve, dirname } = await import('node:path');
|
|
1418
|
+
const files = [
|
|
1419
|
+
resolve(projectDir, 'CLAUDE.md'),
|
|
1420
|
+
resolve(projectDir, 'AGENTS.md'),
|
|
1421
|
+
resolve(projectDir, '.github', 'copilot-instructions.md'),
|
|
1422
|
+
];
|
|
1386
1423
|
const block = '\n' + buildClaudeMdBlock(instanceId) + '\n';
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1424
|
+
const results = {};
|
|
1425
|
+
for (const file of files) {
|
|
1426
|
+
await mkdir(dirname(file), { recursive: true });
|
|
1427
|
+
if (existsSync(file)) {
|
|
1428
|
+
const existing = await readFile(file, 'utf-8');
|
|
1429
|
+
if (existing.includes(CLAUDE_MD_MARKER_START)) {
|
|
1430
|
+
// Idempotent update: replace existing block
|
|
1431
|
+
const updated = existing.replace(new RegExp(`${CLAUDE_MD_MARKER_START}[\s\S]*?${CLAUDE_MD_MARKER_END}`), buildClaudeMdBlock(instanceId));
|
|
1432
|
+
await writeFile(file, updated, 'utf-8');
|
|
1433
|
+
results[file] = 'updated';
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
await appendFile(file, block, 'utf-8');
|
|
1437
|
+
results[file] = 'appended';
|
|
1438
|
+
continue;
|
|
1439
|
+
}
|
|
1440
|
+
await writeFile(file, block.trimStart(), 'utf-8');
|
|
1441
|
+
results[file] = 'written';
|
|
1442
|
+
}
|
|
1443
|
+
return results;
|
|
1400
1444
|
}
|
|
1401
1445
|
// ── CLI: cachly digest ────────────────────────────────────────────────────────
|
|
1402
1446
|
// Usage: npx @cachly-dev/mcp-server@latest digest
|
|
@@ -2212,7 +2256,7 @@ if (!process.argv[2] && process.stdout.isTTY) {
|
|
|
2212
2256
|
console.log(' \x1b[36m npx @cachly-dev/mcp-server@latest upgrade\x1b[0m ← Check for updates');
|
|
2213
2257
|
console.log('');
|
|
2214
2258
|
console.log(' \x1b[90mWorks with: Claude Code · Cursor · Windsurf · GitHub Copilot · Cline · Zed\x1b[0m');
|
|
2215
|
-
console.log(' \x1b[90mFree forever · GDPR · German servers ·
|
|
2259
|
+
console.log(' \x1b[90mFree forever · GDPR · German servers · 121 MCP tools\x1b[0m');
|
|
2216
2260
|
console.log('');
|
|
2217
2261
|
process.exit(0);
|
|
2218
2262
|
}
|
|
@@ -2274,10 +2318,15 @@ if (process.argv[2] === 'init') {
|
|
|
2274
2318
|
else {
|
|
2275
2319
|
console.log(`\n✓ Already configured: ${configFile} (no change)`);
|
|
2276
2320
|
}
|
|
2277
|
-
// Always write
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2321
|
+
// Always write the brain protocol to all instruction files (idempotent —
|
|
2322
|
+
// safe to run multiple times). Covers CLAUDE.md, AGENTS.md and Copilot so any
|
|
2323
|
+
// agent the user runs arrives pre-briefed.
|
|
2324
|
+
const results = await writeInstructions(projectDir, instanceId);
|
|
2325
|
+
for (const [path, action] of Object.entries(results)) {
|
|
2326
|
+
const verb = action === 'updated' ? '✅ Updated' : action === 'appended' ? '✅ Appended to' : '✅ Written';
|
|
2327
|
+
const name = path.split(/[\\/]/).pop() ?? path;
|
|
2328
|
+
console.log(`${verb}: ${name}`);
|
|
2329
|
+
}
|
|
2281
2330
|
// ── CLS Phase 4: Auto-install git post-commit hook ─────────────────────────
|
|
2282
2331
|
try {
|
|
2283
2332
|
const { existsSync } = await import('node:fs');
|
|
@@ -3028,10 +3077,12 @@ if (process.argv[2] === 'setup' || _isAutopilotCli) {
|
|
|
3028
3077
|
console.log(`⚠️ Could not write ~/.claude/mcp.json: ${reason}`);
|
|
3029
3078
|
configWriteFailures.push({ file: '~/.claude/mcp.json', reason });
|
|
3030
3079
|
}
|
|
3031
|
-
// ── Step 5:
|
|
3032
|
-
const
|
|
3033
|
-
const
|
|
3034
|
-
|
|
3080
|
+
// ── Step 5: Instructions für alle Vendors (immer, idempotent) ─────────────
|
|
3081
|
+
const instrResults = await writeInstructions(cwd, instance.id);
|
|
3082
|
+
for (const [file, res] of Object.entries(instrResults)) {
|
|
3083
|
+
const label = res === 'updated' ? '✅ Updated' : res === 'appended' ? '✅ Appended to' : '✅ Written';
|
|
3084
|
+
console.log(`${label}: ${file}`);
|
|
3085
|
+
}
|
|
3035
3086
|
// ── Step 5b: Bootstrap Brain from git history ─────────────────────────────
|
|
3036
3087
|
// Pre-populates the Brain with real lessons so the first session_start shows
|
|
3037
3088
|
// actual project knowledge instead of an empty brain.
|