agentaudit 3.13.0 → 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.
- package/cli.mjs +111 -9
- 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
|
-
|
|
860
|
-
|
|
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(
|
|
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
|
-
|
|
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;
|
|
@@ -4152,6 +4180,7 @@ async function checkPackage(name) {
|
|
|
4152
4180
|
if (!jsonMode) {
|
|
4153
4181
|
console.log(` ${c.yellow}Not found${c.reset} — package "${name}" hasn't been audited yet.`);
|
|
4154
4182
|
console.log(` ${c.dim}Run: agentaudit audit <repo-url> for a deep LLM audit${c.reset}`);
|
|
4183
|
+
await suggestSimilarPackages(name);
|
|
4155
4184
|
}
|
|
4156
4185
|
return null;
|
|
4157
4186
|
}
|
|
@@ -4219,7 +4248,10 @@ function renderOverviewTab(data, width) {
|
|
|
4219
4248
|
const idx = leaderboard.findIndex(e => e.agent_name === creds.agent_name);
|
|
4220
4249
|
if (idx >= 0) rank = `#${idx + 1} of ${leaderboard.length}`;
|
|
4221
4250
|
}
|
|
4222
|
-
|
|
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}`);
|
|
4223
4255
|
profileLines.push(`Points ${c.bold}${fmtNum(agent.total_points)}${c.reset}`);
|
|
4224
4256
|
profileLines.push(`Audits ${c.bold}${fmtNum(agent.total_reports)}${c.reset}`);
|
|
4225
4257
|
profileLines.push(`Findings ${c.bold}${fmtNum(agent.total_findings_submitted)}${c.reset} ${c.dim}(${fmtNum(agent.total_findings_confirmed)} confirmed)${c.reset}`);
|
|
@@ -4254,6 +4286,15 @@ function renderOverviewTab(data, width) {
|
|
|
4254
4286
|
regLines.push(`${c.dim}Could not load registry stats${c.reset}`);
|
|
4255
4287
|
}
|
|
4256
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
|
+
|
|
4257
4298
|
const boxW = halfW + 4;
|
|
4258
4299
|
const profileBox = drawBox('Your Profile', profileLines, boxW);
|
|
4259
4300
|
const registryBox = drawBox('Registry', regLines, boxW);
|
|
@@ -4261,8 +4302,13 @@ function renderOverviewTab(data, width) {
|
|
|
4261
4302
|
// Side by side if wide enough, stacked otherwise
|
|
4262
4303
|
if (width >= boxW * 2 + 4) {
|
|
4263
4304
|
const maxLen = Math.max(profileBox.length, registryBox.length);
|
|
4264
|
-
|
|
4265
|
-
while (
|
|
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
|
+
}
|
|
4266
4312
|
for (let i = 0; i < maxLen; i++) {
|
|
4267
4313
|
lines.push(profileBox[i] + ' ' + registryBox[i].trimStart());
|
|
4268
4314
|
}
|
|
@@ -4270,6 +4316,24 @@ function renderOverviewTab(data, width) {
|
|
|
4270
4316
|
lines.push(...profileBox, '', ...registryBox);
|
|
4271
4317
|
}
|
|
4272
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
|
+
|
|
4273
4337
|
return lines;
|
|
4274
4338
|
}
|
|
4275
4339
|
|
|
@@ -4291,7 +4355,7 @@ function renderLeaderboardTab(data, width, opts = {}) {
|
|
|
4291
4355
|
const entry = leaderboard[i];
|
|
4292
4356
|
const name = (entry.agent_name || '').slice(0, maxNameW);
|
|
4293
4357
|
const isMe = creds && entry.agent_name === creds.agent_name;
|
|
4294
|
-
const prefix = i < 3 ? `
|
|
4358
|
+
const prefix = i < 3 ? ` ${medals[i]} ` : ` ${c.dim}#${String(i + 1).padStart(2)}${c.reset} `;
|
|
4295
4359
|
const nameStr = isMe ? `${c.green}${c.bold}${name}${c.reset}` : name;
|
|
4296
4360
|
const bar = renderBar(entry.total_points || 0, maxPts, barW);
|
|
4297
4361
|
const pts = padLeft(`${fmtNum(entry.total_points || 0)} pts`, 12);
|
|
@@ -4592,6 +4656,31 @@ function renderSearchTab(searchState, width) {
|
|
|
4592
4656
|
return lines;
|
|
4593
4657
|
}
|
|
4594
4658
|
|
|
4659
|
+
async function suggestSimilarPackages(slug) {
|
|
4660
|
+
if (jsonMode || quietMode) return;
|
|
4661
|
+
try {
|
|
4662
|
+
const res = await fetch(`${REGISTRY_URL}/api/lookup?hash=${encodeURIComponent(slug)}`, {
|
|
4663
|
+
signal: AbortSignal.timeout(5_000),
|
|
4664
|
+
});
|
|
4665
|
+
if (!res.ok) return;
|
|
4666
|
+
const data = await res.json();
|
|
4667
|
+
// API returns { reports: [...], findings: [...], total_matches }
|
|
4668
|
+
const reports = data.reports || [];
|
|
4669
|
+
if (reports.length === 0) return;
|
|
4670
|
+
console.log();
|
|
4671
|
+
console.log(` ${c.dim}Did you mean one of these?${c.reset}`);
|
|
4672
|
+
const shown = reports.slice(0, 5);
|
|
4673
|
+
for (const p of shown) {
|
|
4674
|
+
const name = p.skill_slug || p.slug || '?';
|
|
4675
|
+
const risk = p.risk_score ?? 0;
|
|
4676
|
+
const badge = risk === 0 ? `${c.green}safe${c.reset}` : risk <= 25 ? `${c.green}score ${100 - risk}${c.reset}` : risk <= 50 ? `${c.yellow}score ${100 - risk}${c.reset}` : `${c.red}score ${100 - risk}${c.reset}`;
|
|
4677
|
+
console.log(` ${c.cyan}${name}${c.reset} ${badge}`);
|
|
4678
|
+
}
|
|
4679
|
+
if (data.total_matches > 5) console.log(` ${c.dim}...and ${data.total_matches - 5} more${c.reset}`);
|
|
4680
|
+
console.log(` ${c.dim}Use: ${c.cyan}agentaudit search <query>${c.dim} to find packages${c.reset}`);
|
|
4681
|
+
} catch { /* ignore */ }
|
|
4682
|
+
}
|
|
4683
|
+
|
|
4595
4684
|
async function searchCommand(args) {
|
|
4596
4685
|
const query = args.filter(a => !a.startsWith('--')).join(' ').trim();
|
|
4597
4686
|
|
|
@@ -4698,7 +4787,7 @@ async function leaderboardCommand(args) {
|
|
|
4698
4787
|
const entry = data[i];
|
|
4699
4788
|
const name = (entry.agent_name || '').slice(0, 20);
|
|
4700
4789
|
const isMe = creds && entry.agent_name === creds.agent_name;
|
|
4701
|
-
const prefix = i < 3 ? `
|
|
4790
|
+
const prefix = i < 3 ? ` ${medals[i]} ` : ` ${c.dim}#${String(i + 1).padStart(2)}${c.reset} `;
|
|
4702
4791
|
const nameStr = isMe ? `${c.green}${c.bold}${name}${c.reset}` : name;
|
|
4703
4792
|
const bar = renderBar(entry.total_points || 0, maxPts, barW);
|
|
4704
4793
|
const pts = `${fmtNum(entry.total_points || 0)} pts`;
|
|
@@ -5564,9 +5653,22 @@ async function main() {
|
|
|
5564
5653
|
} else {
|
|
5565
5654
|
console.log(` ${c.red}API error (HTTP ${res.status})${c.reset}`);
|
|
5566
5655
|
}
|
|
5656
|
+
// Suggest similar packages via search
|
|
5657
|
+
await suggestSimilarPackages(slug);
|
|
5567
5658
|
return;
|
|
5568
5659
|
}
|
|
5569
5660
|
const data = await res.json();
|
|
5661
|
+
|
|
5662
|
+
// Check if package actually has any reports
|
|
5663
|
+
if ((!data.total_reports && data.total_reports !== undefined) || (data.total_reports === 0 && (!data.findings || data.findings.length === 0))) {
|
|
5664
|
+
if (jsonMode) { console.log(JSON.stringify(data, null, 2)); return; }
|
|
5665
|
+
console.log(` ${c.yellow}No reports found${c.reset} — "${slug}" hasn't been audited yet.`);
|
|
5666
|
+
console.log(` ${c.dim}Run: ${c.cyan}agentaudit audit <repo-url>${c.dim} to create the first audit${c.reset}`);
|
|
5667
|
+
// Suggest similar packages
|
|
5668
|
+
await suggestSimilarPackages(slug);
|
|
5669
|
+
return;
|
|
5670
|
+
}
|
|
5671
|
+
|
|
5570
5672
|
if (jsonMode) { console.log(JSON.stringify(data, null, 2)); return; }
|
|
5571
5673
|
|
|
5572
5674
|
console.log();
|