Add files via upload
This commit is contained in:
1 parent
5bec530a89
commit
38fac38311
85 files changed
+57166
No files matched your search
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* arsenal-doctor.mjs — arsenal readiness report.
|
||||
*
|
||||
* Answers "which of our specialist tools are actually installed, and exactly how do I install
|
||||
* the rest?" — the executable companion to docs/INSTALL_MATRIX.md. Server-first (like
|
||||
* scripts/doctor.mjs): it reads GET /api/arsenal/status, which carries per-tool
|
||||
* available/status/installHint/category. Groups missing command-ready tools by category and
|
||||
* prints the install command for each, so closing the gap is copy-paste.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/arsenal-doctor.mjs human-readable report
|
||||
* node scripts/arsenal-doctor.mjs --json machine-readable
|
||||
* node scripts/arsenal-doctor.mjs --fix print only the install commands for missing tools
|
||||
* node scripts/arsenal-doctor.mjs --strict exit 1 unless every command-ready tool is installed
|
||||
*/
|
||||
import process from 'node:process';
|
||||
|
||||
const baseUrl = (process.env.T3MP3ST_API_URL || 'http://127.0.0.1:3333').replace(/\/$/, '');
|
||||
const jsonMode = process.argv.includes('--json');
|
||||
const fixMode = process.argv.includes('--fix');
|
||||
const strict = process.argv.includes('--strict');
|
||||
|
||||
async function apiGet(path) {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), 3000);
|
||||
try {
|
||||
const res = await fetch(`${baseUrl}${path}`, { signal: controller.signal });
|
||||
const text = await res.text();
|
||||
return { ok: res.ok, status: res.status, data: text ? JSON.parse(text) : {} };
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
function main(status) {
|
||||
const tools = Array.isArray(status.tools) ? status.tools : [];
|
||||
// command-ready = the agent can actually execute it (safe_command | receipt_required)
|
||||
const ready = tools.filter(t => t.execution === 'safe_command' || t.execution === 'receipt_required');
|
||||
const installed = ready.filter(t => t.status === 'installed');
|
||||
const missing = ready.filter(t => t.status !== 'installed');
|
||||
|
||||
if (fixMode) {
|
||||
// de-dupe install hints (several tools share one, e.g. foundry forge/cast)
|
||||
const seen = new Set();
|
||||
for (const t of missing) {
|
||||
const hint = (t.installHint || '').trim();
|
||||
if (hint && !seen.has(hint)) { seen.add(hint); console.log(hint); }
|
||||
}
|
||||
process.exitCode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonMode) {
|
||||
console.log(JSON.stringify({
|
||||
baseUrl,
|
||||
total: tools.length,
|
||||
commandReady: ready.length,
|
||||
installed: installed.length,
|
||||
missing: missing.map(t => ({ id: t.id, binary: t.binary, category: t.category, installHint: t.installHint })),
|
||||
pct: ready.length ? Math.round((installed.length / ready.length) * 100) : 0,
|
||||
}, null, 2));
|
||||
process.exitCode = strict && missing.length ? 1 : 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const pct = ready.length ? Math.round((installed.length / ready.length) * 100) : 0;
|
||||
console.log(`\n⚔️ T3MP3ST ARSENAL READINESS — ${installed.length}/${ready.length} command-ready tools installed (${pct}%)\n`);
|
||||
|
||||
// group by category for the "group of experts" view
|
||||
const byCat = {};
|
||||
for (const t of ready) (byCat[t.category] = byCat[t.category] || []).push(t);
|
||||
for (const cat of Object.keys(byCat).sort()) {
|
||||
const list = byCat[cat];
|
||||
const marks = list.map(t => (t.status === 'installed' ? `✓${t.id}` : `✗${t.id}`)).join(' ');
|
||||
console.log(` ${cat.padEnd(18)} ${marks}`);
|
||||
}
|
||||
|
||||
if (missing.length) {
|
||||
console.log(`\n ── ${missing.length} missing — install commands (or: npm run arsenal:doctor -- --fix) ──`);
|
||||
const seen = new Set();
|
||||
for (const t of missing) {
|
||||
const hint = (t.installHint || 'see docs/INSTALL_MATRIX.md').trim();
|
||||
const key = `${t.category}:${hint}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
console.log(` ${t.binary.padEnd(14)} ${hint}`);
|
||||
}
|
||||
console.log(`\n Reproducible alternative — the full Kali+ image: npm run tools:build (then --tools-image cybench-tools:latest)`);
|
||||
} else {
|
||||
console.log('\n ✅ every command-ready tool is installed — full arsenal armed.');
|
||||
}
|
||||
console.log('');
|
||||
process.exitCode = strict && missing.length ? 1 : 0;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const res = await apiGet('/api/arsenal/status');
|
||||
if (!res.ok) throw new Error(`status ${res.status}`);
|
||||
main(res.data);
|
||||
} catch (e) {
|
||||
console.error(`arsenal-doctor: API offline at ${baseUrl} (${e.message}). Start it with \`npm run server\` for live readiness.`);
|
||||
console.error('Install reference: docs/INSTALL_MATRIX.md · quick host setup: bash scripts/install-tools.sh --web');
|
||||
process.exitCode = 1;
|
||||
}
|
||||
})();
|
||||
Reference in new issue
Block a user