@cachly-dev/mcp-server 0.10.71 → 0.10.73
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 +1 -1
- package/dist/src/handlers/brain.js +40 -5
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/handlers/team.d.ts +18 -0
- package/dist/src/handlers/team.js +248 -3
- package/dist/src/handlers/team.js.map +1 -1
- package/dist/src/index.js +67 -11
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools.d.ts +107 -0
- package/dist/src/tools.js +87 -0
- package/dist/src/tools.js.map +1 -1
- package/package.json +3 -2
- package/server.json +3 -3
package/dist/src/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { jwtExpiryMs, checkJwt, handleApiError, diagnoseAuth, planAuthHeal } from './auth.js';
|
|
3
3
|
import { handleTcoTool } from './handlers/tco.js';
|
|
4
4
|
import { notify } from './notifier.js';
|
|
5
|
-
import { readFile } from 'node:fs/promises';
|
|
6
5
|
import { fileURLToPath } from 'node:url';
|
|
7
6
|
// True only when this file is the entry point (not imported by tests or other modules).
|
|
8
7
|
const _isMain = process.argv[1] != null &&
|
|
@@ -64,7 +63,7 @@ import { Redis } from 'ioredis';
|
|
|
64
63
|
const API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
|
|
65
64
|
let JWT = process.env.CACHLY_JWT ?? '';
|
|
66
65
|
const _EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
|
|
67
|
-
const CURRENT_VERSION = '0.10.
|
|
66
|
+
const CURRENT_VERSION = '0.10.73';
|
|
68
67
|
// Max time to wait for a freshly-provisioned instance to become "running" before
|
|
69
68
|
// giving up. Free-tier provisioning in high-latency regions can take 45–90s, so the
|
|
70
69
|
// old hard-coded 25s caused spurious "instance_not_reachable" on first run.
|
|
@@ -666,7 +665,7 @@ async function handleTool(name, args) {
|
|
|
666
665
|
'',
|
|
667
666
|
'After sign-in: call **any tool again** — your Brain activates instantly.',
|
|
668
667
|
'',
|
|
669
|
-
'✨ Free forever · No credit card ·
|
|
668
|
+
'✨ Free forever · No credit card · 107 MCP tools · GDPR · EU servers',
|
|
670
669
|
].join('\n');
|
|
671
670
|
}
|
|
672
671
|
// Device flow unavailable (network issue) — fall back to manual setup
|
|
@@ -1869,24 +1868,45 @@ if (!process.argv[2] && process.stdout.isTTY) {
|
|
|
1869
1868
|
console.log(' \x1b[36m npx @cachly-dev/mcp-server@latest upgrade\x1b[0m ← Check for updates');
|
|
1870
1869
|
console.log('');
|
|
1871
1870
|
console.log(' \x1b[90mWorks with: Claude Code · Cursor · Windsurf · GitHub Copilot · Cline · Zed\x1b[0m');
|
|
1872
|
-
console.log(' \x1b[90mFree forever · GDPR · German servers ·
|
|
1871
|
+
console.log(' \x1b[90mFree forever · GDPR · German servers · 107 MCP tools\x1b[0m');
|
|
1873
1872
|
console.log('');
|
|
1874
1873
|
process.exit(0);
|
|
1875
1874
|
}
|
|
1876
1875
|
// ── CLI: cachly init ──────────────────────────────────────────────────────────
|
|
1877
1876
|
// Usage: npx @cachly-dev/mcp-server init --instance-id <id> --api-key <key> [--editor claude|cursor|windsurf|copilot|continue] [--project-dir /path]
|
|
1878
1877
|
if (process.argv[2] === 'init') {
|
|
1879
|
-
const
|
|
1878
|
+
const initStart = Date.now();
|
|
1879
|
+
const { writeFile, mkdir, readFile } = await import('node:fs/promises');
|
|
1880
1880
|
const { resolve, dirname } = await import('node:path');
|
|
1881
1881
|
const argv = process.argv.slice(3);
|
|
1882
1882
|
const flag = (name) => { const i = argv.indexOf(`--${name}`); return i !== -1 ? argv[i + 1] : undefined; };
|
|
1883
|
-
|
|
1884
|
-
|
|
1883
|
+
let instanceId = flag('instance-id') ?? process.env.CACHLY_BRAIN_INSTANCE_ID;
|
|
1884
|
+
let apiKey = flag('api-key') ?? process.env.CACHLY_JWT;
|
|
1885
1885
|
const editor = (flag('editor') ?? 'claude').toLowerCase();
|
|
1886
1886
|
const projectDir = resolve(flag('project-dir') ?? '.');
|
|
1887
|
+
// Zero-arg path: fall back to credentials saved by a previous `setup`
|
|
1888
|
+
// (~/.claude/mcp.json). Makes `init` a fast, idempotent re-configuration command
|
|
1889
|
+
// you can run in any project without copy-pasting tokens.
|
|
1890
|
+
if (!instanceId || !apiKey) {
|
|
1891
|
+
try {
|
|
1892
|
+
const { existsSync } = await import('node:fs');
|
|
1893
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? '';
|
|
1894
|
+
const savedPath = home ? resolve(home, '.claude', 'mcp.json') : '';
|
|
1895
|
+
if (savedPath && existsSync(savedPath)) {
|
|
1896
|
+
const saved = JSON.parse(await readFile(savedPath, 'utf-8'));
|
|
1897
|
+
const env = saved.mcpServers?.['cachly']?.env ?? {};
|
|
1898
|
+
apiKey ??= env['CACHLY_JWT'];
|
|
1899
|
+
instanceId ??= env['CACHLY_BRAIN_INSTANCE_ID'];
|
|
1900
|
+
if (apiKey && instanceId) {
|
|
1901
|
+
console.log('✓ Reusing credentials from ~/.claude/mcp.json (saved by a prior setup)');
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
catch { /* fall through to the usage error below */ }
|
|
1906
|
+
}
|
|
1887
1907
|
if (!instanceId || !apiKey) {
|
|
1888
1908
|
console.error('\nUsage: npx @cachly-dev/mcp-server@latest init --instance-id <uuid> --api-key <cky_live_...> [--editor claude|cursor|windsurf|copilot|continue] [--project-dir /path]\n');
|
|
1889
|
-
console.error('
|
|
1909
|
+
console.error('First time? Run the zero-config wizard (signs you in, auto-provisions): npx @cachly-dev/mcp-server@latest setup\n');
|
|
1890
1910
|
console.error('Get your credentials from: https://cachly.dev/setup-ai\n');
|
|
1891
1911
|
process.exit(1);
|
|
1892
1912
|
}
|
|
@@ -1894,9 +1914,17 @@ if (process.argv[2] === 'init') {
|
|
|
1894
1914
|
const configPath = resolve(projectDir, configFile);
|
|
1895
1915
|
await mkdir(dirname(configPath), { recursive: true });
|
|
1896
1916
|
const { existsSync: exInit } = await import('node:fs');
|
|
1917
|
+
const configExisted = exInit(configPath);
|
|
1918
|
+
const prevConfig = configExisted ? await readFile(configPath, 'utf-8').catch(() => '') : '';
|
|
1897
1919
|
const merged = await mergeMcpConfig(configPath, apiKey, instanceId, editor, { readFile, existsSync: exInit });
|
|
1898
|
-
|
|
1899
|
-
|
|
1920
|
+
// Idempotent: only write when the content actually changes.
|
|
1921
|
+
if (merged !== prevConfig) {
|
|
1922
|
+
await writeFile(configPath, merged, 'utf-8');
|
|
1923
|
+
console.log(`\n✅ ${configExisted ? 'Updated' : 'Written'}: ${configFile}`);
|
|
1924
|
+
}
|
|
1925
|
+
else {
|
|
1926
|
+
console.log(`\n✓ Already configured: ${configFile} (no change)`);
|
|
1927
|
+
}
|
|
1900
1928
|
// Always write CLAUDE.md (idempotent — safe to run multiple times)
|
|
1901
1929
|
const result = await writeClaudeMd(projectDir, instanceId);
|
|
1902
1930
|
const action = result === 'updated' ? '✅ Updated' : result === 'appended' ? '✅ Appended to' : '✅ Written';
|
|
@@ -1942,8 +1970,10 @@ if (process.argv[2] === 'init') {
|
|
|
1942
1970
|
}
|
|
1943
1971
|
}
|
|
1944
1972
|
catch { /* non-critical — git hook is a best-effort feature */ }
|
|
1945
|
-
|
|
1973
|
+
const initSecs = ((Date.now() - initStart) / 1000).toFixed(1);
|
|
1974
|
+
console.log(`\n🧠 Cachly AI Brain configured for ${editor === 'claude' ? 'Claude Code' : editor}! (${initSecs}s)`);
|
|
1946
1975
|
console.log(` Restart your editor — the \`cachly\` MCP tools will appear.`);
|
|
1976
|
+
console.log(` Re-run \`init\` anytime — it's idempotent (only writes what changed).`);
|
|
1947
1977
|
console.log(`\n 📛 Add a live badge to your README:`);
|
|
1948
1978
|
console.log(` npx @cachly-dev/mcp-server@latest badge\n`);
|
|
1949
1979
|
process.exit(0);
|
|
@@ -2620,6 +2650,32 @@ if (process.argv[2] === 'setup') {
|
|
|
2620
2650
|
console.log(` Dashboard: https://cachly.dev/instances/${instance.id}`);
|
|
2621
2651
|
console.log(`\n 📛 Add a live badge to your README:`);
|
|
2622
2652
|
console.log(` npx @cachly-dev/mcp-server@latest badge\n`);
|
|
2653
|
+
// ── Step 6c: Governance bootstrap — assign the first admin role ─────────────
|
|
2654
|
+
// Optional nudge: ask once whether the user wants to establish the role model.
|
|
2655
|
+
// team_assign_role handles idempotency (first call is free; later calls need admin).
|
|
2656
|
+
if (!nonInteractive) {
|
|
2657
|
+
try {
|
|
2658
|
+
console.log('\n👑 Governance (optional — role model: admin · reviewer · contributor · viewer)');
|
|
2659
|
+
console.log('────────────────────────────────────────');
|
|
2660
|
+
const addAdmin = await ask(' Set yourself as the first admin? (y/N): ', 'N');
|
|
2661
|
+
if (addAdmin.toLowerCase() === 'y') {
|
|
2662
|
+
const adminHandle = await ask(' Your handle (e.g. "alice"): ');
|
|
2663
|
+
if (adminHandle.trim()) {
|
|
2664
|
+
const assignOut = await handleTool('team_assign_role', {
|
|
2665
|
+
instance_id: instance.id, handle: adminHandle.trim(), role: 'admin',
|
|
2666
|
+
}).catch(() => '');
|
|
2667
|
+
if (assignOut) {
|
|
2668
|
+
console.log(` ✅ ${adminHandle.trim()} is now admin. Invite teammates:\n`);
|
|
2669
|
+
console.log(` team_assign_role(instance_id="${instance.id}", handle="<teammate>", role="contributor", assigned_by="${adminHandle.trim()}")\n`);
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
else {
|
|
2674
|
+
console.log(' Skipped — run team_assign_role at any time.\n');
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
catch { /* non-critical */ }
|
|
2678
|
+
}
|
|
2623
2679
|
// ── Step 7: Email opt-in (non-blocking — at the very end) ────────────────
|
|
2624
2680
|
if (!nonInteractive) {
|
|
2625
2681
|
const email = await ask(' 📬 Stay in the loop? Email for release notes [Enter to skip]: ');
|