@cachly-dev/mcp-server 0.10.81 → 0.10.82
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/dist/src/handlers/brain.js +8 -1
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/index.js +35 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/telemetry-types.d.ts +87 -0
- package/dist/src/telemetry-types.js +13 -0
- package/dist/src/telemetry-types.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
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.
|
|
68
|
+
const CURRENT_VERSION = '0.10.82';
|
|
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.
|
|
@@ -624,15 +624,39 @@ function detectEditor() {
|
|
|
624
624
|
: process.env.CLAUDE_CODE_ENTRYPOINT ? 'claude'
|
|
625
625
|
: 'unknown';
|
|
626
626
|
}
|
|
627
|
-
|
|
627
|
+
/** Derive an anonymous, non-reversible fingerprint from the JWT sub claim. */
|
|
628
|
+
function _jwtUserFingerprint(jwt) {
|
|
629
|
+
try {
|
|
630
|
+
const parts = jwt.split('.');
|
|
631
|
+
if (parts.length !== 3)
|
|
632
|
+
return undefined;
|
|
633
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
|
|
634
|
+
if (!payload.sub)
|
|
635
|
+
return undefined;
|
|
636
|
+
// First 16 hex chars of a deterministic hash — no PII, not reversible.
|
|
637
|
+
let h = 0x811c9dc5;
|
|
638
|
+
for (let i = 0; i < payload.sub.length; i++) {
|
|
639
|
+
h ^= payload.sub.charCodeAt(i);
|
|
640
|
+
h = (h * 0x01000193) >>> 0;
|
|
641
|
+
}
|
|
642
|
+
return h.toString(16).padStart(8, '0') + payload.sub.length.toString(16).padStart(4, '0');
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function sendFunnelEvent(event, extra, metrics) {
|
|
628
649
|
if (process.env.CACHLY_NO_TELEMETRY === '1')
|
|
629
650
|
return;
|
|
651
|
+
const fingerprint = JWT ? _jwtUserFingerprint(JWT) : undefined;
|
|
630
652
|
void fetch(`${API_URL}/api/v1/telemetry/mcp`, {
|
|
631
653
|
method: 'POST',
|
|
632
654
|
headers: { 'Content-Type': 'application/json' },
|
|
633
655
|
body: JSON.stringify({
|
|
634
656
|
event, version: CURRENT_VERSION, editor: detectEditor(),
|
|
635
657
|
...(JWT ? { jwt: JWT } : {}),
|
|
658
|
+
...(fingerprint ? { user_fingerprint: fingerprint } : {}),
|
|
659
|
+
...(metrics ? { metrics } : {}),
|
|
636
660
|
...extra,
|
|
637
661
|
}),
|
|
638
662
|
signal: AbortSignal.timeout(3000),
|
|
@@ -749,7 +773,7 @@ async function handleTool(name, args) {
|
|
|
749
773
|
sendFunnelEvent('learn_from_attempts', telemetryExtra);
|
|
750
774
|
}
|
|
751
775
|
else if (name === 'session_start') {
|
|
752
|
-
sendFunnelEvent('session_start', telemetryExtra);
|
|
776
|
+
sendFunnelEvent('session_start', telemetryExtra, { born_at: undefined, recalls_total: undefined, starter_seeded: false });
|
|
753
777
|
const resultText = typeof brainResult === 'object' && brainResult !== null && 'content' in brainResult
|
|
754
778
|
? JSON.stringify(brainResult)
|
|
755
779
|
: String(brainResult ?? '');
|
|
@@ -779,7 +803,7 @@ async function handleTool(name, args) {
|
|
|
779
803
|
try {
|
|
780
804
|
const seed = await handleShareTool('brain_seed_starter', { instance_id: instanceId }, getConnection, apiFetch);
|
|
781
805
|
if (seed) {
|
|
782
|
-
sendFunnelEvent('brain_seed_starter', { ...telemetryExtra, auto: true });
|
|
806
|
+
sendFunnelEvent('brain_seed_starter', { ...telemetryExtra, auto: true }, { seeded_count: 16, auto: true });
|
|
783
807
|
starterText = '\n---\n' + String(seed);
|
|
784
808
|
}
|
|
785
809
|
}
|
|
@@ -800,14 +824,15 @@ async function handleTool(name, args) {
|
|
|
800
824
|
sendFunnelEvent('session_end', telemetryExtra);
|
|
801
825
|
}
|
|
802
826
|
else if (name === 'smart_recall') {
|
|
803
|
-
sendFunnelEvent('smart_recall', telemetryExtra);
|
|
804
|
-
// smart_recall is the primary recall tool in CLAUDE.md — it retrieves brain lessons
|
|
805
|
-
// just like recall_best_solution. Count it toward BrainRecallCount so the dashboard
|
|
806
|
-
// nudge clears and the first-recall email fires for users following CLAUDE.md.
|
|
807
827
|
const srText = typeof brainResult === 'object' && brainResult !== null && 'content' in brainResult
|
|
808
828
|
? JSON.stringify(brainResult)
|
|
809
829
|
: String(brainResult ?? '');
|
|
810
|
-
|
|
830
|
+
const srHit = srText.length > 50 && !srText.includes('No lessons found') && !srText.includes('no lessons') && !srText.includes('No matches found');
|
|
831
|
+
sendFunnelEvent('smart_recall', telemetryExtra, { hit: srHit, topic: String(args.query ?? '').slice(0, 80) });
|
|
832
|
+
// smart_recall is the primary recall tool in CLAUDE.md — it retrieves brain lessons
|
|
833
|
+
// just like recall_best_solution. Count it toward BrainRecallCount so the dashboard
|
|
834
|
+
// nudge clears and the first-recall email fires for users following CLAUDE.md.
|
|
835
|
+
if (srHit) {
|
|
811
836
|
sendFunnelEvent('recall_best_solution', telemetryExtra);
|
|
812
837
|
}
|
|
813
838
|
// Self-healing: if the credential is degraded, the briefing may be thin or
|
|
@@ -864,7 +889,7 @@ async function handleTool(name, args) {
|
|
|
864
889
|
const telemetryExtra = JWT ? { api_key: JWT, instance_id: instanceId } : { instance_id: instanceId };
|
|
865
890
|
if (name === 'brain_from_git') {
|
|
866
891
|
const gitCounts = _lastBrainFromGitCounts;
|
|
867
|
-
sendFunnelEvent('brain_from_git', { ...telemetryExtra, ...(gitCounts ?? {}) });
|
|
892
|
+
sendFunnelEvent('brain_from_git', { ...telemetryExtra, ...(gitCounts ?? {}) }, gitCounts ? { fixes: gitCounts.fixes, features: gitCounts.features, refactors: gitCounts.refactors, total: gitCounts.total } : undefined);
|
|
868
893
|
}
|
|
869
894
|
else if (name === 'brain_predict_failures') {
|
|
870
895
|
sendFunnelEvent('brain_predict_failures', telemetryExtra);
|