agentaudit 3.13.1 → 3.13.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.
Files changed (2) hide show
  1. package/cli.mjs +72 -9
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -852,17 +852,45 @@ function padLeft(str, len) {
852
852
  return diff > 0 ? ' '.repeat(diff) + str : str;
853
853
  }
854
854
 
855
+ // Truncate a string with ANSI codes to maxLen visible characters
856
+ function truncateAnsi(str, maxLen) {
857
+ if (maxLen <= 0) return '';
858
+ let vis = 0;
859
+ let result = '';
860
+ let i = 0;
861
+ while (i < str.length) {
862
+ if (str[i] === '\x1b') {
863
+ const m = str.slice(i).match(/^\x1b\[[0-9;]*[a-zA-Z]/);
864
+ if (m) { result += m[0]; i += m[0].length; continue; }
865
+ }
866
+ if (vis >= maxLen) break;
867
+ result += str[i];
868
+ vis++;
869
+ i++;
870
+ }
871
+ return result + c.reset;
872
+ }
873
+
855
874
  function drawBox(title, contentLines, width) {
856
875
  const inner = width - 4; // 2 for "│ " + 2 for " │"
857
876
  const totalDash = inner + 2; // total horizontal line chars between corners
858
877
  const lines = [];
859
- const titleStr = title ? ` ${title} ` : '';
860
- const titleLen = visLen(titleStr);
878
+ let titleStr = title ? ` ${title} ` : '';
879
+ let titleLen = visLen(titleStr);
880
+ // Clamp title if wider than available border space
881
+ if (titleLen >= totalDash - 1) {
882
+ const maxTitle = Math.max(1, totalDash - 4);
883
+ titleStr = ` ${title.slice(0, maxTitle)}… `;
884
+ titleLen = visLen(titleStr);
885
+ }
861
886
  // Top: ╭─ Title ────────────╮ (1 dash before title + title + remaining dashes)
862
- const topDash = BOX.h.repeat(Math.max(1, totalDash - 1 - titleLen));
887
+ const topDash = BOX.h.repeat(Math.max(0, totalDash - 1 - titleLen));
863
888
  lines.push(` ${BOX.tl}${c.dim}${BOX.h}${c.reset}${c.bold}${titleStr}${c.reset}${c.dim}${topDash}${c.reset}${BOX.tr}`);
864
889
  for (const line of contentLines) {
865
- lines.push(` ${BOX.v} ${padRight(line, inner + 1)}${BOX.v}`);
890
+ // Truncate content that exceeds box inner width
891
+ const vl = visLen(line);
892
+ const display = vl > inner ? truncateAnsi(line, inner - 1) + '…' : line;
893
+ lines.push(` ${BOX.v} ${padRight(display, inner + 1)}${BOX.v}`);
866
894
  }
867
895
  lines.push(` ${BOX.bl}${c.dim}${BOX.h.repeat(totalDash)}${c.reset}${BOX.br}`);
868
896
  return lines;
@@ -4220,7 +4248,10 @@ function renderOverviewTab(data, width) {
4220
4248
  const idx = leaderboard.findIndex(e => e.agent_name === creds.agent_name);
4221
4249
  if (idx >= 0) rank = `#${idx + 1} of ${leaderboard.length}`;
4222
4250
  }
4223
- profileLines.push(`${c.bold}${creds.agent_name}${c.reset}${' '.repeat(Math.max(1, halfW - 14 - visLen(creds.agent_name) - visLen(rank)))}${c.dim}${rank}${c.reset}`);
4251
+ const nameVis = visLen(creds.agent_name);
4252
+ const rankVis = visLen(rank);
4253
+ const nameGap = Math.max(1, halfW - nameVis - rankVis);
4254
+ profileLines.push(`${c.bold}${creds.agent_name}${c.reset}${' '.repeat(nameGap)}${c.dim}${rank}${c.reset}`);
4224
4255
  profileLines.push(`Points ${c.bold}${fmtNum(agent.total_points)}${c.reset}`);
4225
4256
  profileLines.push(`Audits ${c.bold}${fmtNum(agent.total_reports)}${c.reset}`);
4226
4257
  profileLines.push(`Findings ${c.bold}${fmtNum(agent.total_findings_submitted)}${c.reset} ${c.dim}(${fmtNum(agent.total_findings_confirmed)} confirmed)${c.reset}`);
@@ -4255,6 +4286,15 @@ function renderOverviewTab(data, width) {
4255
4286
  regLines.push(`${c.dim}Could not load registry stats${c.reset}`);
4256
4287
  }
4257
4288
 
4289
+ // Local history stats
4290
+ const localHistory = loadHistory(50);
4291
+ const verifiedCount = localHistory.filter(h => h.verification).length;
4292
+ const localStats = {
4293
+ total: localHistory.length,
4294
+ verified: verifiedCount,
4295
+ lastAudit: localHistory[0] ? timeAgo(localHistory[0].timestamp || localHistory[0].date) : null,
4296
+ };
4297
+
4258
4298
  const boxW = halfW + 4;
4259
4299
  const profileBox = drawBox('Your Profile', profileLines, boxW);
4260
4300
  const registryBox = drawBox('Registry', regLines, boxW);
@@ -4262,8 +4302,13 @@ function renderOverviewTab(data, width) {
4262
4302
  // Side by side if wide enough, stacked otherwise
4263
4303
  if (width >= boxW * 2 + 4) {
4264
4304
  const maxLen = Math.max(profileBox.length, registryBox.length);
4265
- while (profileBox.length < maxLen) profileBox.push(` ${BOX.v} ${' '.repeat(halfW + 1)}${BOX.v}`);
4266
- while (registryBox.length < maxLen) registryBox.push(` ${BOX.v} ${' '.repeat(halfW + 1)}${BOX.v}`);
4305
+ // Insert filler lines BEFORE the bottom border (last line), not after
4306
+ while (profileBox.length < maxLen) {
4307
+ profileBox.splice(profileBox.length - 1, 0, ` ${BOX.v} ${' '.repeat(halfW + 1)}${BOX.v}`);
4308
+ }
4309
+ while (registryBox.length < maxLen) {
4310
+ registryBox.splice(registryBox.length - 1, 0, ` ${BOX.v} ${' '.repeat(halfW + 1)}${BOX.v}`);
4311
+ }
4267
4312
  for (let i = 0; i < maxLen; i++) {
4268
4313
  lines.push(profileBox[i] + ' ' + registryBox[i].trimStart());
4269
4314
  }
@@ -4271,6 +4316,24 @@ function renderOverviewTab(data, width) {
4271
4316
  lines.push(...profileBox, '', ...registryBox);
4272
4317
  }
4273
4318
 
4319
+ // Local history section
4320
+ lines.push('');
4321
+ if (localStats.total > 0) {
4322
+ const histParts = [`${c.bold}${localStats.total}${c.reset} local audits`];
4323
+ if (localStats.verified > 0) histParts.push(`${c.green}${localStats.verified} verified${c.reset}`);
4324
+ if (localStats.lastAudit) histParts.push(`${c.dim}last: ${localStats.lastAudit}${c.reset}`);
4325
+ lines.push(` ${c.dim}Local:${c.reset} ${histParts.join(` ${c.dim}│${c.reset} `)}`);
4326
+ }
4327
+
4328
+ // Quick actions
4329
+ lines.push('');
4330
+ lines.push(` ${c.bold}Quick Actions${c.reset}`);
4331
+ lines.push(` ${c.cyan}agentaudit audit <url>${c.reset} ${c.dim}Deep LLM security audit${c.reset}`);
4332
+ lines.push(` ${c.cyan}agentaudit audit <url> --verify${c.reset} ${c.dim}Audit + adversarial verification${c.reset}`);
4333
+ lines.push(` ${c.cyan}agentaudit audit <url> --remote${c.reset} ${c.dim}Server-side scan (no API key)${c.reset}`);
4334
+ lines.push(` ${c.cyan}agentaudit consensus <pkg>${c.reset} ${c.dim}Cross-model consensus view${c.reset}`);
4335
+ lines.push(` ${c.cyan}agentaudit search <query>${c.reset} ${c.dim}Search the registry${c.reset}`);
4336
+
4274
4337
  return lines;
4275
4338
  }
4276
4339
 
@@ -4292,7 +4355,7 @@ function renderLeaderboardTab(data, width, opts = {}) {
4292
4355
  const entry = leaderboard[i];
4293
4356
  const name = (entry.agent_name || '').slice(0, maxNameW);
4294
4357
  const isMe = creds && entry.agent_name === creds.agent_name;
4295
- const prefix = i < 3 ? ` ${medals[i]} ` : ` ${c.dim}#${String(i + 1).padStart(2)}${c.reset} `;
4358
+ const prefix = i < 3 ? ` ${medals[i]} ` : ` ${c.dim}#${String(i + 1).padStart(2)}${c.reset} `;
4296
4359
  const nameStr = isMe ? `${c.green}${c.bold}${name}${c.reset}` : name;
4297
4360
  const bar = renderBar(entry.total_points || 0, maxPts, barW);
4298
4361
  const pts = padLeft(`${fmtNum(entry.total_points || 0)} pts`, 12);
@@ -4724,7 +4787,7 @@ async function leaderboardCommand(args) {
4724
4787
  const entry = data[i];
4725
4788
  const name = (entry.agent_name || '').slice(0, 20);
4726
4789
  const isMe = creds && entry.agent_name === creds.agent_name;
4727
- const prefix = i < 3 ? ` ${medals[i]} ` : ` #${String(i + 1).padStart(2)} `;
4790
+ const prefix = i < 3 ? ` ${medals[i]} ` : ` ${c.dim}#${String(i + 1).padStart(2)}${c.reset} `;
4728
4791
  const nameStr = isMe ? `${c.green}${c.bold}${name}${c.reset}` : name;
4729
4792
  const bar = renderBar(entry.total_points || 0, maxPts, barW);
4730
4793
  const pts = `${fmtNum(entry.total_points || 0)} pts`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentaudit",
3
- "version": "3.13.1",
3
+ "version": "3.13.2",
4
4
  "description": "Security scanner for AI agent packages — CLI + MCP server",
5
5
  "type": "module",
6
6
  "bin": {