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.
Files changed (2) hide show
  1. package/cli.mjs +100 -8
  2. 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
- const activeProvider = anthropicKey ? 'Anthropic (Claude)' : openaiKey ? 'OpenAI (GPT-4o)' : openrouterKey ? `OpenRouter (${openrouterModel})` : null;
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 (!anthropicKey && !openaiKey && !openrouterKey) {
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 (anthropicKey) {
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': anthropicKey,
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 if (openaiKey || openrouterKey) {
1427
- const isOpenRouter = !openaiKey && !!openrouterKey;
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 ${apiToken}`,
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');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentaudit",
3
- "version": "3.9.17",
3
+ "version": "3.9.18",
4
4
  "description": "Security scanner for AI packages — MCP server + CLI",
5
5
  "type": "module",
6
6
  "bin": {