@code-insights/cli 3.2.0 → 3.3.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/CHANGELOG.md +44 -0
- package/README.md +10 -6
- package/dashboard-dist/assets/index-Rr1JlICa.js +552 -0
- package/dashboard-dist/favicon.svg +17 -3
- package/dashboard-dist/index.html +1 -1
- package/dist/commands/config.js +2 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +10 -2
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +5 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install-hook.d.ts.map +1 -1
- package/dist/commands/install-hook.js +48 -40
- package/dist/commands/install-hook.js.map +1 -1
- package/dist/commands/open.js +2 -2
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/reset.d.ts.map +1 -1
- package/dist/commands/reset.js +5 -2
- package/dist/commands/reset.js.map +1 -1
- package/dist/commands/stats/actions/cost.d.ts.map +1 -1
- package/dist/commands/stats/actions/cost.js +6 -3
- package/dist/commands/stats/actions/cost.js.map +1 -1
- package/dist/commands/stats/actions/models.d.ts.map +1 -1
- package/dist/commands/stats/actions/models.js +6 -3
- package/dist/commands/stats/actions/models.js.map +1 -1
- package/dist/commands/stats/actions/overview.d.ts.map +1 -1
- package/dist/commands/stats/actions/overview.js +6 -3
- package/dist/commands/stats/actions/overview.js.map +1 -1
- package/dist/commands/stats/actions/projects.d.ts.map +1 -1
- package/dist/commands/stats/actions/projects.js +6 -3
- package/dist/commands/stats/actions/projects.js.map +1 -1
- package/dist/commands/stats/actions/today.d.ts.map +1 -1
- package/dist/commands/stats/actions/today.js +6 -3
- package/dist/commands/stats/actions/today.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +66 -58
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +37 -4
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/telemetry.d.ts.map +1 -1
- package/dist/commands/telemetry.js +10 -14
- package/dist/commands/telemetry.js.map +1 -1
- package/dist/utils/banner.d.ts +6 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +30 -0
- package/dist/utils/banner.js.map +1 -0
- package/dist/utils/telemetry.d.ts +52 -31
- package/dist/utils/telemetry.d.ts.map +1 -1
- package/dist/utils/telemetry.js +179 -187
- package/dist/utils/telemetry.js.map +1 -1
- package/dist/utils/welcome.d.ts.map +1 -1
- package/dist/utils/welcome.js +2 -3
- package/dist/utils/welcome.js.map +1 -1
- package/package.json +3 -2
- package/server-dist/index.d.ts.map +1 -1
- package/server-dist/index.js +13 -6
- package/server-dist/index.js.map +1 -1
- package/server-dist/llm/analysis.d.ts +4 -1
- package/server-dist/llm/analysis.d.ts.map +1 -1
- package/server-dist/llm/analysis.js +23 -8
- package/server-dist/llm/analysis.js.map +1 -1
- package/server-dist/llm/prompts.d.ts +14 -2
- package/server-dist/llm/prompts.d.ts.map +1 -1
- package/server-dist/llm/prompts.js +60 -36
- package/server-dist/llm/prompts.js.map +1 -1
- package/server-dist/routes/analysis.d.ts.map +1 -1
- package/server-dist/routes/analysis.js +135 -24
- package/server-dist/routes/analysis.js.map +1 -1
- package/server-dist/routes/export.js +1 -1
- package/server-dist/routes/export.js.map +1 -1
- package/server-dist/routes/telemetry.d.ts +4 -0
- package/server-dist/routes/telemetry.d.ts.map +1 -0
- package/server-dist/routes/telemetry.js +21 -0
- package/server-dist/routes/telemetry.js.map +1 -0
- package/dashboard-dist/assets/index-DzBnL32c.js +0 -549
package/dist/utils/telemetry.js
CHANGED
|
@@ -3,16 +3,18 @@ import * as path from 'path';
|
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
import * as crypto from 'crypto';
|
|
5
5
|
import { createRequire } from 'module';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
// Touch file path that tracks whether the one-time disclosure has been shown
|
|
6
|
+
import { PostHog } from 'posthog-node';
|
|
7
|
+
import { loadConfig, getConfigDir } from './config.js';
|
|
8
|
+
// PostHog write-only API key (public — this is the standard PostHog pattern;
|
|
9
|
+
// write-only keys can only ingest events, not read data).
|
|
10
|
+
const POSTHOG_API_KEY = 'phc_552ZSApq5xuagswylfdw2vx8nckm31jn6LCpTVyVn8j';
|
|
11
|
+
const POSTHOG_HOST = 'https://us.i.posthog.com';
|
|
12
|
+
// Touch file path that tracks whether the disclosure has been shown.
|
|
13
|
+
// Content is the CLI version — if version doesn't match current, notice is re-shown.
|
|
15
14
|
const NOTICE_FILE = path.join(getConfigDir(), '.telemetry-notice-shown');
|
|
15
|
+
// PostHog client — lazily initialized on first trackEvent call.
|
|
16
|
+
// null when telemetry is disabled or init hasn't happened yet.
|
|
17
|
+
let client = null;
|
|
16
18
|
/**
|
|
17
19
|
* Check if telemetry is enabled.
|
|
18
20
|
*
|
|
@@ -28,123 +30,224 @@ export function isTelemetryEnabled() {
|
|
|
28
30
|
if (process.env.DO_NOT_TRACK === '1')
|
|
29
31
|
return false;
|
|
30
32
|
const config = loadConfig();
|
|
31
|
-
// If explicitly set in config, respect it
|
|
32
33
|
if (config !== null && typeof config.telemetry === 'boolean') {
|
|
33
34
|
return config.telemetry;
|
|
34
35
|
}
|
|
35
|
-
// Default: enabled (opt-out model)
|
|
36
36
|
return true;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Get (or lazily create) the PostHog client.
|
|
40
|
+
* Returns null when telemetry is disabled.
|
|
40
41
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
42
|
+
* flushAt: 1 — flush immediately after each capture(); CLI is short-lived
|
|
43
|
+
* flushInterval: 0 — no background timer; avoids keeping process alive
|
|
44
|
+
*/
|
|
45
|
+
function getPostHogClient() {
|
|
46
|
+
if (!isTelemetryEnabled())
|
|
47
|
+
return null;
|
|
48
|
+
if (!client) {
|
|
49
|
+
client = new PostHog(POSTHOG_API_KEY, {
|
|
50
|
+
host: POSTHOG_HOST,
|
|
51
|
+
flushAt: 1,
|
|
52
|
+
flushInterval: 0,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return client;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Flush and shut down the PostHog client.
|
|
59
|
+
* Call this in server SIGINT/SIGTERM handlers before process.exit().
|
|
60
|
+
* No-op if telemetry is disabled or client was never initialized.
|
|
61
|
+
*/
|
|
62
|
+
export async function shutdownTelemetry() {
|
|
63
|
+
if (client) {
|
|
64
|
+
await client.shutdown();
|
|
65
|
+
client = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Show the telemetry disclosure notice if it hasn't been shown for this CLI version.
|
|
43
70
|
*
|
|
44
|
-
*
|
|
71
|
+
* Uses a version-stamped touch file at ~/.code-insights/.telemetry-notice-shown.
|
|
72
|
+
* Re-shown when the CLI version changes (catches existing users on upgrades).
|
|
73
|
+
* Only displays if telemetry is enabled.
|
|
74
|
+
*
|
|
75
|
+
* Returns true if the notice was shown.
|
|
45
76
|
*/
|
|
46
77
|
export function showTelemetryNoticeIfNeeded() {
|
|
47
78
|
if (!isTelemetryEnabled())
|
|
48
79
|
return false;
|
|
49
|
-
|
|
80
|
+
const currentVersion = getCliVersion();
|
|
81
|
+
let shownVersion = null;
|
|
82
|
+
if (fs.existsSync(NOTICE_FILE)) {
|
|
83
|
+
try {
|
|
84
|
+
shownVersion = fs.readFileSync(NOTICE_FILE, 'utf-8').trim();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Can't read — treat as not shown
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (shownVersion === currentVersion)
|
|
50
91
|
return false;
|
|
51
|
-
// Show the disclosure banner
|
|
92
|
+
// Show the updated disclosure banner
|
|
52
93
|
console.log('');
|
|
53
|
-
console.log(' Code Insights collects anonymous usage data to improve the CLI.');
|
|
54
|
-
console.log(' Includes:
|
|
55
|
-
console.log('
|
|
94
|
+
console.log(' Code Insights collects anonymous usage data to improve the CLI and dashboard.');
|
|
95
|
+
console.log(' Includes: commands, page views, OS, CLI version, AI tool types, session counts,');
|
|
96
|
+
console.log(' LLM provider, performance timing.');
|
|
97
|
+
console.log(' Never includes: file paths, project names, session content, API keys, or personal data.');
|
|
56
98
|
console.log('');
|
|
57
99
|
console.log(' Disable: code-insights telemetry disable');
|
|
58
100
|
console.log(' Details: code-insights telemetry status');
|
|
59
101
|
console.log('');
|
|
60
|
-
//
|
|
102
|
+
// Write the current version as content — best-effort, non-fatal
|
|
61
103
|
try {
|
|
62
|
-
// Ensure config dir exists before writing touch file
|
|
63
104
|
const configDir = getConfigDir();
|
|
64
105
|
if (!fs.existsSync(configDir)) {
|
|
65
106
|
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
66
107
|
}
|
|
67
|
-
fs.writeFileSync(NOTICE_FILE,
|
|
108
|
+
fs.writeFileSync(NOTICE_FILE, currentVersion, { encoding: 'utf-8', mode: 0o600 });
|
|
68
109
|
}
|
|
69
110
|
catch {
|
|
70
|
-
// Non-fatal — if we can't write
|
|
71
|
-
// next time. That's acceptable; we don't want to break the CLI over this.
|
|
111
|
+
// Non-fatal — if we can't write, we'll show the notice again next time
|
|
72
112
|
}
|
|
73
113
|
return true;
|
|
74
114
|
}
|
|
75
115
|
/**
|
|
76
|
-
*
|
|
116
|
+
* Classify an error into a structured error_type + error_message pair.
|
|
117
|
+
* Used to enrich trackEvent calls and captureError calls with consistent error metadata.
|
|
118
|
+
*/
|
|
119
|
+
export function classifyError(error) {
|
|
120
|
+
if (error instanceof Error) {
|
|
121
|
+
if (error.name === 'AbortError') {
|
|
122
|
+
return { error_type: 'abort', error_message: error.message };
|
|
123
|
+
}
|
|
124
|
+
// SyntaxError from JSON.parse
|
|
125
|
+
if (error instanceof SyntaxError) {
|
|
126
|
+
return { error_type: 'json_parse_error', error_message: error.message };
|
|
127
|
+
}
|
|
128
|
+
return { error_type: 'api_error', error_message: error.message };
|
|
129
|
+
}
|
|
130
|
+
return { error_type: 'unknown', error_message: String(error) };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Capture an exception in PostHog. Never throws — telemetry must never break the CLI.
|
|
134
|
+
* Respects the same opt-out as trackEvent.
|
|
77
135
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* - 2s AbortController timeout: we don't hang if the endpoint is slow
|
|
81
|
-
* - Swallows ALL errors: network failures, JSON errors, anything — telemetry
|
|
82
|
-
* must never cause a CLI command to fail
|
|
83
|
-
* - No retries: if it fails, it fails. Reliability of individual events
|
|
84
|
-
* matters less than not disrupting the user's workflow.
|
|
136
|
+
* @param error - The caught error (or unknown value)
|
|
137
|
+
* @param properties - Additional context properties (provider, model, etc.)
|
|
85
138
|
*/
|
|
86
|
-
export function
|
|
87
|
-
|
|
139
|
+
export function captureError(error, properties) {
|
|
140
|
+
const ph = getPostHogClient();
|
|
141
|
+
if (!ph)
|
|
88
142
|
return;
|
|
89
|
-
// Build the event synchronously — filesystem reads happen here
|
|
90
|
-
let event;
|
|
91
143
|
try {
|
|
92
|
-
|
|
144
|
+
const { error_type, error_message } = classifyError(error);
|
|
145
|
+
ph.capture({
|
|
146
|
+
distinctId: getStableMachineId(),
|
|
147
|
+
event: '$exception',
|
|
148
|
+
properties: {
|
|
149
|
+
$exception_message: error_message,
|
|
150
|
+
$exception_type: error_type,
|
|
151
|
+
...(properties ?? {}),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
93
154
|
}
|
|
94
155
|
catch {
|
|
95
|
-
//
|
|
156
|
+
// Swallow all errors — telemetry failures are silent
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Send a telemetry event. Never throws — telemetry must never break the CLI.
|
|
161
|
+
*
|
|
162
|
+
* @param event - Event name from TelemetryEventName union
|
|
163
|
+
* @param properties - Arbitrary event properties (success, duration_ms, etc.)
|
|
164
|
+
*/
|
|
165
|
+
export function trackEvent(event, properties) {
|
|
166
|
+
const ph = getPostHogClient();
|
|
167
|
+
if (!ph)
|
|
96
168
|
return;
|
|
169
|
+
try {
|
|
170
|
+
ph.capture({
|
|
171
|
+
distinctId: getStableMachineId(),
|
|
172
|
+
event,
|
|
173
|
+
properties: properties ?? {},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Swallow all errors — telemetry failures are silent
|
|
97
178
|
}
|
|
98
|
-
// Fire-and-forget: intentionally not awaited
|
|
99
|
-
// The void cast suppresses the "floating promise" lint warning
|
|
100
|
-
void sendEvent(event);
|
|
101
179
|
}
|
|
102
180
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
181
|
+
* Set person-level properties via PostHog identify().
|
|
182
|
+
* Call once after the DB is open (so total_sessions can be queried).
|
|
183
|
+
*
|
|
184
|
+
* Commands that never open the DB (init, config, telemetry) can skip this —
|
|
185
|
+
* PostHog retains person properties from previous calls.
|
|
105
186
|
*/
|
|
106
|
-
export function
|
|
107
|
-
|
|
187
|
+
export async function identifyUser() {
|
|
188
|
+
const ph = getPostHogClient();
|
|
189
|
+
if (!ph)
|
|
190
|
+
return;
|
|
191
|
+
try {
|
|
192
|
+
const { getDb } = await import('../db/client.js');
|
|
193
|
+
const db = getDb();
|
|
194
|
+
const row = db.prepare('SELECT COUNT(*) as count FROM sessions').get();
|
|
195
|
+
ph.identify({
|
|
196
|
+
distinctId: getStableMachineId(),
|
|
197
|
+
properties: {
|
|
198
|
+
cli_version: getCliVersion(),
|
|
199
|
+
node_version: process.version.replace('v', ''),
|
|
200
|
+
os: process.platform,
|
|
201
|
+
arch: process.arch,
|
|
202
|
+
installed_providers: detectProviders(),
|
|
203
|
+
has_hook: detectHook(),
|
|
204
|
+
total_sessions: row.count,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// Non-fatal — identify failure doesn't affect event tracking
|
|
210
|
+
}
|
|
108
211
|
}
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
|
-
// Internal helpers
|
|
111
|
-
// ---------------------------------------------------------------------------
|
|
112
212
|
/**
|
|
113
|
-
* Build a
|
|
114
|
-
*
|
|
213
|
+
* Build a preview of what would be collected and sent.
|
|
214
|
+
* Used by `code-insights telemetry status` to show users what is collected.
|
|
115
215
|
*/
|
|
116
|
-
function
|
|
216
|
+
export function buildEventPreview() {
|
|
117
217
|
return {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
success,
|
|
122
|
-
cliVersion: getCliVersion(),
|
|
123
|
-
nodeVersion: process.version.replace('v', ''),
|
|
218
|
+
distinct_id: getStableMachineId(),
|
|
219
|
+
cli_version: getCliVersion(),
|
|
220
|
+
node_version: process.version.replace('v', ''),
|
|
124
221
|
os: process.platform,
|
|
125
222
|
arch: process.arch,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
223
|
+
installed_providers: detectProviders(),
|
|
224
|
+
has_hook: detectHook(),
|
|
225
|
+
total_sessions: '(queried from SQLite when DB is open)',
|
|
226
|
+
sample_event: {
|
|
227
|
+
event: 'cli_sync',
|
|
228
|
+
properties: {
|
|
229
|
+
duration_ms: 1234,
|
|
230
|
+
sessions_synced: 5,
|
|
231
|
+
sessions_by_provider: { 'claude-code': 4, cursor: 1 },
|
|
232
|
+
errors: 0,
|
|
233
|
+
source_filter: null,
|
|
234
|
+
success: true,
|
|
235
|
+
},
|
|
236
|
+
},
|
|
132
237
|
};
|
|
133
238
|
}
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// Internal helpers
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
134
242
|
/**
|
|
135
|
-
*
|
|
243
|
+
* Stable machine ID — does NOT rotate monthly.
|
|
136
244
|
*
|
|
137
|
-
* Format: SHA-256(hostname:username:code-insights
|
|
245
|
+
* Format: SHA-256(hostname:username:code-insights).slice(0, 16)
|
|
138
246
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* - Events within a month can be correlated for "unique users" metrics
|
|
142
|
-
* - No PII: hostname and username are never sent, only their hash
|
|
247
|
+
* No PII: hostname and username are never transmitted, only their hash.
|
|
248
|
+
* Deterministic: same machine always produces the same ID (survives reinstalls).
|
|
143
249
|
*/
|
|
144
|
-
function
|
|
145
|
-
const now = new Date();
|
|
146
|
-
// YYYY-MM format for monthly rotation
|
|
147
|
-
const monthSalt = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
250
|
+
export function getStableMachineId() {
|
|
148
251
|
let username;
|
|
149
252
|
try {
|
|
150
253
|
username = os.userInfo().username;
|
|
@@ -153,18 +256,15 @@ function getMachineId() {
|
|
|
153
256
|
// os.userInfo() throws in Docker/CI when UID has no /etc/passwd entry
|
|
154
257
|
username = `uid-${process.getuid?.() ?? 'unknown'}`;
|
|
155
258
|
}
|
|
156
|
-
const input = [os.hostname(), username,
|
|
259
|
+
const input = [os.hostname(), username, 'code-insights'].join(':');
|
|
157
260
|
return crypto.createHash('sha256').update(input).digest('hex').slice(0, 16);
|
|
158
261
|
}
|
|
159
262
|
/**
|
|
160
263
|
* Read CLI version from package.json.
|
|
161
|
-
* Uses createRequire for JSON imports since this is ESM and JSON imports
|
|
162
|
-
* have inconsistent support across Node versions and bundlers.
|
|
163
264
|
*/
|
|
164
265
|
function getCliVersion() {
|
|
165
266
|
try {
|
|
166
267
|
const require = createRequire(import.meta.url);
|
|
167
|
-
// package.json is two levels up from src/utils/ -> src/ -> cli/
|
|
168
268
|
const pkg = require('../../package.json');
|
|
169
269
|
return pkg.version;
|
|
170
270
|
}
|
|
@@ -174,96 +274,32 @@ function getCliVersion() {
|
|
|
174
274
|
}
|
|
175
275
|
/**
|
|
176
276
|
* Detect which AI coding tool data directories exist on this machine.
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* Returns only the tools that are actually present. This tells us which tools
|
|
180
|
-
* users are running alongside code-insights, helping us prioritize provider support.
|
|
277
|
+
* Checks directory existence only — never reads file contents.
|
|
181
278
|
*/
|
|
182
279
|
function detectProviders() {
|
|
183
280
|
const home = os.homedir();
|
|
184
281
|
const detected = [];
|
|
185
|
-
// Claude Code: ~/.claude/projects/
|
|
186
282
|
if (fs.existsSync(path.join(home, '.claude', 'projects'))) {
|
|
187
283
|
detected.push('claude-code');
|
|
188
284
|
}
|
|
189
|
-
// Cursor: workspace storage directory (cross-platform path)
|
|
190
285
|
const cursorStoragePaths = [
|
|
191
|
-
// macOS
|
|
192
286
|
path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'workspaceStorage'),
|
|
193
|
-
// Linux
|
|
194
287
|
path.join(home, '.config', 'Cursor', 'User', 'workspaceStorage'),
|
|
195
|
-
// Windows
|
|
196
288
|
path.join(home, 'AppData', 'Roaming', 'Cursor', 'User', 'workspaceStorage'),
|
|
197
289
|
];
|
|
198
290
|
if (cursorStoragePaths.some((p) => fs.existsSync(p))) {
|
|
199
291
|
detected.push('cursor');
|
|
200
292
|
}
|
|
201
|
-
// Codex CLI: ~/.codex/sessions
|
|
202
293
|
if (fs.existsSync(path.join(home, '.codex', 'sessions'))) {
|
|
203
294
|
detected.push('codex-cli');
|
|
204
295
|
}
|
|
205
|
-
// GitHub Copilot CLI: ~/.copilot/session-state
|
|
206
296
|
if (fs.existsSync(path.join(home, '.copilot', 'session-state'))) {
|
|
207
297
|
detected.push('copilot-cli');
|
|
208
298
|
}
|
|
209
299
|
return detected;
|
|
210
300
|
}
|
|
211
|
-
/**
|
|
212
|
-
* Count .jsonl files under ~/.claude/projects/ and bucket the count.
|
|
213
|
-
*
|
|
214
|
-
* Buckets are intentionally coarse — we want to understand "heavy vs light"
|
|
215
|
-
* usage without counting exact files, which could vary wildly and feels more
|
|
216
|
-
* private than a range.
|
|
217
|
-
*/
|
|
218
|
-
function getSessionCountBucket() {
|
|
219
|
-
try {
|
|
220
|
-
const claudeDir = getClaudeDir();
|
|
221
|
-
if (!fs.existsSync(claudeDir))
|
|
222
|
-
return '0';
|
|
223
|
-
// Count all .jsonl files recursively under the Claude projects directory
|
|
224
|
-
let count = 0;
|
|
225
|
-
const walk = (dir) => {
|
|
226
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
227
|
-
for (const entry of entries) {
|
|
228
|
-
if (entry.isDirectory()) {
|
|
229
|
-
walk(path.join(dir, entry.name));
|
|
230
|
-
}
|
|
231
|
-
else if (entry.name.endsWith('.jsonl')) {
|
|
232
|
-
count++;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
walk(claudeDir);
|
|
237
|
-
// Coarse buckets for privacy — exact session counts are never sent
|
|
238
|
-
if (count === 0)
|
|
239
|
-
return '0';
|
|
240
|
-
if (count <= 10)
|
|
241
|
-
return '1-10';
|
|
242
|
-
if (count <= 50)
|
|
243
|
-
return '11-50';
|
|
244
|
-
if (count <= 200)
|
|
245
|
-
return '51-200';
|
|
246
|
-
return '200+';
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
249
|
-
return 'unknown';
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Determine the configured data source.
|
|
254
|
-
* Always 'local' in Phase 2+ (SQLite-only).
|
|
255
|
-
*/
|
|
256
|
-
function getDataSource() {
|
|
257
|
-
const config = loadConfig();
|
|
258
|
-
if (!config)
|
|
259
|
-
return 'none';
|
|
260
|
-
return 'local';
|
|
261
|
-
}
|
|
262
301
|
/**
|
|
263
302
|
* Check if code-insights is registered as a Claude Code hook.
|
|
264
|
-
*
|
|
265
|
-
* Reads ~/.claude/settings.json and looks for 'code-insights' anywhere in the
|
|
266
|
-
* file content. A hook registration means the user has automated sync on session end.
|
|
267
303
|
*/
|
|
268
304
|
function detectHook() {
|
|
269
305
|
try {
|
|
@@ -277,48 +313,4 @@ function detectHook() {
|
|
|
277
313
|
return false;
|
|
278
314
|
}
|
|
279
315
|
}
|
|
280
|
-
/**
|
|
281
|
-
* Sign the request body with HMAC-SHA256.
|
|
282
|
-
* Returns the hex digest prefixed with 'sha256=' for the X-Signature header.
|
|
283
|
-
*
|
|
284
|
-
* Note: the key is public (extractable from the npm package) — this is a deterrent
|
|
285
|
-
* against casual data poisoning, not a cryptographic secret. Security is enforced
|
|
286
|
-
* server-side in the Edge Function using constant-time comparison.
|
|
287
|
-
*/
|
|
288
|
-
function signPayload(body) {
|
|
289
|
-
const digest = crypto.createHmac('sha256', HMAC_KEY).update(body).digest('hex');
|
|
290
|
-
return `sha256=${digest}`;
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Internal: Send the event to the telemetry endpoint.
|
|
294
|
-
* AbortController ensures we don't hang longer than 2 seconds.
|
|
295
|
-
* All errors are swallowed — telemetry failures must never propagate.
|
|
296
|
-
*/
|
|
297
|
-
async function sendEvent(event) {
|
|
298
|
-
const controller = new AbortController();
|
|
299
|
-
// 2s timeout — enough for a healthy network, short enough to not delay anything
|
|
300
|
-
const timer = setTimeout(() => controller.abort(), 2000);
|
|
301
|
-
try {
|
|
302
|
-
const body = JSON.stringify(event);
|
|
303
|
-
await fetch(TELEMETRY_ENDPOINT, {
|
|
304
|
-
method: 'POST',
|
|
305
|
-
headers: {
|
|
306
|
-
'Content-Type': 'application/json',
|
|
307
|
-
// HMAC signature — lets the Edge Function verify the payload wasn't tampered.
|
|
308
|
-
// The v2 endpoint accepts but does not require this header initially, allowing
|
|
309
|
-
// older CLI versions (which don't send it) to continue working.
|
|
310
|
-
'X-Signature': signPayload(body),
|
|
311
|
-
},
|
|
312
|
-
body,
|
|
313
|
-
signal: controller.signal,
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
// Swallow everything: network errors, AbortError, JSON serialization errors.
|
|
318
|
-
// Telemetry failures are silent — the CLI command already completed.
|
|
319
|
-
}
|
|
320
|
-
finally {
|
|
321
|
-
clearTimeout(timer);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
316
|
//# sourceMappingURL=telemetry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/utils/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/utils/telemetry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/utils/welcome.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/utils/welcome.ts"],"names":[],"mappings":"AASA;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAiC9D"}
|
package/dist/utils/welcome.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { ensureConfigDir, getConfigDir } from './config.js';
|
|
5
5
|
import { getAllProviders } from '../providers/registry.js';
|
|
6
|
+
import { printBanner } from './banner.js';
|
|
6
7
|
const WELCOME_MARKER = '.welcome-shown';
|
|
7
8
|
/**
|
|
8
9
|
* Show a one-time welcome banner for first-time users.
|
|
@@ -21,9 +22,7 @@ export async function showWelcomeIfFirstRun() {
|
|
|
21
22
|
return false;
|
|
22
23
|
}
|
|
23
24
|
const sessionCount = await countAllSessions();
|
|
24
|
-
|
|
25
|
-
console.log(chalk.bold.cyan(' Welcome to Code Insights!'));
|
|
26
|
-
console.log('');
|
|
25
|
+
printBanner();
|
|
27
26
|
if (sessionCount > 0) {
|
|
28
27
|
console.log(chalk.dim(' Found ') +
|
|
29
28
|
chalk.white.bold(sessionCount) +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"welcome.js","sourceRoot":"","sources":["../../src/utils/welcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"welcome.js","sourceRoot":"","sources":["../../src/utils/welcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAExC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,cAAc,CAAC,CAAC;QAE7D,4CAA4C;QAC5C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,gBAAgB,EAAE,CAAC;QAE9C,WAAW,EAAE,CAAC;QAEd,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;gBACrB,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;gBAC9B,KAAK,CAAC,GAAG,CAAC,WAAW,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,wBAAwB,CAAC,CAC5E,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC,CAAC;QAC1E,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,+CAA+C;QAC/C,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAE/B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,wDAAwD;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,gBAAgB;IAC7B,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IACpC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,MAAM,OAAO,CAAC,UAAU,CACtB,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACxC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,eAAe,EAAE,CAAC;IAClB,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACpD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-insights/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"description": "AI coding session analytics with built-in dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"commander": "^12.1.0",
|
|
73
73
|
"hono": "^4.7.4",
|
|
74
74
|
"inquirer": "^12.6.1",
|
|
75
|
-
"ora": "^8.2.0"
|
|
75
|
+
"ora": "^8.2.0",
|
|
76
|
+
"posthog-node": "^4.18.0"
|
|
76
77
|
},
|
|
77
78
|
"devDependencies": {
|
|
78
79
|
"@types/better-sqlite3": "^7.6.13",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAe5B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAiChC;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDvE"}
|
package/server-dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Hono } from 'hono';
|
|
|
4
4
|
import { existsSync, readFileSync } from 'fs';
|
|
5
5
|
import { relative, join } from 'path';
|
|
6
6
|
import { openUrl } from '@code-insights/cli/utils/browser';
|
|
7
|
+
import { shutdownTelemetry } from '@code-insights/cli/utils/telemetry';
|
|
7
8
|
import projectsRouter from './routes/projects.js';
|
|
8
9
|
import sessionsRouter from './routes/sessions.js';
|
|
9
10
|
import messagesRouter from './routes/messages.js';
|
|
@@ -12,6 +13,7 @@ import analysisRouter from './routes/analysis.js';
|
|
|
12
13
|
import analyticsRouter from './routes/analytics.js';
|
|
13
14
|
import configRouter from './routes/config.js';
|
|
14
15
|
import exportRouter from './routes/export.js';
|
|
16
|
+
import telemetryRouter from './routes/telemetry.js';
|
|
15
17
|
/**
|
|
16
18
|
* Create the Hono app with all API routes mounted.
|
|
17
19
|
* Does NOT add static file serving or call serve() — that's startServer's job.
|
|
@@ -37,6 +39,7 @@ export function createApp() {
|
|
|
37
39
|
app.route('/api/analytics', analyticsRouter);
|
|
38
40
|
app.route('/api/config', configRouter);
|
|
39
41
|
app.route('/api/export', exportRouter);
|
|
42
|
+
app.route('/api/telemetry', telemetryRouter);
|
|
40
43
|
// Health check
|
|
41
44
|
app.get('/api/health', (c) => c.json({ ok: true, version: '0.1.0' }));
|
|
42
45
|
// API 404 catch-all — must come AFTER all /api sub-routers and BEFORE static serving.
|
|
@@ -83,14 +86,18 @@ export async function startServer(options) {
|
|
|
83
86
|
</body></html>
|
|
84
87
|
`));
|
|
85
88
|
}
|
|
86
|
-
// Graceful shutdown:
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
const shutdown = () => {
|
|
89
|
+
// Graceful shutdown: flush PostHog events before exit, then let the process
|
|
90
|
+
// 'exit' handler in cli/src/db/client.ts run WAL checkpoint via closeDb().
|
|
91
|
+
// 3s timeout guards against PostHog SDK stalling on network issues.
|
|
92
|
+
const shutdown = async () => {
|
|
93
|
+
await Promise.race([
|
|
94
|
+
shutdownTelemetry(),
|
|
95
|
+
new Promise((resolve) => setTimeout(resolve, 3000)),
|
|
96
|
+
]);
|
|
90
97
|
process.exit(0);
|
|
91
98
|
};
|
|
92
|
-
process.on('SIGINT', shutdown);
|
|
93
|
-
process.on('SIGTERM', shutdown);
|
|
99
|
+
process.on('SIGINT', () => { void shutdown(); });
|
|
100
|
+
process.on('SIGTERM', () => { void shutdown(); });
|
|
94
101
|
serve({ fetch: app.fetch, port }, (info) => {
|
|
95
102
|
const url = `http://localhost:${info.port}`;
|
|
96
103
|
console.log(` Code Insights dashboard running at ${url}`);
|