agentaudit 3.9.20 → 3.9.22

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 +153 -15
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -58,6 +58,7 @@ function resolveProvider(flagOverride, keys) {
58
58
  // ── Global flags (set in main before command routing) ────
59
59
  let jsonMode = false;
60
60
  let quietMode = false;
61
+ let modelOverride = null; // --model flag or AGENTAUDIT_MODEL env or config
61
62
 
62
63
  // ── ANSI Colors (respects NO_COLOR and --no-color) ───────
63
64
 
@@ -1486,7 +1487,7 @@ async function auditRepo(url) {
1486
1487
  'content-type': 'application/json',
1487
1488
  },
1488
1489
  body: JSON.stringify({
1489
- model: 'claude-sonnet-4-20250514',
1490
+ model: modelOverride || 'claude-sonnet-4-20250514',
1490
1491
  max_tokens: 8192,
1491
1492
  system: systemPrompt,
1492
1493
  messages: [{ role: 'user', content: userMessage }],
@@ -1509,22 +1510,22 @@ async function auditRepo(url) {
1509
1510
  switch (resolvedProvider.id) {
1510
1511
  case 'openrouter':
1511
1512
  apiUrl = 'https://openrouter.ai/api/v1/chat/completions';
1512
- modelName = process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4';
1513
+ modelName = modelOverride || process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4';
1513
1514
  authHeaders = { 'Authorization': `Bearer ${resolvedProvider.key}`, 'HTTP-Referer': 'https://agentaudit.dev', 'X-Title': 'AgentAudit' };
1514
1515
  break;
1515
1516
  case 'ollama':
1516
1517
  apiUrl = `${resolvedProvider.host}/v1/chat/completions`;
1517
- modelName = resolvedProvider.model;
1518
+ modelName = modelOverride || resolvedProvider.model;
1518
1519
  authHeaders = {};
1519
1520
  break;
1520
1521
  case 'custom':
1521
1522
  apiUrl = resolvedProvider.url.endsWith('/chat/completions') ? resolvedProvider.url : `${resolvedProvider.url.replace(/\/$/, '')}/chat/completions`;
1522
- modelName = resolvedProvider.model;
1523
+ modelName = modelOverride || resolvedProvider.model;
1523
1524
  authHeaders = resolvedProvider.key ? { 'Authorization': `Bearer ${resolvedProvider.key}` } : {};
1524
1525
  break;
1525
1526
  default: // openai
1526
1527
  apiUrl = 'https://api.openai.com/v1/chat/completions';
1527
- modelName = 'gpt-4o';
1528
+ modelName = modelOverride || 'gpt-4o';
1528
1529
  authHeaders = { 'Authorization': `Bearer ${resolvedProvider.key}` };
1529
1530
  }
1530
1531
 
@@ -1630,7 +1631,7 @@ async function auditRepo(url) {
1630
1631
 
1631
1632
  // ── Check command ───────────────────────────────────────
1632
1633
 
1633
- async function checkPackage(name) {
1634
+ async function checkPackage(name, { autoAudit = false } = {}) {
1634
1635
  if (!jsonMode) {
1635
1636
  console.log(`${icons.info} Looking up ${c.bold}${name}${c.reset} in registry...`);
1636
1637
  console.log();
@@ -1639,8 +1640,8 @@ async function checkPackage(name) {
1639
1640
  const data = await checkRegistry(name);
1640
1641
  if (!data) {
1641
1642
  if (!jsonMode) {
1642
- // If input looks like a URL, offer to auto-audit
1643
- if (name.includes('github.com') || name.includes('://')) {
1643
+ // Auto-audit: only when called from 'check' command AND input looks like a URL
1644
+ if (autoAudit && (name.includes('github.com') || name.includes('://'))) {
1644
1645
  console.log(` ${c.yellow}Not found in registry.${c.reset}`);
1645
1646
  console.log(` ${c.dim}Starting audit for ${name}...${c.reset}`);
1646
1647
  console.log();
@@ -1649,7 +1650,8 @@ async function checkPackage(name) {
1649
1650
  console.log(` ${c.yellow}✖ Not found${c.reset} — "${name}" hasn't been audited yet.`);
1650
1651
  console.log();
1651
1652
  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 check <repo-url>${c.reset} ${c.dim}Auto-lookup + audit if not found${c.reset}`);
1654
+ console.log(` ${c.cyan}agentaudit audit <repo-url>${c.reset} ${c.dim}Deep LLM audit${c.reset}`);
1653
1655
  console.log(` ${c.cyan}agentaudit scan <repo-url>${c.reset} ${c.dim}Quick static check (no API key)${c.reset}`);
1654
1656
  }
1655
1657
  return null;
@@ -1731,9 +1733,25 @@ async function main() {
1731
1733
  quietMode = rawArgs.includes('--quiet') || rawArgs.includes('-q');
1732
1734
  // --no-color already handled at top level for `c` object
1733
1735
 
1736
+ // --model flag: --model=<name> or --model <name>
1737
+ const modelFlagIdx = rawArgs.findIndex(a => a === '--model');
1738
+ const modelFlagEq = rawArgs.find(a => a.startsWith('--model='));
1739
+ modelOverride = modelFlagEq?.split('=')[1]
1740
+ || (modelFlagIdx >= 0 ? rawArgs[modelFlagIdx + 1] : null)
1741
+ || process.env.AGENTAUDIT_MODEL
1742
+ || loadConfig()?.preferred_model
1743
+ || null;
1744
+
1734
1745
  // Strip global flags from args
1735
1746
  const globalFlags = new Set(['--json', '--quiet', '-q', '--no-color']);
1736
- const args = rawArgs.filter(a => !globalFlags.has(a));
1747
+ let args = rawArgs.filter(a => !globalFlags.has(a));
1748
+ // Strip --model and its value
1749
+ args = args.filter((a, i, arr) => {
1750
+ if (a.startsWith('--model=')) return false;
1751
+ if (a === '--model') { arr[i + 1] = '__skip__'; return false; }
1752
+ if (a === '__skip__') return false;
1753
+ return true;
1754
+ });
1737
1755
 
1738
1756
  if (args[0] === '-v' || args[0] === '--version') {
1739
1757
  console.log(`agentaudit ${getVersion()}`);
@@ -1757,6 +1775,7 @@ async function main() {
1757
1775
  console.log(` ${c.bold}SETUP${c.reset}`);
1758
1776
  console.log(` ${c.cyan}status${c.reset} Check providers & API keys`);
1759
1777
  console.log(` ${c.cyan}setup${c.reset} Register & configure`);
1778
+ console.log(` ${c.cyan}models${c.reset} List available LLM models`);
1760
1779
  console.log(` ${c.cyan}config set${c.reset} <key> <value> Set default provider/options`);
1761
1780
  console.log();
1762
1781
  console.log(` ${c.bold}OPTIONS${c.reset}`);
@@ -1764,6 +1783,7 @@ async function main() {
1764
1783
  console.log(` ${c.dim}--quiet Suppress banner${c.reset}`);
1765
1784
  console.log(` ${c.dim}--no-color Disable colors ${c.reset}${c.dim}(also: NO_COLOR=1)${c.reset}`);
1766
1785
  console.log(` ${c.dim}--provider <p> Force provider ${c.reset}${c.dim}(anthropic|openai|openrouter|ollama|custom)${c.reset}`);
1786
+ console.log(` ${c.dim}--model <m> Override model ${c.reset}${c.dim}(e.g. gpt-4o-mini, claude-3.5-sonnet)${c.reset}`);
1767
1787
  console.log(` ${c.dim}--export Export audit payload to markdown${c.reset}`);
1768
1788
  console.log(` ${c.dim}--debug Show raw LLM response on errors${c.reset}`);
1769
1789
  console.log();
@@ -1775,7 +1795,9 @@ async function main() {
1775
1795
  console.log();
1776
1796
  console.log(` ${c.bold}PROVIDERS${c.reset} ${c.dim}(set any one for deep audits)${c.reset}`);
1777
1797
  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}`);
1798
+ console.log(` ${c.dim}Set default: AGENTAUDIT_PROVIDER=openai AGENTAUDIT_MODEL=gpt-4o-mini${c.reset}`);
1799
+ console.log(` ${c.dim}Or persist: agentaudit config set provider openai${c.reset}`);
1800
+ console.log(` ${c.dim} agentaudit config set model gpt-4o-mini${c.reset}`);
1779
1801
  console.log(` ${c.dim}Run ${c.cyan}agentaudit status${c.dim} to check configuration.${c.reset}`);
1780
1802
  console.log();
1781
1803
  process.exitCode = 0; return;
@@ -1858,9 +1880,11 @@ async function main() {
1858
1880
  const resolved = resolveProvider(null, keys);
1859
1881
  console.log();
1860
1882
  if (resolved) {
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}`);
1883
+ const activeModel = modelOverride || process.env.AGENTAUDIT_MODEL || loadConfig()?.preferred_model;
1884
+ console.log(` ${c.bold}Active:${c.reset} ${c.green}${resolved.label}${c.reset}${activeModel ? ` ${c.dim}model: ${activeModel}${c.reset}` : ''}`);
1885
+ console.log(` ${c.dim}Override: --provider=<name> --model=<name>${c.reset}`);
1863
1886
  console.log(` ${c.dim}Set default: agentaudit config set provider <name>${c.reset}`);
1887
+ console.log(` ${c.dim} agentaudit config set model <name>${c.reset}`);
1864
1888
  } else {
1865
1889
  console.log(` ${c.yellow}⚠ No working LLM provider.${c.reset} Deep audits require one.`);
1866
1890
  console.log(` ${c.dim}Set a key: export ANTHROPIC_API_KEY=sk-ant-...${c.reset}`);
@@ -1888,6 +1912,113 @@ async function main() {
1888
1912
  return;
1889
1913
  }
1890
1914
 
1915
+ if (command === 'models') {
1916
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
1917
+ const openaiKey = process.env.OPENAI_API_KEY;
1918
+ const openrouterKey = process.env.OPENROUTER_API_KEY;
1919
+
1920
+ console.log(` ${c.bold}Available models by provider:${c.reset}`);
1921
+ console.log();
1922
+
1923
+ // Static lists for Anthropic (no list API)
1924
+ console.log(` ${c.bold}Anthropic${c.reset}${anthropicKey ? ` ${c.green}(configured)${c.reset}` : ` ${c.dim}(not configured)${c.reset}`}`);
1925
+ console.log(` ${c.dim}claude-sonnet-4-20250514${c.reset} ${c.dim}(default)${c.reset}`);
1926
+ console.log(` ${c.dim}claude-opus-4-20250514${c.reset}`);
1927
+ console.log(` ${c.dim}claude-haiku-3-20250514${c.reset}`);
1928
+ console.log();
1929
+
1930
+ // Static list for OpenAI
1931
+ console.log(` ${c.bold}OpenAI${c.reset}${openaiKey ? ` ${c.green}(configured)${c.reset}` : ` ${c.dim}(not configured)${c.reset}`}`);
1932
+ console.log(` ${c.dim}gpt-4o${c.reset} ${c.dim}(default)${c.reset}`);
1933
+ console.log(` ${c.dim}gpt-4o-mini${c.reset}`);
1934
+ console.log(` ${c.dim}gpt-4.1${c.reset}`);
1935
+ console.log(` ${c.dim}gpt-4.1-mini${c.reset}`);
1936
+ console.log(` ${c.dim}o3${c.reset}`);
1937
+ console.log(` ${c.dim}o4-mini${c.reset}`);
1938
+ console.log();
1939
+
1940
+ // OpenRouter — fetch from API
1941
+ console.log(` ${c.bold}OpenRouter${c.reset}${openrouterKey ? ` ${c.green}(configured)${c.reset}` : ` ${c.dim}(not configured)${c.reset}`}`);
1942
+ if (openrouterKey || targets.includes('--all')) {
1943
+ process.stdout.write(` ${c.dim}Fetching models...${c.reset}`);
1944
+ try {
1945
+ const res = await fetch('https://openrouter.ai/api/v1/models', {
1946
+ headers: openrouterKey ? { 'Authorization': `Bearer ${openrouterKey}` } : {},
1947
+ signal: AbortSignal.timeout(10_000),
1948
+ });
1949
+ const data = await res.json();
1950
+ const models = (data.data || [])
1951
+ .filter(m => m.id && !m.id.includes(':free') && !m.id.includes('/extended'))
1952
+ .sort((a, b) => (a.id || '').localeCompare(b.id || ''));
1953
+
1954
+ // Group by provider prefix
1955
+ const groups = {};
1956
+ for (const m of models) {
1957
+ const [prefix] = m.id.split('/');
1958
+ if (!groups[prefix]) groups[prefix] = [];
1959
+ groups[prefix].push(m);
1960
+ }
1961
+
1962
+ // Show popular ones first
1963
+ const popular = ['anthropic', 'openai', 'google', 'meta-llama', 'mistralai', 'deepseek'];
1964
+ const shown = new Set();
1965
+ process.stdout.write(`\r ${c.green}${models.length} models available${c.reset} \n`);
1966
+ console.log();
1967
+
1968
+ for (const prefix of popular) {
1969
+ if (!groups[prefix]) continue;
1970
+ shown.add(prefix);
1971
+ console.log(` ${c.bold}${prefix}${c.reset}`);
1972
+ for (const m of groups[prefix].slice(0, 5)) {
1973
+ console.log(` ${c.dim}${m.id}${c.reset}`);
1974
+ }
1975
+ if (groups[prefix].length > 5) {
1976
+ console.log(` ${c.dim}... and ${groups[prefix].length - 5} more${c.reset}`);
1977
+ }
1978
+ }
1979
+
1980
+ const otherCount = Object.keys(groups).filter(k => !shown.has(k)).length;
1981
+ if (otherCount > 0) {
1982
+ console.log();
1983
+ console.log(` ${c.dim}+ ${otherCount} more providers. Use --model=<provider/model>${c.reset}`);
1984
+ console.log(` ${c.dim}Full list: https://openrouter.ai/models${c.reset}`);
1985
+ }
1986
+ } catch (e) {
1987
+ process.stdout.write(`\r ${c.red}Failed to fetch: ${e.message}${c.reset} \n`);
1988
+ }
1989
+ } else {
1990
+ console.log(` ${c.dim}anthropic/claude-sonnet-4${c.reset} ${c.dim}(default)${c.reset}`);
1991
+ console.log(` ${c.dim}Set OPENROUTER_API_KEY to see all ${c.bold}200+${c.reset}${c.dim} models${c.reset}`);
1992
+ console.log(` ${c.dim}Or browse: https://openrouter.ai/models${c.reset}`);
1993
+ }
1994
+ console.log();
1995
+
1996
+ // Ollama
1997
+ const ollamaModel = process.env.OLLAMA_MODEL;
1998
+ const ollamaHost = process.env.OLLAMA_HOST || 'http://localhost:11434';
1999
+ console.log(` ${c.bold}Ollama${c.reset}${ollamaModel ? ` ${c.green}(configured: ${ollamaModel})${c.reset}` : ` ${c.dim}(not configured)${c.reset}`}`);
2000
+ if (ollamaModel || process.env.OLLAMA_HOST) {
2001
+ try {
2002
+ const res = await fetch(`${ollamaHost}/api/tags`, { signal: AbortSignal.timeout(5_000) });
2003
+ const data = await res.json();
2004
+ for (const m of (data.models || []).slice(0, 10)) {
2005
+ console.log(` ${c.dim}${m.name}${c.reset}`);
2006
+ }
2007
+ } catch {
2008
+ console.log(` ${c.dim}(Ollama not running at ${ollamaHost})${c.reset}`);
2009
+ }
2010
+ } else {
2011
+ console.log(` ${c.dim}Set OLLAMA_MODEL to use local models${c.reset}`);
2012
+ }
2013
+ console.log();
2014
+
2015
+ console.log(` ${c.bold}Set model:${c.reset}`);
2016
+ console.log(` ${c.cyan}agentaudit config set model <name>${c.reset}`);
2017
+ console.log(` ${c.cyan}agentaudit audit <url> --model <name>${c.reset}`);
2018
+ console.log(` ${c.dim}Or env: AGENTAUDIT_MODEL=<name>${c.reset}`);
2019
+ return;
2020
+ }
2021
+
1891
2022
  if (command === 'config') {
1892
2023
  const subCmd = targets[0];
1893
2024
  if (subCmd === 'set' && targets[1] === 'provider' && targets[2]) {
@@ -1902,6 +2033,12 @@ async function main() {
1902
2033
  console.log(` ${c.green}✔${c.reset} Default provider set to: ${c.bold}${val}${c.reset}`);
1903
2034
  console.log(` ${c.dim}Override per-command: --provider=<name>${c.reset}`);
1904
2035
  console.log(` ${c.dim}Or env: AGENTAUDIT_PROVIDER=<name>${c.reset}`);
2036
+ } else if (subCmd === 'set' && targets[1] === 'model' && targets[2]) {
2037
+ const val = targets[2];
2038
+ saveConfig({ preferred_model: val });
2039
+ console.log(` ${c.green}✔${c.reset} Default model set to: ${c.bold}${val}${c.reset}`);
2040
+ console.log(` ${c.dim}Override per-command: --model=<name>${c.reset}`);
2041
+ console.log(` ${c.dim}Or env: AGENTAUDIT_MODEL=<name>${c.reset}`);
1905
2042
  } else if (subCmd === 'get' || !subCmd) {
1906
2043
  const cfg = loadConfig();
1907
2044
  console.log(` ${c.bold}Config:${c.reset} ${USER_CONFIG_FILE}`);
@@ -1933,8 +2070,9 @@ async function main() {
1933
2070
  return;
1934
2071
  }
1935
2072
  const results = [];
2073
+ const allowAutoAudit = command === 'check'; // only 'check' auto-audits, 'lookup' never does
1936
2074
  for (const t of names) {
1937
- const data = await checkPackage(t);
2075
+ const data = await checkPackage(t, { autoAudit: allowAutoAudit });
1938
2076
  results.push(data);
1939
2077
  }
1940
2078
  if (jsonMode) {
@@ -2005,7 +2143,7 @@ async function main() {
2005
2143
  }
2006
2144
 
2007
2145
  // Typo correction via Levenshtein distance
2008
- const knownCommands = ['discover', 'scan', 'audit', 'check', 'lookup', 'status', 'setup', 'config'];
2146
+ const knownCommands = ['discover', 'scan', 'audit', 'check', 'lookup', 'status', 'setup', 'config', 'models'];
2009
2147
  const suggestion = knownCommands
2010
2148
  .map(cmd => ({ cmd, dist: levenshtein(command, cmd) }))
2011
2149
  .filter(x => x.dist <= 3)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentaudit",
3
- "version": "3.9.20",
3
+ "version": "3.9.22",
4
4
  "description": "Security scanner for AI packages — MCP server + CLI",
5
5
  "type": "module",
6
6
  "bin": {