@agent-analytics/cli 0.1.8 → 0.1.10

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 CHANGED
@@ -34,6 +34,11 @@ npx @agent-analytics/cli stats <name> # Stats (last 7 days)
34
34
  npx @agent-analytics/cli stats <name> --days 30 # Stats (last 30 days)
35
35
  npx @agent-analytics/cli events <name> # Recent events
36
36
  npx @agent-analytics/cli properties-received <name> # Property keys per event
37
+ npx @agent-analytics/cli insights <name> # Period-over-period comparison (--period 7d)
38
+ npx @agent-analytics/cli breakdown <name> --property path # Top values for a property
39
+ npx @agent-analytics/cli pages <name> # Landing page performance (--type entry|exit|both)
40
+ npx @agent-analytics/cli sessions-dist <name> # Session duration histogram
41
+ npx @agent-analytics/cli heatmap <name> # Peak hours & busiest days
37
42
 
38
43
  # Security
39
44
  npx @agent-analytics/cli revoke-key # Revoke and regenerate API key
package/bin/cli.mjs CHANGED
@@ -10,6 +10,11 @@
10
10
  * npx @agent-analytics/cli stats <name> — Get stats for a project
11
11
  * npx @agent-analytics/cli events <name> — Get recent events
12
12
  * npx @agent-analytics/cli properties-received <name> — Show property keys per event
13
+ * npx @agent-analytics/cli insights <name> — Period-over-period comparison
14
+ * npx @agent-analytics/cli breakdown <name> — Property value distribution
15
+ * npx @agent-analytics/cli pages <name> — Entry/exit page stats
16
+ * npx @agent-analytics/cli sessions-dist <name> — Session duration distribution
17
+ * npx @agent-analytics/cli heatmap <name> — Peak hours & busiest days
13
18
  * npx @agent-analytics/cli init <name> — Alias for create
14
19
  * npx @agent-analytics/cli delete <id> — Delete a project
15
20
  * npx @agent-analytics/cli revoke-key — Revoke and regenerate API key
@@ -42,6 +47,22 @@ function requireKey() {
42
47
  return new AgentAnalyticsAPI(key, getBaseUrl());
43
48
  }
44
49
 
50
+ function withApi(fn) {
51
+ return async (...args) => {
52
+ const api = requireKey();
53
+ try {
54
+ return await fn(api, ...args);
55
+ } catch (err) {
56
+ error(err.message);
57
+ }
58
+ };
59
+ }
60
+
61
+ function ifEmpty(arr, label) {
62
+ if (!arr || arr.length === 0) { log(` No ${label} found.`); return true; }
63
+ return false;
64
+ }
65
+
45
66
  // ==================== COMMANDS ====================
46
67
 
47
68
  async function cmdLogin(token) {
@@ -77,198 +98,257 @@ async function cmdLogin(token) {
77
98
  }
78
99
  }
79
100
 
80
- async function cmdCreate(name, domain) {
101
+ const cmdCreate = withApi(async (api, name, domain) => {
81
102
  if (!name) error('Usage: npx @agent-analytics/cli create <project-name> --domain https://mysite.com');
82
103
  if (!domain) error('Usage: npx @agent-analytics/cli create <project-name> --domain https://mysite.com\n\nThe domain is required so we can restrict tracking to your site.');
83
104
 
84
- const api = requireKey();
85
-
86
105
  heading(`Creating project: ${name}`);
87
106
 
88
- try {
89
- const data = await api.createProject(name, domain);
107
+ const data = await api.createProject(name, domain);
90
108
 
91
- success(data.existing
92
- ? `Found existing project for ${BOLD}${domain}${RESET}!\n`
93
- : `Project created for ${BOLD}${domain}${RESET}!\n`);
109
+ success(data.existing
110
+ ? `Found existing project for ${BOLD}${domain}${RESET}!\n`
111
+ : `Project created for ${BOLD}${domain}${RESET}!\n`);
94
112
 
95
- heading('1. Add this snippet to your site:');
96
- log(`${CYAN}${data.snippet}${RESET}\n`);
113
+ heading('1. Add this snippet to your site:');
114
+ log(`${CYAN}${data.snippet}${RESET}\n`);
97
115
 
98
- heading('2. Your agent queries stats with:');
99
- log(`${CYAN}${data.api_example}${RESET}\n`);
116
+ heading('2. Your agent queries stats with:');
117
+ log(`${CYAN}${data.api_example}${RESET}\n`);
100
118
 
101
- heading('Project token (for the snippet):');
102
- log(`${YELLOW}${data.project_token}${RESET}\n`);
119
+ heading('Project token (for the snippet):');
120
+ log(`${YELLOW}${data.project_token}${RESET}\n`);
121
+ });
103
122
 
104
- } catch (err) {
105
- error(`Failed to create project: ${err.message}`);
123
+ const cmdProjects = withApi(async (api) => {
124
+ const data = await api.listProjects();
125
+ const projects = data.projects;
126
+
127
+ if (!projects || projects.length === 0) {
128
+ log('No projects yet. Create one:');
129
+ log(` ${CYAN}npx @agent-analytics/cli create my-site${RESET}`);
130
+ return;
106
131
  }
107
- }
108
132
 
109
- async function cmdProjects() {
110
- const api = requireKey();
133
+ heading(`Your Projects (${projects.length})`);
134
+ log('');
111
135
 
112
- try {
113
- const data = await api.listProjects();
114
- const projects = data.projects;
136
+ for (const p of projects) {
137
+ const created = new Date(p.created_at).toLocaleDateString();
138
+ log(` ${BOLD}${p.name}${RESET} ${DIM}created ${created}${RESET}`);
139
+ log(` ${DIM}token:${RESET} ${p.project_token}`);
140
+ log(` ${DIM}origins:${RESET} ${p.allowed_origins || '*'}`);
141
+ log('');
142
+ }
143
+ });
115
144
 
116
- if (!projects || projects.length === 0) {
117
- log('No projects yet. Create one:');
118
- log(` ${CYAN}npx @agent-analytics/cli create my-site${RESET}`);
119
- return;
145
+ const cmdStats = withApi(async (api, project, days = 7) => {
146
+ if (!project) error('Usage: npx @agent-analytics/cli stats <project-name> [--days N]');
147
+
148
+ const result = await api.getStats(project, days, { returnHeaders: true });
149
+ const data = result.data;
150
+ const headers = result.headers;
151
+
152
+ heading(`Stats: ${project} (last ${days} days)`);
153
+ log('');
154
+
155
+ if (data.totals) {
156
+ log(` ${BOLD}Total events:${RESET} ${data.totals.total_events || 0}`);
157
+ log(` ${BOLD}Unique users:${RESET} ${data.totals.unique_users || 0}`);
158
+ }
159
+
160
+ if (data.events && data.events.length > 0) {
161
+ log('');
162
+ heading('Events:');
163
+ for (const e of data.events) {
164
+ log(` ${e.event} ${DIM}→${RESET} ${BOLD}${e.count}${RESET} ${DIM}(${e.unique_users} users)${RESET}`);
120
165
  }
166
+ }
121
167
 
122
- heading(`Your Projects (${projects.length})`);
168
+ if (data.daily && data.daily.length > 0) {
123
169
  log('');
170
+ heading('Daily:');
171
+ for (const d of data.daily) {
172
+ const bar = '█'.repeat(Math.min(Math.ceil(d.total_events / 5), 40));
173
+ log(` ${d.date} ${GREEN}${bar}${RESET} ${d.total_events} events`);
174
+ }
175
+ }
124
176
 
125
- for (const p of projects) {
126
- const created = new Date(p.created_at).toLocaleDateString();
127
- log(` ${BOLD}${p.name}${RESET} ${DIM}created ${created}${RESET}`);
128
- log(` ${DIM}token:${RESET} ${p.project_token}`);
129
- log(` ${DIM}origins:${RESET} ${p.allowed_origins || '*'}`);
130
- log('');
177
+ // Monthly usage summary from response headers
178
+ const monthlyUsage = headers['x-monthly-usage'];
179
+ if (monthlyUsage) {
180
+ const events = parseInt(monthlyUsage, 10);
181
+ const bill = (events / 1000) * 2;
182
+ const monthlyLimit = headers['x-monthly-limit'];
183
+ const pct = headers['x-monthly-usage-percent'];
184
+ log('');
185
+ if (monthlyLimit && pct) {
186
+ const capDollars = (parseInt(monthlyLimit, 10) / 1000) * 2;
187
+ log(` ${DIM}Monthly usage:${RESET} ${events.toLocaleString()} events ($${bill.toFixed(2)}) — ${pct}% of $${capDollars.toFixed(2)} cap`);
188
+ } else {
189
+ log(` ${DIM}Monthly usage:${RESET} ${events.toLocaleString()} events ($${bill.toFixed(2)})`);
131
190
  }
132
- } catch (err) {
133
- error(`Failed to list projects: ${err.message}`);
134
191
  }
135
- }
136
192
 
137
- async function cmdStats(project, days = 7) {
138
- if (!project) error('Usage: npx @agent-analytics/cli stats <project-name> [--days N]');
193
+ log('');
194
+ });
139
195
 
140
- const api = requireKey();
196
+ const cmdEvents = withApi(async (api, project, opts = {}) => {
197
+ if (!project) error('Usage: npx @agent-analytics/cli events <project-name> [--days N] [--limit N]');
141
198
 
142
- try {
143
- const result = await api.getStats(project, days, { returnHeaders: true });
144
- const data = result.data;
145
- const headers = result.headers;
199
+ const data = await api.getEvents(project, opts);
146
200
 
147
- heading(`Stats: ${project} (last ${days} days)`);
148
- log('');
201
+ heading(`Events: ${project}`);
202
+ log('');
149
203
 
150
- if (data.totals) {
151
- log(` ${BOLD}Total events:${RESET} ${data.totals.total_events || 0}`);
152
- log(` ${BOLD}Unique users:${RESET} ${data.totals.unique_users || 0}`);
153
- }
204
+ if (ifEmpty(data.events, 'events')) return;
154
205
 
155
- if (data.events && data.events.length > 0) {
156
- log('');
157
- heading('Events:');
158
- for (const e of data.events) {
159
- log(` ${e.event} ${DIM}→${RESET} ${BOLD}${e.count}${RESET} ${DIM}(${e.unique_users} users)${RESET}`);
160
- }
206
+ for (const e of data.events) {
207
+ const time = new Date(e.timestamp).toLocaleString();
208
+ log(` ${DIM}${time}${RESET} ${BOLD}${e.event}${RESET} ${DIM}${e.user_id || ''}${RESET}`);
209
+ if (e.properties) {
210
+ log(` ${DIM}${JSON.stringify(e.properties)}${RESET}`);
161
211
  }
212
+ }
213
+ log('');
214
+ });
162
215
 
163
- if (data.daily && data.daily.length > 0) {
164
- log('');
165
- heading('Daily:');
166
- for (const d of data.daily) {
167
- const bar = '█'.repeat(Math.min(Math.ceil(d.total_events / 5), 40));
168
- log(` ${d.date} ${GREEN}${bar}${RESET} ${d.total_events} events`);
169
- }
170
- }
216
+ const cmdPropertiesReceived = withApi(async (api, project, opts = {}) => {
217
+ if (!project) error('Usage: npx @agent-analytics/cli properties-received <project-name> [--since DATE] [--sample N]');
218
+
219
+ const data = await api.getPropertiesReceived(project, opts);
220
+
221
+ heading(`Received Properties: ${project}`);
222
+ log('');
223
+
224
+ if (ifEmpty(data.properties, 'properties')) return;
171
225
 
172
- // Monthly usage summary from response headers
173
- const monthlyUsage = headers['x-monthly-usage'];
174
- if (monthlyUsage) {
175
- const events = parseInt(monthlyUsage, 10);
176
- const bill = (events / 1000) * 2;
177
- const monthlyLimit = headers['x-monthly-limit'];
178
- const pct = headers['x-monthly-usage-percent'];
179
- log('');
180
- if (monthlyLimit && pct) {
181
- const capDollars = (parseInt(monthlyLimit, 10) / 1000) * 2;
182
- log(` ${DIM}Monthly usage:${RESET} ${events.toLocaleString()} events ($${bill.toFixed(2)}) — ${pct}% of $${capDollars.toFixed(2)} cap`);
183
- } else {
184
- log(` ${DIM}Monthly usage:${RESET} ${events.toLocaleString()} events ($${bill.toFixed(2)})`);
185
- }
226
+ // Group by event for display
227
+ const byEvent = {};
228
+ for (const p of data.properties) {
229
+ if (!byEvent[p.event]) byEvent[p.event] = [];
230
+ byEvent[p.event].push(p.key);
231
+ }
232
+
233
+ for (const [event, keys] of Object.entries(byEvent)) {
234
+ log(` ${BOLD}${event}${RESET}`);
235
+ for (const key of keys) {
236
+ log(` ${CYAN}${key}${RESET}`);
186
237
  }
238
+ }
187
239
 
188
- log('');
189
- } catch (err) {
190
- error(`Failed to get stats: ${err.message}`);
240
+ log(`\n${DIM}Sampled from last ${data.sample_size} events${RESET}`);
241
+ log('');
242
+ });
243
+
244
+ const cmdInsights = withApi(async (api, project, period = '7d') => {
245
+ if (!project) error('Usage: npx @agent-analytics/cli insights <project-name> [--period 7d]');
246
+
247
+ const data = await api.getInsights(project, { period });
248
+
249
+ heading(`Insights: ${project} (${period} vs previous)`);
250
+ log('');
251
+
252
+ const m = data.metrics;
253
+ for (const [key, metric] of Object.entries(m)) {
254
+ const label = key.replace(/_/g, ' ');
255
+ const arrow = metric.change > 0 ? `${GREEN}↑` : metric.change < 0 ? `${RED}↓` : `${DIM}—`;
256
+ const pct = metric.change_pct !== null ? ` (${metric.change_pct > 0 ? '+' : ''}${metric.change_pct}%)` : '';
257
+ log(` ${BOLD}${label}:${RESET} ${metric.current} ${arrow}${pct}${RESET} ${DIM}was ${metric.previous}${RESET}`);
191
258
  }
192
- }
193
259
 
194
- async function cmdEvents(project, opts = {}) {
195
- if (!project) error('Usage: npx @agent-analytics/cli events <project-name> [--days N] [--limit N]');
260
+ log('');
261
+ log(` ${BOLD}Trend:${RESET} ${data.trend}`);
262
+ log('');
263
+ });
196
264
 
197
- const api = requireKey();
265
+ const cmdBreakdown = withApi(async (api, project, property, opts = {}) => {
266
+ if (!project || !property) error('Usage: npx @agent-analytics/cli breakdown <project-name> --property <key> [--event page_view] [--limit 20]');
198
267
 
199
- try {
200
- const data = await api.getEvents(project, opts);
268
+ const data = await api.getBreakdown(project, { property, ...opts });
201
269
 
202
- heading(`Events: ${project}`);
203
- log('');
270
+ heading(`Breakdown: ${project} — ${property}${data.event ? ` (${data.event})` : ''}`);
271
+ log('');
204
272
 
205
- if (!data.events || data.events.length === 0) {
206
- log(' No events yet.');
207
- return;
208
- }
273
+ if (ifEmpty(data.values, 'data')) return;
209
274
 
210
- for (const e of data.events) {
211
- const time = new Date(e.timestamp).toLocaleString();
212
- log(` ${DIM}${time}${RESET} ${BOLD}${e.event}${RESET} ${DIM}${e.user_id || ''}${RESET}`);
213
- if (e.properties) {
214
- log(` ${DIM}${JSON.stringify(e.properties)}${RESET}`);
215
- }
216
- }
217
- log('');
218
- } catch (err) {
219
- error(`Failed to get events: ${err.message}`);
275
+ for (const v of data.values) {
276
+ log(` ${BOLD}${v.value}${RESET} ${v.count} events ${DIM}(${v.unique_users} users)${RESET}`);
220
277
  }
221
- }
278
+ log(`\n${DIM}${data.total_with_property} of ${data.total_events} events have this property${RESET}`);
279
+ log('');
280
+ });
222
281
 
223
- async function cmdPropertiesReceived(project, opts = {}) {
224
- if (!project) error('Usage: npx @agent-analytics/cli properties-received <project-name> [--since DATE] [--sample N]');
282
+ const cmdPages = withApi(async (api, project, type = 'entry', opts = {}) => {
283
+ if (!project) error('Usage: npx @agent-analytics/cli pages <project-name> [--type entry|exit|both] [--limit 20]');
225
284
 
226
- const api = requireKey();
285
+ const data = await api.getPages(project, { type, ...opts });
227
286
 
228
- try {
229
- const data = await api.getPropertiesReceived(project, opts);
287
+ heading(`Pages: ${project} (${type})`);
288
+ log('');
230
289
 
231
- heading(`Received Properties: ${project}`);
232
- log('');
290
+ const pages = data.entry_pages || data.exit_pages || [];
291
+ if (ifEmpty(pages, 'page data')) return;
233
292
 
234
- if (!data.properties || data.properties.length === 0) {
235
- log(' No properties found.');
236
- return;
237
- }
293
+ for (const p of pages) {
294
+ const bounceStr = `${Math.round(p.bounce_rate * 100)}% bounce`;
295
+ const durStr = `${Math.round(p.avg_duration / 1000)}s avg`;
296
+ log(` ${BOLD}${p.page}${RESET} ${p.sessions} sessions ${DIM}${bounceStr} ${durStr} ${p.avg_events} events/session${RESET}`);
297
+ }
238
298
 
239
- // Group by event for display
240
- const byEvent = {};
241
- for (const p of data.properties) {
242
- if (!byEvent[p.event]) byEvent[p.event] = [];
243
- byEvent[p.event].push(p.key);
299
+ if (data.exit_pages && data.entry_pages) {
300
+ log('');
301
+ heading('Exit pages:');
302
+ for (const p of data.exit_pages) {
303
+ log(` ${BOLD}${p.page}${RESET} ${p.sessions} sessions`);
244
304
  }
305
+ }
306
+ log('');
307
+ });
245
308
 
246
- for (const [event, keys] of Object.entries(byEvent)) {
247
- log(` ${BOLD}${event}${RESET}`);
248
- for (const key of keys) {
249
- log(` ${CYAN}${key}${RESET}`);
250
- }
251
- }
309
+ const cmdSessionsDist = withApi(async (api, project) => {
310
+ if (!project) error('Usage: npx @agent-analytics/cli sessions-dist <project-name>');
252
311
 
253
- log(`\n${DIM}Sampled from last ${data.sample_size} events${RESET}`);
254
- log('');
255
- } catch (err) {
256
- error(`Failed to get properties: ${err.message}`);
312
+ const data = await api.getSessionDistribution(project);
313
+
314
+ heading(`Session Distribution: ${project}`);
315
+ log('');
316
+
317
+ if (ifEmpty(data.distribution, 'session data')) return;
318
+
319
+ for (const b of data.distribution) {
320
+ const bar = '█'.repeat(Math.min(Math.ceil(b.pct / 2), 40));
321
+ log(` ${b.bucket.padEnd(7)} ${GREEN}${bar}${RESET} ${b.sessions} (${b.pct}%)`);
257
322
  }
258
- }
259
323
 
260
- async function cmdDelete(id) {
261
- if (!id) error('Usage: npx @agent-analytics/cli delete <project-id>');
324
+ log('');
325
+ log(` ${BOLD}Median:${RESET} ${data.median_bucket} ${BOLD}Engaged:${RESET} ${data.engaged_pct}% (sessions ≥30s)`);
326
+ log('');
327
+ });
262
328
 
263
- const api = requireKey();
329
+ const cmdHeatmap = withApi(async (api, project) => {
330
+ if (!project) error('Usage: npx @agent-analytics/cli heatmap <project-name>');
264
331
 
265
- try {
266
- await api.deleteProject(id);
267
- success(`Project ${id} deleted`);
268
- } catch (err) {
269
- error(`Failed to delete project: ${err.message}`);
332
+ const data = await api.getHeatmap(project);
333
+
334
+ heading(`Heatmap: ${project}`);
335
+ log('');
336
+
337
+ if (ifEmpty(data.heatmap, 'heatmap data')) return;
338
+
339
+ if (data.peak) {
340
+ log(` ${BOLD}Peak:${RESET} ${data.peak.day_name} at ${data.peak.hour}:00 (${data.peak.events} events, ${data.peak.users} users)`);
270
341
  }
271
- }
342
+ log(` ${BOLD}Busiest day:${RESET} ${data.busiest_day}`);
343
+ log(` ${BOLD}Busiest hour:${RESET} ${data.busiest_hour}:00`);
344
+ log('');
345
+ });
346
+
347
+ const cmdDelete = withApi(async (api, id) => {
348
+ if (!id) error('Usage: npx @agent-analytics/cli delete <project-id>');
349
+ await api.deleteProject(id);
350
+ success(`Project ${id} deleted`);
351
+ });
272
352
 
273
353
  function cmdDeleteAccount() {
274
354
  heading('Delete Account');
@@ -278,42 +358,30 @@ function cmdDeleteAccount() {
278
358
  log('');
279
359
  }
280
360
 
281
- async function cmdRevokeKey() {
282
- const api = requireKey();
283
-
284
- try {
285
- const data = await api.revokeKey();
286
- setApiKey(data.api_key);
287
-
288
- warn('Old API key revoked');
289
- success('New API key generated and saved\n');
290
- heading('New API key:');
291
- log(`${YELLOW}${data.api_key}${RESET}`);
292
- log(`${DIM}Saved to ~/.config/agent-analytics/config.json${RESET}\n`);
293
- warn('Update your agent with this new key!');
294
- } catch (err) {
295
- error(`Failed to revoke key: ${err.message}`);
296
- }
297
- }
298
-
299
- async function cmdWhoami() {
300
- const api = requireKey();
301
-
302
- try {
303
- const data = await api.getAccount();
304
- heading('Account');
305
- log(` ${BOLD}Email:${RESET} ${data.email}`);
306
- log(` ${BOLD}GitHub:${RESET} ${data.github_login || 'N/A'}`);
307
- log(` ${BOLD}Tier:${RESET} ${data.tier}`);
308
- log(` ${BOLD}Projects:${RESET} ${data.projects_count}/${data.projects_limit}`);
309
- if (data.tier === 'pro' && data.monthly_spend_cap_dollars != null) {
310
- log(` ${BOLD}Spend cap:${RESET} $${data.monthly_spend_cap_dollars.toFixed(2)}/month`);
311
- }
312
- log('');
313
- } catch (err) {
314
- error(`Failed to get account: ${err.message}`);
361
+ const cmdRevokeKey = withApi(async (api) => {
362
+ const data = await api.revokeKey();
363
+ setApiKey(data.api_key);
364
+
365
+ warn('Old API key revoked');
366
+ success('New API key generated and saved\n');
367
+ heading('New API key:');
368
+ log(`${YELLOW}${data.api_key}${RESET}`);
369
+ log(`${DIM}Saved to ~/.config/agent-analytics/config.json${RESET}\n`);
370
+ warn('Update your agent with this new key!');
371
+ });
372
+
373
+ const cmdWhoami = withApi(async (api) => {
374
+ const data = await api.getAccount();
375
+ heading('Account');
376
+ log(` ${BOLD}Email:${RESET} ${data.email}`);
377
+ log(` ${BOLD}GitHub:${RESET} ${data.github_login || 'N/A'}`);
378
+ log(` ${BOLD}Tier:${RESET} ${data.tier}`);
379
+ log(` ${BOLD}Projects:${RESET} ${data.projects_count}/${data.projects_limit}`);
380
+ if (data.tier === 'pro' && data.monthly_spend_cap_dollars != null) {
381
+ log(` ${BOLD}Spend cap:${RESET} $${data.monthly_spend_cap_dollars.toFixed(2)}/month`);
315
382
  }
316
- }
383
+ log('');
384
+ });
317
385
 
318
386
  function showHelp() {
319
387
  log(`
@@ -331,16 +399,25 @@ ${BOLD}COMMANDS${RESET}
331
399
  ${CYAN}stats${RESET} <name> Get stats for a project
332
400
  ${CYAN}events${RESET} <name> Get recent events
333
401
  ${CYAN}properties-received${RESET} <name> Show property keys per event
402
+ ${CYAN}insights${RESET} <name> Period-over-period comparison
403
+ ${CYAN}breakdown${RESET} <name> Property value distribution
404
+ ${CYAN}pages${RESET} <name> Entry/exit page performance
405
+ ${CYAN}sessions-dist${RESET} <name> Session duration distribution
406
+ ${CYAN}heatmap${RESET} <name> Peak hours & busiest days
334
407
  ${CYAN}whoami${RESET} Show current account
335
408
  ${CYAN}revoke-key${RESET} Revoke and regenerate API key
336
409
  ${CYAN}delete-account${RESET} Delete your account (opens dashboard)
337
410
 
338
411
  ${BOLD}OPTIONS${RESET}
339
412
  --days <N> Days of data (default: 7)
340
- --limit <N> Max events to return (default: 100)
413
+ --limit <N> Max events/rows to return (default: 100)
341
414
  --domain <url> Your site domain (required for create)
342
415
  --since <date> ISO date for properties-received (default: 7 days)
343
416
  --sample <N> Max events to sample (default: 5000)
417
+ --period <P> Period for insights: 1d, 7d, 14d, 30d, 90d (default: 7d)
418
+ --property <key> Property key for breakdown (required)
419
+ --event <name> Filter by event name (breakdown only)
420
+ --type <T> Page type: entry, exit, both (default: entry)
344
421
 
345
422
  ${BOLD}ENVIRONMENT${RESET}
346
423
  AGENT_ANALYTICS_API_KEY API key (overrides config file)
@@ -388,20 +465,40 @@ try {
388
465
  await cmdProjects();
389
466
  break;
390
467
  case 'stats':
391
- await cmdStats(args[1], parseInt(getArg('--days') || '7'));
468
+ await cmdStats(args[1], parseInt(getArg('--days') || '7', 10));
392
469
  break;
393
470
  case 'events':
394
471
  await cmdEvents(args[1], {
395
- days: parseInt(getArg('--days') || '7'),
396
- limit: parseInt(getArg('--limit') || '100'),
472
+ days: parseInt(getArg('--days') || '7', 10),
473
+ limit: parseInt(getArg('--limit') || '100', 10),
397
474
  });
398
475
  break;
399
476
  case 'properties-received':
400
477
  await cmdPropertiesReceived(args[1], {
401
478
  since: getArg('--since'),
402
- sample: getArg('--sample') ? parseInt(getArg('--sample')) : undefined,
479
+ sample: getArg('--sample') ? parseInt(getArg('--sample'), 10) : undefined,
403
480
  });
404
481
  break;
482
+ case 'insights':
483
+ await cmdInsights(args[1], getArg('--period') || '7d');
484
+ break;
485
+ case 'breakdown':
486
+ await cmdBreakdown(args[1], getArg('--property'), {
487
+ event: getArg('--event'),
488
+ limit: getArg('--limit') ? parseInt(getArg('--limit'), 10) : undefined,
489
+ });
490
+ break;
491
+ case 'pages':
492
+ await cmdPages(args[1], getArg('--type') || 'entry', {
493
+ limit: getArg('--limit') ? parseInt(getArg('--limit'), 10) : undefined,
494
+ });
495
+ break;
496
+ case 'sessions-dist':
497
+ await cmdSessionsDist(args[1]);
498
+ break;
499
+ case 'heatmap':
500
+ await cmdHeatmap(args[1]);
501
+ break;
405
502
  case 'delete':
406
503
  await cmdDelete(args[1]);
407
504
  break;
package/lib/api.mjs CHANGED
@@ -11,6 +11,13 @@ export class AgentAnalyticsAPI {
11
11
  this.baseUrl = baseUrl;
12
12
  }
13
13
 
14
+ _qs(params) {
15
+ return Object.entries(params)
16
+ .filter(([, v]) => v != null)
17
+ .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
18
+ .join('&');
19
+ }
20
+
14
21
  async request(method, path, body, { returnHeaders = false } = {}) {
15
22
  const opts = {
16
23
  method,
@@ -68,23 +75,39 @@ export class AgentAnalyticsAPI {
68
75
 
69
76
  // Stats
70
77
  async getStats(project, days = 7, { returnHeaders = false } = {}) {
71
- return this.request('GET', `/stats?project=${encodeURIComponent(project)}&days=${days}`, undefined, { returnHeaders });
78
+ return this.request('GET', `/stats?${this._qs({ project, days })}`, undefined, { returnHeaders });
72
79
  }
73
80
 
74
81
  async getEvents(project, { event, days = 7, limit = 100 } = {}) {
75
- let qs = `project=${encodeURIComponent(project)}&days=${days}&limit=${limit}`;
76
- if (event) qs += `&event=${encodeURIComponent(event)}`;
77
- return this.request('GET', `/events?${qs}`);
82
+ return this.request('GET', `/events?${this._qs({ project, days, limit, event })}`);
78
83
  }
79
84
 
80
85
  async getProperties(project, days = 30) {
81
- return this.request('GET', `/properties?project=${encodeURIComponent(project)}&days=${days}`);
86
+ return this.request('GET', `/properties?${this._qs({ project, days })}`);
82
87
  }
83
88
 
84
89
  async getPropertiesReceived(project, { since, sample } = {}) {
85
- let qs = `project=${encodeURIComponent(project)}`;
86
- if (since) qs += `&since=${encodeURIComponent(since)}`;
87
- if (sample) qs += `&sample=${sample}`;
88
- return this.request('GET', `/properties/received?${qs}`);
90
+ return this.request('GET', `/properties/received?${this._qs({ project, since, sample })}`);
91
+ }
92
+
93
+ // Analytics
94
+ async getBreakdown(project, { property, event, since, limit = 20 } = {}) {
95
+ return this.request('GET', `/breakdown?${this._qs({ project, property, event, since, limit })}`);
96
+ }
97
+
98
+ async getInsights(project, { period = '7d' } = {}) {
99
+ return this.request('GET', `/insights?${this._qs({ project, period })}`);
100
+ }
101
+
102
+ async getPages(project, { type = 'entry', since, limit = 20 } = {}) {
103
+ return this.request('GET', `/pages?${this._qs({ project, type, since, limit })}`);
104
+ }
105
+
106
+ async getSessionDistribution(project, { since } = {}) {
107
+ return this.request('GET', `/sessions/distribution?${this._qs({ project, since })}`);
108
+ }
109
+
110
+ async getHeatmap(project, { since } = {}) {
111
+ return this.request('GET', `/heatmap?${this._qs({ project, since })}`);
89
112
  }
90
113
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-analytics/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Web analytics your AI agent can read. CLI for managing projects and querying stats.",
5
5
  "bin": {
6
6
  "agent-analytics": "./bin/cli.mjs"