@agent-analytics/cli 0.1.7
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/LICENSE +21 -0
- package/README.md +80 -0
- package/bin/cli.mjs +428 -0
- package/lib/api.mjs +90 -0
- package/lib/config.mjs +40 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Agent Analytics
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# agent-analytics
|
|
2
|
+
|
|
3
|
+
Web analytics your AI agent can read. Drop a JS snippet on your site, query the data via API.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. Get your API key from https://api.agentanalytics.sh (sign in with GitHub)
|
|
9
|
+
|
|
10
|
+
# 2. Save your key
|
|
11
|
+
npx agent-analytics login --token aak_your_key
|
|
12
|
+
|
|
13
|
+
# 3. Create a project
|
|
14
|
+
npx agent-analytics create my-site --domain https://mysite.com
|
|
15
|
+
|
|
16
|
+
# 4. Check your stats
|
|
17
|
+
npx agent-analytics stats my-site
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Auth
|
|
24
|
+
npx agent-analytics login --token <key> # Save your API key
|
|
25
|
+
npx agent-analytics whoami # Show current account
|
|
26
|
+
|
|
27
|
+
# Projects
|
|
28
|
+
npx agent-analytics create <name> --domain <url> # Create a project
|
|
29
|
+
npx agent-analytics projects # List your projects
|
|
30
|
+
npx agent-analytics delete <id> # Delete a project
|
|
31
|
+
|
|
32
|
+
# Analytics
|
|
33
|
+
npx agent-analytics stats <name> # Stats (last 7 days)
|
|
34
|
+
npx agent-analytics stats <name> --days 30 # Stats (last 30 days)
|
|
35
|
+
npx agent-analytics events <name> # Recent events
|
|
36
|
+
npx agent-analytics properties-received <name> # Property keys per event
|
|
37
|
+
|
|
38
|
+
# Security
|
|
39
|
+
npx agent-analytics revoke-key # Revoke and regenerate API key
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## For AI Agents
|
|
43
|
+
|
|
44
|
+
Set the env var and call the API directly — no CLI needed:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
export AGENT_ANALYTICS_API_KEY=aak_your_key
|
|
48
|
+
|
|
49
|
+
# Query stats
|
|
50
|
+
curl "https://api.agentanalytics.sh/stats?project=my-site&days=7" \
|
|
51
|
+
-H "X-API-Key: $AGENT_ANALYTICS_API_KEY"
|
|
52
|
+
|
|
53
|
+
# Create a project
|
|
54
|
+
curl -X POST "https://api.agentanalytics.sh/projects" \
|
|
55
|
+
-H "X-API-Key: $AGENT_ANALYTICS_API_KEY" \
|
|
56
|
+
-H "Content-Type: application/json" \
|
|
57
|
+
-d '{"name": "new-site", "allowed_origins": "https://mysite.com"}'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Environment Variables
|
|
61
|
+
|
|
62
|
+
| Variable | Description |
|
|
63
|
+
|----------|-------------|
|
|
64
|
+
| `AGENT_ANALYTICS_API_KEY` | API key (overrides config file) |
|
|
65
|
+
| `AGENT_ANALYTICS_URL` | Custom API URL (for self-hosted) |
|
|
66
|
+
|
|
67
|
+
## Config
|
|
68
|
+
|
|
69
|
+
Stored at `~/.config/agent-analytics/config.json` (file permissions: 600).
|
|
70
|
+
|
|
71
|
+
## Links
|
|
72
|
+
|
|
73
|
+
- **Dashboard:** https://api.agentanalytics.sh
|
|
74
|
+
- **Website:** https://agentanalytics.sh
|
|
75
|
+
- **GitHub:** https://github.com/Agent-Analytics
|
|
76
|
+
- **Self-host:** https://github.com/Agent-Analytics/agent-analytics
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* agent-analytics CLI
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx agent-analytics login --token <key> — Save your API key
|
|
8
|
+
* npx agent-analytics create <name> — Create a project and get your snippet
|
|
9
|
+
* npx agent-analytics projects — List your projects
|
|
10
|
+
* npx agent-analytics stats <name> — Get stats for a project
|
|
11
|
+
* npx agent-analytics events <name> — Get recent events
|
|
12
|
+
* npx agent-analytics properties-received <name> — Show property keys per event
|
|
13
|
+
* npx agent-analytics init <name> — Alias for create
|
|
14
|
+
* npx agent-analytics delete <id> — Delete a project
|
|
15
|
+
* npx agent-analytics revoke-key — Revoke and regenerate API key
|
|
16
|
+
* npx agent-analytics delete-account — Delete your account (opens dashboard)
|
|
17
|
+
* npx agent-analytics whoami — Show current account
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { AgentAnalyticsAPI } from '../lib/api.mjs';
|
|
21
|
+
import { getApiKey, setApiKey, getBaseUrl, getConfig, saveConfig } from '../lib/config.mjs';
|
|
22
|
+
|
|
23
|
+
const BOLD = '\x1b[1m';
|
|
24
|
+
const DIM = '\x1b[2m';
|
|
25
|
+
const GREEN = '\x1b[32m';
|
|
26
|
+
const YELLOW = '\x1b[33m';
|
|
27
|
+
const CYAN = '\x1b[36m';
|
|
28
|
+
const RED = '\x1b[31m';
|
|
29
|
+
const RESET = '\x1b[0m';
|
|
30
|
+
|
|
31
|
+
function log(msg = '') { console.log(msg); }
|
|
32
|
+
function success(msg) { log(`${GREEN}✓${RESET} ${msg}`); }
|
|
33
|
+
function warn(msg) { log(`${YELLOW}⚠${RESET} ${msg}`); }
|
|
34
|
+
function error(msg) { log(`${RED}✗${RESET} ${msg}`); process.exit(1); }
|
|
35
|
+
function heading(msg) { log(`\n${BOLD}${msg}${RESET}`); }
|
|
36
|
+
|
|
37
|
+
function requireKey() {
|
|
38
|
+
const key = getApiKey();
|
|
39
|
+
if (!key) {
|
|
40
|
+
error('Not logged in. Run: npx agent-analytics login');
|
|
41
|
+
}
|
|
42
|
+
return new AgentAnalyticsAPI(key, getBaseUrl());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ==================== COMMANDS ====================
|
|
46
|
+
|
|
47
|
+
async function cmdLogin(token) {
|
|
48
|
+
if (!token) {
|
|
49
|
+
heading('Agent Analytics — Login');
|
|
50
|
+
log('');
|
|
51
|
+
log('Pass your API key from the dashboard:');
|
|
52
|
+
log(` ${CYAN}npx agent-analytics login --token aak_your_key_here${RESET}`);
|
|
53
|
+
log('');
|
|
54
|
+
log('Or set it as an environment variable:');
|
|
55
|
+
log(` ${CYAN}export AGENT_ANALYTICS_API_KEY=aak_your_key_here${RESET}`);
|
|
56
|
+
log('');
|
|
57
|
+
log(`Get your API key at: ${CYAN}https://app.agentanalytics.sh${RESET}`);
|
|
58
|
+
log(`${DIM}Sign in with GitHub → your API key is shown once on first signup.${RESET}`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Validate the token works
|
|
63
|
+
const api = new AgentAnalyticsAPI(token, getBaseUrl());
|
|
64
|
+
try {
|
|
65
|
+
const account = await api.getAccount();
|
|
66
|
+
setApiKey(token);
|
|
67
|
+
const config = getConfig();
|
|
68
|
+
config.email = account.email;
|
|
69
|
+
config.github_login = account.github_login;
|
|
70
|
+
saveConfig(config);
|
|
71
|
+
|
|
72
|
+
success(`Logged in as ${BOLD}${account.github_login || account.email}${RESET}`);
|
|
73
|
+
log(`${DIM}API key saved to ~/.config/agent-analytics/config.json${RESET}`);
|
|
74
|
+
log(`\nNext: ${CYAN}npx agent-analytics create my-site${RESET}`);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
error(`Invalid API key: ${err.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function cmdCreate(name, domain) {
|
|
81
|
+
if (!name) error('Usage: npx agent-analytics create <project-name> --domain https://mysite.com');
|
|
82
|
+
if (!domain) error('Usage: npx agent-analytics create <project-name> --domain https://mysite.com\n\nThe domain is required so we can restrict tracking to your site.');
|
|
83
|
+
|
|
84
|
+
const api = requireKey();
|
|
85
|
+
|
|
86
|
+
heading(`Creating project: ${name}`);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const data = await api.createProject(name, domain);
|
|
90
|
+
|
|
91
|
+
success(data.existing
|
|
92
|
+
? `Found existing project for ${BOLD}${domain}${RESET}!\n`
|
|
93
|
+
: `Project created for ${BOLD}${domain}${RESET}!\n`);
|
|
94
|
+
|
|
95
|
+
heading('1. Add this snippet to your site:');
|
|
96
|
+
log(`${CYAN}${data.snippet}${RESET}\n`);
|
|
97
|
+
|
|
98
|
+
heading('2. Your agent queries stats with:');
|
|
99
|
+
log(`${CYAN}${data.api_example}${RESET}\n`);
|
|
100
|
+
|
|
101
|
+
heading('Project token (for the snippet):');
|
|
102
|
+
log(`${YELLOW}${data.project_token}${RESET}\n`);
|
|
103
|
+
|
|
104
|
+
} catch (err) {
|
|
105
|
+
error(`Failed to create project: ${err.message}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function cmdProjects() {
|
|
110
|
+
const api = requireKey();
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const data = await api.listProjects();
|
|
114
|
+
const projects = data.projects;
|
|
115
|
+
|
|
116
|
+
if (!projects || projects.length === 0) {
|
|
117
|
+
log('No projects yet. Create one:');
|
|
118
|
+
log(` ${CYAN}npx agent-analytics create my-site${RESET}`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
heading(`Your Projects (${projects.length})`);
|
|
123
|
+
log('');
|
|
124
|
+
|
|
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('');
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
error(`Failed to list projects: ${err.message}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function cmdStats(project, days = 7) {
|
|
138
|
+
if (!project) error('Usage: npx agent-analytics stats <project-name> [--days N]');
|
|
139
|
+
|
|
140
|
+
const api = requireKey();
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const result = await api.getStats(project, days, { returnHeaders: true });
|
|
144
|
+
const data = result.data;
|
|
145
|
+
const headers = result.headers;
|
|
146
|
+
|
|
147
|
+
heading(`Stats: ${project} (last ${days} days)`);
|
|
148
|
+
log('');
|
|
149
|
+
|
|
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
|
+
}
|
|
154
|
+
|
|
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
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
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
|
+
}
|
|
171
|
+
|
|
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
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
log('');
|
|
189
|
+
} catch (err) {
|
|
190
|
+
error(`Failed to get stats: ${err.message}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function cmdEvents(project, opts = {}) {
|
|
195
|
+
if (!project) error('Usage: npx agent-analytics events <project-name> [--days N] [--limit N]');
|
|
196
|
+
|
|
197
|
+
const api = requireKey();
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const data = await api.getEvents(project, opts);
|
|
201
|
+
|
|
202
|
+
heading(`Events: ${project}`);
|
|
203
|
+
log('');
|
|
204
|
+
|
|
205
|
+
if (!data.events || data.events.length === 0) {
|
|
206
|
+
log(' No events yet.');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
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}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function cmdPropertiesReceived(project, opts = {}) {
|
|
224
|
+
if (!project) error('Usage: npx agent-analytics properties-received <project-name> [--since DATE] [--sample N]');
|
|
225
|
+
|
|
226
|
+
const api = requireKey();
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const data = await api.getPropertiesReceived(project, opts);
|
|
230
|
+
|
|
231
|
+
heading(`Received Properties: ${project}`);
|
|
232
|
+
log('');
|
|
233
|
+
|
|
234
|
+
if (!data.properties || data.properties.length === 0) {
|
|
235
|
+
log(' No properties found.');
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
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);
|
|
244
|
+
}
|
|
245
|
+
|
|
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
|
+
}
|
|
252
|
+
|
|
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}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function cmdDelete(id) {
|
|
261
|
+
if (!id) error('Usage: npx agent-analytics delete <project-id>');
|
|
262
|
+
|
|
263
|
+
const api = requireKey();
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
await api.deleteProject(id);
|
|
267
|
+
success(`Project ${id} deleted`);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
error(`Failed to delete project: ${err.message}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function cmdDeleteAccount() {
|
|
274
|
+
heading('Delete Account');
|
|
275
|
+
log('');
|
|
276
|
+
log('For security, account deletion must be done from the dashboard.');
|
|
277
|
+
log(`Visit: ${CYAN}https://app.agentanalytics.sh/settings${RESET}`);
|
|
278
|
+
log('');
|
|
279
|
+
}
|
|
280
|
+
|
|
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}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function showHelp() {
|
|
319
|
+
log(`
|
|
320
|
+
${BOLD}agent-analytics${RESET} — Web analytics your AI agent can read
|
|
321
|
+
|
|
322
|
+
${BOLD}USAGE${RESET}
|
|
323
|
+
npx agent-analytics <command> [options]
|
|
324
|
+
|
|
325
|
+
${BOLD}COMMANDS${RESET}
|
|
326
|
+
${CYAN}login${RESET} --token <key> Save your API key
|
|
327
|
+
${CYAN}create${RESET} <name> Create a project and get your snippet
|
|
328
|
+
${CYAN}projects${RESET} List your projects
|
|
329
|
+
${CYAN}init${RESET} <name> Alias for create
|
|
330
|
+
${CYAN}delete${RESET} <id> Delete a project
|
|
331
|
+
${CYAN}stats${RESET} <name> Get stats for a project
|
|
332
|
+
${CYAN}events${RESET} <name> Get recent events
|
|
333
|
+
${CYAN}properties-received${RESET} <name> Show property keys per event
|
|
334
|
+
${CYAN}whoami${RESET} Show current account
|
|
335
|
+
${CYAN}revoke-key${RESET} Revoke and regenerate API key
|
|
336
|
+
${CYAN}delete-account${RESET} Delete your account (opens dashboard)
|
|
337
|
+
|
|
338
|
+
${BOLD}OPTIONS${RESET}
|
|
339
|
+
--days <N> Days of data (default: 7)
|
|
340
|
+
--limit <N> Max events to return (default: 100)
|
|
341
|
+
--domain <url> Your site domain (required for create)
|
|
342
|
+
--since <date> ISO date for properties-received (default: 7 days)
|
|
343
|
+
--sample <N> Max events to sample (default: 5000)
|
|
344
|
+
|
|
345
|
+
${BOLD}ENVIRONMENT${RESET}
|
|
346
|
+
AGENT_ANALYTICS_API_KEY API key (overrides config file)
|
|
347
|
+
AGENT_ANALYTICS_URL Custom API URL
|
|
348
|
+
|
|
349
|
+
${BOLD}EXAMPLES${RESET}
|
|
350
|
+
${DIM}# First time: save your API key (from app.agentanalytics.sh)${RESET}
|
|
351
|
+
npx agent-analytics login --token aak_your_key
|
|
352
|
+
|
|
353
|
+
${DIM}# Create a project${RESET}
|
|
354
|
+
npx agent-analytics create my-site --domain https://mysite.com
|
|
355
|
+
|
|
356
|
+
${DIM}# Check how your site is doing${RESET}
|
|
357
|
+
npx agent-analytics stats my-site --days 30
|
|
358
|
+
|
|
359
|
+
${DIM}# Your agent can also use the API directly${RESET}
|
|
360
|
+
curl "https://api.agentanalytics.sh/stats?project=my-site&days=7" \\
|
|
361
|
+
-H "X-API-Key: \$AGENT_ANALYTICS_API_KEY"
|
|
362
|
+
|
|
363
|
+
${DIM}https://agentanalytics.sh${RESET}
|
|
364
|
+
`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ==================== MAIN ====================
|
|
368
|
+
|
|
369
|
+
const args = process.argv.slice(2);
|
|
370
|
+
const command = args[0];
|
|
371
|
+
|
|
372
|
+
function getArg(flag) {
|
|
373
|
+
const idx = args.indexOf(flag);
|
|
374
|
+
return idx > -1 && args[idx + 1] ? args[idx + 1] : null;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
switch (command) {
|
|
379
|
+
case 'login':
|
|
380
|
+
await cmdLogin(getArg('--token'));
|
|
381
|
+
break;
|
|
382
|
+
case 'create':
|
|
383
|
+
case 'init':
|
|
384
|
+
await cmdCreate(args[1], getArg('--domain'));
|
|
385
|
+
break;
|
|
386
|
+
case 'projects':
|
|
387
|
+
case 'list':
|
|
388
|
+
await cmdProjects();
|
|
389
|
+
break;
|
|
390
|
+
case 'stats':
|
|
391
|
+
await cmdStats(args[1], parseInt(getArg('--days') || '7'));
|
|
392
|
+
break;
|
|
393
|
+
case 'events':
|
|
394
|
+
await cmdEvents(args[1], {
|
|
395
|
+
days: parseInt(getArg('--days') || '7'),
|
|
396
|
+
limit: parseInt(getArg('--limit') || '100'),
|
|
397
|
+
});
|
|
398
|
+
break;
|
|
399
|
+
case 'properties-received':
|
|
400
|
+
await cmdPropertiesReceived(args[1], {
|
|
401
|
+
since: getArg('--since'),
|
|
402
|
+
sample: getArg('--sample') ? parseInt(getArg('--sample')) : undefined,
|
|
403
|
+
});
|
|
404
|
+
break;
|
|
405
|
+
case 'delete':
|
|
406
|
+
await cmdDelete(args[1]);
|
|
407
|
+
break;
|
|
408
|
+
case 'revoke-key':
|
|
409
|
+
await cmdRevokeKey();
|
|
410
|
+
break;
|
|
411
|
+
case 'delete-account':
|
|
412
|
+
cmdDeleteAccount();
|
|
413
|
+
break;
|
|
414
|
+
case 'whoami':
|
|
415
|
+
await cmdWhoami();
|
|
416
|
+
break;
|
|
417
|
+
case 'help':
|
|
418
|
+
case '--help':
|
|
419
|
+
case '-h':
|
|
420
|
+
case undefined:
|
|
421
|
+
showHelp();
|
|
422
|
+
break;
|
|
423
|
+
default:
|
|
424
|
+
error(`Unknown command: ${command}. Run: npx agent-analytics help`);
|
|
425
|
+
}
|
|
426
|
+
} catch (err) {
|
|
427
|
+
error(err.message);
|
|
428
|
+
}
|
package/lib/api.mjs
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Analytics API client.
|
|
3
|
+
* Zero dependencies — uses native fetch.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const BASE_URL = 'https://api.agentanalytics.sh';
|
|
7
|
+
|
|
8
|
+
export class AgentAnalyticsAPI {
|
|
9
|
+
constructor(apiKey, baseUrl = BASE_URL) {
|
|
10
|
+
this.apiKey = apiKey;
|
|
11
|
+
this.baseUrl = baseUrl;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async request(method, path, body, { returnHeaders = false } = {}) {
|
|
15
|
+
const opts = {
|
|
16
|
+
method,
|
|
17
|
+
headers: { 'Content-Type': 'application/json' },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
if (this.apiKey) {
|
|
21
|
+
opts.headers['X-API-Key'] = this.apiKey;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (body) {
|
|
25
|
+
opts.body = JSON.stringify(body);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const res = await fetch(`${this.baseUrl}${path}`, opts);
|
|
29
|
+
const data = await res.json();
|
|
30
|
+
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
throw new Error(data.error || `HTTP ${res.status}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (returnHeaders) {
|
|
36
|
+
const headers = {};
|
|
37
|
+
res.headers.forEach((v, k) => { headers[k] = v; });
|
|
38
|
+
return { data, headers };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return data;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Account
|
|
45
|
+
async getAccount() {
|
|
46
|
+
return this.request('GET', '/account');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async revokeKey() {
|
|
50
|
+
return this.request('POST', '/account/revoke-key');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Projects
|
|
54
|
+
async createProject(name, allowedOrigins = '*') {
|
|
55
|
+
return this.request('POST', '/projects', {
|
|
56
|
+
name,
|
|
57
|
+
allowed_origins: allowedOrigins,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async listProjects() {
|
|
62
|
+
return this.request('GET', '/projects');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async deleteProject(id) {
|
|
66
|
+
return this.request('DELETE', `/projects/${id}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Stats
|
|
70
|
+
async getStats(project, days = 7, { returnHeaders = false } = {}) {
|
|
71
|
+
return this.request('GET', `/stats?project=${encodeURIComponent(project)}&days=${days}`, undefined, { returnHeaders });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
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}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async getProperties(project, days = 30) {
|
|
81
|
+
return this.request('GET', `/properties?project=${encodeURIComponent(project)}&days=${days}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
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}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
package/lib/config.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config management — stores API key locally.
|
|
3
|
+
* ~/.config/agent-analytics/config.json
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
|
|
10
|
+
const CONFIG_DIR = join(homedir(), '.config', 'agent-analytics');
|
|
11
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
12
|
+
|
|
13
|
+
export function getConfig() {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
|
|
16
|
+
} catch {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function saveConfig(config) {
|
|
22
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
23
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getApiKey() {
|
|
27
|
+
const config = getConfig();
|
|
28
|
+
return process.env.AGENT_ANALYTICS_API_KEY || config.api_key || null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function setApiKey(key) {
|
|
32
|
+
const config = getConfig();
|
|
33
|
+
config.api_key = key;
|
|
34
|
+
saveConfig(config);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getBaseUrl() {
|
|
38
|
+
const config = getConfig();
|
|
39
|
+
return process.env.AGENT_ANALYTICS_URL || config.base_url || 'https://api.agentanalytics.sh';
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agent-analytics/cli",
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"description": "Web analytics your AI agent can read. CLI for managing projects and querying stats.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"agent-analytics": "./bin/cli.mjs"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"analytics",
|
|
11
|
+
"ai",
|
|
12
|
+
"agent",
|
|
13
|
+
"cli",
|
|
14
|
+
"web-analytics"
|
|
15
|
+
],
|
|
16
|
+
"author": "Agent Analytics <danny@animaapp.com>",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/Agent-Analytics/agent-analytics-cli"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://agentanalytics.sh",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin/",
|
|
28
|
+
"lib/",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"test": "node --test test/*.test.mjs"
|
|
34
|
+
}
|
|
35
|
+
}
|