agentaudit 3.9.21 → 3.9.23
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 +148 -10
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -39,7 +39,7 @@ function resolveProvider(flagOverride, keys) {
|
|
|
39
39
|
// Aliases
|
|
40
40
|
const aliases = { claude: 'anthropic', gpt: 'openai', 'gpt-4o': 'openai', 'gpt4': 'openai', or: 'openrouter', local: 'ollama' };
|
|
41
41
|
|
|
42
|
-
// Priority: --provider flag > AGENTAUDIT_PROVIDER env > config file > auto-detect
|
|
42
|
+
// Priority: --provider flag > AGENTAUDIT_PROVIDER env > config file > model-inferred > auto-detect
|
|
43
43
|
const preferred = flagOverride
|
|
44
44
|
|| process.env.AGENTAUDIT_PROVIDER?.toLowerCase()
|
|
45
45
|
|| loadConfig()?.preferred_provider
|
|
@@ -51,6 +51,14 @@ function resolveProvider(flagOverride, keys) {
|
|
|
51
51
|
if (!p) return null;
|
|
52
52
|
return p;
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
// Smart inference: if model override looks like "provider/model" (e.g. z-ai/glm-5, anthropic/claude-sonnet-4)
|
|
56
|
+
// → auto-select OpenRouter (which uses that format)
|
|
57
|
+
const activeModel = globalModelOverride || process.env.AGENTAUDIT_MODEL || loadConfig()?.preferred_model;
|
|
58
|
+
if (activeModel && activeModel.includes('/') && providers.openrouter) {
|
|
59
|
+
return providers.openrouter;
|
|
60
|
+
}
|
|
61
|
+
|
|
54
62
|
// Auto-detect priority: Anthropic > OpenAI > OpenRouter > Custom > Ollama (local last — usually weaker)
|
|
55
63
|
return providers.anthropic || providers.openai || providers.openrouter || providers.custom || providers.ollama || null;
|
|
56
64
|
}
|
|
@@ -59,6 +67,7 @@ function resolveProvider(flagOverride, keys) {
|
|
|
59
67
|
let jsonMode = false;
|
|
60
68
|
let quietMode = false;
|
|
61
69
|
let modelOverride = null; // --model flag or AGENTAUDIT_MODEL env or config
|
|
70
|
+
let globalModelOverride = null; // same, but set early for resolveProvider
|
|
62
71
|
|
|
63
72
|
// ── ANSI Colors (respects NO_COLOR and --no-color) ───────
|
|
64
73
|
|
|
@@ -1457,8 +1466,22 @@ async function auditRepo(url) {
|
|
|
1457
1466
|
return null;
|
|
1458
1467
|
}
|
|
1459
1468
|
|
|
1469
|
+
// Determine actual model name for display
|
|
1470
|
+
let actualModel;
|
|
1471
|
+
if (resolvedProvider.id === 'anthropic') {
|
|
1472
|
+
actualModel = modelOverride || 'claude-sonnet-4-20250514';
|
|
1473
|
+
} else if (resolvedProvider.id === 'openrouter') {
|
|
1474
|
+
actualModel = modelOverride || process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4';
|
|
1475
|
+
} else if (resolvedProvider.id === 'openai') {
|
|
1476
|
+
actualModel = modelOverride || 'gpt-4o';
|
|
1477
|
+
} else if (resolvedProvider.id === 'ollama') {
|
|
1478
|
+
actualModel = modelOverride || resolvedProvider.model;
|
|
1479
|
+
} else {
|
|
1480
|
+
actualModel = modelOverride || resolvedProvider.model || 'unknown';
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1460
1483
|
// We have an API key — run LLM audit
|
|
1461
|
-
process.stdout.write(` ${c.dim}[4/4]${c.reset} Running LLM analysis ${c.dim}(${
|
|
1484
|
+
process.stdout.write(` ${c.dim}[4/4]${c.reset} Running LLM analysis ${c.dim}(${resolvedProvider.id}: ${actualModel})${c.reset}...`);
|
|
1462
1485
|
|
|
1463
1486
|
const systemPrompt = auditPrompt || 'You are a security auditor. Analyze the code and report findings as JSON.';
|
|
1464
1487
|
const userMessage = [
|
|
@@ -1579,7 +1602,11 @@ async function auditRepo(url) {
|
|
|
1579
1602
|
// Display results
|
|
1580
1603
|
console.log();
|
|
1581
1604
|
const riskScore = report.risk_score || 0;
|
|
1582
|
-
|
|
1605
|
+
const trustScore = 100 - riskScore;
|
|
1606
|
+
const trustColor = trustScore >= 70 ? c.green : trustScore >= 40 ? c.yellow : c.red;
|
|
1607
|
+
const trustLabel = trustScore >= 70 ? 'SAFE' : trustScore >= 40 ? 'CAUTION' : 'UNSAFE';
|
|
1608
|
+
console.log(` ${trustColor}${c.bold}${trustLabel}${c.reset} ${trustColor}Trust Score: ${trustScore}/100${c.reset} ${c.dim}(Risk: ${riskScore}/100)${c.reset}`);
|
|
1609
|
+
console.log(` ${c.dim}Model: ${resolvedProvider.id}/${actualModel} Duration: ${elapsed(start)}${c.reset}`);
|
|
1583
1610
|
console.log();
|
|
1584
1611
|
|
|
1585
1612
|
if (report.findings && report.findings.length > 0) {
|
|
@@ -1608,7 +1635,7 @@ async function auditRepo(url) {
|
|
|
1608
1635
|
'Authorization': `Bearer ${creds.api_key}`,
|
|
1609
1636
|
'Content-Type': 'application/json',
|
|
1610
1637
|
},
|
|
1611
|
-
body: JSON.stringify(report),
|
|
1638
|
+
body: JSON.stringify({ ...report, audit_model: actualModel, audit_provider: resolvedProvider.id }),
|
|
1612
1639
|
signal: AbortSignal.timeout(15_000),
|
|
1613
1640
|
});
|
|
1614
1641
|
if (res.ok) {
|
|
@@ -1631,7 +1658,7 @@ async function auditRepo(url) {
|
|
|
1631
1658
|
|
|
1632
1659
|
// ── Check command ───────────────────────────────────────
|
|
1633
1660
|
|
|
1634
|
-
async function checkPackage(name) {
|
|
1661
|
+
async function checkPackage(name, { autoAudit = false } = {}) {
|
|
1635
1662
|
if (!jsonMode) {
|
|
1636
1663
|
console.log(`${icons.info} Looking up ${c.bold}${name}${c.reset} in registry...`);
|
|
1637
1664
|
console.log();
|
|
@@ -1640,8 +1667,8 @@ async function checkPackage(name) {
|
|
|
1640
1667
|
const data = await checkRegistry(name);
|
|
1641
1668
|
if (!data) {
|
|
1642
1669
|
if (!jsonMode) {
|
|
1643
|
-
//
|
|
1644
|
-
if (name.includes('github.com') || name.includes('://')) {
|
|
1670
|
+
// Auto-audit: only when called from 'check' command AND input looks like a URL
|
|
1671
|
+
if (autoAudit && (name.includes('github.com') || name.includes('://'))) {
|
|
1645
1672
|
console.log(` ${c.yellow}Not found in registry.${c.reset}`);
|
|
1646
1673
|
console.log(` ${c.dim}Starting audit for ${name}...${c.reset}`);
|
|
1647
1674
|
console.log();
|
|
@@ -1650,7 +1677,8 @@ async function checkPackage(name) {
|
|
|
1650
1677
|
console.log(` ${c.yellow}✖ Not found${c.reset} — "${name}" hasn't been audited yet.`);
|
|
1651
1678
|
console.log();
|
|
1652
1679
|
console.log(` ${c.dim}Next steps:${c.reset}`);
|
|
1653
|
-
console.log(` ${c.cyan}agentaudit
|
|
1680
|
+
console.log(` ${c.cyan}agentaudit check <repo-url>${c.reset} ${c.dim}Auto-lookup + audit if not found${c.reset}`);
|
|
1681
|
+
console.log(` ${c.cyan}agentaudit audit <repo-url>${c.reset} ${c.dim}Deep LLM audit${c.reset}`);
|
|
1654
1682
|
console.log(` ${c.cyan}agentaudit scan <repo-url>${c.reset} ${c.dim}Quick static check (no API key)${c.reset}`);
|
|
1655
1683
|
}
|
|
1656
1684
|
return null;
|
|
@@ -1740,6 +1768,7 @@ async function main() {
|
|
|
1740
1768
|
|| process.env.AGENTAUDIT_MODEL
|
|
1741
1769
|
|| loadConfig()?.preferred_model
|
|
1742
1770
|
|| null;
|
|
1771
|
+
globalModelOverride = modelOverride;
|
|
1743
1772
|
|
|
1744
1773
|
// Strip global flags from args
|
|
1745
1774
|
const globalFlags = new Set(['--json', '--quiet', '-q', '--no-color']);
|
|
@@ -1774,6 +1803,7 @@ async function main() {
|
|
|
1774
1803
|
console.log(` ${c.bold}SETUP${c.reset}`);
|
|
1775
1804
|
console.log(` ${c.cyan}status${c.reset} Check providers & API keys`);
|
|
1776
1805
|
console.log(` ${c.cyan}setup${c.reset} Register & configure`);
|
|
1806
|
+
console.log(` ${c.cyan}models${c.reset} List available LLM models`);
|
|
1777
1807
|
console.log(` ${c.cyan}config set${c.reset} <key> <value> Set default provider/options`);
|
|
1778
1808
|
console.log();
|
|
1779
1809
|
console.log(` ${c.bold}OPTIONS${c.reset}`);
|
|
@@ -1910,6 +1940,113 @@ async function main() {
|
|
|
1910
1940
|
return;
|
|
1911
1941
|
}
|
|
1912
1942
|
|
|
1943
|
+
if (command === 'models') {
|
|
1944
|
+
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
1945
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
1946
|
+
const openrouterKey = process.env.OPENROUTER_API_KEY;
|
|
1947
|
+
|
|
1948
|
+
console.log(` ${c.bold}Available models by provider:${c.reset}`);
|
|
1949
|
+
console.log();
|
|
1950
|
+
|
|
1951
|
+
// Static lists for Anthropic (no list API)
|
|
1952
|
+
console.log(` ${c.bold}Anthropic${c.reset}${anthropicKey ? ` ${c.green}(configured)${c.reset}` : ` ${c.dim}(not configured)${c.reset}`}`);
|
|
1953
|
+
console.log(` ${c.dim}claude-sonnet-4-20250514${c.reset} ${c.dim}(default)${c.reset}`);
|
|
1954
|
+
console.log(` ${c.dim}claude-opus-4-20250514${c.reset}`);
|
|
1955
|
+
console.log(` ${c.dim}claude-haiku-3-20250514${c.reset}`);
|
|
1956
|
+
console.log();
|
|
1957
|
+
|
|
1958
|
+
// Static list for OpenAI
|
|
1959
|
+
console.log(` ${c.bold}OpenAI${c.reset}${openaiKey ? ` ${c.green}(configured)${c.reset}` : ` ${c.dim}(not configured)${c.reset}`}`);
|
|
1960
|
+
console.log(` ${c.dim}gpt-4o${c.reset} ${c.dim}(default)${c.reset}`);
|
|
1961
|
+
console.log(` ${c.dim}gpt-4o-mini${c.reset}`);
|
|
1962
|
+
console.log(` ${c.dim}gpt-4.1${c.reset}`);
|
|
1963
|
+
console.log(` ${c.dim}gpt-4.1-mini${c.reset}`);
|
|
1964
|
+
console.log(` ${c.dim}o3${c.reset}`);
|
|
1965
|
+
console.log(` ${c.dim}o4-mini${c.reset}`);
|
|
1966
|
+
console.log();
|
|
1967
|
+
|
|
1968
|
+
// OpenRouter — fetch from API
|
|
1969
|
+
console.log(` ${c.bold}OpenRouter${c.reset}${openrouterKey ? ` ${c.green}(configured)${c.reset}` : ` ${c.dim}(not configured)${c.reset}`}`);
|
|
1970
|
+
if (openrouterKey || targets.includes('--all')) {
|
|
1971
|
+
process.stdout.write(` ${c.dim}Fetching models...${c.reset}`);
|
|
1972
|
+
try {
|
|
1973
|
+
const res = await fetch('https://openrouter.ai/api/v1/models', {
|
|
1974
|
+
headers: openrouterKey ? { 'Authorization': `Bearer ${openrouterKey}` } : {},
|
|
1975
|
+
signal: AbortSignal.timeout(10_000),
|
|
1976
|
+
});
|
|
1977
|
+
const data = await res.json();
|
|
1978
|
+
const models = (data.data || [])
|
|
1979
|
+
.filter(m => m.id && !m.id.includes(':free') && !m.id.includes('/extended'))
|
|
1980
|
+
.sort((a, b) => (a.id || '').localeCompare(b.id || ''));
|
|
1981
|
+
|
|
1982
|
+
// Group by provider prefix
|
|
1983
|
+
const groups = {};
|
|
1984
|
+
for (const m of models) {
|
|
1985
|
+
const [prefix] = m.id.split('/');
|
|
1986
|
+
if (!groups[prefix]) groups[prefix] = [];
|
|
1987
|
+
groups[prefix].push(m);
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// Show popular ones first
|
|
1991
|
+
const popular = ['anthropic', 'openai', 'google', 'meta-llama', 'mistralai', 'deepseek'];
|
|
1992
|
+
const shown = new Set();
|
|
1993
|
+
process.stdout.write(`\r ${c.green}${models.length} models available${c.reset} \n`);
|
|
1994
|
+
console.log();
|
|
1995
|
+
|
|
1996
|
+
for (const prefix of popular) {
|
|
1997
|
+
if (!groups[prefix]) continue;
|
|
1998
|
+
shown.add(prefix);
|
|
1999
|
+
console.log(` ${c.bold}${prefix}${c.reset}`);
|
|
2000
|
+
for (const m of groups[prefix].slice(0, 5)) {
|
|
2001
|
+
console.log(` ${c.dim}${m.id}${c.reset}`);
|
|
2002
|
+
}
|
|
2003
|
+
if (groups[prefix].length > 5) {
|
|
2004
|
+
console.log(` ${c.dim}... and ${groups[prefix].length - 5} more${c.reset}`);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
const otherCount = Object.keys(groups).filter(k => !shown.has(k)).length;
|
|
2009
|
+
if (otherCount > 0) {
|
|
2010
|
+
console.log();
|
|
2011
|
+
console.log(` ${c.dim}+ ${otherCount} more providers. Use --model=<provider/model>${c.reset}`);
|
|
2012
|
+
console.log(` ${c.dim}Full list: https://openrouter.ai/models${c.reset}`);
|
|
2013
|
+
}
|
|
2014
|
+
} catch (e) {
|
|
2015
|
+
process.stdout.write(`\r ${c.red}Failed to fetch: ${e.message}${c.reset} \n`);
|
|
2016
|
+
}
|
|
2017
|
+
} else {
|
|
2018
|
+
console.log(` ${c.dim}anthropic/claude-sonnet-4${c.reset} ${c.dim}(default)${c.reset}`);
|
|
2019
|
+
console.log(` ${c.dim}Set OPENROUTER_API_KEY to see all ${c.bold}200+${c.reset}${c.dim} models${c.reset}`);
|
|
2020
|
+
console.log(` ${c.dim}Or browse: https://openrouter.ai/models${c.reset}`);
|
|
2021
|
+
}
|
|
2022
|
+
console.log();
|
|
2023
|
+
|
|
2024
|
+
// Ollama
|
|
2025
|
+
const ollamaModel = process.env.OLLAMA_MODEL;
|
|
2026
|
+
const ollamaHost = process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
2027
|
+
console.log(` ${c.bold}Ollama${c.reset}${ollamaModel ? ` ${c.green}(configured: ${ollamaModel})${c.reset}` : ` ${c.dim}(not configured)${c.reset}`}`);
|
|
2028
|
+
if (ollamaModel || process.env.OLLAMA_HOST) {
|
|
2029
|
+
try {
|
|
2030
|
+
const res = await fetch(`${ollamaHost}/api/tags`, { signal: AbortSignal.timeout(5_000) });
|
|
2031
|
+
const data = await res.json();
|
|
2032
|
+
for (const m of (data.models || []).slice(0, 10)) {
|
|
2033
|
+
console.log(` ${c.dim}${m.name}${c.reset}`);
|
|
2034
|
+
}
|
|
2035
|
+
} catch {
|
|
2036
|
+
console.log(` ${c.dim}(Ollama not running at ${ollamaHost})${c.reset}`);
|
|
2037
|
+
}
|
|
2038
|
+
} else {
|
|
2039
|
+
console.log(` ${c.dim}Set OLLAMA_MODEL to use local models${c.reset}`);
|
|
2040
|
+
}
|
|
2041
|
+
console.log();
|
|
2042
|
+
|
|
2043
|
+
console.log(` ${c.bold}Set model:${c.reset}`);
|
|
2044
|
+
console.log(` ${c.cyan}agentaudit config set model <name>${c.reset}`);
|
|
2045
|
+
console.log(` ${c.cyan}agentaudit audit <url> --model <name>${c.reset}`);
|
|
2046
|
+
console.log(` ${c.dim}Or env: AGENTAUDIT_MODEL=<name>${c.reset}`);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
1913
2050
|
if (command === 'config') {
|
|
1914
2051
|
const subCmd = targets[0];
|
|
1915
2052
|
if (subCmd === 'set' && targets[1] === 'provider' && targets[2]) {
|
|
@@ -1961,8 +2098,9 @@ async function main() {
|
|
|
1961
2098
|
return;
|
|
1962
2099
|
}
|
|
1963
2100
|
const results = [];
|
|
2101
|
+
const allowAutoAudit = command === 'check'; // only 'check' auto-audits, 'lookup' never does
|
|
1964
2102
|
for (const t of names) {
|
|
1965
|
-
const data = await checkPackage(t);
|
|
2103
|
+
const data = await checkPackage(t, { autoAudit: allowAutoAudit });
|
|
1966
2104
|
results.push(data);
|
|
1967
2105
|
}
|
|
1968
2106
|
if (jsonMode) {
|
|
@@ -2033,7 +2171,7 @@ async function main() {
|
|
|
2033
2171
|
}
|
|
2034
2172
|
|
|
2035
2173
|
// Typo correction via Levenshtein distance
|
|
2036
|
-
const knownCommands = ['discover', 'scan', 'audit', 'check', 'lookup', 'status', 'setup', 'config'];
|
|
2174
|
+
const knownCommands = ['discover', 'scan', 'audit', 'check', 'lookup', 'status', 'setup', 'config', 'models'];
|
|
2037
2175
|
const suggestion = knownCommands
|
|
2038
2176
|
.map(cmd => ({ cmd, dist: levenshtein(command, cmd) }))
|
|
2039
2177
|
.filter(x => x.dist <= 3)
|