agentaudit 3.9.19 → 3.9.20
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 +170 -83
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* AgentAudit CLI — Security scanner for AI
|
|
3
|
+
* AgentAudit CLI — Security scanner for AI tools
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* agentaudit scan <repo-url> [--deep] Quick scan (or deep audit with --deep)
|
|
9
|
-
* agentaudit audit <repo-url> Deep LLM-powered security audit
|
|
10
|
-
* agentaudit lookup <name> Look up package in registry
|
|
11
|
-
* agentaudit check <name|url> Lookup + auto-audit if not found
|
|
12
|
-
* agentaudit status Check configured API keys + providers
|
|
13
|
-
* agentaudit setup Register + configure API key
|
|
5
|
+
* Scan & Audit: scan <url>, audit <url>, discover
|
|
6
|
+
* Registry: check <name|url>, lookup <name>
|
|
7
|
+
* Setup: status, setup, config
|
|
14
8
|
*
|
|
15
|
-
* Global flags: --json, --quiet, --no-color
|
|
9
|
+
* Global flags: --json, --quiet, --no-color, --provider, --debug, --export
|
|
16
10
|
*/
|
|
17
11
|
|
|
18
12
|
import fs from 'fs';
|
|
@@ -27,7 +21,7 @@ const SKILL_DIR = path.resolve(__dirname);
|
|
|
27
21
|
const REGISTRY_URL = 'https://agentaudit.dev';
|
|
28
22
|
|
|
29
23
|
// ── Provider resolution ────
|
|
30
|
-
function resolveProvider(
|
|
24
|
+
function resolveProvider(flagOverride, keys) {
|
|
31
25
|
const orModel = process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4';
|
|
32
26
|
const ollamaModel = process.env.OLLAMA_MODEL || 'llama3.1';
|
|
33
27
|
const ollamaHost = process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
@@ -45,6 +39,12 @@ function resolveProvider(preferred, keys) {
|
|
|
45
39
|
// Aliases
|
|
46
40
|
const aliases = { claude: 'anthropic', gpt: 'openai', 'gpt-4o': 'openai', 'gpt4': 'openai', or: 'openrouter', local: 'ollama' };
|
|
47
41
|
|
|
42
|
+
// Priority: --provider flag > AGENTAUDIT_PROVIDER env > config file > auto-detect
|
|
43
|
+
const preferred = flagOverride
|
|
44
|
+
|| process.env.AGENTAUDIT_PROVIDER?.toLowerCase()
|
|
45
|
+
|| loadConfig()?.preferred_provider
|
|
46
|
+
|| null;
|
|
47
|
+
|
|
48
48
|
if (preferred) {
|
|
49
49
|
const resolved = aliases[preferred] || preferred;
|
|
50
50
|
const p = providers[resolved];
|
|
@@ -129,6 +129,24 @@ function saveCredentials(data) {
|
|
|
129
129
|
} catch {}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
const USER_CONFIG_FILE = path.join(USER_CRED_DIR, 'config.json');
|
|
133
|
+
|
|
134
|
+
function loadConfig() {
|
|
135
|
+
try {
|
|
136
|
+
if (fs.existsSync(USER_CONFIG_FILE)) {
|
|
137
|
+
return JSON.parse(fs.readFileSync(USER_CONFIG_FILE, 'utf8'));
|
|
138
|
+
}
|
|
139
|
+
} catch {}
|
|
140
|
+
return {};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function saveConfig(data) {
|
|
144
|
+
const existing = loadConfig();
|
|
145
|
+
const merged = { ...existing, ...data };
|
|
146
|
+
fs.mkdirSync(USER_CRED_DIR, { recursive: true });
|
|
147
|
+
fs.writeFileSync(USER_CONFIG_FILE, JSON.stringify(merged, null, 2), { mode: 0o600 });
|
|
148
|
+
}
|
|
149
|
+
|
|
132
150
|
function askQuestion(question) {
|
|
133
151
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
134
152
|
return new Promise(resolve => rl.question(question, answer => { rl.close(); resolve(answer.trim()); }));
|
|
@@ -312,6 +330,28 @@ async function setupCommand() {
|
|
|
312
330
|
console.log();
|
|
313
331
|
}
|
|
314
332
|
|
|
333
|
+
// ── Structured error output ─────────────────────────────
|
|
334
|
+
|
|
335
|
+
function emitError(code, message, hint, exitCode = 2) {
|
|
336
|
+
if (jsonMode) {
|
|
337
|
+
process.stderr.write(JSON.stringify({ error: true, code, message, hint: hint || undefined, exitCode }) + '\n');
|
|
338
|
+
}
|
|
339
|
+
process.exitCode = exitCode;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── Levenshtein distance for typo correction ────────────
|
|
343
|
+
|
|
344
|
+
function levenshtein(a, b) {
|
|
345
|
+
const m = a.length, n = b.length;
|
|
346
|
+
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
347
|
+
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
348
|
+
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
349
|
+
for (let i = 1; i <= m; i++)
|
|
350
|
+
for (let j = 1; j <= n; j++)
|
|
351
|
+
dp[i][j] = Math.min(dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1] + (a[i-1] !== b[j-1] ? 1 : 0));
|
|
352
|
+
return dp[m][n];
|
|
353
|
+
}
|
|
354
|
+
|
|
315
355
|
// ── Helpers ──────────────────────────────────────────────
|
|
316
356
|
|
|
317
357
|
function getVersion() {
|
|
@@ -324,8 +364,8 @@ function getVersion() {
|
|
|
324
364
|
function banner() {
|
|
325
365
|
if (quietMode || jsonMode) return;
|
|
326
366
|
console.log();
|
|
327
|
-
console.log(` ${c.bold}${c.cyan}AgentAudit${c.reset} ${c.dim}v${getVersion()}${c.reset}`);
|
|
328
|
-
console.log(` ${c.dim}Security scanner for AI
|
|
367
|
+
console.log(` 🛡 ${c.bold}${c.cyan}AgentAudit${c.reset} ${c.dim}v${getVersion()}${c.reset}`);
|
|
368
|
+
console.log(` ${c.dim}Security scanner for AI tools${c.reset}`);
|
|
329
369
|
console.log();
|
|
330
370
|
}
|
|
331
371
|
|
|
@@ -1606,8 +1646,11 @@ async function checkPackage(name) {
|
|
|
1606
1646
|
console.log();
|
|
1607
1647
|
return await auditRepo(name);
|
|
1608
1648
|
}
|
|
1609
|
-
console.log(` ${c.yellow}Not found${c.reset} —
|
|
1610
|
-
console.log(
|
|
1649
|
+
console.log(` ${c.yellow}✖ Not found${c.reset} — "${name}" hasn't been audited yet.`);
|
|
1650
|
+
console.log();
|
|
1651
|
+
console.log(` ${c.dim}Next steps:${c.reset}`);
|
|
1652
|
+
console.log(` ${c.cyan}agentaudit audit <repo-url>${c.reset} ${c.dim}Run a deep audit yourself${c.reset}`);
|
|
1653
|
+
console.log(` ${c.cyan}agentaudit scan <repo-url>${c.reset} ${c.dim}Quick static check (no API key)${c.reset}`);
|
|
1611
1654
|
}
|
|
1612
1655
|
return null;
|
|
1613
1656
|
}
|
|
@@ -1699,52 +1742,41 @@ async function main() {
|
|
|
1699
1742
|
|
|
1700
1743
|
if (args[0] === '--help' || args[0] === '-h') {
|
|
1701
1744
|
banner();
|
|
1702
|
-
console.log(` ${c.bold}
|
|
1703
|
-
console.log();
|
|
1704
|
-
console.log(` ${c.cyan}agentaudit${c.reset} Discover MCP servers (same as discover)`);
|
|
1705
|
-
console.log(` ${c.cyan}agentaudit discover${c.reset} Find MCP servers in your AI editors (Cursor, Claude, VS Code, Windsurf)`);
|
|
1706
|
-
console.log(` ${c.cyan}agentaudit discover --quick${c.reset} Discover + auto-scan all servers`);
|
|
1707
|
-
console.log(` ${c.cyan}agentaudit discover --deep${c.reset} Discover + select servers to deep-audit`);
|
|
1708
|
-
console.log(` ${c.cyan}agentaudit scan${c.reset} <url> [url...] Quick static scan (regex, local)`);
|
|
1709
|
-
console.log(` ${c.cyan}agentaudit scan${c.reset} <url> ${c.dim}--deep${c.reset} Deep audit (same as audit)`);
|
|
1710
|
-
console.log(` ${c.cyan}agentaudit audit${c.reset} <url> [url...] Deep LLM-powered security audit`);
|
|
1711
|
-
console.log(` ${c.cyan}agentaudit lookup${c.reset} <name> Look up package in registry`);
|
|
1712
|
-
console.log(` ${c.cyan}agentaudit check${c.reset} <name|url> Lookup + auto-audit if not found`);
|
|
1713
|
-
console.log(` ${c.cyan}agentaudit status${c.reset} Check API keys + active provider`);
|
|
1714
|
-
console.log(` ${c.cyan}agentaudit setup${c.reset} Register + configure API key`);
|
|
1745
|
+
console.log(` ${c.bold}USAGE${c.reset}`);
|
|
1746
|
+
console.log(` ${c.cyan}agentaudit${c.reset} <command> [options]`);
|
|
1715
1747
|
console.log();
|
|
1716
|
-
console.log(` ${c.bold}
|
|
1717
|
-
console.log(` ${c.
|
|
1718
|
-
console.log(` ${c.
|
|
1719
|
-
console.log(` ${c.
|
|
1720
|
-
console.log(` ${c.dim}--provider Force LLM provider (anthropic, openai, openrouter)${c.reset}`);
|
|
1748
|
+
console.log(` ${c.bold}SCAN & AUDIT${c.reset}`);
|
|
1749
|
+
console.log(` ${c.cyan}scan${c.reset} <url> [url...] Quick static analysis ${c.dim}(~2s, no API key)${c.reset}`);
|
|
1750
|
+
console.log(` ${c.cyan}audit${c.reset} <url> [url...] Deep LLM security audit ${c.dim}(~30s)${c.reset}`);
|
|
1751
|
+
console.log(` ${c.cyan}discover${c.reset} Find MCP servers in your editors`);
|
|
1721
1752
|
console.log();
|
|
1722
|
-
console.log(` ${c.bold}
|
|
1723
|
-
console.log(` ${c.
|
|
1724
|
-
console.log(` ${c.
|
|
1753
|
+
console.log(` ${c.bold}REGISTRY${c.reset}`);
|
|
1754
|
+
console.log(` ${c.cyan}check${c.reset} <name|url> Look up or auto-audit package`);
|
|
1755
|
+
console.log(` ${c.cyan}lookup${c.reset} <name> Look up package in registry`);
|
|
1725
1756
|
console.log();
|
|
1726
|
-
console.log(` ${c.bold}
|
|
1727
|
-
console.log(` ${c.
|
|
1757
|
+
console.log(` ${c.bold}SETUP${c.reset}`);
|
|
1758
|
+
console.log(` ${c.cyan}status${c.reset} Check providers & API keys`);
|
|
1759
|
+
console.log(` ${c.cyan}setup${c.reset} Register & configure`);
|
|
1760
|
+
console.log(` ${c.cyan}config set${c.reset} <key> <value> Set default provider/options`);
|
|
1728
1761
|
console.log();
|
|
1729
|
-
console.log(` ${c.bold}
|
|
1730
|
-
console.log(`
|
|
1731
|
-
console.log(`
|
|
1732
|
-
console.log(`
|
|
1733
|
-
console.log(`
|
|
1734
|
-
console.log(`
|
|
1762
|
+
console.log(` ${c.bold}OPTIONS${c.reset}`);
|
|
1763
|
+
console.log(` ${c.dim}--json Machine-readable JSON output${c.reset}`);
|
|
1764
|
+
console.log(` ${c.dim}--quiet Suppress banner${c.reset}`);
|
|
1765
|
+
console.log(` ${c.dim}--no-color Disable colors ${c.reset}${c.dim}(also: NO_COLOR=1)${c.reset}`);
|
|
1766
|
+
console.log(` ${c.dim}--provider <p> Force provider ${c.reset}${c.dim}(anthropic|openai|openrouter|ollama|custom)${c.reset}`);
|
|
1767
|
+
console.log(` ${c.dim}--export Export audit payload to markdown${c.reset}`);
|
|
1768
|
+
console.log(` ${c.dim}--debug Show raw LLM response on errors${c.reset}`);
|
|
1735
1769
|
console.log();
|
|
1736
|
-
console.log(` ${c.bold}
|
|
1737
|
-
console.log(` ${c.dim}
|
|
1738
|
-
console.log(` ${c.dim}
|
|
1739
|
-
console.log(` ${c.dim}
|
|
1740
|
-
console.log(` ${c.dim}
|
|
1741
|
-
console.log(` ${c.dim}LLM_API_URL Any OpenAI-compatible API (+ LLM_API_KEY, LLM_MODEL)${c.reset}`);
|
|
1770
|
+
console.log(` ${c.bold}EXAMPLES${c.reset}`);
|
|
1771
|
+
console.log(` ${c.dim}$${c.reset} agentaudit scan https://github.com/owner/repo`);
|
|
1772
|
+
console.log(` ${c.dim}$${c.reset} agentaudit audit https://github.com/owner/repo`);
|
|
1773
|
+
console.log(` ${c.dim}$${c.reset} agentaudit check fastmcp`);
|
|
1774
|
+
console.log(` ${c.dim}$${c.reset} agentaudit status`);
|
|
1742
1775
|
console.log();
|
|
1743
|
-
console.log(` ${c.
|
|
1744
|
-
console.log();
|
|
1745
|
-
console.log(`
|
|
1746
|
-
console.log(` ${c.dim}
|
|
1747
|
-
console.log(` ${c.dim}{ "agentaudit": { "command": "npx", "args": ["-y", "agentaudit"] } }${c.reset}`);
|
|
1776
|
+
console.log(` ${c.bold}PROVIDERS${c.reset} ${c.dim}(set any one for deep audits)${c.reset}`);
|
|
1777
|
+
console.log(` ${c.dim}ANTHROPIC_API_KEY · OPENAI_API_KEY · OPENROUTER_API_KEY · OLLAMA_MODEL · LLM_API_URL${c.reset}`);
|
|
1778
|
+
console.log(` ${c.dim}Set default: AGENTAUDIT_PROVIDER=openai or agentaudit config set provider openai${c.reset}`);
|
|
1779
|
+
console.log(` ${c.dim}Run ${c.cyan}agentaudit status${c.dim} to check configuration.${c.reset}`);
|
|
1748
1780
|
console.log();
|
|
1749
1781
|
process.exitCode = 0; return;
|
|
1750
1782
|
}
|
|
@@ -1774,8 +1806,8 @@ async function main() {
|
|
|
1774
1806
|
|
|
1775
1807
|
const checks = [
|
|
1776
1808
|
{ 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' }] }) },
|
|
1777
|
-
{ name: 'OpenAI', env: 'OPENAI_API_KEY', key: keys.openaiKey, testUrl: 'https://api.openai.com/v1/
|
|
1778
|
-
{ name: 'OpenRouter', env: 'OPENROUTER_API_KEY', key: keys.openrouterKey, testUrl: 'https://openrouter.ai/api/v1/
|
|
1809
|
+
{ name: 'OpenAI', env: 'OPENAI_API_KEY', key: keys.openaiKey, testUrl: 'https://api.openai.com/v1/chat/completions', testHeaders: (k) => ({ 'Authorization': `Bearer ${k}`, 'Content-Type': 'application/json' }), testBody: JSON.stringify({ model: 'gpt-4o-mini', max_tokens: 1, messages: [{ role: 'user', content: 'hi' }] }) },
|
|
1810
|
+
{ name: 'OpenRouter', env: 'OPENROUTER_API_KEY', key: keys.openrouterKey, testUrl: 'https://openrouter.ai/api/v1/chat/completions', testHeaders: (k) => ({ 'Authorization': `Bearer ${k}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://agentaudit.dev', 'X-Title': 'AgentAudit' }), testBody: JSON.stringify({ model: 'openai/gpt-4o-mini', max_tokens: 1, messages: [{ role: 'user', content: 'hi' }] }) },
|
|
1779
1811
|
{ name: 'Ollama', env: 'OLLAMA_MODEL', key: ollamaModel, testUrl: `${ollamaHost}/api/tags`, testHeaders: () => ({}), testBody: null },
|
|
1780
1812
|
{ name: 'Custom', env: 'LLM_API_URL', key: customUrl, testUrl: customUrl ? `${customUrl.replace(/\/$/, '')}/models` : null, testHeaders: (k) => process.env.LLM_API_KEY ? ({ 'Authorization': `Bearer ${process.env.LLM_API_KEY}` }) : {}, testBody: null },
|
|
1781
1813
|
];
|
|
@@ -1798,8 +1830,25 @@ async function main() {
|
|
|
1798
1830
|
process.stdout.write(`\r ${c.green}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} ${c.green}valid ✓${c.reset} \n`);
|
|
1799
1831
|
} else {
|
|
1800
1832
|
const body = await res.json().catch(() => ({}));
|
|
1801
|
-
const
|
|
1802
|
-
|
|
1833
|
+
const rawMsg = body?.error?.message || body?.message || `HTTP ${res.status}`;
|
|
1834
|
+
// Detect specific error types for clearer messages
|
|
1835
|
+
const lcMsg = rawMsg.toLowerCase();
|
|
1836
|
+
let errMsg = rawMsg;
|
|
1837
|
+
let hint = '';
|
|
1838
|
+
if (lcMsg.includes('credit') || lcMsg.includes('balance') || lcMsg.includes('quota') || lcMsg.includes('billing') || lcMsg.includes('exceeded') || lcMsg.includes('insufficient')) {
|
|
1839
|
+
errMsg = 'no credits';
|
|
1840
|
+
if (p.name === 'Anthropic') hint = `\n ${c.dim}└─ Add credits: console.anthropic.com/settings/plans${c.reset}`;
|
|
1841
|
+
else if (p.name === 'OpenAI') hint = `\n ${c.dim}└─ Check usage: platform.openai.com/usage${c.reset}`;
|
|
1842
|
+
else if (p.name === 'OpenRouter') hint = `\n ${c.dim}└─ Check balance: openrouter.ai/credits${c.reset}`;
|
|
1843
|
+
} else if (res.status === 401 || lcMsg.includes('invalid') || lcMsg.includes('unauthorized') || lcMsg.includes('authentication')) {
|
|
1844
|
+
errMsg = 'invalid key';
|
|
1845
|
+
if (p.name === 'Anthropic') hint = `\n ${c.dim}└─ Check key: console.anthropic.com/settings/keys${c.reset}`;
|
|
1846
|
+
else if (p.name === 'OpenAI') hint = `\n ${c.dim}└─ Check key: platform.openai.com/api-keys${c.reset}`;
|
|
1847
|
+
} else if (res.status === 429) {
|
|
1848
|
+
errMsg = 'rate limited';
|
|
1849
|
+
hint = `\n ${c.dim}└─ Try again in a moment${c.reset}`;
|
|
1850
|
+
}
|
|
1851
|
+
process.stdout.write(`\r ${c.red}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} ${c.red}✖ ${errMsg}${c.reset}${hint} \n`);
|
|
1803
1852
|
}
|
|
1804
1853
|
} catch (e) {
|
|
1805
1854
|
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`);
|
|
@@ -1809,10 +1858,13 @@ async function main() {
|
|
|
1809
1858
|
const resolved = resolveProvider(null, keys);
|
|
1810
1859
|
console.log();
|
|
1811
1860
|
if (resolved) {
|
|
1812
|
-
console.log(` ${c.bold}Active
|
|
1813
|
-
console.log(` ${c.dim}Override
|
|
1861
|
+
console.log(` ${c.bold}Active:${c.reset} ${c.green}${resolved.label}${c.reset}`);
|
|
1862
|
+
console.log(` ${c.dim}Override: --provider=<name> or AGENTAUDIT_PROVIDER=<name>${c.reset}`);
|
|
1863
|
+
console.log(` ${c.dim}Set default: agentaudit config set provider <name>${c.reset}`);
|
|
1814
1864
|
} else {
|
|
1815
|
-
console.log(` ${c.yellow}No LLM provider
|
|
1865
|
+
console.log(` ${c.yellow}⚠ No working LLM provider.${c.reset} Deep audits require one.`);
|
|
1866
|
+
console.log(` ${c.dim}Set a key: export ANTHROPIC_API_KEY=sk-ant-...${c.reset}`);
|
|
1867
|
+
console.log(` ${c.dim}Or scan without LLM: agentaudit scan <url>${c.reset}`);
|
|
1816
1868
|
}
|
|
1817
1869
|
|
|
1818
1870
|
// AgentAudit registry key
|
|
@@ -1836,10 +1888,47 @@ async function main() {
|
|
|
1836
1888
|
return;
|
|
1837
1889
|
}
|
|
1838
1890
|
|
|
1891
|
+
if (command === 'config') {
|
|
1892
|
+
const subCmd = targets[0];
|
|
1893
|
+
if (subCmd === 'set' && targets[1] === 'provider' && targets[2]) {
|
|
1894
|
+
const validProviders = ['anthropic', 'openai', 'openrouter', 'ollama', 'custom', 'claude', 'gpt'];
|
|
1895
|
+
const val = targets[2].toLowerCase();
|
|
1896
|
+
if (!validProviders.includes(val)) {
|
|
1897
|
+
console.log(` ${c.red}✖ Unknown provider: ${val}${c.reset}`);
|
|
1898
|
+
console.log(` ${c.dim}Valid: anthropic, openai, openrouter, ollama, custom${c.reset}`);
|
|
1899
|
+
process.exitCode = 2; return;
|
|
1900
|
+
}
|
|
1901
|
+
saveConfig({ preferred_provider: val });
|
|
1902
|
+
console.log(` ${c.green}✔${c.reset} Default provider set to: ${c.bold}${val}${c.reset}`);
|
|
1903
|
+
console.log(` ${c.dim}Override per-command: --provider=<name>${c.reset}`);
|
|
1904
|
+
console.log(` ${c.dim}Or env: AGENTAUDIT_PROVIDER=<name>${c.reset}`);
|
|
1905
|
+
} else if (subCmd === 'get' || !subCmd) {
|
|
1906
|
+
const cfg = loadConfig();
|
|
1907
|
+
console.log(` ${c.bold}Config:${c.reset} ${USER_CONFIG_FILE}`);
|
|
1908
|
+
if (Object.keys(cfg).length === 0) {
|
|
1909
|
+
console.log(` ${c.dim}(empty — using defaults)${c.reset}`);
|
|
1910
|
+
} else {
|
|
1911
|
+
for (const [k, v] of Object.entries(cfg)) {
|
|
1912
|
+
console.log(` ${c.dim}${k}:${c.reset} ${v}`);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
} else if (subCmd === 'reset') {
|
|
1916
|
+
try { fs.unlinkSync(USER_CONFIG_FILE); } catch {}
|
|
1917
|
+
console.log(` ${c.green}✔${c.reset} Config reset to defaults.`);
|
|
1918
|
+
} else {
|
|
1919
|
+
console.log(` ${c.red}✖ Unknown config command${c.reset}`);
|
|
1920
|
+
console.log(` ${c.dim}Usage: agentaudit config set provider <name>${c.reset}`);
|
|
1921
|
+
console.log(` ${c.dim} agentaudit config get${c.reset}`);
|
|
1922
|
+
console.log(` ${c.dim} agentaudit config reset${c.reset}`);
|
|
1923
|
+
}
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1839
1927
|
if (command === 'lookup' || command === 'check') {
|
|
1840
1928
|
const names = targets.filter(t => !t.startsWith('--'));
|
|
1841
1929
|
if (names.length === 0) {
|
|
1842
|
-
console.log(` ${c.red}
|
|
1930
|
+
console.log(` ${c.red}✖ Package name or URL required${c.reset}`);
|
|
1931
|
+
console.log(` ${c.dim}Usage: agentaudit check <name|url>${c.reset}`);
|
|
1843
1932
|
process.exitCode = 2;
|
|
1844
1933
|
return;
|
|
1845
1934
|
}
|
|
@@ -1852,31 +1941,18 @@ async function main() {
|
|
|
1852
1941
|
console.log(JSON.stringify(results.length === 1 ? (results[0] || { error: 'not_found' }) : results, null, 2));
|
|
1853
1942
|
}
|
|
1854
1943
|
process.exitCode = 0; return;
|
|
1855
|
-
return;
|
|
1856
1944
|
}
|
|
1857
1945
|
|
|
1858
1946
|
if (command === 'scan') {
|
|
1859
|
-
const deepFlag = targets.includes('--deep');
|
|
1860
1947
|
const urls = targets.filter(t => !t.startsWith('--'));
|
|
1861
1948
|
if (urls.length === 0) {
|
|
1862
|
-
console.log(` ${c.red}
|
|
1863
|
-
console.log(`
|
|
1864
|
-
console.log(`
|
|
1949
|
+
console.log(` ${c.red}✖ Repository URL required${c.reset}`);
|
|
1950
|
+
console.log(` ${c.dim}Usage: agentaudit scan <url>${c.reset}`);
|
|
1951
|
+
console.log(` ${c.dim}Or discover local servers: ${c.cyan}agentaudit discover${c.reset}`);
|
|
1865
1952
|
process.exitCode = 2;
|
|
1866
1953
|
return;
|
|
1867
1954
|
}
|
|
1868
1955
|
|
|
1869
|
-
// --deep redirects to audit flow
|
|
1870
|
-
if (deepFlag) {
|
|
1871
|
-
let hasFindings = false;
|
|
1872
|
-
for (const url of urls) {
|
|
1873
|
-
const report = await auditRepo(url);
|
|
1874
|
-
if (report?.findings?.length > 0) hasFindings = true;
|
|
1875
|
-
}
|
|
1876
|
-
process.exitCode = hasFindings ? 1 : 0;
|
|
1877
|
-
return;
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
1956
|
const results = [];
|
|
1881
1957
|
let hadErrors = false;
|
|
1882
1958
|
for (const url of urls) {
|
|
@@ -1913,7 +1989,8 @@ async function main() {
|
|
|
1913
1989
|
if (command === 'audit') {
|
|
1914
1990
|
const urls = targets.filter(t => !t.startsWith('--'));
|
|
1915
1991
|
if (urls.length === 0) {
|
|
1916
|
-
console.log(` ${c.red}
|
|
1992
|
+
console.log(` ${c.red}✖ Repository URL required${c.reset}`);
|
|
1993
|
+
console.log(` ${c.dim}Usage: agentaudit audit <url>${c.reset}`);
|
|
1917
1994
|
process.exitCode = 2;
|
|
1918
1995
|
return;
|
|
1919
1996
|
}
|
|
@@ -1927,8 +2004,18 @@ async function main() {
|
|
|
1927
2004
|
return;
|
|
1928
2005
|
}
|
|
1929
2006
|
|
|
1930
|
-
|
|
1931
|
-
|
|
2007
|
+
// Typo correction via Levenshtein distance
|
|
2008
|
+
const knownCommands = ['discover', 'scan', 'audit', 'check', 'lookup', 'status', 'setup', 'config'];
|
|
2009
|
+
const suggestion = knownCommands
|
|
2010
|
+
.map(cmd => ({ cmd, dist: levenshtein(command, cmd) }))
|
|
2011
|
+
.filter(x => x.dist <= 3)
|
|
2012
|
+
.sort((a, b) => a.dist - b.dist)[0];
|
|
2013
|
+
|
|
2014
|
+
console.log(` ${c.red}✖ Unknown command: ${command}${c.reset}`);
|
|
2015
|
+
if (suggestion) {
|
|
2016
|
+
console.log(` ${c.dim}Did you mean: ${c.cyan}agentaudit ${suggestion.cmd}${c.reset}${c.dim}?${c.reset}`);
|
|
2017
|
+
}
|
|
2018
|
+
console.log(` ${c.dim}Run ${c.cyan}agentaudit --help${c.dim} for usage${c.reset}`);
|
|
1932
2019
|
process.exitCode = 2;
|
|
1933
2020
|
}
|
|
1934
2021
|
|