viberag 0.6.2 → 0.7.1
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 +2 -0
- package/dist/cli/app.js +15 -0
- package/dist/cli/commands/useCommands.d.ts +4 -1
- package/dist/cli/commands/useCommands.js +160 -2
- package/dist/client/index.d.ts +9 -1
- package/dist/client/index.js +37 -27
- package/dist/client/types.d.ts +7 -0
- package/dist/daemon/handlers.js +19 -0
- package/dist/daemon/index.js +20 -2
- package/dist/daemon/lib/constants.d.ts +6 -0
- package/dist/daemon/lib/constants.js +8 -0
- package/dist/daemon/lib/telemetry/client.d.ts +31 -0
- package/dist/daemon/lib/telemetry/client.js +206 -0
- package/dist/daemon/lib/telemetry/keys.d.ts +16 -0
- package/dist/daemon/lib/telemetry/keys.js +16 -0
- package/dist/daemon/lib/telemetry/privacy-policy.d.ts +1 -0
- package/dist/daemon/lib/telemetry/privacy-policy.js +66 -0
- package/dist/daemon/lib/telemetry/sanitize.d.ts +19 -0
- package/dist/daemon/lib/telemetry/sanitize.js +193 -0
- package/dist/daemon/lib/telemetry/sentry.d.ts +18 -0
- package/dist/daemon/lib/telemetry/sentry.js +89 -0
- package/dist/daemon/lib/user-settings.d.ts +30 -0
- package/dist/daemon/lib/user-settings.js +139 -0
- package/dist/daemon/lifecycle.d.ts +2 -1
- package/dist/daemon/lifecycle.js +11 -1
- package/dist/daemon/server.d.ts +3 -0
- package/dist/daemon/server.js +84 -0
- package/dist/mcp/index.js +27 -2
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +179 -16
- package/package.json +9 -3
- package/scripts/bake-telemetry-keys-local.js +114 -0
- package/scripts/bake-telemetry-keys.js +54 -0
package/README.md
CHANGED
|
@@ -132,6 +132,8 @@ The following sections describe manual MCP server setup configurations for vario
|
|
|
132
132
|
claude mcp add viberag -- npx viberag-mcp
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
+
> **Tool Search:** Claude Code supports MCP Tool Search (beta) to discover MCP tools on-demand when many tools are installed. It is enabled by default; to force-enable set `ENABLE_TOOL_SEARCH=true` when launching `claude`.
|
|
136
|
+
|
|
135
137
|
**Global Config:** `~/.claude.json`
|
|
136
138
|
|
|
137
139
|
```json
|
package/dist/cli/app.js
CHANGED
|
@@ -28,9 +28,17 @@ import { getViberagDir } from '../daemon/lib/constants.js';
|
|
|
28
28
|
import { loadConfig } from '../daemon/lib/config.js';
|
|
29
29
|
import { checkNpmForUpdate } from '../daemon/lib/update-check.js';
|
|
30
30
|
import { checkV2IndexCompatibility } from '../daemon/services/v2/manifest.js';
|
|
31
|
+
import { createTelemetryClient } from '../daemon/lib/telemetry/client.js';
|
|
32
|
+
import { initSentry } from '../daemon/lib/telemetry/sentry.js';
|
|
31
33
|
const require = createRequire(import.meta.url);
|
|
32
34
|
// Path is relative from dist/ after compilation
|
|
33
35
|
const { version } = require('../../package.json');
|
|
36
|
+
const cliTelemetry = createTelemetryClient({
|
|
37
|
+
service: 'cli',
|
|
38
|
+
projectRoot: process.cwd(),
|
|
39
|
+
version,
|
|
40
|
+
});
|
|
41
|
+
const cliSentry = initSentry({ service: 'cli', version });
|
|
34
42
|
// Available slash commands for autocomplete with descriptions
|
|
35
43
|
const COMMANDS = [
|
|
36
44
|
{ command: '/help', description: 'Show available commands' },
|
|
@@ -46,6 +54,8 @@ const COMMANDS = [
|
|
|
46
54
|
{ command: '/status', description: 'Show daemon and index status' },
|
|
47
55
|
{ command: '/cancel', description: 'Cancel indexing or warmup' },
|
|
48
56
|
{ command: '/mcp-setup', description: 'Configure MCP for AI tools' },
|
|
57
|
+
{ command: '/telemetry', description: 'Configure telemetry mode' },
|
|
58
|
+
{ command: '/privacy-policy', description: 'Show privacy policy' },
|
|
49
59
|
{ command: '/clean', description: 'Remove Viberag from project' },
|
|
50
60
|
{ command: '/quit', description: 'Exit the application' },
|
|
51
61
|
];
|
|
@@ -218,6 +228,11 @@ function AppContent() {
|
|
|
218
228
|
startMcpSetupWizard,
|
|
219
229
|
startCleanWizard,
|
|
220
230
|
isInitialized: isInitialized ?? false,
|
|
231
|
+
telemetry: cliTelemetry,
|
|
232
|
+
shutdownTelemetry: async () => {
|
|
233
|
+
await cliTelemetry.shutdown();
|
|
234
|
+
await cliSentry.shutdown();
|
|
235
|
+
},
|
|
221
236
|
});
|
|
222
237
|
const handleSubmit = (text) => {
|
|
223
238
|
if (!text.trim())
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Consolidates all command routing and handler implementations.
|
|
4
4
|
*/
|
|
5
5
|
import type { SearchResultsData } from '../../common/types.js';
|
|
6
|
+
import type { TelemetryClient } from '../../daemon/lib/telemetry/client.js';
|
|
6
7
|
type CommandContext = {
|
|
7
8
|
addOutput: (type: 'user' | 'system', content: string) => void;
|
|
8
9
|
addSearchResults: (data: SearchResultsData) => void;
|
|
@@ -12,8 +13,10 @@ type CommandContext = {
|
|
|
12
13
|
startMcpSetupWizard: (showPrompt?: boolean) => void;
|
|
13
14
|
startCleanWizard: () => void;
|
|
14
15
|
isInitialized: boolean;
|
|
16
|
+
telemetry: TelemetryClient;
|
|
17
|
+
shutdownTelemetry: () => Promise<void>;
|
|
15
18
|
};
|
|
16
|
-
export declare function useCommands({ addOutput, addSearchResults, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, }: CommandContext): {
|
|
19
|
+
export declare function useCommands({ addOutput, addSearchResults, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, telemetry, shutdownTelemetry, }: CommandContext): {
|
|
17
20
|
isCommand: (text: string) => boolean;
|
|
18
21
|
executeCommand: (text: string) => void;
|
|
19
22
|
};
|
|
@@ -2,13 +2,22 @@
|
|
|
2
2
|
* CLI command handling hook.
|
|
3
3
|
* Consolidates all command routing and handler implementations.
|
|
4
4
|
*/
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import fs from 'node:fs/promises';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
5
10
|
import { useCallback } from 'react';
|
|
6
11
|
import { useApp } from 'ink';
|
|
7
12
|
import { runIndex, formatIndexStats, runSearch, getStatus, loadIndexStats, cancelActivity, runEval, formatEvalReport, } from './handlers.js';
|
|
8
13
|
import { setupVSCodeTerminal } from '../../common/commands/terminalSetup.js';
|
|
9
14
|
import { useAppDispatch } from '../store/hooks.js';
|
|
10
15
|
import { AppActions } from '../store/app/slice.js';
|
|
11
|
-
|
|
16
|
+
import { DaemonClient } from '../../client/index.js';
|
|
17
|
+
import { VIBERAG_PRIVACY_POLICY } from '../../daemon/lib/telemetry/privacy-policy.js';
|
|
18
|
+
import { loadUserSettings, parseTelemetryMode, resolveEffectiveTelemetryMode, setTelemetryMode, } from '../../daemon/lib/user-settings.js';
|
|
19
|
+
import { captureException, flushSentry, } from '../../daemon/lib/telemetry/sentry.js';
|
|
20
|
+
export function useCommands({ addOutput, addSearchResults, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, telemetry, shutdownTelemetry, }) {
|
|
12
21
|
const dispatch = useAppDispatch();
|
|
13
22
|
const { exit } = useApp();
|
|
14
23
|
// Command handlers
|
|
@@ -25,6 +34,8 @@ export function useCommands({ addOutput, addSearchResults, projectRoot, stdout,
|
|
|
25
34
|
/status - Show index status
|
|
26
35
|
/cancel [target] - Cancel indexing or warmup (targets: indexing, warmup)
|
|
27
36
|
/mcp-setup - Configure MCP server for AI coding tools
|
|
37
|
+
/telemetry [mode] - Set telemetry (disabled|stripped|default)
|
|
38
|
+
/privacy-policy - Show privacy policy for telemetry
|
|
28
39
|
/clean - Remove VibeRAG from project (delete project data)
|
|
29
40
|
/quit - Exit
|
|
30
41
|
|
|
@@ -126,6 +137,134 @@ Manual MCP Setup:
|
|
|
126
137
|
const handleClean = useCallback(() => {
|
|
127
138
|
startCleanWizard();
|
|
128
139
|
}, [startCleanWizard]);
|
|
140
|
+
const handleTelemetry = useCallback((arg) => {
|
|
141
|
+
const requested = arg?.trim();
|
|
142
|
+
const showCurrent = async () => {
|
|
143
|
+
const settings = await loadUserSettings();
|
|
144
|
+
const effective = resolveEffectiveTelemetryMode(settings);
|
|
145
|
+
const source = effective.source === 'env'
|
|
146
|
+
? 'VIBERAG_TELEMETRY env var'
|
|
147
|
+
: 'global settings file';
|
|
148
|
+
addOutput('system', `Telemetry mode: ${effective.mode} (from ${source})\n\nModes:\n disabled - no telemetry or error reporting\n stripped - privacy-preserving telemetry (no query text)\n default - includes query text (best-effort redaction)\n\nSet with:\n /telemetry disabled|stripped|default\n\nThis setting is global (applies to CLI, daemon, and MCP).`);
|
|
149
|
+
await telemetry.captureOperation({
|
|
150
|
+
operation_kind: 'cli_command',
|
|
151
|
+
name: '/telemetry',
|
|
152
|
+
projectRoot,
|
|
153
|
+
input: { action: 'show', effective_mode: effective.mode },
|
|
154
|
+
output: null,
|
|
155
|
+
success: true,
|
|
156
|
+
duration_ms: 0,
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
const setMode = async (mode) => {
|
|
160
|
+
const parsed = parseTelemetryMode(mode);
|
|
161
|
+
if (!parsed) {
|
|
162
|
+
addOutput('system', `Invalid telemetry mode: ${mode}\n\nUsage:\n /telemetry disabled|stripped|default`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
await setTelemetryMode(parsed);
|
|
166
|
+
addOutput('system', `Telemetry mode set to: ${parsed}\n\nThis setting is global (applies to CLI, daemon, and MCP).\nIf the daemon is already running, it may take a few seconds to pick up the change.`);
|
|
167
|
+
await telemetry.captureOperation({
|
|
168
|
+
operation_kind: 'cli_command',
|
|
169
|
+
name: '/telemetry',
|
|
170
|
+
projectRoot,
|
|
171
|
+
input: { action: 'set', mode: parsed },
|
|
172
|
+
output: null,
|
|
173
|
+
success: true,
|
|
174
|
+
duration_ms: 0,
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
void (async () => {
|
|
178
|
+
if (!requested) {
|
|
179
|
+
await showCurrent();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
await setMode(requested);
|
|
183
|
+
})().catch(err => {
|
|
184
|
+
addOutput('system', `Telemetry error: ${err.message}`);
|
|
185
|
+
});
|
|
186
|
+
}, [addOutput, projectRoot, telemetry]);
|
|
187
|
+
const handlePrivacyPolicy = useCallback(() => {
|
|
188
|
+
addOutput('system', VIBERAG_PRIVACY_POLICY);
|
|
189
|
+
telemetry.capture({
|
|
190
|
+
event: 'viberag_privacy_policy_viewed',
|
|
191
|
+
properties: { service: 'cli' },
|
|
192
|
+
});
|
|
193
|
+
}, [addOutput, telemetry]);
|
|
194
|
+
const handleTestException = useCallback((arg) => {
|
|
195
|
+
void (async () => {
|
|
196
|
+
const testId = crypto.randomUUID();
|
|
197
|
+
const settings = await loadUserSettings();
|
|
198
|
+
const effective = resolveEffectiveTelemetryMode(settings);
|
|
199
|
+
if (effective.mode === 'disabled') {
|
|
200
|
+
addOutput('system', `Telemetry is disabled, so error reporting is also disabled.\n\nSet with:\n /telemetry default\n\nThen re-run:\n /test-exception`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
addOutput('system', `Triggering test exceptions (test_id=${testId}).\nThis is an undocumented command.`);
|
|
204
|
+
// CLI exception (captured, not fatal)
|
|
205
|
+
const cliError = new Error(`VibeRAG test exception (cli)${arg ? `: ${arg}` : ''}`);
|
|
206
|
+
captureException(cliError, {
|
|
207
|
+
tags: { service: 'cli', test_exception: 'true' },
|
|
208
|
+
extra: { test_id: testId },
|
|
209
|
+
});
|
|
210
|
+
await flushSentry(2000);
|
|
211
|
+
// Daemon exception (captured inside daemon handler)
|
|
212
|
+
if (isInitialized) {
|
|
213
|
+
const client = new DaemonClient(projectRoot);
|
|
214
|
+
try {
|
|
215
|
+
await client.testException(`test_id=${testId}`);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// Expected: daemon throws
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
await client.disconnect();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
addOutput('system', 'Skipping daemon test exception (project not initialized).');
|
|
226
|
+
}
|
|
227
|
+
// MCP exception (one-shot process)
|
|
228
|
+
const modulePath = fileURLToPath(import.meta.url);
|
|
229
|
+
const mcpScriptPath = path.resolve(path.dirname(modulePath), '../../mcp/index.js');
|
|
230
|
+
const env = {
|
|
231
|
+
...process.env,
|
|
232
|
+
VIBERAG_TEST_EXCEPTION: '1',
|
|
233
|
+
VIBERAG_TEST_EXCEPTION_ID: testId,
|
|
234
|
+
};
|
|
235
|
+
const spawnAndWait = (command, args) => new Promise((resolve, reject) => {
|
|
236
|
+
const child = spawn(command, args, {
|
|
237
|
+
cwd: projectRoot,
|
|
238
|
+
env,
|
|
239
|
+
stdio: 'ignore',
|
|
240
|
+
windowsHide: true,
|
|
241
|
+
});
|
|
242
|
+
child.on('error', reject);
|
|
243
|
+
child.on('exit', code => resolve(code));
|
|
244
|
+
});
|
|
245
|
+
try {
|
|
246
|
+
await fs.access(mcpScriptPath);
|
|
247
|
+
const exitCode = await spawnAndWait(process.execPath, [
|
|
248
|
+
mcpScriptPath,
|
|
249
|
+
]);
|
|
250
|
+
addOutput('system', `MCP test exception process exited (code ${exitCode ?? 'unknown'}).`);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
try {
|
|
254
|
+
const exitCode = await spawnAndWait('npx', ['viberag-mcp']);
|
|
255
|
+
addOutput('system', `MCP test exception process exited (code ${exitCode ?? 'unknown'}).`);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
259
|
+
addOutput('system', `Failed to run MCP test exception process: ${message}\n\nTry manually:\n VIBERAG_TEST_EXCEPTION=1 npx viberag-mcp`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
addOutput('system', `Done. Check Sentry for events tagged test_exception=true (test_id=${testId}).`);
|
|
263
|
+
})().catch(err => {
|
|
264
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
265
|
+
addOutput('system', `Test exception command failed: ${message}`);
|
|
266
|
+
});
|
|
267
|
+
}, [addOutput, isInitialized, projectRoot]);
|
|
129
268
|
const handleUnknown = useCallback((command) => {
|
|
130
269
|
addOutput('system', `Unknown command: ${command}. Type /help for available commands.`);
|
|
131
270
|
}, [addOutput]);
|
|
@@ -153,6 +292,16 @@ Manual MCP Setup:
|
|
|
153
292
|
handleCancel(target || undefined);
|
|
154
293
|
return;
|
|
155
294
|
}
|
|
295
|
+
if (command.startsWith('/telemetry')) {
|
|
296
|
+
const arg = trimmed.slice('/telemetry'.length).trim();
|
|
297
|
+
handleTelemetry(arg || undefined);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (command.startsWith('/test-exception')) {
|
|
301
|
+
const arg = trimmed.slice('/test-exception'.length).trim();
|
|
302
|
+
handleTestException(arg || undefined);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
156
305
|
switch (command) {
|
|
157
306
|
case '/help':
|
|
158
307
|
handleHelp();
|
|
@@ -181,6 +330,9 @@ Manual MCP Setup:
|
|
|
181
330
|
case '/mcp-setup':
|
|
182
331
|
handleMcpSetup();
|
|
183
332
|
break;
|
|
333
|
+
case '/privacy-policy':
|
|
334
|
+
handlePrivacyPolicy();
|
|
335
|
+
break;
|
|
184
336
|
case '/clean':
|
|
185
337
|
case '/uninstall':
|
|
186
338
|
handleClean();
|
|
@@ -188,7 +340,9 @@ Manual MCP Setup:
|
|
|
188
340
|
case '/quit':
|
|
189
341
|
case '/exit':
|
|
190
342
|
case '/q':
|
|
191
|
-
|
|
343
|
+
void shutdownTelemetry()
|
|
344
|
+
.catch(() => { })
|
|
345
|
+
.finally(() => exit());
|
|
192
346
|
break;
|
|
193
347
|
default:
|
|
194
348
|
handleUnknown(command);
|
|
@@ -206,6 +360,10 @@ Manual MCP Setup:
|
|
|
206
360
|
handleEval,
|
|
207
361
|
handleCancel,
|
|
208
362
|
handleMcpSetup,
|
|
363
|
+
handleTelemetry,
|
|
364
|
+
handlePrivacyPolicy,
|
|
365
|
+
handleTestException,
|
|
366
|
+
shutdownTelemetry,
|
|
209
367
|
handleClean,
|
|
210
368
|
handleUnknown,
|
|
211
369
|
]);
|
package/dist/client/index.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare class DaemonClient {
|
|
|
19
19
|
private readonly socketPath;
|
|
20
20
|
private readonly autoStart;
|
|
21
21
|
private readonly connectTimeout;
|
|
22
|
+
private readonly clientSource;
|
|
22
23
|
private connection;
|
|
23
24
|
private connectPromise;
|
|
24
25
|
constructor(options: DaemonClientOptions | string);
|
|
@@ -48,6 +49,7 @@ export declare class DaemonClient {
|
|
|
48
49
|
* Ensure connected before making a request.
|
|
49
50
|
*/
|
|
50
51
|
private ensureConnected;
|
|
52
|
+
private request;
|
|
51
53
|
/**
|
|
52
54
|
* Search the codebase.
|
|
53
55
|
*/
|
|
@@ -75,7 +77,7 @@ export declare class DaemonClient {
|
|
|
75
77
|
/**
|
|
76
78
|
* Index the codebase.
|
|
77
79
|
*/
|
|
78
|
-
index(options?: ClientIndexOptions): Promise<IndexStats>;
|
|
80
|
+
index(options?: ClientIndexOptions, timeoutMs?: number): Promise<IndexStats>;
|
|
79
81
|
/**
|
|
80
82
|
* Start indexing asynchronously.
|
|
81
83
|
*/
|
|
@@ -115,6 +117,12 @@ export declare class DaemonClient {
|
|
|
115
117
|
indexStatus: string;
|
|
116
118
|
protocolVersion: number;
|
|
117
119
|
}>;
|
|
120
|
+
/**
|
|
121
|
+
* Trigger a test exception in the daemon (undocumented).
|
|
122
|
+
*
|
|
123
|
+
* Useful for validating Sentry error reporting.
|
|
124
|
+
*/
|
|
125
|
+
testException(message?: string): Promise<void>;
|
|
118
126
|
}
|
|
119
127
|
export { getSocketPath, getLockPath, isDaemonRunning, isDaemonLocked, } from './auto-start.js';
|
|
120
128
|
export type { DaemonClientOptions, ClientSearchOptions, ClientIndexOptions, DaemonStatusResponse, PingResponse, SearchResults, IndexStats, WatcherStatus, SlotState, FailedChunk, } from './types.js';
|
package/dist/client/index.js
CHANGED
|
@@ -44,6 +44,12 @@ export class DaemonClient {
|
|
|
44
44
|
writable: true,
|
|
45
45
|
value: void 0
|
|
46
46
|
});
|
|
47
|
+
Object.defineProperty(this, "clientSource", {
|
|
48
|
+
enumerable: true,
|
|
49
|
+
configurable: true,
|
|
50
|
+
writable: true,
|
|
51
|
+
value: void 0
|
|
52
|
+
});
|
|
47
53
|
Object.defineProperty(this, "connection", {
|
|
48
54
|
enumerable: true,
|
|
49
55
|
configurable: true,
|
|
@@ -61,11 +67,13 @@ export class DaemonClient {
|
|
|
61
67
|
this.projectRoot = options;
|
|
62
68
|
this.autoStart = true;
|
|
63
69
|
this.connectTimeout = 5000;
|
|
70
|
+
this.clientSource = 'cli';
|
|
64
71
|
}
|
|
65
72
|
else {
|
|
66
73
|
this.projectRoot = options.projectRoot;
|
|
67
74
|
this.autoStart = options.autoStart ?? true;
|
|
68
75
|
this.connectTimeout = options.connectTimeout ?? 5000;
|
|
76
|
+
this.clientSource = options.clientSource ?? 'cli';
|
|
69
77
|
}
|
|
70
78
|
this.socketPath = getSocketPath(this.projectRoot);
|
|
71
79
|
}
|
|
@@ -144,12 +152,18 @@ export class DaemonClient {
|
|
|
144
152
|
await this.connect();
|
|
145
153
|
}
|
|
146
154
|
}
|
|
155
|
+
async request(method, params, timeoutMs) {
|
|
156
|
+
await this.ensureConnected();
|
|
157
|
+
const withMeta = params
|
|
158
|
+
? { ...params, __client: { source: this.clientSource } }
|
|
159
|
+
: { __client: { source: this.clientSource } };
|
|
160
|
+
return this.connection.request(method, withMeta, timeoutMs);
|
|
161
|
+
}
|
|
147
162
|
/**
|
|
148
163
|
* Search the codebase.
|
|
149
164
|
*/
|
|
150
165
|
async search(query, options) {
|
|
151
|
-
|
|
152
|
-
return this.connection.request('search', {
|
|
166
|
+
return this.request('search', {
|
|
153
167
|
query,
|
|
154
168
|
...options,
|
|
155
169
|
});
|
|
@@ -158,86 +172,82 @@ export class DaemonClient {
|
|
|
158
172
|
* Fetch a symbol definition row by symbol_id.
|
|
159
173
|
*/
|
|
160
174
|
async getSymbol(symbol_id) {
|
|
161
|
-
|
|
162
|
-
return this.connection.request('getSymbol', { symbol_id });
|
|
175
|
+
return this.request('getSymbol', { symbol_id });
|
|
163
176
|
}
|
|
164
177
|
/**
|
|
165
178
|
* Find usages for a symbol name or symbol_id.
|
|
166
179
|
*/
|
|
167
180
|
async findUsages(options) {
|
|
168
|
-
|
|
169
|
-
return this.connection.request('findUsages', options);
|
|
181
|
+
return this.request('findUsages', options);
|
|
170
182
|
}
|
|
171
183
|
/**
|
|
172
184
|
* Run the v2 eval harness (quality + latency).
|
|
173
185
|
*/
|
|
174
186
|
async eval(options) {
|
|
175
|
-
|
|
176
|
-
return this.connection.request('eval', options);
|
|
187
|
+
return this.request('eval', options);
|
|
177
188
|
}
|
|
178
189
|
/**
|
|
179
190
|
* Expand context for a hit (symbols/chunks/files).
|
|
180
191
|
*/
|
|
181
192
|
async expandContext(args) {
|
|
182
|
-
|
|
183
|
-
return this.connection.request('expandContext', args);
|
|
193
|
+
return this.request('expandContext', args);
|
|
184
194
|
}
|
|
185
195
|
/**
|
|
186
196
|
* Index the codebase.
|
|
187
197
|
*/
|
|
188
|
-
async index(options) {
|
|
189
|
-
|
|
190
|
-
return this.connection.request('index', options);
|
|
198
|
+
async index(options, timeoutMs) {
|
|
199
|
+
return this.request('index', options, timeoutMs);
|
|
191
200
|
}
|
|
192
201
|
/**
|
|
193
202
|
* Start indexing asynchronously.
|
|
194
203
|
*/
|
|
195
204
|
async indexAsync(options) {
|
|
196
|
-
|
|
197
|
-
return this.connection.request('indexAsync', options);
|
|
205
|
+
return this.request('indexAsync', options);
|
|
198
206
|
}
|
|
199
207
|
/**
|
|
200
208
|
* Get daemon status.
|
|
201
209
|
* Clients should poll this endpoint for state updates.
|
|
202
210
|
*/
|
|
203
211
|
async status() {
|
|
204
|
-
|
|
205
|
-
return this.connection.request('status');
|
|
212
|
+
return this.request('status');
|
|
206
213
|
}
|
|
207
214
|
/**
|
|
208
215
|
* Get watcher status.
|
|
209
216
|
*/
|
|
210
217
|
async watchStatus() {
|
|
211
|
-
|
|
212
|
-
return this.connection.request('watchStatus');
|
|
218
|
+
return this.request('watchStatus');
|
|
213
219
|
}
|
|
214
220
|
/**
|
|
215
221
|
* Request daemon shutdown.
|
|
216
222
|
*/
|
|
217
223
|
async shutdown(reason) {
|
|
218
|
-
await this.
|
|
219
|
-
await this.connection.request('shutdown', { reason });
|
|
224
|
+
await this.request('shutdown', { reason });
|
|
220
225
|
}
|
|
221
226
|
/**
|
|
222
227
|
* Cancel the current daemon activity (indexing or warmup).
|
|
223
228
|
*/
|
|
224
229
|
async cancel(options) {
|
|
225
|
-
|
|
226
|
-
return this.connection.request('cancel', options);
|
|
230
|
+
return this.request('cancel', options);
|
|
227
231
|
}
|
|
228
232
|
/**
|
|
229
233
|
* Ping the daemon.
|
|
230
234
|
*/
|
|
231
235
|
async ping() {
|
|
232
|
-
|
|
233
|
-
return this.connection.request('ping');
|
|
236
|
+
return this.request('ping');
|
|
234
237
|
}
|
|
235
238
|
/**
|
|
236
239
|
* Get health information.
|
|
237
240
|
*/
|
|
238
241
|
async health() {
|
|
239
|
-
|
|
240
|
-
|
|
242
|
+
return this.request('health');
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Trigger a test exception in the daemon (undocumented).
|
|
246
|
+
*
|
|
247
|
+
* Useful for validating Sentry error reporting.
|
|
248
|
+
*/
|
|
249
|
+
async testException(message) {
|
|
250
|
+
await this.request('testException', { message });
|
|
241
251
|
}
|
|
242
252
|
}
|
|
243
253
|
// Re-export types and utilities
|
package/dist/client/types.d.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface DaemonClientOptions {
|
|
|
37
37
|
autoStart?: boolean;
|
|
38
38
|
/** Connection timeout in ms (default: 5000) */
|
|
39
39
|
connectTimeout?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Caller identity for telemetry correlation and filtering.
|
|
42
|
+
*
|
|
43
|
+
* Used to reduce duplicate telemetry events (e.g. when MCP tools already
|
|
44
|
+
* capture operations at the tool boundary).
|
|
45
|
+
*/
|
|
46
|
+
clientSource?: 'cli' | 'mcp' | 'unknown';
|
|
40
47
|
}
|
|
41
48
|
/**
|
|
42
49
|
* Search options for client.
|
package/dist/daemon/handlers.js
CHANGED
|
@@ -12,6 +12,7 @@ import { z } from 'zod';
|
|
|
12
12
|
import { PROTOCOL_VERSION } from './protocol.js';
|
|
13
13
|
import { daemonState } from './state.js';
|
|
14
14
|
import { isAbortError } from './lib/abort.js';
|
|
15
|
+
import { captureException, flushSentry } from './lib/telemetry/sentry.js';
|
|
15
16
|
// ============================================================================
|
|
16
17
|
// Parameter Schemas
|
|
17
18
|
// ============================================================================
|
|
@@ -232,6 +233,23 @@ const healthHandler = async (_params, ctx) => {
|
|
|
232
233
|
protocolVersion: PROTOCOL_VERSION,
|
|
233
234
|
};
|
|
234
235
|
};
|
|
236
|
+
/**
|
|
237
|
+
* Test exception handler (undocumented).
|
|
238
|
+
*
|
|
239
|
+
* Used to validate Sentry error reporting across services.
|
|
240
|
+
*/
|
|
241
|
+
const testExceptionHandler = async (params, _ctx) => {
|
|
242
|
+
const messageValue = params?.['message'];
|
|
243
|
+
const message = typeof messageValue === 'string' ? messageValue : undefined;
|
|
244
|
+
const error = new Error(`VibeRAG test exception (daemon)${message ? `: ${message}` : ''}`);
|
|
245
|
+
captureException(error, {
|
|
246
|
+
tags: { service: 'daemon', test_exception: 'true' },
|
|
247
|
+
extra: { message: message ?? null },
|
|
248
|
+
});
|
|
249
|
+
// Best-effort flush so it shows up quickly.
|
|
250
|
+
await flushSentry(2000);
|
|
251
|
+
throw error;
|
|
252
|
+
};
|
|
235
253
|
// ============================================================================
|
|
236
254
|
// Handler Registry
|
|
237
255
|
// ============================================================================
|
|
@@ -253,5 +271,6 @@ export function createHandlers() {
|
|
|
253
271
|
shutdown: shutdownHandler,
|
|
254
272
|
ping: pingHandler,
|
|
255
273
|
health: healthHandler,
|
|
274
|
+
testException: testExceptionHandler,
|
|
256
275
|
};
|
|
257
276
|
}
|
package/dist/daemon/index.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* - Auto-shutdown after 5 minutes of idle (no connected clients)
|
|
23
23
|
*/
|
|
24
24
|
import fs from 'node:fs/promises';
|
|
25
|
+
import { createRequire } from 'node:module';
|
|
25
26
|
import lockfile from 'proper-lockfile';
|
|
26
27
|
import { DaemonOwner } from './owner.js';
|
|
27
28
|
import { DaemonServer } from './server.js';
|
|
@@ -29,7 +30,17 @@ import { LifecycleManager } from './lifecycle.js';
|
|
|
29
30
|
import { createHandlers } from './handlers.js';
|
|
30
31
|
import { configExists, loadConfig } from './lib/config.js';
|
|
31
32
|
import { getCanonicalProjectRoot, getDaemonLockPath, getRunDir, } from './lib/constants.js';
|
|
33
|
+
import { createTelemetryClient } from './lib/telemetry/client.js';
|
|
34
|
+
import { captureException, initSentry } from './lib/telemetry/sentry.js';
|
|
35
|
+
const require = createRequire(import.meta.url);
|
|
36
|
+
const pkg = require('../../package.json');
|
|
32
37
|
const projectRoot = getCanonicalProjectRoot(process.env['VIBERAG_PROJECT_ROOT'] ?? process.cwd());
|
|
38
|
+
const telemetry = createTelemetryClient({
|
|
39
|
+
service: 'daemon',
|
|
40
|
+
projectRoot,
|
|
41
|
+
version: pkg.version,
|
|
42
|
+
});
|
|
43
|
+
const sentry = initSentry({ service: 'daemon', version: pkg.version });
|
|
33
44
|
// Lock file path - inside the global run directory
|
|
34
45
|
const LOCK_FILE_PATH = getDaemonLockPath(projectRoot);
|
|
35
46
|
const RUN_DIR = getRunDir(projectRoot);
|
|
@@ -106,7 +117,11 @@ async function main() {
|
|
|
106
117
|
// Create components
|
|
107
118
|
const owner = new DaemonOwner(projectRoot);
|
|
108
119
|
const server = new DaemonServer(owner);
|
|
109
|
-
|
|
120
|
+
server.setTelemetry(telemetry);
|
|
121
|
+
const lifecycle = new LifecycleManager(server, owner, idleTimeoutMs, async () => {
|
|
122
|
+
await telemetry.shutdown();
|
|
123
|
+
await sentry.shutdown();
|
|
124
|
+
});
|
|
110
125
|
// Register handlers
|
|
111
126
|
server.setHandlers(createHandlers());
|
|
112
127
|
// Start server
|
|
@@ -122,8 +137,11 @@ async function main() {
|
|
|
122
137
|
console.error(`[daemon] PID: ${process.pid}`);
|
|
123
138
|
}
|
|
124
139
|
// Run main with error handling
|
|
125
|
-
main().catch(error => {
|
|
140
|
+
main().catch(async (error) => {
|
|
126
141
|
// Pass Error object directly to preserve stack trace (ADR-011)
|
|
127
142
|
console.error('[daemon] Fatal error:', error);
|
|
143
|
+
captureException(error, { tags: { service: 'daemon', fatal: 'true' } });
|
|
144
|
+
await telemetry.shutdown();
|
|
145
|
+
await sentry.shutdown();
|
|
128
146
|
process.exit(1);
|
|
129
147
|
});
|
|
@@ -86,6 +86,12 @@ export declare function getDaemonPidPath(projectRoot: string): string;
|
|
|
86
86
|
* Get the daemon lock file path.
|
|
87
87
|
*/
|
|
88
88
|
export declare function getDaemonLockPath(projectRoot: string): string;
|
|
89
|
+
/**
|
|
90
|
+
* Get the global user settings file path.
|
|
91
|
+
*
|
|
92
|
+
* Path: {VIBERAG_HOME}/settings.json
|
|
93
|
+
*/
|
|
94
|
+
export declare function getUserSettingsPath(): string;
|
|
89
95
|
/**
|
|
90
96
|
* Get the global secrets directory.
|
|
91
97
|
*/
|
|
@@ -150,6 +150,14 @@ export function getDaemonLockPath(projectRoot) {
|
|
|
150
150
|
// ============================================================================
|
|
151
151
|
// Secrets Paths
|
|
152
152
|
// ============================================================================
|
|
153
|
+
/**
|
|
154
|
+
* Get the global user settings file path.
|
|
155
|
+
*
|
|
156
|
+
* Path: {VIBERAG_HOME}/settings.json
|
|
157
|
+
*/
|
|
158
|
+
export function getUserSettingsPath() {
|
|
159
|
+
return path.join(getViberagHomeDir(), 'settings.json');
|
|
160
|
+
}
|
|
153
161
|
/**
|
|
154
162
|
* Get the global secrets directory.
|
|
155
163
|
*/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostHog telemetry client wrapper for VibeRAG.
|
|
3
|
+
*
|
|
4
|
+
* - Telemetry is enabled by default (opt-out).
|
|
5
|
+
* - Settings are global under VIBERAG_HOME and shared by CLI/daemon/MCP.
|
|
6
|
+
* - Captures inputs/outputs but strips file contents / code text.
|
|
7
|
+
*/
|
|
8
|
+
export type TelemetryServiceName = 'cli' | 'daemon' | 'mcp';
|
|
9
|
+
export type TelemetryClient = {
|
|
10
|
+
captureOperation: (args: {
|
|
11
|
+
operation_kind: 'daemon_method' | 'mcp_tool' | 'cli_command';
|
|
12
|
+
name: string;
|
|
13
|
+
projectRoot?: string;
|
|
14
|
+
input?: unknown;
|
|
15
|
+
output?: unknown;
|
|
16
|
+
success: boolean;
|
|
17
|
+
duration_ms: number;
|
|
18
|
+
error?: unknown;
|
|
19
|
+
request_id?: string;
|
|
20
|
+
}) => Promise<string>;
|
|
21
|
+
capture: (args: {
|
|
22
|
+
event: string;
|
|
23
|
+
properties?: Record<string, unknown>;
|
|
24
|
+
}) => void;
|
|
25
|
+
shutdown: () => Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
export declare function createTelemetryClient(args: {
|
|
28
|
+
service: TelemetryServiceName;
|
|
29
|
+
projectRoot?: string;
|
|
30
|
+
version: string;
|
|
31
|
+
}): TelemetryClient;
|