agentaudit 3.13.9 → 3.13.11

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 +55 -23
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -2876,27 +2876,36 @@ async function safeJsonParse(res, llmConfig) {
2876
2876
  }
2877
2877
 
2878
2878
  function getMaxOutputTokens(model) {
2879
- // Known max_completion_tokens from provider docs / OpenRouter API
2880
- const limits = {
2881
- // Anthropic
2882
- 'claude-haiku-4-5': 8192, 'claude-3-haiku': 4096, 'claude-3-5-haiku': 8192,
2883
- 'claude-sonnet-4-6': 64000, 'claude-sonnet-4-5': 16384, 'claude-3-5-sonnet': 8192,
2884
- 'claude-opus-4-6': 32768, 'claude-opus-4': 32768,
2885
- // Google Gemini (all current models support 65536)
2886
- 'gemini-3': 65536, 'gemini-2.5': 65536, 'gemini-2.0': 65536,
2887
- // Qwen
2888
- 'qwen3.5': 65536, 'qwen3': 32768, 'qwen2.5': 32768,
2879
+ // Known max_completion_tokens from provider docs (2026-02)
2880
+ // Array (not object) to guarantee match order — specific keys before generic ones
2881
+ const limits = [
2882
+ // Anthropic (specific versions first, then generic)
2883
+ ['claude-haiku-4-5', 8192], ['claude-3-haiku', 4096], ['claude-3-5-haiku', 8192],
2884
+ ['claude-sonnet-4-6', 64000], ['claude-sonnet-4-5', 16384], ['claude-3-5-sonnet', 8192], ['claude-sonnet-4', 16384],
2885
+ ['claude-opus-4-6', 32768], ['claude-opus-4', 32768],
2886
+ // Google Gemini
2887
+ ['gemini-3', 65536], ['gemini-2.5', 65536], ['gemini-2.0', 65536],
2888
+ // Qwen (OpenRouter)
2889
+ ['qwen3.5', 65536], ['qwen3', 32768], ['qwen2.5', 32768],
2889
2890
  // xAI
2890
- 'grok-4': 32768, 'grok-3': 16384,
2891
+ ['grok-4', 32768], ['grok-3', 16384],
2891
2892
  // OpenAI
2892
- 'gpt-4.1': 32768, 'gpt-4o': 16384, 'gpt-4-turbo': 4096,
2893
- 'o3': 100000, 'o4-mini': 100000,
2894
- };
2893
+ ['gpt-4.1', 32768], ['gpt-4o', 16384], ['gpt-4-turbo', 4096], ['o3', 100000], ['o4-mini', 100000],
2894
+ // DeepSeek (8K standard mode — thinking mode allows 64K but we use standard)
2895
+ ['deepseek', 8192],
2896
+ // Mistral
2897
+ ['mistral-large', 32768], ['mistral-medium', 32768], ['mistral-small', 32768],
2898
+ // Meta Llama (served by Groq 32K, Together, Fireworks, Cerebras)
2899
+ ['llama-3.3', 32768], ['llama-v3p3', 32768], ['llama-3.1', 32768], ['llama-v3p1', 32768],
2900
+ ['llama-4', 32768], ['llama-3', 16384],
2901
+ // Zhipu / z.ai
2902
+ ['glm-4', 16384], ['glm-3', 8192],
2903
+ ];
2895
2904
  const m = (model || '').toLowerCase();
2896
- for (const [key, val] of Object.entries(limits)) {
2905
+ for (const [key, val] of limits) {
2897
2906
  if (m.includes(key)) return val;
2898
2907
  }
2899
- return 16384; // conservative fallback for unknown models
2908
+ return 8192; // conservative fallback — safe for all providers
2900
2909
  }
2901
2910
 
2902
2911
  async function callLlm(llmConfig, systemPrompt, userMessage) {
@@ -2904,6 +2913,11 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
2904
2913
  if (!apiKey) return { error: `Missing API key: ${llmConfig.key}` };
2905
2914
  const start = Date.now();
2906
2915
 
2916
+ // --timeout flag (seconds), default 180s (3 min)
2917
+ const timeoutArgIdx = process.argv.indexOf('--timeout');
2918
+ const timeoutSec = timeoutArgIdx !== -1 ? Math.max(30, Math.min(600, parseInt(process.argv[timeoutArgIdx + 1], 10) || 180)) : 180;
2919
+ const timeoutMs = timeoutSec * 1000;
2920
+
2907
2921
  // Context window warning
2908
2922
  const ctxCheck = checkContextLimit(llmConfig.model, systemPrompt, userMessage);
2909
2923
  if (ctxCheck) {
@@ -2913,6 +2927,17 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
2913
2927
  }
2914
2928
  }
2915
2929
 
2930
+ // Live timer — updates every second while waiting for LLM
2931
+ let liveTimer = null;
2932
+ if (process.stdout.isTTY && !quietMode) {
2933
+ liveTimer = setInterval(() => {
2934
+ const secs = Math.round((Date.now() - start) / 1000);
2935
+ const remaining = timeoutSec - secs;
2936
+ const timerColor = remaining <= 30 ? c.yellow : c.dim;
2937
+ process.stdout.write(`\r ${stepProgress(4, 4)} Running LLM analysis ${c.dim}(${llmConfig.name})${c.reset} ${timerColor}${secs}s/${timeoutSec}s${c.reset} `);
2938
+ }, 1000);
2939
+ }
2940
+
2916
2941
  let _text = '';
2917
2942
  try {
2918
2943
  let data;
@@ -2921,7 +2946,7 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
2921
2946
  method: 'POST',
2922
2947
  headers: { 'x-api-key': apiKey, 'anthropic-version': '2023-06-01', 'content-type': 'application/json' },
2923
2948
  body: JSON.stringify({ model: llmConfig.model, max_tokens: getMaxOutputTokens(llmConfig.model), system: systemPrompt, messages: [{ role: 'user', content: userMessage }] }),
2924
- signal: AbortSignal.timeout(180_000),
2949
+ signal: AbortSignal.timeout(timeoutMs),
2925
2950
  });
2926
2951
  data = await safeJsonParse(res, llmConfig);
2927
2952
  if (data.error) {
@@ -2954,7 +2979,7 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
2954
2979
  contents: [{ role: 'user', parts: [{ text: userMessage }] }],
2955
2980
  generationConfig: { maxOutputTokens: getMaxOutputTokens(llmConfig.model), responseMimeType: 'application/json', thinkingConfig: { thinkingBudget: 8192 } },
2956
2981
  }),
2957
- signal: AbortSignal.timeout(180_000),
2982
+ signal: AbortSignal.timeout(timeoutMs),
2958
2983
  });
2959
2984
  data = await safeJsonParse(res, llmConfig);
2960
2985
  if (data.error) {
@@ -2982,7 +3007,7 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
2982
3007
  method: 'POST',
2983
3008
  headers,
2984
3009
  body: JSON.stringify({ model: llmConfig.model, max_tokens: getMaxOutputTokens(llmConfig.model), messages: [{ role: 'system', content: systemPrompt }, { role: 'user', content: userMessage }] }),
2985
- signal: AbortSignal.timeout(180_000),
3010
+ signal: AbortSignal.timeout(timeoutMs),
2986
3011
  });
2987
3012
  data = await safeJsonParse(res, llmConfig);
2988
3013
  if (data.error) {
@@ -3008,9 +3033,11 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
3008
3033
  }
3009
3034
  } catch (err) {
3010
3035
  const dur = Date.now() - start;
3011
- if (err.name === 'TimeoutError' || err.message?.includes('timeout')) return { error: 'Request timed out (180s)', hint: 'Try again or use a faster model', duration: dur };
3036
+ if (err.name === 'TimeoutError' || err.message?.includes('timeout')) return { error: `Request timed out (${timeoutSec}s)`, hint: `Increase timeout: --timeout ${timeoutSec * 2}`, duration: dur };
3012
3037
  if (err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED' || err.message?.includes('fetch failed')) return { error: `Network error: could not reach ${llmConfig.provider}`, hint: 'Check your internet connection', duration: dur };
3013
3038
  return { error: err.message, duration: dur };
3039
+ } finally {
3040
+ if (liveTimer) clearInterval(liveTimer);
3014
3041
  }
3015
3042
  }
3016
3043
 
@@ -3818,14 +3845,16 @@ async function auditRepo(url) {
3818
3845
 
3819
3846
  const llmResult = await callLlm(activeLlm, systemPrompt, userMessage);
3820
3847
 
3848
+ // Clear live timer line and print final status
3849
+ if (process.stdout.isTTY) process.stdout.write('\r\x1b[K');
3821
3850
  if (llmResult.error) {
3822
- console.log(` ${c.red}failed${c.reset}`);
3851
+ console.log(` ${stepProgress(4, 4)} Running LLM analysis ${c.dim}(${modelLabel})${c.reset} ${c.red}failed${c.reset} ${c.dim}(${elapsed(start)})${c.reset}`);
3823
3852
  console.log(` ${c.red}${llmResult.error}${c.reset}`);
3824
3853
  if (llmResult.hint) console.log(` ${c.dim}${llmResult.hint}${c.reset}`);
3825
3854
  return null;
3826
3855
  }
3827
3856
 
3828
- console.log(` ${c.green}done${c.reset} ${c.dim}(${elapsed(start)})${c.reset}`);
3857
+ console.log(` ${stepProgress(4, 4)} Running LLM analysis ${c.dim}(${modelLabel})${c.reset} ${c.green}done${c.reset} ${c.dim}(${elapsed(start)})${c.reset}`);
3829
3858
 
3830
3859
  if (llmResult.truncated) {
3831
3860
  console.log();
@@ -5234,11 +5263,13 @@ async function main() {
5234
5263
  // Strip global flags from args (including --model <value>, --format <value>)
5235
5264
  const globalFlags = new Set(['--json', '--quiet', '-q', '--no-color', '--no-upload', '--remote']);
5236
5265
  let args = rawArgs.filter(a => !globalFlags.has(a));
5237
- // Remove --model <value> and --models <value> pairs
5266
+ // Remove --model <value>, --models <value>, --timeout <value> pairs
5238
5267
  const modelIdx = args.indexOf('--model');
5239
5268
  if (modelIdx !== -1) args.splice(modelIdx, 2);
5240
5269
  const modelsIdx = args.indexOf('--models');
5241
5270
  if (modelsIdx !== -1) args.splice(modelsIdx, 2);
5271
+ const timeoutIdx = args.indexOf('--timeout');
5272
+ if (timeoutIdx !== -1) args.splice(timeoutIdx, 2);
5242
5273
  // Remove --format <value> pair
5243
5274
  const formatIdx = args.indexOf('--format');
5244
5275
  const formatFlag = formatIdx !== -1 ? args.splice(formatIdx, 2)[1] : null;
@@ -5314,6 +5345,7 @@ async function main() {
5314
5345
  ` <name> — specific model as verifier (e.g. sonnet)`,
5315
5346
  ` --no-verify Disable verification (even if default)`,
5316
5347
  ` --remote Use agentaudit.dev server (no LLM key needed, 3/day free)`,
5348
+ ` --timeout <sec> LLM request timeout in seconds (default: 180, max: 600)`,
5317
5349
  ` --model <name> Override LLM model for this run`,
5318
5350
  ` --models <a,b,c> Multi-model audit (parallel calls, consensus comparison)`,
5319
5351
  ` --no-upload Skip uploading report to registry`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentaudit",
3
- "version": "3.13.9",
3
+ "version": "3.13.11",
4
4
  "description": "Security scanner for AI agent packages — CLI + MCP server",
5
5
  "type": "module",
6
6
  "bin": {