agentaudit 3.10.0 → 3.10.2
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 +496 -145
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* AgentAudit CLI — Security scanner for AI packages
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* Usage: agentaudit <command> [options]
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* discover Find MCP servers in AI editors
|
|
9
|
+
* scan <url> [url...] Quick static scan (regex)
|
|
10
|
+
* audit <url> [url...] Deep LLM-powered security audit
|
|
11
|
+
* lookup <name> Look up package in registry
|
|
12
|
+
* model [name|reset] Configure LLM provider + model
|
|
13
|
+
* setup Create account / enter API key
|
|
14
|
+
* status Show current config + auth status
|
|
15
|
+
* help [command] Show help
|
|
16
|
+
*
|
|
17
|
+
* Flags: --json, --quiet, --no-color, --no-upload, --model, --export, --debug
|
|
14
18
|
*/
|
|
15
19
|
|
|
16
20
|
import fs from 'fs';
|
|
@@ -49,6 +53,51 @@ const LLM_PROVIDERS = [
|
|
|
49
53
|
{ key: 'OPENROUTER_API_KEY', name: 'OpenRouter', provider: 'openrouter', type: 'openai', model: 'anthropic/claude-sonnet-4', url: 'https://openrouter.ai/api/v1/chat/completions' },
|
|
50
54
|
];
|
|
51
55
|
|
|
56
|
+
// ── Provider-specific model choices (for interactive menu) ──
|
|
57
|
+
const PROVIDER_MODELS = {
|
|
58
|
+
anthropic: [
|
|
59
|
+
{ label: 'claude-sonnet-4-20250514', sublabel: 'fast + smart (default)', value: 'claude-sonnet-4-20250514' },
|
|
60
|
+
{ label: 'claude-opus-4-20250514', sublabel: 'most capable', value: 'claude-opus-4-20250514' },
|
|
61
|
+
],
|
|
62
|
+
openai: [
|
|
63
|
+
{ label: 'gpt-4o', sublabel: 'fast multimodal (default)', value: 'gpt-4o' },
|
|
64
|
+
{ label: 'gpt-4.1', sublabel: 'latest', value: 'gpt-4.1' },
|
|
65
|
+
],
|
|
66
|
+
google: [
|
|
67
|
+
{ label: 'gemini-2.5-flash', sublabel: 'fast + cheap (default)', value: 'gemini-2.5-flash' },
|
|
68
|
+
{ label: 'gemini-2.5-pro', sublabel: 'most capable', value: 'gemini-2.5-pro' },
|
|
69
|
+
],
|
|
70
|
+
deepseek: [
|
|
71
|
+
{ label: 'deepseek-chat', sublabel: 'cost-effective (default)', value: 'deepseek-chat' },
|
|
72
|
+
],
|
|
73
|
+
mistral: [
|
|
74
|
+
{ label: 'mistral-large-latest', sublabel: 'EU-hosted (default)', value: 'mistral-large-latest' },
|
|
75
|
+
],
|
|
76
|
+
groq: [
|
|
77
|
+
{ label: 'llama-3.3-70b-versatile', sublabel: 'ultra-fast (default)', value: 'llama-3.3-70b-versatile' },
|
|
78
|
+
],
|
|
79
|
+
xai: [
|
|
80
|
+
{ label: 'grok-3', sublabel: 'real-time knowledge (default)', value: 'grok-3' },
|
|
81
|
+
],
|
|
82
|
+
together: [
|
|
83
|
+
{ label: 'meta-llama/Llama-3.3-70B-Instruct-Turbo', sublabel: 'open source (default)', value: 'meta-llama/Llama-3.3-70B-Instruct-Turbo' },
|
|
84
|
+
],
|
|
85
|
+
fireworks: [
|
|
86
|
+
{ label: 'accounts/fireworks/models/llama-v3p3-70b-instruct', sublabel: 'open source (default)', value: 'accounts/fireworks/models/llama-v3p3-70b-instruct' },
|
|
87
|
+
],
|
|
88
|
+
cerebras: [
|
|
89
|
+
{ label: 'llama-3.3-70b', sublabel: 'fast inference (default)', value: 'llama-3.3-70b' },
|
|
90
|
+
],
|
|
91
|
+
zhipu: [
|
|
92
|
+
{ label: 'glm-4.7', sublabel: 'Chinese language (default)', value: 'glm-4.7' },
|
|
93
|
+
],
|
|
94
|
+
openrouter: [
|
|
95
|
+
{ label: 'anthropic/claude-sonnet-4', sublabel: 'default', value: 'anthropic/claude-sonnet-4' },
|
|
96
|
+
{ label: 'qwen/qwen3-coder', sublabel: 'code specialist', value: 'qwen/qwen3-coder' },
|
|
97
|
+
{ label: 'meta-llama/Llama-3.3-70B', sublabel: 'open source', value: 'meta-llama/Llama-3.3-70B' },
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
|
|
52
101
|
// ── ANSI Colors (respects NO_COLOR and --no-color) ───────
|
|
53
102
|
|
|
54
103
|
const noColor = !!(process.env.NO_COLOR || process.argv.includes('--no-color'));
|
|
@@ -114,20 +163,23 @@ function loadLlmConfig() {
|
|
|
114
163
|
if (fs.existsSync(f)) {
|
|
115
164
|
try {
|
|
116
165
|
const data = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
117
|
-
if (data.llm_model
|
|
166
|
+
if (data.llm_model || data.preferred_provider) {
|
|
167
|
+
return { llm_model: data.llm_model || null, preferred_provider: data.preferred_provider || null };
|
|
168
|
+
}
|
|
118
169
|
} catch {}
|
|
119
170
|
}
|
|
120
171
|
}
|
|
121
172
|
return null;
|
|
122
173
|
}
|
|
123
174
|
|
|
124
|
-
function saveLlmConfig(model) {
|
|
175
|
+
function saveLlmConfig(model, provider) {
|
|
125
176
|
// Merge into existing credentials
|
|
126
177
|
let existing = {};
|
|
127
178
|
if (fs.existsSync(USER_CRED_FILE)) {
|
|
128
179
|
try { existing = JSON.parse(fs.readFileSync(USER_CRED_FILE, 'utf8')); } catch {}
|
|
129
180
|
}
|
|
130
|
-
existing.llm_model = model;
|
|
181
|
+
if (model !== undefined) existing.llm_model = model;
|
|
182
|
+
if (provider !== undefined) existing.preferred_provider = provider;
|
|
131
183
|
const json = JSON.stringify(existing, null, 2);
|
|
132
184
|
fs.mkdirSync(USER_CRED_DIR, { recursive: true });
|
|
133
185
|
fs.writeFileSync(USER_CRED_FILE, json, { mode: 0o600 });
|
|
@@ -136,12 +188,32 @@ function saveLlmConfig(model) {
|
|
|
136
188
|
if (fs.existsSync(SKILL_CRED_FILE)) {
|
|
137
189
|
try { skillExisting = JSON.parse(fs.readFileSync(SKILL_CRED_FILE, 'utf8')); } catch {}
|
|
138
190
|
}
|
|
139
|
-
skillExisting.llm_model = model;
|
|
191
|
+
if (model !== undefined) skillExisting.llm_model = model;
|
|
192
|
+
if (provider !== undefined) skillExisting.preferred_provider = provider;
|
|
140
193
|
fs.mkdirSync(path.dirname(SKILL_CRED_FILE), { recursive: true });
|
|
141
194
|
fs.writeFileSync(SKILL_CRED_FILE, JSON.stringify(skillExisting, null, 2), { mode: 0o600 });
|
|
142
195
|
} catch {}
|
|
143
196
|
}
|
|
144
197
|
|
|
198
|
+
function resolveProvider() {
|
|
199
|
+
const config = loadLlmConfig();
|
|
200
|
+
const preferred = config?.preferred_provider;
|
|
201
|
+
|
|
202
|
+
if (preferred) {
|
|
203
|
+
// Find provider by name, check if any of their keys is set
|
|
204
|
+
const match = LLM_PROVIDERS.find(p => p.provider === preferred && process.env[p.key]);
|
|
205
|
+
if (match) return match;
|
|
206
|
+
// Key missing for preferred provider — warn + fallback
|
|
207
|
+
const providerInfo = LLM_PROVIDERS.find(p => p.provider === preferred);
|
|
208
|
+
if (providerInfo && !quietMode) {
|
|
209
|
+
console.log(` ${c.yellow}Preferred provider "${providerInfo.name}" missing key (${providerInfo.key}), falling back...${c.reset}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Fallback: first match wins
|
|
214
|
+
return LLM_PROVIDERS.find(p => process.env[p.key]) || null;
|
|
215
|
+
}
|
|
216
|
+
|
|
145
217
|
function saveCredentials(data) {
|
|
146
218
|
const json = JSON.stringify(data, null, 2);
|
|
147
219
|
fs.mkdirSync(USER_CRED_DIR, { recursive: true });
|
|
@@ -398,31 +470,17 @@ async function setupCommand() {
|
|
|
398
470
|
|
|
399
471
|
console.log();
|
|
400
472
|
|
|
401
|
-
// ── LLM
|
|
473
|
+
// ── LLM configuration hint ──
|
|
402
474
|
const llmConfig = loadLlmConfig();
|
|
403
|
-
console.log(` ${c.bold}LLM
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
console.log();
|
|
409
|
-
}
|
|
410
|
-
console.log(` ${c.dim}
|
|
411
|
-
console.log(`
|
|
412
|
-
console.log(` ${c.dim}gpt-4o${c.reset} ${c.dim}(OpenAI)${c.reset}`);
|
|
413
|
-
console.log(` ${c.dim}gemini-2.5-flash${c.reset} ${c.dim}(Google)${c.reset}`);
|
|
414
|
-
console.log(` ${c.dim}qwen/qwen3.5-coder-32b${c.reset} ${c.dim}(OpenRouter)${c.reset}`);
|
|
415
|
-
console.log(` ${c.dim}deepseek-chat${c.reset} ${c.dim}(DeepSeek)${c.reset}`);
|
|
416
|
-
console.log();
|
|
417
|
-
const modelAnswer = await askQuestion(` Model ${c.dim}(Enter to keep default, or type model name)${c.reset}: `);
|
|
418
|
-
if (modelAnswer) {
|
|
419
|
-
saveLlmConfig(modelAnswer);
|
|
420
|
-
console.log(` ${icons.safe} Model set to ${c.bold}${modelAnswer}${c.reset}`);
|
|
421
|
-
} else if (llmConfig) {
|
|
422
|
-
console.log(` ${c.dim}Keeping: ${llmConfig.llm_model}${c.reset}`);
|
|
423
|
-
} else {
|
|
424
|
-
console.log(` ${c.dim}No custom model — will use provider default${c.reset}`);
|
|
425
|
-
}
|
|
475
|
+
console.log(` ${c.bold}LLM Configuration${c.reset}`);
|
|
476
|
+
if (llmConfig?.llm_model || llmConfig?.preferred_provider) {
|
|
477
|
+
const parts = [];
|
|
478
|
+
if (llmConfig.preferred_provider) parts.push(llmConfig.preferred_provider);
|
|
479
|
+
if (llmConfig.llm_model) parts.push(llmConfig.llm_model);
|
|
480
|
+
console.log(` ${icons.safe} Current: ${c.bold}${parts.join(' → ')}${c.reset}`);
|
|
481
|
+
}
|
|
482
|
+
console.log(` ${c.dim}Run ${c.cyan}agentaudit model${c.dim} to configure your LLM provider and model.${c.reset}`);
|
|
483
|
+
console.log(` ${c.dim}Deep audits require an LLM API key in your environment.${c.reset}`);
|
|
426
484
|
console.log();
|
|
427
485
|
|
|
428
486
|
console.log(` ${c.bold}Ready!${c.reset} You can now:`);
|
|
@@ -1513,8 +1571,8 @@ async function auditRepo(url) {
|
|
|
1513
1571
|
console.log(` ${c.green}done${c.reset}`);
|
|
1514
1572
|
|
|
1515
1573
|
// Step 4: LLM Analysis
|
|
1516
|
-
//
|
|
1517
|
-
const activeLlm =
|
|
1574
|
+
// Resolve provider: preferred_provider from config → first match fallback
|
|
1575
|
+
const activeLlm = resolveProvider();
|
|
1518
1576
|
const llmApiKey = activeLlm ? process.env[activeLlm.key] : null;
|
|
1519
1577
|
const activeProvider = activeLlm ? activeLlm.name : null;
|
|
1520
1578
|
|
|
@@ -1529,34 +1587,16 @@ async function auditRepo(url) {
|
|
|
1529
1587
|
}
|
|
1530
1588
|
|
|
1531
1589
|
if (!activeLlm) {
|
|
1532
|
-
// No LLM API key —
|
|
1590
|
+
// No LLM API key — compact explanation
|
|
1533
1591
|
console.log();
|
|
1534
1592
|
console.log(` ${c.yellow}No LLM API key found.${c.reset} The ${c.bold}audit${c.reset} command needs an LLM to analyze code.`);
|
|
1535
1593
|
console.log();
|
|
1536
|
-
console.log(` ${c.bold}
|
|
1537
|
-
console.log(` ${c.
|
|
1538
|
-
console.log(` ${c.cyan}OPENAI_API_KEY${c.reset} OpenAI GPT-4o`);
|
|
1539
|
-
console.log(` ${c.cyan}GEMINI_API_KEY${c.reset} Google Gemini`);
|
|
1540
|
-
console.log(` ${c.cyan}DEEPSEEK_API_KEY${c.reset} DeepSeek`);
|
|
1541
|
-
console.log(` ${c.cyan}MISTRAL_API_KEY${c.reset} Mistral`);
|
|
1542
|
-
console.log(` ${c.cyan}XAI_API_KEY${c.reset} xAI Grok`);
|
|
1543
|
-
console.log(` ${c.cyan}GROQ_API_KEY${c.reset} Groq`);
|
|
1544
|
-
console.log(` ${c.cyan}TOGETHER_API_KEY${c.reset} Together AI`);
|
|
1545
|
-
console.log(` ${c.cyan}FIREWORKS_API_KEY${c.reset} Fireworks AI`);
|
|
1546
|
-
console.log(` ${c.cyan}CEREBRAS_API_KEY${c.reset} Cerebras`);
|
|
1547
|
-
console.log(` ${c.cyan}ZAI_API_KEY${c.reset} Zhipu AI (GLM)`);
|
|
1548
|
-
console.log(` ${c.cyan}OPENROUTER_API_KEY${c.reset} OpenRouter (any model)`);
|
|
1549
|
-
console.log();
|
|
1550
|
-
console.log(` ${c.dim}export ANTHROPIC_API_KEY=sk-ant-...${c.reset}`);
|
|
1551
|
-
console.log(` ${c.dim}export OPENAI_API_KEY=sk-...${c.reset}`);
|
|
1594
|
+
console.log(` ${c.bold}Set an API key${c.reset} (e.g. ${c.cyan}export OPENROUTER_API_KEY=sk-or-...${c.reset})`);
|
|
1595
|
+
console.log(` ${c.dim}Run "agentaudit model" to configure provider + model interactively${c.reset}`);
|
|
1552
1596
|
console.log();
|
|
1553
|
-
console.log(` ${c.bold}
|
|
1554
|
-
console.log(` ${c.
|
|
1555
|
-
console.log(` ${c.dim}
|
|
1556
|
-
console.log();
|
|
1557
|
-
console.log(` ${c.bold}Option 3: Use MCP in Claude/Cursor/Windsurf (no API key needed)${c.reset}`);
|
|
1558
|
-
console.log(` ${c.dim}Add AgentAudit as MCP server — your editor's agent runs the audit using its own LLM.${c.reset}`);
|
|
1559
|
-
console.log(` ${c.dim}Config: { "mcpServers": { "agentaudit": { "command": "npx", "args": ["-y", "agentaudit"] } } }${c.reset}`);
|
|
1597
|
+
console.log(` ${c.bold}Or export for manual review:${c.reset} ${c.cyan}agentaudit audit ${url} --export${c.reset}`);
|
|
1598
|
+
console.log(` ${c.bold}Or use as MCP server${c.reset} in Cursor/Claude ${c.dim}(no extra API key needed)${c.reset}`);
|
|
1599
|
+
console.log(` ${c.dim}{ "agentaudit": { "command": "npx", "args": ["-y", "agentaudit"] } }${c.reset}`);
|
|
1560
1600
|
console.log();
|
|
1561
1601
|
|
|
1562
1602
|
// Check if --export flag
|
|
@@ -1793,9 +1833,12 @@ async function auditRepo(url) {
|
|
|
1793
1833
|
console.log();
|
|
1794
1834
|
}
|
|
1795
1835
|
|
|
1796
|
-
// Upload to registry
|
|
1797
|
-
const
|
|
1798
|
-
|
|
1836
|
+
// Upload to registry (skip with --no-upload)
|
|
1837
|
+
const noUpload = process.argv.includes('--no-upload');
|
|
1838
|
+
let creds = loadCredentials();
|
|
1839
|
+
if (noUpload) {
|
|
1840
|
+
// Skip silently
|
|
1841
|
+
} else if (creds) {
|
|
1799
1842
|
process.stdout.write(` Uploading report to registry...`);
|
|
1800
1843
|
try {
|
|
1801
1844
|
const res = await fetch(`${REGISTRY_URL}/api/reports`, {
|
|
@@ -1817,8 +1860,51 @@ async function auditRepo(url) {
|
|
|
1817
1860
|
} catch (err) {
|
|
1818
1861
|
console.log(` ${c.yellow}failed${c.reset}`);
|
|
1819
1862
|
}
|
|
1863
|
+
} else if (process.stdin.isTTY) {
|
|
1864
|
+
// No credentials — offer inline registration
|
|
1865
|
+
console.log();
|
|
1866
|
+
console.log(` ${c.bold}Share this audit with the community?${c.reset}`);
|
|
1867
|
+
console.log(` ${c.dim}Uploading helps others assess package security. Account is free.${c.reset}`);
|
|
1868
|
+
console.log();
|
|
1869
|
+
console.log(` ${c.bold}1)${c.reset} Create account + upload ${c.dim}(free)${c.reset}`);
|
|
1870
|
+
console.log(` ${c.bold}2)${c.reset} Skip`);
|
|
1871
|
+
console.log();
|
|
1872
|
+
const uploadChoice = await askQuestion(` Choice ${c.dim}(1/2)${c.reset}: `);
|
|
1873
|
+
if (uploadChoice === '1') {
|
|
1874
|
+
const name = await askQuestion(` Agent name ${c.dim}(e.g. my-scanner, claude-desktop)${c.reset}: `);
|
|
1875
|
+
if (!name || !/^[a-zA-Z0-9._-]{2,64}$/.test(name)) {
|
|
1876
|
+
console.log(` ${c.red}Invalid name. Use 2-64 chars: letters, numbers, dash, underscore, dot.${c.reset}`);
|
|
1877
|
+
} else {
|
|
1878
|
+
try {
|
|
1879
|
+
process.stdout.write(` Registering ${c.bold}${name}${c.reset}...`);
|
|
1880
|
+
const regData = await registerAgent(name);
|
|
1881
|
+
saveCredentials({ api_key: regData.api_key, agent_name: regData.agent_name });
|
|
1882
|
+
console.log(` ${c.green}done!${c.reset}`);
|
|
1883
|
+
creds = { api_key: regData.api_key, agent_name: regData.agent_name };
|
|
1884
|
+
process.stdout.write(` Uploading report...`);
|
|
1885
|
+
const res = await fetch(`${REGISTRY_URL}/api/reports`, {
|
|
1886
|
+
method: 'POST',
|
|
1887
|
+
headers: {
|
|
1888
|
+
'Authorization': `Bearer ${creds.api_key}`,
|
|
1889
|
+
'Content-Type': 'application/json',
|
|
1890
|
+
},
|
|
1891
|
+
body: JSON.stringify(report),
|
|
1892
|
+
signal: AbortSignal.timeout(15_000),
|
|
1893
|
+
});
|
|
1894
|
+
if (res.ok) {
|
|
1895
|
+
console.log(` ${c.green}done${c.reset}`);
|
|
1896
|
+
console.log(` ${c.dim}Report: ${REGISTRY_URL}/skills/${slug}${c.reset}`);
|
|
1897
|
+
} else {
|
|
1898
|
+
console.log(` ${c.yellow}failed (HTTP ${res.status})${c.reset}`);
|
|
1899
|
+
}
|
|
1900
|
+
} catch (err) {
|
|
1901
|
+
console.log(` ${c.red}failed${c.reset}`);
|
|
1902
|
+
console.log(` ${c.dim}${err.message}${c.reset}`);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1820
1906
|
} else {
|
|
1821
|
-
console.log(` ${c.dim}Run ${c.cyan}agentaudit setup${c.dim} to
|
|
1907
|
+
console.log(` ${c.dim}Run ${c.cyan}agentaudit setup${c.dim} to create an account and upload reports${c.reset}`);
|
|
1822
1908
|
}
|
|
1823
1909
|
|
|
1824
1910
|
console.log();
|
|
@@ -1871,60 +1957,196 @@ async function main() {
|
|
|
1871
1957
|
// --no-color already handled at top level for `c` object
|
|
1872
1958
|
|
|
1873
1959
|
// Strip global flags from args (including --model <value>)
|
|
1874
|
-
const globalFlags = new Set(['--json', '--quiet', '-q', '--no-color']);
|
|
1960
|
+
const globalFlags = new Set(['--json', '--quiet', '-q', '--no-color', '--no-upload']);
|
|
1875
1961
|
let args = rawArgs.filter(a => !globalFlags.has(a));
|
|
1876
1962
|
// Remove --model <value> pair
|
|
1877
1963
|
const modelIdx = args.indexOf('--model');
|
|
1878
1964
|
if (modelIdx !== -1) args.splice(modelIdx, 2);
|
|
1879
1965
|
|
|
1966
|
+
// Detect per-command --help BEFORE stripping (e.g. `agentaudit model --help`)
|
|
1967
|
+
const wantsHelp = args.includes('--help') || args.includes('-h');
|
|
1968
|
+
// Strip --help/-h from args for routing
|
|
1969
|
+
args = args.filter(a => a !== '--help' && a !== '-h');
|
|
1970
|
+
|
|
1880
1971
|
if (args[0] === '-v' || args[0] === '--version') {
|
|
1881
1972
|
console.log(`agentaudit ${getVersion()}`);
|
|
1882
1973
|
process.exitCode = 0; return;
|
|
1883
1974
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1975
|
+
|
|
1976
|
+
// Subcommand help definitions
|
|
1977
|
+
const subcommandHelp = {
|
|
1978
|
+
discover: [
|
|
1979
|
+
`${c.bold}agentaudit discover${c.reset} [options]`,
|
|
1980
|
+
``,
|
|
1981
|
+
`Find MCP servers configured in your AI editors (Cursor, Claude Desktop, VS Code, Windsurf).`,
|
|
1982
|
+
``,
|
|
1983
|
+
`${c.bold}Options:${c.reset}`,
|
|
1984
|
+
` --quick, -s Auto-scan all discovered servers (regex-based)`,
|
|
1985
|
+
` --deep, -a Interactively select servers for deep LLM audit`,
|
|
1986
|
+
``,
|
|
1987
|
+
`${c.bold}Examples:${c.reset}`,
|
|
1988
|
+
` agentaudit discover`,
|
|
1989
|
+
` agentaudit discover --quick`,
|
|
1990
|
+
` agentaudit discover --deep`,
|
|
1991
|
+
],
|
|
1992
|
+
scan: [
|
|
1993
|
+
`${c.bold}agentaudit scan${c.reset} <url> [url...] [options]`,
|
|
1994
|
+
``,
|
|
1995
|
+
`Quick regex-based static security scan (~2s). Checks for 15+ patterns`,
|
|
1996
|
+
`(command injection, eval, hardcoded secrets, path traversal, etc.)`,
|
|
1997
|
+
``,
|
|
1998
|
+
`${c.bold}Options:${c.reset}`,
|
|
1999
|
+
` --deep Run deep LLM audit instead (same as \`agentaudit audit\`)`,
|
|
2000
|
+
``,
|
|
2001
|
+
`${c.bold}Examples:${c.reset}`,
|
|
2002
|
+
` agentaudit scan https://github.com/owner/repo`,
|
|
2003
|
+
` agentaudit scan https://github.com/a/b https://github.com/c/d`,
|
|
2004
|
+
` agentaudit scan https://github.com/owner/repo --deep`,
|
|
2005
|
+
],
|
|
2006
|
+
audit: [
|
|
2007
|
+
`${c.bold}agentaudit audit${c.reset} <url> [url...] [options]`,
|
|
2008
|
+
``,
|
|
2009
|
+
`Deep LLM-powered 3-pass security audit (~30s). Requires an LLM API key.`,
|
|
2010
|
+
``,
|
|
2011
|
+
`${c.bold}Options:${c.reset}`,
|
|
2012
|
+
` --model <name> Override LLM model for this run`,
|
|
2013
|
+
` --no-upload Skip uploading report to registry`,
|
|
2014
|
+
` --export Export audit payload as markdown (for manual LLM review)`,
|
|
2015
|
+
` --debug Show raw LLM response on parse errors`,
|
|
2016
|
+
``,
|
|
2017
|
+
`${c.bold}Examples:${c.reset}`,
|
|
2018
|
+
` agentaudit audit https://github.com/owner/repo`,
|
|
2019
|
+
` agentaudit audit https://github.com/owner/repo --no-upload`,
|
|
2020
|
+
` agentaudit audit https://github.com/owner/repo --model gpt-4o`,
|
|
2021
|
+
` agentaudit audit https://github.com/owner/repo --export`,
|
|
2022
|
+
],
|
|
2023
|
+
lookup: [
|
|
2024
|
+
`${c.bold}agentaudit lookup${c.reset} <name> [options]`,
|
|
2025
|
+
``,
|
|
2026
|
+
`Look up a package in the AgentAudit registry. Shows trust score,`,
|
|
2027
|
+
`findings, confidence level, and audit history.`,
|
|
2028
|
+
``,
|
|
2029
|
+
`${c.bold}Aliases:${c.reset} lookup, check`,
|
|
2030
|
+
``,
|
|
2031
|
+
`${c.bold}Examples:${c.reset}`,
|
|
2032
|
+
` agentaudit lookup fastmcp`,
|
|
2033
|
+
` agentaudit lookup @anthropic/mcp-server-filesystem --json`,
|
|
2034
|
+
` agentaudit check fastmcp`,
|
|
2035
|
+
],
|
|
2036
|
+
check: null, // alias → lookup
|
|
2037
|
+
model: [
|
|
2038
|
+
`${c.bold}agentaudit model${c.reset} [name|reset]`,
|
|
2039
|
+
``,
|
|
2040
|
+
`Configure LLM provider and model. Interactive two-step menu when`,
|
|
2041
|
+
`called without arguments: choose provider, then choose model.`,
|
|
2042
|
+
``,
|
|
2043
|
+
`${c.bold}Usage:${c.reset}`,
|
|
2044
|
+
` agentaudit model Interactive provider + model menu`,
|
|
2045
|
+
` agentaudit model <name> Set model directly (keeps current provider)`,
|
|
2046
|
+
` agentaudit model reset Reset provider + model to defaults`,
|
|
2047
|
+
``,
|
|
2048
|
+
`${c.bold}Examples:${c.reset}`,
|
|
2049
|
+
` agentaudit model`,
|
|
2050
|
+
` agentaudit model gpt-4o`,
|
|
2051
|
+
` agentaudit model qwen/qwen3-coder`,
|
|
2052
|
+
` agentaudit model reset`,
|
|
2053
|
+
],
|
|
2054
|
+
setup: [
|
|
2055
|
+
`${c.bold}agentaudit setup${c.reset}`,
|
|
2056
|
+
``,
|
|
2057
|
+
`Create an AgentAudit account or enter an existing API key.`,
|
|
2058
|
+
`This is for uploading audit reports to the registry — not for`,
|
|
2059
|
+
`LLM provider configuration (use \`agentaudit model\` for that).`,
|
|
2060
|
+
``,
|
|
2061
|
+
`${c.bold}Examples:${c.reset}`,
|
|
2062
|
+
` agentaudit setup`,
|
|
2063
|
+
],
|
|
2064
|
+
status: [
|
|
2065
|
+
`${c.bold}agentaudit status${c.reset}`,
|
|
2066
|
+
``,
|
|
2067
|
+
`Show current configuration: active LLM provider, model, account`,
|
|
2068
|
+
`status, and which API keys are available.`,
|
|
2069
|
+
``,
|
|
2070
|
+
`${c.bold}Aliases:${c.reset} status, whoami`,
|
|
2071
|
+
``,
|
|
2072
|
+
`${c.bold}Examples:${c.reset}`,
|
|
2073
|
+
` agentaudit status`,
|
|
2074
|
+
` agentaudit status --json`,
|
|
2075
|
+
],
|
|
2076
|
+
};
|
|
2077
|
+
|
|
2078
|
+
// Show subcommand help: `agentaudit help <cmd>` or `agentaudit <cmd> --help`
|
|
2079
|
+
function showSubcommandHelp(cmd) {
|
|
2080
|
+
let helpLines = subcommandHelp[cmd];
|
|
2081
|
+
if (helpLines === null && subcommandHelp[cmd] === null) {
|
|
2082
|
+
// alias redirect
|
|
2083
|
+
const alias = cmd === 'check' ? 'lookup' : cmd;
|
|
2084
|
+
helpLines = subcommandHelp[alias];
|
|
2085
|
+
}
|
|
2086
|
+
if (!helpLines) {
|
|
2087
|
+
console.log(` ${c.red}No help available for "${cmd}"${c.reset}`);
|
|
2088
|
+
console.log(` ${c.dim}Run ${c.cyan}agentaudit help${c.dim} for available commands${c.reset}`);
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
1886
2091
|
banner();
|
|
1887
|
-
console.log(` ${
|
|
2092
|
+
for (const line of helpLines) console.log(` ${line}`);
|
|
1888
2093
|
console.log();
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
// Handle: `agentaudit <cmd> --help` (wantsHelp + has a command)
|
|
2097
|
+
if (wantsHelp && args[0] && args[0] !== '--help' && args[0] !== '-h') {
|
|
2098
|
+
const cmd = args[0];
|
|
2099
|
+
if (subcommandHelp[cmd] !== undefined) {
|
|
2100
|
+
showSubcommandHelp(cmd);
|
|
2101
|
+
process.exitCode = 0; return;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
// Handle: `agentaudit help [cmd]`
|
|
2106
|
+
if (args[0] === 'help') {
|
|
2107
|
+
const helpCmd = args[1];
|
|
2108
|
+
if (helpCmd && subcommandHelp[helpCmd] !== undefined) {
|
|
2109
|
+
showSubcommandHelp(helpCmd);
|
|
2110
|
+
process.exitCode = 0; return;
|
|
2111
|
+
}
|
|
2112
|
+
// No subcommand or unknown → show main help
|
|
2113
|
+
wantsHelp || (args[0] = '--help'); // fall through to main help
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help' || (wantsHelp && args.length === 0)) {
|
|
2117
|
+
banner();
|
|
2118
|
+
console.log(` ${c.bold}USAGE${c.reset}`);
|
|
2119
|
+
console.log(` agentaudit <command> [options]`);
|
|
1899
2120
|
console.log();
|
|
1900
|
-
console.log(` ${c.bold}
|
|
1901
|
-
console.log(` ${c.
|
|
1902
|
-
console.log(` ${c.
|
|
1903
|
-
console.log(` ${c.
|
|
1904
|
-
console.log(` ${c.
|
|
2121
|
+
console.log(` ${c.bold}SCAN & AUDIT${c.reset}`);
|
|
2122
|
+
console.log(` ${c.cyan}discover${c.reset} Find MCP servers in your AI editors`);
|
|
2123
|
+
console.log(` ${c.cyan}scan${c.reset} <url> [url...] Quick static scan (regex, ~2s)`);
|
|
2124
|
+
console.log(` ${c.cyan}audit${c.reset} <url> [url...] Deep LLM-powered security audit (~30s)`);
|
|
2125
|
+
console.log(` ${c.cyan}lookup${c.reset} <name> Look up package in registry`);
|
|
1905
2126
|
console.log();
|
|
1906
|
-
console.log(` ${c.bold}
|
|
1907
|
-
console.log(` ${c.
|
|
1908
|
-
console.log(` ${c.
|
|
2127
|
+
console.log(` ${c.bold}CONFIGURATION${c.reset}`);
|
|
2128
|
+
console.log(` ${c.cyan}model${c.reset} Configure LLM provider + model`);
|
|
2129
|
+
console.log(` ${c.cyan}setup${c.reset} Create account / enter API key`);
|
|
2130
|
+
console.log(` ${c.cyan}status${c.reset} Show current config + auth status`);
|
|
1909
2131
|
console.log();
|
|
1910
|
-
console.log(` ${c.bold}
|
|
1911
|
-
console.log(` ${c.dim}
|
|
2132
|
+
console.log(` ${c.bold}FLAGS${c.reset}`);
|
|
2133
|
+
console.log(` ${c.dim}--json Machine-readable JSON output${c.reset}`);
|
|
2134
|
+
console.log(` ${c.dim}--quiet Suppress banner${c.reset}`);
|
|
2135
|
+
console.log(` ${c.dim}--no-color Disable ANSI colors (also: NO_COLOR env)${c.reset}`);
|
|
2136
|
+
console.log(` ${c.dim}--model <name> Override LLM model for this run${c.reset}`);
|
|
2137
|
+
console.log(` ${c.dim}--no-upload Skip uploading report to registry${c.reset}`);
|
|
2138
|
+
console.log(` ${c.dim}--export Export audit payload as markdown${c.reset}`);
|
|
2139
|
+
console.log(` ${c.dim}--debug Show raw LLM response on parse errors${c.reset}`);
|
|
1912
2140
|
console.log();
|
|
1913
|
-
console.log(` ${c.bold}
|
|
1914
|
-
console.log(` agentaudit`);
|
|
2141
|
+
console.log(` ${c.bold}EXAMPLES${c.reset}`);
|
|
1915
2142
|
console.log(` agentaudit discover --quick`);
|
|
1916
2143
|
console.log(` agentaudit scan https://github.com/owner/repo`);
|
|
1917
2144
|
console.log(` agentaudit audit https://github.com/owner/repo`);
|
|
1918
2145
|
console.log(` agentaudit lookup fastmcp --json`);
|
|
1919
2146
|
console.log();
|
|
1920
|
-
console.log(` ${c.bold}
|
|
1921
|
-
console.log(` ${c.dim}
|
|
1922
|
-
console.log(` ${c.dim}
|
|
1923
|
-
console.log(` ${c.dim}FIREWORKS_API_KEY, CEREBRAS_API_KEY, ZAI_API_KEY, or OPENROUTER_API_KEY${c.reset}`);
|
|
1924
|
-
console.log();
|
|
1925
|
-
console.log(` ${c.bold}Or use as MCP server${c.reset} in Cursor/Claude ${c.dim}(no extra API key needed):${c.reset}`);
|
|
1926
|
-
console.log(` ${c.dim}Add to your MCP config:${c.reset}`);
|
|
1927
|
-
console.log(` ${c.dim}{ "agentaudit": { "command": "npx", "args": ["-y", "agentaudit"] } }${c.reset}`);
|
|
2147
|
+
console.log(` ${c.bold}LEARN MORE${c.reset}`);
|
|
2148
|
+
console.log(` ${c.dim}agentaudit help <command>${c.reset} Help for a specific command`);
|
|
2149
|
+
console.log(` ${c.dim}https://agentaudit.dev${c.reset} Documentation`);
|
|
1928
2150
|
console.log();
|
|
1929
2151
|
process.exitCode = 0; return;
|
|
1930
2152
|
}
|
|
@@ -1940,25 +2162,98 @@ async function main() {
|
|
|
1940
2162
|
return;
|
|
1941
2163
|
}
|
|
1942
2164
|
|
|
2165
|
+
if (command === 'status' || command === 'whoami') {
|
|
2166
|
+
// ── Status / diagnostic overview ──
|
|
2167
|
+
const config = loadLlmConfig();
|
|
2168
|
+
const creds = loadCredentials();
|
|
2169
|
+
const resolved = resolveProvider();
|
|
2170
|
+
|
|
2171
|
+
console.log(` ${c.bold}Status${c.reset}`);
|
|
2172
|
+
console.log();
|
|
2173
|
+
|
|
2174
|
+
// Provider + Model
|
|
2175
|
+
const prefProvider = config?.preferred_provider;
|
|
2176
|
+
const prefModel = config?.llm_model;
|
|
2177
|
+
if (prefProvider && resolved) {
|
|
2178
|
+
console.log(` Provider ${c.bold}${resolved.name}${c.reset} ${c.dim}(${resolved.key})${c.reset} ${c.green}✔${c.reset}`);
|
|
2179
|
+
} else if (resolved) {
|
|
2180
|
+
console.log(` Provider ${c.bold}${resolved.name}${c.reset} ${c.dim}(auto-detected via ${resolved.key})${c.reset} ${c.green}✔${c.reset}`);
|
|
2181
|
+
} else {
|
|
2182
|
+
console.log(` Provider ${c.yellow}none${c.reset} ${c.dim}— set an API key or run ${c.cyan}agentaudit model${c.dim}${c.reset}`);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
if (prefModel) {
|
|
2186
|
+
console.log(` Model ${c.bold}${prefModel}${c.reset} ${c.dim}(custom)${c.reset}`);
|
|
2187
|
+
} else if (resolved) {
|
|
2188
|
+
console.log(` Model ${c.dim}${resolved.model} (provider default)${c.reset}`);
|
|
2189
|
+
} else {
|
|
2190
|
+
console.log(` Model ${c.dim}—${c.reset}`);
|
|
2191
|
+
}
|
|
2192
|
+
console.log();
|
|
2193
|
+
|
|
2194
|
+
// Account / auth
|
|
2195
|
+
if (creds) {
|
|
2196
|
+
console.log(` Account ${c.bold}${creds.agent_name}${c.reset} ${c.green}✔ logged in${c.reset}`);
|
|
2197
|
+
console.log(` ${c.dim} Key: ${creds.api_key.slice(0, 12)}...${c.reset}`);
|
|
2198
|
+
} else {
|
|
2199
|
+
console.log(` Account ${c.yellow}not configured${c.reset} ${c.dim}— run ${c.cyan}agentaudit setup${c.dim} to create one${c.reset}`);
|
|
2200
|
+
}
|
|
2201
|
+
console.log();
|
|
2202
|
+
|
|
2203
|
+
// All provider keys
|
|
2204
|
+
const seen = new Set();
|
|
2205
|
+
const keyLines = [];
|
|
2206
|
+
for (const p of LLM_PROVIDERS) {
|
|
2207
|
+
if (seen.has(p.provider)) continue;
|
|
2208
|
+
seen.add(p.provider);
|
|
2209
|
+
const keys = LLM_PROVIDERS.filter(x => x.provider === p.provider);
|
|
2210
|
+
const hasKey = keys.some(x => process.env[x.key]);
|
|
2211
|
+
keyLines.push({ name: p.name, key: p.key, hasKey });
|
|
2212
|
+
}
|
|
2213
|
+
console.log(` ${c.bold}API Keys${c.reset}`);
|
|
2214
|
+
for (const k of keyLines) {
|
|
2215
|
+
const icon = k.hasKey ? `${c.green}✔${c.reset}` : `${c.dim}✗${c.reset}`;
|
|
2216
|
+
const label = k.hasKey ? k.name : `${c.dim}${k.name}${c.reset}`;
|
|
2217
|
+
console.log(` ${icon} ${label} ${c.dim}${k.key}${c.reset}`);
|
|
2218
|
+
}
|
|
2219
|
+
console.log();
|
|
2220
|
+
|
|
2221
|
+
// JSON mode
|
|
2222
|
+
if (jsonMode) {
|
|
2223
|
+
const status = {
|
|
2224
|
+
version: getVersion(),
|
|
2225
|
+
provider: resolved ? { name: resolved.name, provider: resolved.provider, key: resolved.key, model: prefModel || resolved.model } : null,
|
|
2226
|
+
account: creds ? { agent_name: creds.agent_name, has_key: true } : null,
|
|
2227
|
+
api_keys: keyLines.reduce((acc, k) => { acc[k.key] = k.hasKey; return acc; }, {}),
|
|
2228
|
+
};
|
|
2229
|
+
console.log(JSON.stringify(status, null, 2));
|
|
2230
|
+
}
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
1943
2234
|
if (command === 'model') {
|
|
1944
2235
|
const newModel = targets.filter(t => !t.startsWith('--'))[0];
|
|
1945
|
-
const
|
|
2236
|
+
const config = loadLlmConfig();
|
|
2237
|
+
const current = config?.llm_model;
|
|
2238
|
+
const currentProvider = config?.preferred_provider;
|
|
1946
2239
|
|
|
1947
|
-
// Direct set: agentaudit model
|
|
2240
|
+
// Direct set: agentaudit model reset
|
|
1948
2241
|
if (newModel === 'reset' || newModel === 'clear') {
|
|
1949
2242
|
for (const f of [USER_CRED_FILE, SKILL_CRED_FILE]) {
|
|
1950
2243
|
if (fs.existsSync(f)) {
|
|
1951
2244
|
try {
|
|
1952
2245
|
const data = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
1953
2246
|
delete data.llm_model;
|
|
2247
|
+
delete data.preferred_provider;
|
|
1954
2248
|
fs.writeFileSync(f, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
1955
2249
|
} catch {}
|
|
1956
2250
|
}
|
|
1957
2251
|
}
|
|
1958
|
-
console.log(` ${icons.safe} Model reset to
|
|
2252
|
+
console.log(` ${icons.safe} Model + provider reset to defaults`);
|
|
1959
2253
|
return;
|
|
1960
2254
|
}
|
|
1961
2255
|
|
|
2256
|
+
// Direct set: agentaudit model <name> (sets model only, keeps provider)
|
|
1962
2257
|
if (newModel) {
|
|
1963
2258
|
saveLlmConfig(newModel);
|
|
1964
2259
|
console.log(` ${icons.safe} Model set to ${c.bold}${newModel}${c.reset}`);
|
|
@@ -1966,82 +2261,136 @@ async function main() {
|
|
|
1966
2261
|
return;
|
|
1967
2262
|
}
|
|
1968
2263
|
|
|
1969
|
-
// No argument — interactive menu
|
|
1970
|
-
|
|
1971
|
-
|
|
2264
|
+
// ── No argument — interactive two-step menu ──
|
|
2265
|
+
|
|
2266
|
+
// Build deduplicated provider list
|
|
2267
|
+
const seen = new Set();
|
|
2268
|
+
const providerList = [];
|
|
2269
|
+
for (const p of LLM_PROVIDERS) {
|
|
2270
|
+
if (seen.has(p.provider)) continue;
|
|
2271
|
+
seen.add(p.provider);
|
|
2272
|
+
// Check if ANY key for this provider is set
|
|
2273
|
+
const keys = LLM_PROVIDERS.filter(x => x.provider === p.provider);
|
|
2274
|
+
const hasKey = keys.some(x => process.env[x.key]);
|
|
2275
|
+
const keyName = p.key;
|
|
2276
|
+
providerList.push({
|
|
2277
|
+
label: p.name,
|
|
2278
|
+
sublabel: hasKey
|
|
2279
|
+
? `${keyName} ${c.green}✔${c.reset}`
|
|
2280
|
+
: `${keyName} ${c.red}✗${c.reset} ${c.dim}set: export ${keyName}=...${c.reset}`,
|
|
2281
|
+
value: p.provider,
|
|
2282
|
+
hasKey,
|
|
2283
|
+
keyName,
|
|
2284
|
+
});
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
// Show status
|
|
2288
|
+
const resolved = resolveProvider();
|
|
2289
|
+
console.log(` ${c.bold}LLM Configuration${c.reset}`);
|
|
1972
2290
|
console.log();
|
|
1973
|
-
if (current) {
|
|
1974
|
-
|
|
2291
|
+
if (currentProvider && current) {
|
|
2292
|
+
const provInfo = LLM_PROVIDERS.find(p => p.provider === currentProvider);
|
|
2293
|
+
console.log(` Active: ${c.bold}${c.cyan}${provInfo?.name || currentProvider} → ${current}${c.reset}`);
|
|
2294
|
+
} else if (current) {
|
|
2295
|
+
console.log(` Active: ${c.bold}${c.cyan}${resolved?.name || 'auto'} → ${current}${c.reset}`);
|
|
2296
|
+
} else if (resolved) {
|
|
2297
|
+
console.log(` Active: ${c.dim}${resolved.name} → ${resolved.model} (defaults)${c.reset}`);
|
|
1975
2298
|
} else {
|
|
1976
|
-
console.log(`
|
|
1977
|
-
}
|
|
1978
|
-
if (activeLlm) {
|
|
1979
|
-
console.log(` Provider: ${c.dim}${activeLlm.name} (${activeLlm.key})${c.reset}`);
|
|
2299
|
+
console.log(` Active: ${c.yellow}no provider configured${c.reset}`);
|
|
1980
2300
|
}
|
|
1981
2301
|
console.log();
|
|
1982
2302
|
|
|
1983
|
-
//
|
|
2303
|
+
// Step A: Provider selection
|
|
2304
|
+
const providerChoices = [
|
|
2305
|
+
...providerList,
|
|
2306
|
+
{ label: '(keep current)', sublabel: '', value: '__keep__', hasKey: true },
|
|
2307
|
+
];
|
|
2308
|
+
|
|
2309
|
+
const selectedProvider = await singleSelect(providerChoices, {
|
|
2310
|
+
title: 'Choose provider',
|
|
2311
|
+
hint: '↑↓ move Enter select Esc cancel',
|
|
2312
|
+
});
|
|
2313
|
+
|
|
2314
|
+
if (selectedProvider === null) {
|
|
2315
|
+
console.log(` ${c.dim}Cancelled${c.reset}`);
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
if (selectedProvider === '__keep__') {
|
|
2320
|
+
console.log(` ${c.dim}Keeping current config${c.reset}`);
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
// Check if provider has key set
|
|
2325
|
+
const chosenEntry = providerList.find(p => p.value === selectedProvider);
|
|
2326
|
+
if (chosenEntry && !chosenEntry.hasKey) {
|
|
2327
|
+
console.log();
|
|
2328
|
+
console.log(` ${c.yellow}${chosenEntry.keyName} is not set.${c.reset}`);
|
|
2329
|
+
console.log(` ${c.dim}Run: export ${chosenEntry.keyName}=your-key-here${c.reset}`);
|
|
2330
|
+
console.log(` ${c.dim}Then run "agentaudit model" again.${c.reset}`);
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
// Step B: Model selection for chosen provider
|
|
2335
|
+
console.log();
|
|
2336
|
+
const providerModels = PROVIDER_MODELS[selectedProvider] || [];
|
|
1984
2337
|
const modelChoices = [
|
|
1985
|
-
|
|
1986
|
-
{ label: '
|
|
1987
|
-
{ label: '
|
|
1988
|
-
{ label: 'gpt-4.1', sublabel: 'OpenAI — latest', value: 'gpt-4.1' },
|
|
1989
|
-
{ label: 'gemini-2.5-flash', sublabel: 'Google — fast + cheap', value: 'gemini-2.5-flash' },
|
|
1990
|
-
{ label: 'gemini-2.5-pro', sublabel: 'Google — most capable', value: 'gemini-2.5-pro' },
|
|
1991
|
-
{ label: 'deepseek-chat', sublabel: 'DeepSeek — cost-effective', value: 'deepseek-chat' },
|
|
1992
|
-
{ label: 'qwen/qwen3-coder', sublabel: 'OpenRouter — code specialist', value: 'qwen/qwen3-coder' },
|
|
1993
|
-
{ label: 'mistral-large-latest', sublabel: 'Mistral — EU-hosted', value: 'mistral-large-latest' },
|
|
1994
|
-
{ label: 'grok-3', sublabel: 'xAI — real-time knowledge', value: 'grok-3' },
|
|
1995
|
-
{ label: 'meta-llama/Llama-3.3-70B-Instruct-Turbo', sublabel: 'Together AI — open source', value: 'meta-llama/Llama-3.3-70B-Instruct-Turbo' },
|
|
1996
|
-
{ label: 'llama-3.3-70b-versatile', sublabel: 'Groq — ultra-fast inference', value: 'llama-3.3-70b-versatile' },
|
|
1997
|
-
{ label: '(reset to provider default)', sublabel: '', value: '__reset__' },
|
|
1998
|
-
{ label: '(enter custom model name)', sublabel: '', value: '__custom__' },
|
|
2338
|
+
...providerModels,
|
|
2339
|
+
{ label: '(enter custom model name)', sublabel: '', value: '__custom__' },
|
|
2340
|
+
{ label: '(reset to provider default)', sublabel: '', value: '__reset__' },
|
|
1999
2341
|
];
|
|
2000
2342
|
|
|
2001
|
-
const
|
|
2002
|
-
|
|
2003
|
-
|
|
2343
|
+
const providerName = LLM_PROVIDERS.find(p => p.provider === selectedProvider)?.name || selectedProvider;
|
|
2344
|
+
const selectedModel = await singleSelect(modelChoices, {
|
|
2345
|
+
title: `Choose model for ${providerName}`,
|
|
2346
|
+
hint: '↑↓ move Enter select Esc cancel',
|
|
2004
2347
|
});
|
|
2005
2348
|
|
|
2006
|
-
if (
|
|
2349
|
+
if (selectedModel === null) {
|
|
2007
2350
|
console.log(` ${c.dim}Cancelled${c.reset}`);
|
|
2008
2351
|
return;
|
|
2009
2352
|
}
|
|
2010
2353
|
|
|
2011
|
-
if (
|
|
2354
|
+
if (selectedModel === '__reset__') {
|
|
2355
|
+
// Save provider, clear model (use provider default)
|
|
2356
|
+
saveLlmConfig(undefined, selectedProvider);
|
|
2357
|
+
// Also clear llm_model from both files
|
|
2012
2358
|
for (const f of [USER_CRED_FILE, SKILL_CRED_FILE]) {
|
|
2013
2359
|
if (fs.existsSync(f)) {
|
|
2014
2360
|
try {
|
|
2015
2361
|
const data = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
2016
2362
|
delete data.llm_model;
|
|
2363
|
+
data.preferred_provider = selectedProvider;
|
|
2017
2364
|
fs.writeFileSync(f, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
2018
2365
|
} catch {}
|
|
2019
2366
|
}
|
|
2020
2367
|
}
|
|
2368
|
+
const defaultModel = LLM_PROVIDERS.find(p => p.provider === selectedProvider)?.model;
|
|
2021
2369
|
console.log();
|
|
2022
|
-
console.log(` ${icons.safe}
|
|
2370
|
+
console.log(` ${icons.safe} Provider: ${c.bold}${providerName}${c.reset}, model: ${c.dim}${defaultModel} (default)${c.reset}`);
|
|
2023
2371
|
return;
|
|
2024
2372
|
}
|
|
2025
2373
|
|
|
2026
|
-
if (
|
|
2374
|
+
if (selectedModel === '__custom__') {
|
|
2027
2375
|
console.log();
|
|
2028
2376
|
const custom = await askQuestion(` Model name: `);
|
|
2029
2377
|
if (!custom) {
|
|
2030
2378
|
console.log(` ${c.dim}Cancelled${c.reset}`);
|
|
2031
2379
|
return;
|
|
2032
2380
|
}
|
|
2033
|
-
saveLlmConfig(custom);
|
|
2034
|
-
console.log(` ${icons.safe}
|
|
2035
|
-
if (current) console.log(` ${c.dim}Was: ${current}${c.reset}`);
|
|
2381
|
+
saveLlmConfig(custom, selectedProvider);
|
|
2382
|
+
console.log(` ${icons.safe} Provider: ${c.bold}${providerName}${c.reset}, model: ${c.bold}${custom}${c.reset}`);
|
|
2383
|
+
if (current) console.log(` ${c.dim}Was: ${currentProvider || 'auto'} → ${current}${c.reset}`);
|
|
2036
2384
|
return;
|
|
2037
2385
|
}
|
|
2038
2386
|
|
|
2039
|
-
|
|
2387
|
+
// Normal model selection
|
|
2388
|
+
saveLlmConfig(selectedModel, selectedProvider);
|
|
2040
2389
|
console.log();
|
|
2041
|
-
console.log(` ${icons.safe}
|
|
2042
|
-
if (current) console.log(` ${c.dim}Was: ${current}${c.reset}`);
|
|
2390
|
+
console.log(` ${icons.safe} Provider: ${c.bold}${providerName}${c.reset}, model: ${c.bold}${selectedModel}${c.reset}`);
|
|
2391
|
+
if (current) console.log(` ${c.dim}Was: ${currentProvider || 'auto'} → ${current}${c.reset}`);
|
|
2043
2392
|
console.log();
|
|
2044
|
-
console.log(` ${c.dim}Tip: agentaudit model <name> to set directly, or agentaudit model reset${c.reset}`);
|
|
2393
|
+
console.log(` ${c.dim}Tip: agentaudit model <name> to set model directly, or agentaudit model reset${c.reset}`);
|
|
2045
2394
|
return;
|
|
2046
2395
|
}
|
|
2047
2396
|
|
|
@@ -2056,6 +2405,7 @@ async function main() {
|
|
|
2056
2405
|
const names = targets.filter(t => !t.startsWith('--'));
|
|
2057
2406
|
if (names.length === 0) {
|
|
2058
2407
|
console.log(` ${c.red}Error: package name required${c.reset}`);
|
|
2408
|
+
console.log(` ${c.dim}Usage: ${c.cyan}agentaudit lookup <name>${c.dim} — e.g. agentaudit lookup fastmcp${c.reset}`);
|
|
2059
2409
|
process.exitCode = 2;
|
|
2060
2410
|
return;
|
|
2061
2411
|
}
|
|
@@ -2068,7 +2418,6 @@ async function main() {
|
|
|
2068
2418
|
console.log(JSON.stringify(results.length === 1 ? (results[0] || { error: 'not_found' }) : results, null, 2));
|
|
2069
2419
|
}
|
|
2070
2420
|
process.exitCode = 0; return;
|
|
2071
|
-
return;
|
|
2072
2421
|
}
|
|
2073
2422
|
|
|
2074
2423
|
if (command === 'scan') {
|
|
@@ -2130,6 +2479,8 @@ async function main() {
|
|
|
2130
2479
|
const urls = targets.filter(t => !t.startsWith('--'));
|
|
2131
2480
|
if (urls.length === 0) {
|
|
2132
2481
|
console.log(` ${c.red}Error: at least one repository URL required${c.reset}`);
|
|
2482
|
+
console.log(` ${c.dim}Usage: ${c.cyan}agentaudit audit <url>${c.dim} — e.g. agentaudit audit https://github.com/owner/repo${c.reset}`);
|
|
2483
|
+
console.log(` ${c.dim}Tip: use ${c.cyan}agentaudit discover --deep${c.dim} to find & audit locally installed MCP servers${c.reset}`);
|
|
2133
2484
|
process.exitCode = 2;
|
|
2134
2485
|
return;
|
|
2135
2486
|
}
|
|
@@ -2144,7 +2495,7 @@ async function main() {
|
|
|
2144
2495
|
}
|
|
2145
2496
|
|
|
2146
2497
|
console.log(` ${c.red}Unknown command: ${command}${c.reset}`);
|
|
2147
|
-
console.log(` ${c.dim}Run agentaudit
|
|
2498
|
+
console.log(` ${c.dim}Run ${c.cyan}agentaudit help${c.dim} for available commands${c.reset}`);
|
|
2148
2499
|
process.exitCode = 2;
|
|
2149
2500
|
}
|
|
2150
2501
|
|