@agent-analytics/cli 0.1.10 → 0.1.11
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/bin/cli.mjs +139 -0
- package/lib/api.mjs +21 -0
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
* npx @agent-analytics/cli init <name> — Alias for create
|
|
19
19
|
* npx @agent-analytics/cli delete <id> — Delete a project
|
|
20
20
|
* npx @agent-analytics/cli revoke-key — Revoke and regenerate API key
|
|
21
|
+
* npx @agent-analytics/cli experiments list <project> — List experiments
|
|
22
|
+
* npx @agent-analytics/cli experiments create <p> ... — Create experiment
|
|
23
|
+
* npx @agent-analytics/cli experiments get <id> — Get experiment with results
|
|
24
|
+
* npx @agent-analytics/cli experiments pause <id> — Pause experiment
|
|
25
|
+
* npx @agent-analytics/cli experiments resume <id> — Resume experiment
|
|
26
|
+
* npx @agent-analytics/cli experiments complete <id> — Complete experiment
|
|
27
|
+
* npx @agent-analytics/cli experiments delete <id> — Delete experiment
|
|
21
28
|
* npx @agent-analytics/cli delete-account — Delete your account (opens dashboard)
|
|
22
29
|
* npx @agent-analytics/cli whoami — Show current account
|
|
23
30
|
*/
|
|
@@ -383,6 +390,122 @@ const cmdWhoami = withApi(async (api) => {
|
|
|
383
390
|
log('');
|
|
384
391
|
});
|
|
385
392
|
|
|
393
|
+
// ==================== EXPERIMENTS ====================
|
|
394
|
+
|
|
395
|
+
const cmdExperiments = withApi(async (api, sub, ...rest) => {
|
|
396
|
+
if (!sub) error('Usage: npx @agent-analytics/cli experiments <list|create|get|pause|resume|complete|delete> ...');
|
|
397
|
+
|
|
398
|
+
switch (sub) {
|
|
399
|
+
case 'list': {
|
|
400
|
+
const project = rest[0];
|
|
401
|
+
if (!project) error('Usage: npx @agent-analytics/cli experiments list <project>');
|
|
402
|
+
const data = await api.listExperiments(project);
|
|
403
|
+
heading(`Experiments: ${project}`);
|
|
404
|
+
log('');
|
|
405
|
+
if (ifEmpty(data.experiments, 'experiments')) return;
|
|
406
|
+
for (const e of data.experiments) {
|
|
407
|
+
const status = e.status === 'active' ? `${GREEN}active${RESET}` : e.status === 'paused' ? `${YELLOW}paused${RESET}` : `${DIM}completed${RESET}`;
|
|
408
|
+
log(` ${BOLD}${e.name}${RESET} ${DIM}${e.id}${RESET} ${status} ${DIM}goal: ${e.goal_event}${RESET}`);
|
|
409
|
+
const variants = e.variants.map(v => `${v.key}(${v.weight}%)`).join(', ');
|
|
410
|
+
log(` variants: ${variants}`);
|
|
411
|
+
if (e.winner) log(` ${GREEN}winner: ${e.winner}${RESET}`);
|
|
412
|
+
}
|
|
413
|
+
log('');
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
case 'create': {
|
|
417
|
+
const project = rest[0];
|
|
418
|
+
if (!project) error('Usage: npx @agent-analytics/cli experiments create <project> --name <name> --variants control,new_cta --goal <event> [--weights 60,40]');
|
|
419
|
+
const name = getArg('--name');
|
|
420
|
+
const variantsStr = getArg('--variants');
|
|
421
|
+
const goal = getArg('--goal');
|
|
422
|
+
if (!name || !variantsStr || !goal) error('Required: --name, --variants, --goal');
|
|
423
|
+
const variants = variantsStr.split(',').map(v => v.trim());
|
|
424
|
+
const weightsStr = getArg('--weights');
|
|
425
|
+
const weights = weightsStr ? weightsStr.split(',').map(w => parseInt(w.trim(), 10)) : undefined;
|
|
426
|
+
const data = await api.createExperiment(project, { name, variants, goal_event: goal, weights });
|
|
427
|
+
success(`Experiment created: ${BOLD}${data.name}${RESET} (${data.id})`);
|
|
428
|
+
log(` ${DIM}variants:${RESET} ${data.variants.map(v => `${v.key}(${v.weight}%)`).join(', ')}`);
|
|
429
|
+
log(` ${DIM}goal:${RESET} ${data.goal_event}`);
|
|
430
|
+
log('');
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
case 'get': {
|
|
434
|
+
const id = rest[0];
|
|
435
|
+
if (!id) error('Usage: npx @agent-analytics/cli experiments get <id>');
|
|
436
|
+
const data = await api.getExperiment(id);
|
|
437
|
+
const status = data.status === 'active' ? `${GREEN}active${RESET}` : data.status === 'paused' ? `${YELLOW}paused${RESET}` : `${DIM}completed${RESET}`;
|
|
438
|
+
heading(`Experiment: ${data.name}`);
|
|
439
|
+
log(` ${DIM}id:${RESET} ${data.id} status: ${status} ${DIM}goal: ${data.goal_event}${RESET}`);
|
|
440
|
+
if (data.winner) log(` ${GREEN}winner: ${data.winner}${RESET}`);
|
|
441
|
+
log('');
|
|
442
|
+
if (data.results) {
|
|
443
|
+
heading('Results:');
|
|
444
|
+
for (const v of data.results.variants) {
|
|
445
|
+
const rate = (v.conversion_rate * 100).toFixed(1);
|
|
446
|
+
log(` ${BOLD}${v.key}${RESET} ${v.unique_users} users ${v.conversions} conversions ${CYAN}${rate}%${RESET}`);
|
|
447
|
+
}
|
|
448
|
+
log('');
|
|
449
|
+
if (data.results.probability_best) {
|
|
450
|
+
heading('Probability best:');
|
|
451
|
+
for (const [k, v] of Object.entries(data.results.probability_best)) {
|
|
452
|
+
const pct = (v * 100).toFixed(1);
|
|
453
|
+
log(` ${BOLD}${k}:${RESET} ${pct}%`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (data.results.lift) {
|
|
457
|
+
heading('Lift:');
|
|
458
|
+
for (const [k, v] of Object.entries(data.results.lift)) {
|
|
459
|
+
const pct = (v * 100).toFixed(1);
|
|
460
|
+
const arrow = v > 0 ? `${GREEN}+${pct}%` : v < 0 ? `${RED}${pct}%` : `${DIM}0%`;
|
|
461
|
+
log(` ${BOLD}${k}:${RESET} ${arrow}${RESET}`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
log('');
|
|
465
|
+
log(` ${BOLD}Sufficient data:${RESET} ${data.results.sufficient_data ? `${GREEN}yes` : `${YELLOW}no`}${RESET}`);
|
|
466
|
+
if (data.results.recommendation) {
|
|
467
|
+
log(` ${BOLD}Recommendation:${RESET} ${data.results.recommendation}`);
|
|
468
|
+
}
|
|
469
|
+
} else {
|
|
470
|
+
log(` ${DIM}No results available yet (need exposure + conversion events)${RESET}`);
|
|
471
|
+
}
|
|
472
|
+
log('');
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
case 'pause': {
|
|
476
|
+
const id = rest[0];
|
|
477
|
+
if (!id) error('Usage: npx @agent-analytics/cli experiments pause <id>');
|
|
478
|
+
await api.updateExperiment(id, { status: 'paused' });
|
|
479
|
+
success(`Experiment ${id} paused`);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
case 'resume': {
|
|
483
|
+
const id = rest[0];
|
|
484
|
+
if (!id) error('Usage: npx @agent-analytics/cli experiments resume <id>');
|
|
485
|
+
await api.updateExperiment(id, { status: 'active' });
|
|
486
|
+
success(`Experiment ${id} resumed`);
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
case 'complete': {
|
|
490
|
+
const id = rest[0];
|
|
491
|
+
if (!id) error('Usage: npx @agent-analytics/cli experiments complete <id> [--winner <variant>]');
|
|
492
|
+
const winner = getArg('--winner');
|
|
493
|
+
await api.updateExperiment(id, { status: 'completed', winner });
|
|
494
|
+
success(`Experiment ${id} completed${winner ? ` — winner: ${winner}` : ''}`);
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
case 'delete': {
|
|
498
|
+
const id = rest[0];
|
|
499
|
+
if (!id) error('Usage: npx @agent-analytics/cli experiments delete <id>');
|
|
500
|
+
await api.deleteExperiment(id);
|
|
501
|
+
success(`Experiment ${id} deleted`);
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
default:
|
|
505
|
+
error(`Unknown experiments subcommand: ${sub}. Use: list, create, get, pause, resume, complete, delete`);
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
386
509
|
function showHelp() {
|
|
387
510
|
log(`
|
|
388
511
|
${BOLD}agent-analytics${RESET} — Web analytics your AI agent can read
|
|
@@ -404,6 +527,14 @@ ${BOLD}COMMANDS${RESET}
|
|
|
404
527
|
${CYAN}pages${RESET} <name> Entry/exit page performance
|
|
405
528
|
${CYAN}sessions-dist${RESET} <name> Session duration distribution
|
|
406
529
|
${CYAN}heatmap${RESET} <name> Peak hours & busiest days
|
|
530
|
+
${CYAN}experiments${RESET} <sub> A/B testing (pro only)
|
|
531
|
+
${DIM}list <project>${RESET} List experiments
|
|
532
|
+
${DIM}create <project>${RESET} Create experiment
|
|
533
|
+
${DIM}get <id>${RESET} Get experiment with results
|
|
534
|
+
${DIM}pause <id>${RESET} Pause experiment
|
|
535
|
+
${DIM}resume <id>${RESET} Resume experiment
|
|
536
|
+
${DIM}complete <id>${RESET} Complete experiment
|
|
537
|
+
${DIM}delete <id>${RESET} Delete experiment
|
|
407
538
|
${CYAN}whoami${RESET} Show current account
|
|
408
539
|
${CYAN}revoke-key${RESET} Revoke and regenerate API key
|
|
409
540
|
${CYAN}delete-account${RESET} Delete your account (opens dashboard)
|
|
@@ -418,6 +549,11 @@ ${BOLD}OPTIONS${RESET}
|
|
|
418
549
|
--property <key> Property key for breakdown (required)
|
|
419
550
|
--event <name> Filter by event name (breakdown only)
|
|
420
551
|
--type <T> Page type: entry, exit, both (default: entry)
|
|
552
|
+
--name <name> Experiment name (experiments create)
|
|
553
|
+
--variants <a,b> Comma-separated variant keys (experiments create)
|
|
554
|
+
--goal <event> Goal event name (experiments create)
|
|
555
|
+
--weights <50,50> Comma-separated weights (experiments create, optional)
|
|
556
|
+
--winner <variant> Winning variant (experiments complete, optional)
|
|
421
557
|
|
|
422
558
|
${BOLD}ENVIRONMENT${RESET}
|
|
423
559
|
AGENT_ANALYTICS_API_KEY API key (overrides config file)
|
|
@@ -499,6 +635,9 @@ try {
|
|
|
499
635
|
case 'heatmap':
|
|
500
636
|
await cmdHeatmap(args[1]);
|
|
501
637
|
break;
|
|
638
|
+
case 'experiments':
|
|
639
|
+
await cmdExperiments(args[1], args[2]);
|
|
640
|
+
break;
|
|
502
641
|
case 'delete':
|
|
503
642
|
await cmdDelete(args[1]);
|
|
504
643
|
break;
|
package/lib/api.mjs
CHANGED
|
@@ -110,4 +110,25 @@ export class AgentAnalyticsAPI {
|
|
|
110
110
|
async getHeatmap(project, { since } = {}) {
|
|
111
111
|
return this.request('GET', `/heatmap?${this._qs({ project, since })}`);
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
// Experiments
|
|
115
|
+
async createExperiment(project, { name, variants, goal_event, weights }) {
|
|
116
|
+
return this.request('POST', '/experiments', { project, name, variants, goal_event, weights });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async listExperiments(project) {
|
|
120
|
+
return this.request('GET', `/experiments?${this._qs({ project })}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async getExperiment(id) {
|
|
124
|
+
return this.request('GET', `/experiments/${id}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async updateExperiment(id, { status, winner }) {
|
|
128
|
+
return this.request('PATCH', `/experiments/${id}`, { status, winner });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async deleteExperiment(id) {
|
|
132
|
+
return this.request('DELETE', `/experiments/${id}`);
|
|
133
|
+
}
|
|
113
134
|
}
|