@cachly-dev/mcp-server 0.10.87 → 0.10.88

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.
@@ -1,3 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { tokenize, splitMultiQuery, levenshtein, recencyBoost, extractTimestamp, STOPWORDS, katakanaToRomaji, arabicLightStem, expandCrossLingual, CROSS_LINGUAL_MAP } from './search.js';
3
3
  export { tokenize, splitMultiQuery, levenshtein, recencyBoost, extractTimestamp, STOPWORDS, katakanaToRomaji, arabicLightStem, expandCrossLingual, CROSS_LINGUAL_MAP };
4
+ export declare const EDITOR_FILES: Record<string, string>;
5
+ export declare const CLAUDE_MD_MARKER_START = "<!-- cachly-brain-start -->";
6
+ export declare const CLAUDE_MD_MARKER_END = "<!-- cachly-brain-end -->";
7
+ export declare function buildServerEnv(apiKey: string, instanceId: string): Record<string, string>;
8
+ export declare function buildMcpConfig(apiKey: string, instanceId: string, editor: string): string;
9
+ export declare function mergeMcpConfig(configPath: string, apiKey: string, instanceId: string, editor: string, fsOps: {
10
+ readFile: (p: string, enc: BufferEncoding) => Promise<string>;
11
+ existsSync: (p: string) => boolean;
12
+ }): Promise<string>;
13
+ export declare function buildClaudeMdBlock(instanceId: string): string;
package/dist/src/index.js CHANGED
@@ -65,7 +65,7 @@ import { Redis } from 'ioredis';
65
65
  let API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
66
66
  let JWT = process.env.CACHLY_JWT ?? '';
67
67
  const _EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
68
- const CURRENT_VERSION = '0.10.87';
68
+ const CURRENT_VERSION = '0.10.88';
69
69
  // Max time to wait for a freshly-provisioned instance to become "running" before
70
70
  // giving up. Free-tier provisioning in high-latency regions can take 45–90s, so the
71
71
  // old hard-coded 25s caused spurious "instance_not_reachable" on first run.
@@ -1213,7 +1213,7 @@ process.on('unhandledRejection', (reason) => {
1213
1213
  process.stderr.write(`[cachly-mcp] unhandledRejection: ${msg}\n`);
1214
1214
  });
1215
1215
  // ── CLI helpers ───────────────────────────────────────────────────────────────
1216
- const EDITOR_FILES = {
1216
+ export const EDITOR_FILES = {
1217
1217
  claude: '.mcp.json',
1218
1218
  cursor: '.cursor/mcp.json',
1219
1219
  windsurf: '.windsurf/mcp.json',
@@ -1222,8 +1222,8 @@ const EDITOR_FILES = {
1222
1222
  cline: '.vscode/mcp.json', // Cline respects VS Code project MCP config
1223
1223
  zed: '.zed/settings.json', // Zed project-level context_servers
1224
1224
  };
1225
- const CLAUDE_MD_MARKER_START = '<!-- cachly-brain-start -->';
1226
- const CLAUDE_MD_MARKER_END = '<!-- cachly-brain-end -->';
1225
+ export const CLAUDE_MD_MARKER_START = '<!-- cachly-brain-start -->';
1226
+ export const CLAUDE_MD_MARKER_END = '<!-- cachly-brain-end -->';
1227
1227
  const DEFAULT_API_URL = 'https://api.cachly.dev';
1228
1228
  // Build the env block written into an editor's MCP config. Self-hosting is
1229
1229
  // first-class: if the operator runs setup/init against their own backend
@@ -1231,13 +1231,13 @@ const DEFAULT_API_URL = 'https://api.cachly.dev';
1231
1231
  // the editor-launched server talks to the self-hosted instance — not api.cachly.dev.
1232
1232
  // For the default cloud backend we OMIT CACHLY_API_URL to keep configs clean (the
1233
1233
  // binary already defaults to it).
1234
- function buildServerEnv(apiKey, instanceId) {
1234
+ export function buildServerEnv(apiKey, instanceId) {
1235
1235
  const env = { CACHLY_JWT: apiKey, CACHLY_BRAIN_INSTANCE_ID: instanceId };
1236
1236
  if (API_URL && API_URL !== DEFAULT_API_URL)
1237
1237
  env.CACHLY_API_URL = API_URL;
1238
1238
  return env;
1239
1239
  }
1240
- function buildMcpConfig(apiKey, instanceId, editor) {
1240
+ export function buildMcpConfig(apiKey, instanceId, editor) {
1241
1241
  if (editor === 'continue') {
1242
1242
  return JSON.stringify({
1243
1243
  experimental: {
@@ -1274,7 +1274,7 @@ function buildMcpConfig(apiKey, instanceId, editor) {
1274
1274
  }
1275
1275
  // mergeMcpConfig reads an existing config file (if any), merges the cachly entry,
1276
1276
  // and returns the updated JSON string — preserving all other MCP servers and settings.
1277
- async function mergeMcpConfig(configPath, apiKey, instanceId, editor, fsOps) {
1277
+ export async function mergeMcpConfig(configPath, apiKey, instanceId, editor, fsOps) {
1278
1278
  const cachlyEntry = {
1279
1279
  command: 'npx',
1280
1280
  args: ['-y', '@cachly-dev/mcp-server@latest'],
@@ -1320,7 +1320,7 @@ async function mergeMcpConfig(configPath, apiKey, instanceId, editor, fsOps) {
1320
1320
  existing['mcpServers'] = servers;
1321
1321
  return JSON.stringify(existing, null, 2);
1322
1322
  }
1323
- function buildClaudeMdBlock(instanceId) {
1323
+ export function buildClaudeMdBlock(instanceId) {
1324
1324
  return `${CLAUDE_MD_MARKER_START}
1325
1325
  ## Cachly AI Brain — Always Active
1326
1326
 
@@ -2899,6 +2899,10 @@ if (process.argv[2] === 'setup' || _isAutopilotCli) {
2899
2899
  // Uses mergeMcpConfig to preserve all existing MCP servers — only adds/updates
2900
2900
  // the cachly entry without touching filesystem, github, or other servers.
2901
2901
  const { existsSync: exSetup } = await import('node:fs');
2902
+ // Track config-write failures so the final summary tells the truth instead of
2903
+ // always claiming "Brain is ready" even when a config never got written.
2904
+ const configWriteFailures = [];
2905
+ let configWriteSuccesses = 0;
2902
2906
  for (const editor of editorsToSetup) {
2903
2907
  const configFile = EDITOR_FILES[editor] ?? '.mcp.json';
2904
2908
  const configPath = resolve(cwd, configFile);
@@ -2908,10 +2912,13 @@ if (process.argv[2] === 'setup' || _isAutopilotCli) {
2908
2912
  const merged = await mergeMcpConfig(configPath, token, instance.id, editor, { readFile, existsSync: exSetup });
2909
2913
  await writeFile(configPath, merged, 'utf-8');
2910
2914
  console.log(`✅ ${wasExisting ? 'Updated' : 'Written'}: ${configFile}`);
2915
+ configWriteSuccesses++;
2911
2916
  }
2912
2917
  catch (writeErr) {
2913
- console.log(`⚠️ Could not write ${configFile}: ${writeErr.message}`);
2918
+ const reason = writeErr.message;
2919
+ console.log(`⚠️ Could not write ${configFile}: ${reason}`);
2914
2920
  console.log(` Fix permissions or run with sudo, then re-run setup.`);
2921
+ configWriteFailures.push({ file: configFile, reason });
2915
2922
  }
2916
2923
  }
2917
2924
  // ── Step 4b: Write global Claude Code config (~/.claude/mcp.json) ─────────
@@ -2935,9 +2942,12 @@ if (process.argv[2] === 'setup' || _isAutopilotCli) {
2935
2942
  };
2936
2943
  await writeFile(globalClaudePath, JSON.stringify(globalConfig, null, 2), 'utf-8');
2937
2944
  console.log(`✅ Written: ~/.claude/mcp.json (global — works in every project)`);
2945
+ configWriteSuccesses++;
2938
2946
  }
2939
2947
  catch (e) {
2940
- console.log(`⚠️ Could not write ~/.claude/mcp.json: ${e.message}`);
2948
+ const reason = e.message;
2949
+ console.log(`⚠️ Could not write ~/.claude/mcp.json: ${reason}`);
2950
+ configWriteFailures.push({ file: '~/.claude/mcp.json', reason });
2941
2951
  }
2942
2952
  // ── Step 5: CLAUDE.md (always — idempotent) ───────────────────────────────
2943
2953
  const mdResult = await writeClaudeMd(cwd, instance.id);
@@ -3135,15 +3145,51 @@ if (process.argv[2] === 'setup' || _isAutopilotCli) {
3135
3145
  }
3136
3146
  }
3137
3147
  catch { /* non-critical */ }
3138
- // Setup reached the end successfully — close the funnel.
3148
+ // Setup reached the end — close the funnel. Report config-write outcome so the
3149
+ // weekly funnel can spot permission/path problems blocking activation.
3139
3150
  JWT = token;
3140
3151
  const setupElapsedS = Math.round((Date.now() - setupStartMs) / 1000);
3141
- sendFunnelEvent('setup_completed', { instance_id: instance.id, elapsed_s: setupElapsedS });
3152
+ if (configWriteFailures.length > 0) {
3153
+ sendFunnelEvent('setup_config_write_failed', {
3154
+ instance_id: instance.id,
3155
+ failed: configWriteFailures.length,
3156
+ succeeded: configWriteSuccesses,
3157
+ });
3158
+ }
3159
+ sendFunnelEvent('setup_completed', {
3160
+ instance_id: instance.id,
3161
+ elapsed_s: setupElapsedS,
3162
+ config_failures: configWriteFailures.length,
3163
+ });
3164
+ if (configWriteFailures.length > 0 && configWriteSuccesses === 0) {
3165
+ // Nothing got written — be honest, this is NOT "ready".
3166
+ console.log('\n╔══════════════════════════════════════════════════════╗');
3167
+ console.log('║ ⚠️ Setup incomplete — no editor config was written ║');
3168
+ console.log('╚══════════════════════════════════════════════════════╝');
3169
+ console.log('\n Your Brain instance exists, but no editor knows about it yet.');
3170
+ console.log(' Failed writes:');
3171
+ for (const f of configWriteFailures)
3172
+ console.log(` • ${f.file} — ${f.reason}`);
3173
+ console.log('\n Fix the permissions above and re-run, or configure manually:');
3174
+ console.log(` CACHLY_JWT=${token.slice(0, 12)}… CACHLY_BRAIN_INSTANCE_ID=${instance.id}`);
3175
+ console.log(` Docs: https://cachly.dev/docs/mcp\n`);
3176
+ rl.close();
3177
+ await new Promise(r => setTimeout(r, 300));
3178
+ process.exit(1);
3179
+ }
3142
3180
  console.log('\n╔══════════════════════════════════════════════════════╗');
3143
3181
  console.log('║ 🚀 Brain is ready. ║');
3144
3182
  console.log(`║ ⏱️ Setup completed in ${String(setupElapsedS + 's').padEnd(5)} — restart your editor ║`);
3145
3183
  console.log('║ and your AI arrives pre-briefed every session. ║');
3146
- console.log('╚══════════════════════════════════════════════════════╝\n');
3184
+ console.log('╚══════════════════════════════════════════════════════╝');
3185
+ if (configWriteFailures.length > 0) {
3186
+ // Partial success — some editors configured, some not. Tell the truth.
3187
+ console.log('\n ⚠️ Some editor configs could not be written (others succeeded):');
3188
+ for (const f of configWriteFailures)
3189
+ console.log(` • ${f.file} — ${f.reason}`);
3190
+ console.log(' Fix the permissions above and re-run setup to configure them too.');
3191
+ }
3192
+ console.log('');
3147
3193
  rl.close();
3148
3194
  // Give the fire-and-forget telemetry a moment to flush before exit.
3149
3195
  await new Promise(r => setTimeout(r, 300));