agentaudit 3.9.17 → 3.9.18
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/cli.mjs +100 -8
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* agentaudit audit <repo-url> Deep LLM-powered security audit
|
|
10
10
|
* agentaudit lookup <name> Look up package in registry
|
|
11
11
|
* agentaudit check <name|url> Lookup + auto-audit if not found
|
|
12
|
+
* agentaudit status Check configured API keys + providers
|
|
12
13
|
* agentaudit setup Register + configure API key
|
|
13
14
|
*
|
|
14
15
|
* Global flags: --json, --quiet, --no-color
|
|
@@ -25,6 +26,26 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
25
26
|
const SKILL_DIR = path.resolve(__dirname);
|
|
26
27
|
const REGISTRY_URL = 'https://agentaudit.dev';
|
|
27
28
|
|
|
29
|
+
// ── Provider resolution ────
|
|
30
|
+
function resolveProvider(preferred, keys) {
|
|
31
|
+
const providers = {
|
|
32
|
+
anthropic: keys.anthropicKey ? { id: 'anthropic', label: 'Anthropic (Claude)', key: keys.anthropicKey } : null,
|
|
33
|
+
openai: keys.openaiKey ? { id: 'openai', label: 'OpenAI (GPT-4o)', key: keys.openaiKey } : null,
|
|
34
|
+
openrouter: keys.openrouterKey ? { id: 'openrouter', label: `OpenRouter (${process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4'})`, key: keys.openrouterKey } : null,
|
|
35
|
+
};
|
|
36
|
+
// Aliases
|
|
37
|
+
const aliases = { claude: 'anthropic', gpt: 'openai', 'gpt-4o': 'openai', 'gpt4': 'openai', or: 'openrouter' };
|
|
38
|
+
|
|
39
|
+
if (preferred) {
|
|
40
|
+
const resolved = aliases[preferred] || preferred;
|
|
41
|
+
const p = providers[resolved];
|
|
42
|
+
if (!p) return null; // requested provider not available
|
|
43
|
+
return p;
|
|
44
|
+
}
|
|
45
|
+
// Auto-detect: Anthropic > OpenAI > OpenRouter
|
|
46
|
+
return providers.anthropic || providers.openai || providers.openrouter || null;
|
|
47
|
+
}
|
|
48
|
+
|
|
28
49
|
// ── Global flags (set in main before command routing) ────
|
|
29
50
|
let jsonMode = false;
|
|
30
51
|
let quietMode = false;
|
|
@@ -1312,9 +1333,15 @@ async function auditRepo(url) {
|
|
|
1312
1333
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
1313
1334
|
const openrouterKey = process.env.OPENROUTER_API_KEY;
|
|
1314
1335
|
const openrouterModel = process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4';
|
|
1315
|
-
|
|
1336
|
+
|
|
1337
|
+
// --provider flag overrides auto-detection
|
|
1338
|
+
const providerFlag = process.argv.find(a => a.startsWith('--provider='))?.split('=')[1]?.toLowerCase()
|
|
1339
|
+
|| (process.argv.includes('--provider') ? process.argv[process.argv.indexOf('--provider') + 1]?.toLowerCase() : null);
|
|
1340
|
+
|
|
1341
|
+
const resolvedProvider = resolveProvider(providerFlag, { anthropicKey, openaiKey, openrouterKey });
|
|
1342
|
+
const activeProvider = resolvedProvider?.label || null;
|
|
1316
1343
|
|
|
1317
|
-
if (!
|
|
1344
|
+
if (!resolvedProvider) {
|
|
1318
1345
|
// No LLM API key — clear explanation
|
|
1319
1346
|
console.log();
|
|
1320
1347
|
console.log(` ${c.yellow}No LLM API key found.${c.reset} The ${c.bold}audit${c.reset} command needs an LLM to analyze code.`);
|
|
@@ -1397,11 +1424,11 @@ async function auditRepo(url) {
|
|
|
1397
1424
|
let _lastLlmText = '';
|
|
1398
1425
|
|
|
1399
1426
|
try {
|
|
1400
|
-
if (
|
|
1427
|
+
if (resolvedProvider.id === 'anthropic') {
|
|
1401
1428
|
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
1402
1429
|
method: 'POST',
|
|
1403
1430
|
headers: {
|
|
1404
|
-
'x-api-key':
|
|
1431
|
+
'x-api-key': resolvedProvider.key,
|
|
1405
1432
|
'anthropic-version': '2023-06-01',
|
|
1406
1433
|
'content-type': 'application/json',
|
|
1407
1434
|
},
|
|
@@ -1423,17 +1450,17 @@ async function auditRepo(url) {
|
|
|
1423
1450
|
const text = data.content?.[0]?.text || '';
|
|
1424
1451
|
_lastLlmText = text;
|
|
1425
1452
|
report = extractJSON(text);
|
|
1426
|
-
} else
|
|
1427
|
-
|
|
1453
|
+
} else {
|
|
1454
|
+
// OpenAI or OpenRouter (both use OpenAI-compatible API)
|
|
1455
|
+
const isOpenRouter = resolvedProvider.id === 'openrouter';
|
|
1428
1456
|
const apiUrl = isOpenRouter ? 'https://openrouter.ai/api/v1/chat/completions' : 'https://api.openai.com/v1/chat/completions';
|
|
1429
|
-
const apiToken = isOpenRouter ? openrouterKey : openaiKey;
|
|
1430
1457
|
const modelName = isOpenRouter ? (process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4') : 'gpt-4o';
|
|
1431
1458
|
const extraHeaders = isOpenRouter ? { 'HTTP-Referer': 'https://agentaudit.dev', 'X-Title': 'AgentAudit' } : {};
|
|
1432
1459
|
|
|
1433
1460
|
const res = await fetch(apiUrl, {
|
|
1434
1461
|
method: 'POST',
|
|
1435
1462
|
headers: {
|
|
1436
|
-
'Authorization': `Bearer ${
|
|
1463
|
+
'Authorization': `Bearer ${resolvedProvider.key}`,
|
|
1437
1464
|
'Content-Type': 'application/json',
|
|
1438
1465
|
...extraHeaders,
|
|
1439
1466
|
},
|
|
@@ -1656,12 +1683,14 @@ async function main() {
|
|
|
1656
1683
|
console.log(` ${c.cyan}agentaudit audit${c.reset} <url> [url...] Deep LLM-powered security audit`);
|
|
1657
1684
|
console.log(` ${c.cyan}agentaudit lookup${c.reset} <name> Look up package in registry`);
|
|
1658
1685
|
console.log(` ${c.cyan}agentaudit check${c.reset} <name|url> Lookup + auto-audit if not found`);
|
|
1686
|
+
console.log(` ${c.cyan}agentaudit status${c.reset} Check API keys + active provider`);
|
|
1659
1687
|
console.log(` ${c.cyan}agentaudit setup${c.reset} Register + configure API key`);
|
|
1660
1688
|
console.log();
|
|
1661
1689
|
console.log(` ${c.bold}Global flags:${c.reset}`);
|
|
1662
1690
|
console.log(` ${c.dim}--json Output JSON to stdout (machine-readable)${c.reset}`);
|
|
1663
1691
|
console.log(` ${c.dim}--quiet Suppress banner and tree visualization${c.reset}`);
|
|
1664
1692
|
console.log(` ${c.dim}--no-color Disable ANSI colors (also: NO_COLOR env)${c.reset}`);
|
|
1693
|
+
console.log(` ${c.dim}--provider Force LLM provider (anthropic, openai, openrouter)${c.reset}`);
|
|
1665
1694
|
console.log();
|
|
1666
1695
|
console.log(` ${c.bold}Quick Scan${c.reset} vs ${c.bold}Deep Audit${c.reset}:`);
|
|
1667
1696
|
console.log(` ${c.dim}scan = fast regex-based static analysis (~2s)${c.reset}`);
|
|
@@ -1709,6 +1738,69 @@ async function main() {
|
|
|
1709
1738
|
return;
|
|
1710
1739
|
}
|
|
1711
1740
|
|
|
1741
|
+
if (command === 'status') {
|
|
1742
|
+
console.log(` ${c.bold}LLM Providers:${c.reset}`);
|
|
1743
|
+
console.log();
|
|
1744
|
+
const keys = {
|
|
1745
|
+
anthropicKey: process.env.ANTHROPIC_API_KEY,
|
|
1746
|
+
openaiKey: process.env.OPENAI_API_KEY,
|
|
1747
|
+
openrouterKey: process.env.OPENROUTER_API_KEY,
|
|
1748
|
+
};
|
|
1749
|
+
const checks = [
|
|
1750
|
+
{ name: 'Anthropic', env: 'ANTHROPIC_API_KEY', key: keys.anthropicKey, testUrl: 'https://api.anthropic.com/v1/messages', testHeaders: (k) => ({ 'x-api-key': k, 'anthropic-version': '2023-06-01', 'content-type': 'application/json' }), testBody: JSON.stringify({ model: 'claude-sonnet-4-20250514', max_tokens: 1, messages: [{ role: 'user', content: 'hi' }] }) },
|
|
1751
|
+
{ name: 'OpenAI', env: 'OPENAI_API_KEY', key: keys.openaiKey, testUrl: 'https://api.openai.com/v1/models', testHeaders: (k) => ({ 'Authorization': `Bearer ${k}` }), testBody: null },
|
|
1752
|
+
{ name: 'OpenRouter', env: 'OPENROUTER_API_KEY', key: keys.openrouterKey, testUrl: 'https://openrouter.ai/api/v1/models', testHeaders: (k) => ({ 'Authorization': `Bearer ${k}` }), testBody: null },
|
|
1753
|
+
];
|
|
1754
|
+
|
|
1755
|
+
for (const p of checks) {
|
|
1756
|
+
if (!p.key) {
|
|
1757
|
+
console.log(` ${c.dim}○${c.reset} ${p.name.padEnd(12)} ${c.dim}not set${c.reset} ${c.dim}(${p.env})${c.reset}`);
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
const masked = p.key.substring(0, 8) + '...' + p.key.substring(p.key.length - 4);
|
|
1761
|
+
process.stdout.write(` ${c.yellow}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} checking...`);
|
|
1762
|
+
try {
|
|
1763
|
+
const res = await fetch(p.testUrl, {
|
|
1764
|
+
method: p.testBody ? 'POST' : 'GET',
|
|
1765
|
+
headers: p.testHeaders(p.key),
|
|
1766
|
+
...(p.testBody ? { body: p.testBody } : {}),
|
|
1767
|
+
signal: AbortSignal.timeout(10_000),
|
|
1768
|
+
});
|
|
1769
|
+
if (res.ok || res.status === 200 || res.status === 201) {
|
|
1770
|
+
process.stdout.write(`\r ${c.green}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} ${c.green}valid ✓${c.reset} \n`);
|
|
1771
|
+
} else {
|
|
1772
|
+
const body = await res.json().catch(() => ({}));
|
|
1773
|
+
const errMsg = body?.error?.message || `HTTP ${res.status}`;
|
|
1774
|
+
process.stdout.write(`\r ${c.red}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} ${c.red}invalid ✗${c.reset} ${c.dim}(${errMsg})${c.reset} \n`);
|
|
1775
|
+
}
|
|
1776
|
+
} catch (e) {
|
|
1777
|
+
process.stdout.write(`\r ${c.red}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} ${c.red}error ✗${c.reset} ${c.dim}(${e.message})${c.reset} \n`);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
const resolved = resolveProvider(null, keys);
|
|
1782
|
+
console.log();
|
|
1783
|
+
if (resolved) {
|
|
1784
|
+
console.log(` ${c.bold}Active provider:${c.reset} ${resolved.label}`);
|
|
1785
|
+
console.log(` ${c.dim}Override with: --provider=openai | --provider=openrouter | --provider=anthropic${c.reset}`);
|
|
1786
|
+
} else {
|
|
1787
|
+
console.log(` ${c.yellow}No LLM provider configured.${c.reset} Set one of the API keys above for deep audits.`);
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// AgentAudit registry key
|
|
1791
|
+
console.log();
|
|
1792
|
+
console.log(` ${c.bold}Registry:${c.reset}`);
|
|
1793
|
+
const creds = loadCredentials();
|
|
1794
|
+
if (creds?.api_key) {
|
|
1795
|
+
const masked = creds.api_key.substring(0, 8) + '...' + creds.api_key.substring(creds.api_key.length - 4);
|
|
1796
|
+
console.log(` ${c.green}●${c.reset} AgentAudit ${c.dim}${masked}${c.reset} ${c.dim}(${creds.agent_name || 'unknown'})${c.reset}`);
|
|
1797
|
+
} else {
|
|
1798
|
+
console.log(` ${c.dim}○${c.reset} AgentAudit ${c.dim}not set${c.reset} ${c.dim}(run: agentaudit setup)${c.reset}`);
|
|
1799
|
+
}
|
|
1800
|
+
console.log();
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1712
1804
|
if (command === 'discover') {
|
|
1713
1805
|
const scanFlag = targets.includes('--quick') || targets.includes('--scan') || targets.includes('-s');
|
|
1714
1806
|
const auditFlag = targets.includes('--deep') || targets.includes('--audit') || targets.includes('-a');
|