Skip to content

Commit bebe486

Browse files
committed
Use saved auth for website scans
1 parent ce414a1 commit bebe486

4 files changed

Lines changed: 138 additions & 11 deletions

File tree

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ Get the fastest path to useful analytics before installing events:
2121

2222
```bash
2323
# 1. Preview what your agent should track first
24-
npx @agent-analytics/cli@0.5.16 scan https://mysite.com --json
24+
npx @agent-analytics/cli@0.5.17 scan https://mysite.com --json
2525

2626
# 2. Sign in when you want the full instrumentation plan
27-
npx @agent-analytics/cli@0.5.16 login
27+
npx @agent-analytics/cli@0.5.17 login
2828

29-
# 3. Resume and upgrade the analysis after login
30-
npx @agent-analytics/cli@0.5.16 scan \
29+
# 3. Run the full signed-in analysis
30+
npx @agent-analytics/cli@0.5.17 scan https://mysite.com --full --json
31+
32+
# Or resume and upgrade the anonymous analysis after login
33+
npx @agent-analytics/cli@0.5.17 scan \
3134
--resume <analysis_id> \
3235
--resume-token <resume_token> \
3336
--full \
@@ -71,7 +74,8 @@ login --token <key> Advanced fallback: save a raw API key
7174
logout Clear your saved local auth
7275
auth status Show local auth path and expiry metadata
7376
scan <url> Analyze what your agent should track first
74-
scan <url> --json Return anonymous preview JSON for agents
77+
scan <url> --json Return preview JSON for agents; uses saved auth when logged in
78+
scan <url> --full --json Run a full signed-in analysis with saved auth
7579
scan --resume <id> --resume-token <token>
7680
Resume a preview by analysis id and token
7781
scan --resume <id> --resume-token <token> --full --project <name>

bin/cli.mjs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -451,12 +451,19 @@ function printScanResult(data, { full = false } = {}) {
451451

452452
async function cmdScan({ url, resumeId, resumeToken, full = false, project, jsonOutput = false } = {}) {
453453
if (full) {
454-
if (!resumeId || !resumeToken) {
455-
error('Usage: npx @agent-analytics/cli scan --resume <id> --resume-token <token> --full [--project <name>] [--json]');
456-
}
457454
try {
458455
const api = await requireClient();
459-
const data = await api.upgradeWebsiteScan(resumeId, { resumeToken, project });
456+
let data;
457+
if (resumeId) {
458+
if (!resumeToken) {
459+
error('Usage: npx @agent-analytics/cli scan --resume <id> --resume-token <token> --full [--project <name>] [--json]');
460+
}
461+
data = await api.upgradeWebsiteScan(resumeId, { resumeToken, project });
462+
} else if (url) {
463+
data = await api.createWebsiteScan(url, { full: true });
464+
} else {
465+
error('Usage: npx @agent-analytics/cli scan <url> --full [--json]\n or: npx @agent-analytics/cli scan --resume <id> --resume-token <token> --full [--project <name>] [--json]');
466+
}
460467
if (jsonOutput) {
461468
printJson(data);
462469
} else {
@@ -475,7 +482,7 @@ async function cmdScan({ url, resumeId, resumeToken, full = false, project, json
475482
if (!resumeId && !url) {
476483
error('Usage: npx @agent-analytics/cli scan <url> [--json]');
477484
}
478-
const api = createApiClient(null);
485+
const api = resumeId ? createApiClient(null) : createApiClient();
479486
const data = resumeId
480487
? await api.getWebsiteScan(resumeId, { resumeToken })
481488
: await api.createWebsiteScan(url);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@agent-analytics/cli",
3-
"version": "0.5.16",
3+
"version": "0.5.17",
44
"description": "Stop juggling dashboards. Let your agent do it. Analytics your AI agent can actually use — track, analyze, experiment, optimize.",
55
"bin": {
66
"agent-analytics": "bin/cli.mjs"

test/cli.test.mjs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,122 @@ describe('CLI', () => {
10541054
}
10551055
});
10561056

1057+
it('runs a direct authenticated full scan with scan <url> --full', async () => {
1058+
let requestBody;
1059+
let requestAuth;
1060+
const config = createExplicitConfigDir({
1061+
agent_session: {
1062+
id: 'sess_scan_full',
1063+
access_token: 'aas_scan_full',
1064+
refresh_token: 'aar_scan_full',
1065+
access_expires_at: 1893456000000,
1066+
refresh_expires_at: 1924992000000,
1067+
scopes: ['account:read'],
1068+
},
1069+
});
1070+
const server = await startServer(async (req, res) => {
1071+
if (req.method === 'POST' && req.url === '/website-scans') {
1072+
requestAuth = req.headers.authorization;
1073+
requestBody = await readRequestJson(req);
1074+
res.writeHead(200, { 'Content-Type': 'application/json' });
1075+
res.end(JSON.stringify({
1076+
ok: true,
1077+
analysis_id: 'scan_full_direct',
1078+
mode: 'full',
1079+
normalized_url: 'https://example.com/',
1080+
result: {
1081+
minimum_viable_instrumentation: [{ event: 'primary_cta_clicked', priority: 1 }],
1082+
},
1083+
}));
1084+
return;
1085+
}
1086+
res.writeHead(404, { 'Content-Type': 'application/json' });
1087+
res.end(JSON.stringify({ error: 'not found' }));
1088+
});
1089+
1090+
try {
1091+
const { code, stdout } = await run([
1092+
'--config-dir', config.configDir,
1093+
'scan',
1094+
'https://example.com/',
1095+
'--full',
1096+
'--json',
1097+
], {
1098+
env: {
1099+
AGENT_ANALYTICS_URL: server.baseUrl,
1100+
AGENT_ANALYTICS_API_KEY: '',
1101+
},
1102+
});
1103+
const data = JSON.parse(stdout);
1104+
1105+
assert.equal(code, 0);
1106+
assert.equal(requestAuth, 'Bearer aas_scan_full');
1107+
assert.deepEqual(requestBody, {
1108+
url: 'https://example.com/',
1109+
mode: 'full',
1110+
});
1111+
assert.equal(data.mode, 'full');
1112+
} finally {
1113+
await server.close();
1114+
config.cleanup();
1115+
}
1116+
});
1117+
1118+
it('uses saved auth for scan <url> previews when logged in', async () => {
1119+
let requestAuth;
1120+
const config = createExplicitConfigDir({
1121+
agent_session: {
1122+
id: 'sess_scan_preview',
1123+
access_token: 'aas_scan_preview',
1124+
refresh_token: 'aar_scan_preview',
1125+
access_expires_at: 1893456000000,
1126+
refresh_expires_at: 1924992000000,
1127+
scopes: ['account:read'],
1128+
},
1129+
});
1130+
const server = await startServer(async (req, res) => {
1131+
if (req.method === 'POST' && req.url === '/website-scans') {
1132+
requestAuth = req.headers.authorization;
1133+
await readRequestJson(req);
1134+
res.writeHead(200, { 'Content-Type': 'application/json' });
1135+
res.end(JSON.stringify({
1136+
ok: true,
1137+
analysis_id: 'scan_auth_preview',
1138+
mode: 'authenticated_preview',
1139+
normalized_url: 'https://example.com/',
1140+
preview: {
1141+
minimum_viable_instrumentation: [{ event: 'primary_cta_clicked', priority: 1 }],
1142+
},
1143+
}));
1144+
return;
1145+
}
1146+
res.writeHead(404, { 'Content-Type': 'application/json' });
1147+
res.end(JSON.stringify({ error: 'not found' }));
1148+
});
1149+
1150+
try {
1151+
const { code, stdout } = await run([
1152+
'--config-dir', config.configDir,
1153+
'scan',
1154+
'https://example.com/',
1155+
'--json',
1156+
], {
1157+
env: {
1158+
AGENT_ANALYTICS_URL: server.baseUrl,
1159+
AGENT_ANALYTICS_API_KEY: '',
1160+
},
1161+
});
1162+
const data = JSON.parse(stdout);
1163+
1164+
assert.equal(code, 0);
1165+
assert.equal(requestAuth, 'Bearer aas_scan_preview');
1166+
assert.equal(data.mode, 'authenticated_preview');
1167+
} finally {
1168+
await server.close();
1169+
config.cleanup();
1170+
}
1171+
});
1172+
10571173
it('prints login guidance for scan --full without auth', async () => {
10581174
const tempHome = createTempConfigHome();
10591175
const { code, stdout } = await run([

0 commit comments

Comments
 (0)