@yobekasbah/cli 1.0.0

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.
@@ -0,0 +1,766 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Kasbah Guard — unified CLI
6
+ *
7
+ * kasbah scan "<text>" — input governance
8
+ * kasbah govern --prompt "<text>" [--agent X]
9
+ * kasbah explain "<text>" — causal trace for a verdict
10
+ * kasbah multiverse "<text>" — fuse the full engine
11
+ * kasbah ethics evaluate "<scenario>" — Nomos parliament
12
+ * kasbah ethics debate "<scenario>" — Nomos debate mode
13
+ * kasbah products list — list reference detectors
14
+ * kasbah products run <slug> "<text>" — run a specific product
15
+ * kasbah algorithms list [--category X] — list 63 algorithms
16
+ * kasbah algorithms run <slug> --input "<text>"
17
+ * kasbah agents list — list active agent sessions
18
+ * kasbah redteam suite — list adversarial suite
19
+ * kasbah redteam run — run adversarial suite
20
+ * kasbah bench [--limit N] [--systems s1,s2] — run detection benchmark
21
+ * kasbah bench corpus — corpus summary
22
+ * kasbah bench last — most recent result
23
+ * kasbah receipt verify <proof> — offline verification
24
+ * kasbah os check --content "<text>" [--agent X --verb QUERY]
25
+ * kasbah os metrics — live OS metrics
26
+ * kasbah os status — OS status
27
+ * kasbah swarm — swarm-radar snapshot
28
+ * kasbah forecast [--window 3600000] — threat forecast
29
+ * kasbah biometric <fingerprint|rppg|voice|codec> --json '{...}'
30
+ * kasbah zk <generate|verify|commitment|range> --json '{...}'
31
+ * kasbah security <consent|shamir-split|shamir-combine|threshold> --json '{...}'
32
+ * kasbah honeytokens deploy [--service X]
33
+ * kasbah honeytokens check "<text>"
34
+ * kasbah agent gcmdp --action X --state '{...}'
35
+ * kasbah agent maqasid --content "<text>"
36
+ * kasbah kernel gate --operation X --context '{...}'
37
+ * kasbah policy [get|put --file P] — policy editor
38
+ * kasbah health
39
+ *
40
+ * Global flags:
41
+ * --api <url> — override KASBAH_API_URL (default http://127.0.0.1:8788)
42
+ * --json — emit raw JSON (default: pretty summary)
43
+ * --quiet — suppress headers, useful in pipelines
44
+ *
45
+ * Honest scope: this is a thin HTTP wrapper. Every command calls one route
46
+ * on the api-server. Zero local logic, zero new endpoints.
47
+ */
48
+
49
+ const http = require('http');
50
+ const https = require('https');
51
+ const fs = require('fs');
52
+ const url = require('url');
53
+
54
+ const PKG_VERSION = '1.0.0';
55
+
56
+ // ─── arg parsing ────────────────────────────────────────────────────────────
57
+ const argv = process.argv.slice(2);
58
+ const flags = {};
59
+ const positional = [];
60
+ for (let i = 0; i < argv.length; i++) {
61
+ const a = argv[i];
62
+ if (a.startsWith('--')) {
63
+ const key = a.slice(2);
64
+ const next = argv[i + 1];
65
+ if (next === undefined || next.startsWith('--')) { flags[key] = true; }
66
+ else { flags[key] = next; i++; }
67
+ } else positional.push(a);
68
+ }
69
+
70
+ const API_BASE = flags.api || process.env.KASBAH_API_URL || 'http://127.0.0.1:8788';
71
+ const EMIT_JSON = !!flags.json;
72
+ const QUIET = !!flags.quiet;
73
+
74
+ // ─── http ───────────────────────────────────────────────────────────────────
75
+ function request(method, path, body = null) {
76
+ return new Promise((resolve, reject) => {
77
+ const u = new URL(API_BASE + path);
78
+ const lib = u.protocol === 'https:' ? https : http;
79
+ const data = body !== null ? JSON.stringify(body) : null;
80
+ const req = lib.request({
81
+ hostname: u.hostname, port: u.port || (u.protocol === 'https:' ? 443 : 80),
82
+ path: u.pathname + (u.search || ''), method,
83
+ headers: Object.assign(
84
+ { Accept: 'application/json' },
85
+ data ? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } : {},
86
+ process.env.KASBAH_API_KEY ? { Authorization: 'Bearer ' + process.env.KASBAH_API_KEY } : {}
87
+ )
88
+ }, (res) => {
89
+ let chunks = '';
90
+ res.on('data', c => chunks += c);
91
+ res.on('end', () => {
92
+ const ok = res.statusCode >= 200 && res.statusCode < 300;
93
+ let parsed = null;
94
+ try { parsed = chunks ? JSON.parse(chunks) : null; } catch { parsed = chunks; }
95
+ if (!ok) reject(Object.assign(new Error('HTTP ' + res.statusCode), { status: res.statusCode, body: parsed }));
96
+ else resolve(parsed);
97
+ });
98
+ });
99
+ req.on('error', reject);
100
+ req.setTimeout(60000, () => req.destroy(new Error('timeout')));
101
+ if (data) req.write(data);
102
+ req.end();
103
+ });
104
+ }
105
+
106
+ // ─── output helpers ─────────────────────────────────────────────────────────
107
+ const C = { reset:'\x1b[0m', dim:'\x1b[2m', bold:'\x1b[1m',
108
+ red:'\x1b[31m', green:'\x1b[32m', yellow:'\x1b[33m',
109
+ blue:'\x1b[34m', cyan:'\x1b[36m', gray:'\x1b[90m' };
110
+ const usingColor = process.stdout.isTTY && !process.env.NO_COLOR;
111
+ const c = (color, s) => usingColor ? C[color] + s + C.reset : s;
112
+ function header(msg) { if (!QUIET && !EMIT_JSON) console.error(c('dim', msg)); }
113
+ function output(obj) {
114
+ if (EMIT_JSON) { console.log(JSON.stringify(obj, null, 2)); return; }
115
+ console.log(typeof obj === 'string' ? obj : JSON.stringify(obj, null, 2));
116
+ }
117
+ function verdictColor(v) {
118
+ const u = String(v || '').toUpperCase();
119
+ if (u.includes('DENY') || u.includes('BLOCK') || u.includes('PROHIBIT')) return 'red';
120
+ if (u.includes('WARN') || u.includes('FLAG')) return 'yellow';
121
+ if (u.includes('ALLOW') || u.includes('PASS') || u.includes('CLEAN')) return 'green';
122
+ return 'cyan';
123
+ }
124
+ function deriveVerdict(d) {
125
+ if (!d) return '?';
126
+ const raw = d.verdict || d.decision || d.classification;
127
+ if (raw) return String(raw).toUpperCase();
128
+ const risk = typeof d.risk === 'number' ? d.risk : typeof d.risk_score === 'number' ? d.risk_score : null;
129
+ if (risk !== null) return risk >= 0.6 ? 'DENY' : risk >= 0.3 ? 'WARN' : 'ALLOW';
130
+ if (Array.isArray(d.threats)) return d.threats.length ? 'WARN' : 'ALLOW';
131
+ if (typeof d.safe === 'boolean') return d.safe ? 'ALLOW' : 'WARN';
132
+ return '?';
133
+ }
134
+ function summary(d, primary = []) {
135
+ const v = deriveVerdict(d);
136
+ const r = typeof d.risk === 'number' ? d.risk : typeof d.risk_score === 'number' ? d.risk_score : null;
137
+ let line = c(verdictColor(v), v);
138
+ if (r !== null) line += ' ' + c('dim', 'risk ' + (r * 100).toFixed(0) + '%');
139
+ if (Array.isArray(d.threats) && d.threats.length) line += ' ' + c('dim', d.threats.slice(0, 3).map(t => typeof t === 'string' ? t : (t.type || t.name)).join(', '));
140
+ if (typeof d.latency_ms === 'number' || typeof d.latencyMs === 'number') {
141
+ line += ' ' + c('dim', (d.latency_ms || d.latencyMs) + 'ms');
142
+ }
143
+ console.log(line);
144
+ for (const k of primary) if (k in d) console.log(c('gray', ' ' + k + ': ') + (typeof d[k] === 'object' ? JSON.stringify(d[k]).slice(0, 120) : d[k]));
145
+ }
146
+
147
+ // ─── smart-inference (mirrors desktop UI) ───────────────────────────────────
148
+ function detectUseCase(t) {
149
+ if (!t) return null;
150
+ const low = t.toLowerCase();
151
+ const lines = t.split('\n').map(l => l.trim()).filter(Boolean);
152
+ if (lines.length >= 2) {
153
+ const verbLines = lines.filter(l => /^(EXTRACT|FORMAT|SEND|READ|WRITE|EXEC|SCAN|LIST|DELETE|UPLOAD|DOWNLOAD|QUERY|SEARCH|CREATE|MODIFY|MOVE|COPY)\s/.test(l));
154
+ if (verbLines.length >= 2 && verbLines.length >= lines.length * 0.5)
155
+ return { slug: 'intent-analyzer', label: 'an agent trajectory' };
156
+ }
157
+ if (/\b(experience|education|skills|employment|résumé|resume|curriculum|cv)\b/.test(low)
158
+ && /^\s*[-•·]\s/m.test(t) && lines.length >= 5)
159
+ return { slug: 'resume-authentic', label: 'a resume or CV' };
160
+ if (/\b(swipe|tinder|bumble|hinge|match|profile|bio|love bombing)\b/.test(low)
161
+ || /\b(send money|western union|gift card|crypto|wire transfer).{0,100}\b(urgent|emergency|stuck|hospital|airport)\b/.test(low))
162
+ return { slug: 'dating-guard', label: 'a dating or romance scenario' };
163
+ if (/\b(you always|you never|i never said|that didn't happen|you're imagining|you're crazy|stop overreacting|after everything I)\b/.test(low))
164
+ return { slug: 'relationship-guard', label: 'a manipulative conversation' };
165
+ if (/\b(plaintiff|defendant|whereas|hereby|witnessed|notarized|jurisdiction|affidavit|statute)\b/.test(low)
166
+ || /\b(article \d|section \d|clause \d|exhibit [A-Z])\b/i.test(t))
167
+ return { slug: 'legal-evidence', label: 'a legal document' };
168
+ if (/\b(dao|on-chain|tokenholders|quorum|snapshot|governance vote|treasury allocation|ratify|abstain)\b/.test(low))
169
+ return { slug: 'dao-vote-guard', label: 'a DAO governance proposal' };
170
+ if (/^@\w+:?/m.test(t) || /\b(retweet|repost|engagement farm|coordinated|botnet|sockpuppet)\b/.test(low))
171
+ return { slug: 'botnet-radar', label: 'a social-media thread' };
172
+ if (/AKIA[A-Z0-9]{16}|sk-proj-|sk_live_|ghp_|-----BEGIN [A-Z ]+PRIVATE KEY-----|rm\s+-rf|drop\s+table|ignore\s+(all|previous)\s+instructions/i.test(t))
173
+ return null;
174
+ return null;
175
+ }
176
+
177
+ function usage() {
178
+ console.error(`Kasbah Guard CLI v${PKG_VERSION} · ${c('dim', 'api: ' + API_BASE)}
179
+
180
+ ${c('bold', '⚙️ DEPLOYMENT')}
181
+ ${c('bold', 'kasbah deploy')} init <aws|gcp|azure> [--region REGION]
182
+ ${c('bold', 'kasbah deploy')} apply [--auto-approve]
183
+ ${c('bold', 'kasbah deploy')} status | endpoint | verify
184
+ ${c('bold', 'kasbah deploy')} logs [--tail N] | scale --replicas N | destroy [--force]
185
+
186
+ ${c('bold', 'SETUP')}
187
+ ${c('bold', 'kasbah init')} --auto-setup
188
+ ${c('bold', 'kasbah validate')}
189
+
190
+ ${c('bold', '🛡️ DETECTION')}
191
+ ${c('bold', 'kasbah scan')} "<text>"
192
+ ${c('bold', 'kasbah govern')} --prompt "<text>" [--agent X]
193
+ ${c('bold', 'kasbah explain')} "<text>"
194
+ ${c('bold', 'kasbah multiverse')} "<text>"
195
+ ${c('bold', 'kasbah ethics')} <evaluate|debate> "<scenario>"
196
+ ${c('bold', 'kasbah products')} <list | run <slug> "<text>">
197
+ ${c('bold', 'kasbah algorithms')} <list [--category X] | run <slug> --input "<text>">
198
+ ${c('bold', 'kasbah agents')} list
199
+ ${c('bold', 'kasbah redteam')} <suite | run>
200
+ ${c('bold', 'kasbah bench')} [--limit N | corpus | last]
201
+ ${c('bold', 'kasbah receipt verify')} <proof>
202
+ ${c('bold', 'kasbah os')} <ingest|health|retrieve|keygen|sign|verify|binary|audit|merkle|pipeline|modules|ping>
203
+ ${c('bold', 'kasbah os crypto')} <keygen|sign|verify|pubkey>
204
+ ${c('bold', 'kasbah swarm')}
205
+ ${c('bold', 'kasbah forecast')} [--window 3600000]
206
+ ${c('bold', 'kasbah compliance')} <VERB> [--target T] [--content "..."] [--packs ...]
207
+ ${c('bold', 'kasbah silent-speech')} <VERB> ${c('dim', '— block silent-speech/biometric surveillance (BIOMETRIC_GUARD)')}
208
+ ${c('bold', 'kasbah biometric')} <fingerprint|rppg|voice|codec> --json '{...}'
209
+ ${c('bold', 'kasbah zk')} <generate|verify|commitment|range> --json '{...}'
210
+ ${c('bold', 'kasbah security')} <consent|shamir-split|shamir-combine|threshold> --json '{...}'
211
+ ${c('bold', 'kasbah honeytokens')} <deploy [--service X] | check "<text>">
212
+ ${c('bold', 'kasbah agent')} <gcmdp|maqasid> ...
213
+ ${c('bold', 'kasbah kernel gate')} --operation X --context '{...}'
214
+ ${c('bold', 'kasbah policy')} [get|put --file P]
215
+ ${c('bold', 'kasbah health')}
216
+
217
+ Flags: ${c('dim', '--api <url> --json --quiet')}
218
+ Env: ${c('dim', 'KASBAH_API_URL KASBAH_API_KEY')}
219
+ `);
220
+ process.exit(1);
221
+ }
222
+
223
+ // ─── command dispatch ───────────────────────────────────────────────────────
224
+ const cmd = positional[0];
225
+ const sub = positional[1];
226
+ const rest = positional.slice(2);
227
+
228
+ async function main() {
229
+ if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') return usage();
230
+
231
+ // Setup / init
232
+ if (cmd === 'init') {
233
+ const init = require('./init-setup.js');
234
+ if (sub === '--auto-setup' || flags['auto-setup']) {
235
+ return await init();
236
+ }
237
+ console.error('Usage: kasbah init --auto-setup');
238
+ process.exit(2);
239
+ }
240
+
241
+ if (cmd === 'validate') {
242
+ const validate = require('./validate-setup.js');
243
+ return await validate();
244
+ }
245
+
246
+ // Deployment management
247
+ if (cmd === 'deploy') {
248
+ const { DeploymentManager } = require('./deploy.js');
249
+ const manager = new DeploymentManager();
250
+
251
+ const options = {};
252
+ for (const key in flags) {
253
+ options[key] = flags[key];
254
+ }
255
+
256
+ if (sub === 'init') {
257
+ const cloud = rest[0];
258
+ if (!cloud) { console.error('Usage: kasbah deploy init <cloud> [--region REGION]'); process.exit(2); }
259
+ return await manager.init(cloud, options);
260
+ }
261
+ if (sub === 'apply') return await manager.apply(options);
262
+ if (sub === 'status') return await manager.status();
263
+ if (sub === 'endpoint') return await manager.endpoint();
264
+ if (sub === 'logs') return await manager.logs(options);
265
+ if (sub === 'scale') return await manager.scale(options);
266
+ if (sub === 'verify') return await manager.verify();
267
+ if (sub === 'destroy') return await manager.destroy(options);
268
+
269
+ console.error(`
270
+ Usage:
271
+ kasbah deploy init <cloud> [--region REGION]
272
+ kasbah deploy apply [--auto-approve]
273
+ kasbah deploy status
274
+ kasbah deploy endpoint
275
+ kasbah deploy logs [--tail N]
276
+ kasbah deploy scale --replicas N
277
+ kasbah deploy verify
278
+ kasbah deploy destroy [--force]
279
+
280
+ Supported clouds: aws, gcp, azure
281
+ `);
282
+ process.exit(1);
283
+ }
284
+
285
+ // Single-word commands first
286
+ if (cmd === 'health') return summary(await request('GET', '/v1/health'));
287
+ if (cmd === 'swarm') return output(await request('GET', '/v1/swarm/status'));
288
+ if (cmd === 'forecast') return output(await request('GET', '/v1/forecast?window=' + (flags.window || 3600000)));
289
+
290
+ if (cmd === 'scan') {
291
+ const text = sub || flags.text || flags.prompt;
292
+ if (!text) { console.error(' kasbah scan "<text>"'); process.exit(2); }
293
+ // Smart inference — quietly routes to the right product when the content shape is obvious.
294
+ // Disable with --no-infer or pass --as <slug> to force a specific product.
295
+ const explicit = flags.as;
296
+ const inferred = (flags['no-infer'] || explicit) ? null : detectUseCase(text);
297
+ const slug = explicit || inferred?.slug;
298
+ if (slug && slug !== 'general') {
299
+ if (!QUIET && !EMIT_JSON) console.error(c('dim', ' detected: ' + (inferred?.label || slug) + (explicit ? ' (forced)' : '')));
300
+ if (slug === 'intent-analyzer') {
301
+ return summary(await request('POST', '/v1/algorithms/intent-analyzer/run', { steps: text, input: text }), ['summary']);
302
+ }
303
+ let body;
304
+ if (slug === 'dating-guard') body = { input: text, profile: { bio: text } };
305
+ else if (slug === 'botnet-radar') body = { accounts:[{id:'a1',posts:[text]},{id:'a2',posts:[text]},{id:'a3',posts:[text]}] };
306
+ else body = { input: text };
307
+ return summary(await request('POST', '/v1/products/' + slug + '/analyze', body), ['summary']);
308
+ }
309
+ return summary(await request('POST', '/v1/scan', { text, prompt: text }));
310
+ }
311
+
312
+ if (cmd === 'govern') {
313
+ const prompt = flags.prompt || sub;
314
+ if (!prompt) { console.error(' kasbah govern --prompt "<text>" [--agent X]'); process.exit(2); }
315
+ return summary(await request('POST', '/v1/govern', { prompt, agent: flags.agent || 'cli' }));
316
+ }
317
+
318
+ if (cmd === 'compliance') {
319
+ // Evaluate an action against compliance + biometric-surveillance rule packs.
320
+ const verb = flags.verb || sub;
321
+ if (!verb) { console.error(' kasbah compliance <VERB> [--target T] [--content "..."] [--packs HIPAA,GDPR,SOC2,BIOMETRIC_GUARD]'); process.exit(2); }
322
+ const packs = flags.packs || 'HIPAA,GDPR,SOC2,BIOMETRIC_GUARD';
323
+ return summary(await request('POST', '/v1/algorithms/policy-engine/run', {
324
+ verb, target: flags.target || '', content: flags.content || rest.join(' ') || '', packs
325
+ }), ['decision', 'reason', 'violations']);
326
+ }
327
+
328
+ if (cmd === 'explain') {
329
+ const text = sub || flags.text;
330
+ if (!text) { console.error(' kasbah explain "<text>"'); process.exit(2); }
331
+ const r = await request('POST', '/v1/explain', { prompt: text });
332
+ summary(r, ['explanation', 'categories']);
333
+ return;
334
+ }
335
+
336
+ if (cmd === 'multiverse') {
337
+ const input = sub || flags.input;
338
+ if (!input) { console.error(' kasbah multiverse "<text>"'); process.exit(2); }
339
+ const r = await request('POST', '/v1/multiverse/analyze', { input });
340
+ summary(r, ['summary']);
341
+ if (r.superposition?.results && !EMIT_JSON) {
342
+ console.log(c('gray', ' universes:'));
343
+ for (const [k, v] of Object.entries(r.superposition.results)) {
344
+ if (v.error) console.log(' ' + k + ' ' + c('red', 'error'));
345
+ else {
346
+ const risk = v.risk_score ?? v.risk ?? v.score ?? null;
347
+ const r2 = risk !== null ? (risk * 100).toFixed(0) + '%' : '—';
348
+ console.log(' ' + k.padEnd(22) + c('dim', r2));
349
+ }
350
+ }
351
+ }
352
+ return;
353
+ }
354
+
355
+ if (cmd === 'ethics') {
356
+ const text = rest.join(' ') || flags.scenario || flags.content;
357
+ if (!text) { console.error(' kasbah ethics <evaluate|debate> "<scenario>"'); process.exit(2); }
358
+ if (sub === 'evaluate' || !sub) {
359
+ const r = await request('POST', '/v1/nomos/evaluate', { content: text, context: {} });
360
+ console.log(c(verdictColor(r.verdict), r.verdict || '?'));
361
+ const trads = r.traditions || {};
362
+ for (const [k, t] of Object.entries(trads)) {
363
+ const v = t.verdict || t.judgment || '?';
364
+ console.log(' ' + k.padEnd(15) + c(verdictColor(v), v));
365
+ }
366
+ return;
367
+ }
368
+ if (sub === 'debate') {
369
+ const pre = await request('POST', '/v1/nomos/evaluate', { content: text, context: {} }).catch(() => null);
370
+ const initialVerdict = pre?.verdict || 'CONTESTED';
371
+ const r = await request('POST', '/v1/nomos/debate', {
372
+ content: text, initialVerdict,
373
+ challenge: flags.challenge || 'Argue the opposite verdict.'
374
+ });
375
+ output(r);
376
+ return;
377
+ }
378
+ }
379
+
380
+ if (cmd === 'products') {
381
+ if (sub === 'list' || !sub) {
382
+ const r = await request('GET', '/v1/products');
383
+ if (EMIT_JSON) return output(r);
384
+ console.log(c('bold', (r.count || 0) + ' products, ' + (r.available || 0) + ' available'));
385
+ for (const p of r.products || []) {
386
+ const dot = p.available ? c('green', '●') : c('red', '○');
387
+ console.log(' ' + dot + ' ' + p.slug.padEnd(22) + c('dim', p.name));
388
+ }
389
+ return;
390
+ }
391
+ if (sub === 'run') {
392
+ const slug = rest[0];
393
+ const text = rest.slice(1).join(' ') || flags.input;
394
+ if (!slug || !text) { console.error(' kasbah products run <slug> "<text>"'); process.exit(2); }
395
+ const r = await request('POST', '/v1/products/' + slug + '/analyze', { input: text });
396
+ console.log(c(verdictColor(r.verdict), r.verdict || '?') + ' ' + c('dim', (r.summary || '').slice(0, 100)));
397
+ if (typeof r.score === 'number') console.log(' ' + c('gray', 'score: ') + r.score.toFixed(3));
398
+ if (typeof r.latencyMs === 'number') console.log(' ' + c('gray', 'latency: ') + r.latencyMs + 'ms');
399
+ return;
400
+ }
401
+ }
402
+
403
+ if (cmd === 'algorithms') {
404
+ if (sub === 'list' || !sub) {
405
+ const r = await request('GET', '/v1/algorithms');
406
+ if (EMIT_JSON) return output(r);
407
+ console.log(c('bold', (r.count || 0) + ' algorithms · ') + c('dim', (r.categories || []).join(' · ')));
408
+ const cat = flags.category;
409
+ for (const a of r.algorithms || []) {
410
+ if (cat && a.category !== cat) continue;
411
+ console.log(' ' + a.slug.padEnd(28) + c('gray', a.category.padEnd(15)) + c('dim', a.name));
412
+ }
413
+ return;
414
+ }
415
+ if (sub === 'run') {
416
+ const slug = rest[0];
417
+ const input = flags.input || rest.slice(1).join(' ');
418
+ if (!slug) { console.error(' kasbah algorithms run <slug> --input "<text>"'); process.exit(2); }
419
+ const body = flags.json ? JSON.parse(flags.json) : { input };
420
+ const r = await request('POST', '/v1/algorithms/' + slug + '/run', body);
421
+ summary(r);
422
+ return;
423
+ }
424
+ }
425
+
426
+ if (cmd === 'agents') {
427
+ if (sub === 'list' || !sub) {
428
+ const r = await request('GET', '/v1/agents');
429
+ if (EMIT_JSON) return output(r);
430
+ const sessions = r.sessions || [];
431
+ console.log(c('bold', sessions.length + ' active session' + (sessions.length === 1 ? '' : 's')));
432
+ for (const s of sessions) console.log(' ' + (s.sessionId || s.id || '?').padEnd(20) + c('dim', s.status || ''));
433
+ return;
434
+ }
435
+ }
436
+
437
+ if (cmd === 'redteam') {
438
+ if (sub === 'suite') return output(await request('GET', '/v1/redteam/suite'));
439
+ if (sub === 'run' || !sub) {
440
+ const r = await request('POST', '/v1/redteam/run', flags.json ? JSON.parse(flags.json) : {});
441
+ if (EMIT_JSON) return output(r);
442
+ const cases = r.results || r.cases || [];
443
+ const passed = cases.filter(c => c.passed || c.caught).length;
444
+ const pct = cases.length ? Math.round(passed / cases.length * 100) : 0;
445
+ const col = pct >= 80 ? 'green' : pct >= 50 ? 'yellow' : 'red';
446
+ console.log(c(col, pct + '%') + ' ' + passed + '/' + cases.length + ' attacks caught');
447
+ for (const cc of cases.filter(x => !(x.passed || x.caught)).slice(0, 10)) {
448
+ console.log(' ' + c('red', '✕') + ' ' + (cc.id || cc.name || '?').padEnd(20) + c('dim', (cc.prompt || cc.input || '').toString().slice(0, 60)));
449
+ }
450
+ return;
451
+ }
452
+ }
453
+
454
+ if (cmd === 'bench') {
455
+ if (sub === 'corpus') return output(await request('GET', '/v1/bench/corpus'));
456
+ if (sub === 'last') return output(await request('GET', '/v1/bench/last'));
457
+ // run
458
+ const body = { systems: (flags.systems || 'scan,govern,baselines').split(',') };
459
+ if (flags.limit) body.limit = parseInt(flags.limit);
460
+ const r = await request('POST', '/v1/bench/run', body);
461
+ if (EMIT_JSON) return output(r);
462
+ const s = r.summary || {};
463
+ const systems = Object.keys(s).sort((a, b) => (s[b].f1 || 0) - (s[a].f1 || 0));
464
+ console.log(c('bold', 'System'.padEnd(28)) + ['Prec','Rec','F1','Acc'].map(h => c('bold', h.padStart(7))).join(' '));
465
+ for (const name of systems) {
466
+ const m = s[name];
467
+ const isK = !name.startsWith('baseline:');
468
+ const f1col = m.f1 >= 0.75 ? 'green' : m.f1 >= 0.55 ? 'yellow' : 'red';
469
+ console.log(
470
+ (isK ? c('cyan', name) : name).padEnd(28) +
471
+ (m.precision * 100).toFixed(0).padStart(6) + '% ' +
472
+ (m.recall * 100).toFixed(0).padStart(6) + '% ' +
473
+ c(f1col, (m.f1 * 100).toFixed(0).padStart(6) + '%') + ' ' +
474
+ (m.accuracy * 100).toFixed(0).padStart(6) + '%'
475
+ );
476
+ }
477
+ return;
478
+ }
479
+
480
+ if (cmd === 'receipt') {
481
+ if (sub === 'verify') {
482
+ const proof = rest[0];
483
+ if (!proof) { console.error(' kasbah receipt verify <proof>'); process.exit(2); }
484
+ return summary(await request('POST', '/v1/receipt/verify', { receipt: proof }));
485
+ }
486
+ }
487
+
488
+ if (cmd === 'os') {
489
+ if (sub === 'status') return summary(await request('GET', '/v1/os/status'));
490
+ if (sub === 'metrics') return output(await request('GET', '/v1/os/metrics'));
491
+ if (sub === 'check') {
492
+ const content = flags.content || rest.join(' ');
493
+ if (!content) { console.error(' kasbah os check --content "<text>" [--agent X --verb QUERY]'); process.exit(2); }
494
+ return summary(await request('POST', '/v1/os/check', {
495
+ agent: flags.agent || 'cli', verb: flags.verb || 'QUERY', target: 'content', content
496
+ }));
497
+ }
498
+ }
499
+
500
+ if (cmd === 'biometric') {
501
+ const map = { fingerprint: 'neural-fingerprint', rppg: 'rppg', voice: 'voice-clone', codec: 'neural-codec' };
502
+ const route = map[sub];
503
+ if (!route) { console.error(' kasbah biometric <fingerprint|rppg|voice|codec> --json \'{...}\''); process.exit(2); }
504
+ const body = flags.json ? JSON.parse(flags.json) : {};
505
+ return output(await request('POST', '/v1/biometric/' + route, body));
506
+ }
507
+
508
+ if (cmd === 'zk') {
509
+ const map = { generate: 'proof/generate', verify: 'proof/verify', commitment: 'commitment', range: 'range-proof' };
510
+ const route = map[sub];
511
+ if (!route) { console.error(' kasbah zk <generate|verify|commitment|range> --json \'{...}\''); process.exit(2); }
512
+ const body = flags.json ? JSON.parse(flags.json) : {};
513
+ return output(await request('POST', '/v1/zk/' + route, body));
514
+ }
515
+
516
+ if (cmd === 'security') {
517
+ const map = { consent: 'consent/check', 'shamir-split': 'shamir/split', 'shamir-combine': 'shamir/combine', threshold: 'threshold/adjust', 'cross-modal': 'cross-modal/verify' };
518
+ const route = map[sub];
519
+ if (!route) { console.error(' kasbah security <consent|shamir-split|shamir-combine|threshold|cross-modal> --json \'{...}\''); process.exit(2); }
520
+ const body = flags.json ? JSON.parse(flags.json) : {};
521
+ return output(await request('POST', '/v1/security/' + route, body));
522
+ }
523
+
524
+ if (cmd === 'honeytokens') {
525
+ if (sub === 'deploy') return output(await request('POST', '/v1/honeytokens/deploy', { service: flags.service || 'cli' }));
526
+ if (sub === 'check') return output(await request('POST', '/v1/honeytokens/check', { text: rest.join(' ') }));
527
+ }
528
+
529
+ if (cmd === 'agent') {
530
+ if (sub === 'gcmdp') {
531
+ const state = flags.state ? JSON.parse(flags.state) : {};
532
+ return output(await request('POST', '/v1/agent/gcmdp', { action: flags.action || 'READ', state, constraints: {} }));
533
+ }
534
+ if (sub === 'maqasid') {
535
+ return output(await request('POST', '/v1/agent/maqasid', { content: flags.content || rest.join(' ') }));
536
+ }
537
+ }
538
+
539
+ if (cmd === 'kernel' && sub === 'gate') {
540
+ const context = flags.context ? JSON.parse(flags.context) : {};
541
+ return output(await request('POST', '/v1/kernel/gate', { operation: flags.operation || 'exec', context }));
542
+ }
543
+
544
+ if (cmd === 'policy') {
545
+ if (sub === 'put') {
546
+ if (!flags.file) { console.error(' kasbah policy put --file <path>'); process.exit(2); }
547
+ const policy = JSON.parse(fs.readFileSync(flags.file, 'utf8'));
548
+ return output(await request('PUT', '/v1/policy', policy));
549
+ }
550
+ return output(await request('GET', '/v1/policy'));
551
+ }
552
+
553
+ // ── New v3 endpoints ─────────────────────────────────────────────────────
554
+
555
+ if (cmd === 'forge' && sub === 'ksi') {
556
+ return output(await request('POST', '/v1/forge/ksi', { text: rest.join(' ') }));
557
+ }
558
+ if (cmd === 'nexus') {
559
+ if (sub === 'status') return output(await request('GET', '/v1/nexus/status'));
560
+ return output(await request('POST', '/v1/nexus/command', { command: rest.join(' ') }));
561
+ }
562
+ if (cmd === 'cortex') {
563
+ return output(await request('POST', '/v1/cortex/exec', { command: rest.join(' ') }));
564
+ }
565
+
566
+ if (cmd === 'products' && sub === 'status') {
567
+ return output(await request('GET', '/v1/products/status'));
568
+ }
569
+ if (cmd === 'products' && sub === 'scan') {
570
+ const product = rest[0];
571
+ const text = rest.slice(1).join(' ');
572
+ if (!product || !text) { console.error(' kasbah products scan <product> "<text>"'); process.exit(2); }
573
+ return output(await request('POST', `/v1/products/${product}`, { text }));
574
+ }
575
+
576
+ if (cmd === 'crypto') {
577
+ if (sub === 'shamir-split') {
578
+ const body = flags.json ? JSON.parse(flags.json) : { secret: rest[0], n: parseInt(rest[1]) || 5, k: parseInt(rest[2]) || 3 };
579
+ return output(await request('POST', '/v1/crypto/shamir/split', body));
580
+ }
581
+ if (sub === 'shamir-combine') {
582
+ const body = flags.json ? JSON.parse(flags.json) : { shares: rest };
583
+ return output(await request('POST', '/v1/crypto/shamir/combine', body));
584
+ }
585
+ if (sub === 'mmr-append') {
586
+ return output(await request('POST', '/v1/crypto/mmr/append', { data: rest.join(' ') }));
587
+ }
588
+ if (sub === 'vdf-eval') {
589
+ return output(await request('POST', '/v1/crypto/vdf/eval', { input: rest.join(' '), iterations: parseInt(flags.iterations) || 1000 }));
590
+ }
591
+ }
592
+
593
+ if (cmd === 'silent-speech') {
594
+ // DEFENSIVE: Kasbah protects users AGAINST silent-speech / facial-micro-expression /
595
+ // subvocal / BCI / QAI intent-surveillance. It does NOT capture or analyze facial
596
+ // data. This checks whether an action is such a surveillance attempt and BLOCKS it
597
+ // via the BIOMETRIC_GUARD pack.
598
+ const verb = flags.verb || sub || 'CAPTURE_VIDEO';
599
+ return summary(await request('POST', '/v1/algorithms/policy-engine/run', {
600
+ verb,
601
+ target: flags.target || 'facial_expression',
602
+ content: flags.content || rest.join(' ') || 'silent-speech intent inference',
603
+ packs: 'BIOMETRIC_GUARD'
604
+ }), ['decision', 'reason', 'violations']);
605
+ }
606
+ if (cmd === 'audit-receipt') {
607
+ const body = flags.json ? JSON.parse(flags.json) : { policyDecision: 'block', sourceDomain: rest[0] || 'api', riskScore: 50 };
608
+ return output(await request('POST', '/v1/audit/receipt', body));
609
+ }
610
+
611
+ if (cmd === 'receipt-sign') {
612
+ const body = flags.json ? JSON.parse(flags.json) : { verdict: 'ALLOW', risk: 0 };
613
+ return output(await request('POST', '/v1/sign', body));
614
+ }
615
+ if (cmd === 'receipt-sign-v3') {
616
+ const body = flags.json ? JSON.parse(flags.json) : { verdict: 'ALLOW', risk: 0 };
617
+ return output(await request('POST', '/v1/sign?v=3', body));
618
+ }
619
+ if (cmd === 'receipt-verify-batch') {
620
+ const body = flags.json ? JSON.parse(flags.json) : {};
621
+ return output(await request('POST', '/v1/receipt/verify-batch', body));
622
+ }
623
+
624
+ if (cmd === 'kasbah-os') {
625
+ // v2.0
626
+ if (sub === 'ingest') {
627
+ return output(await request('POST', '/v1/kasbah-os/ingest', { secret_text: rest.join(' '), source: 'cli' }));
628
+ }
629
+ if (sub === 'health') return output(await request('GET', '/v1/kasbah-os/health'));
630
+ if (sub === 'retrieve') {
631
+ return output(await request('POST', '/v1/kasbah-os/retrieve', { receipt_id: rest[0] }));
632
+ }
633
+ // v4.0 crypto
634
+ if (sub === 'keygen') return output(await request('POST', '/v1/kasbah-os/crypto/keygen'));
635
+ if (sub === 'sign') return output(await request('POST', '/v1/kasbah-os/crypto/sign',
636
+ { message_hex: rest[0], seed_hex: rest[1] }));
637
+ if (sub === 'verify') return output(await request('POST', '/v1/kasbah-os/crypto/verify',
638
+ { message_hex: rest[0], signature_hex: rest[1], public_key_hex: rest[2] }));
639
+ if (sub === 'pubkey') return output(await request('POST', '/v1/kasbah-os/crypto/pubkey',
640
+ { seed_hex: rest[0] }));
641
+ // v4.0 binary
642
+ if (sub === 'binary') {
643
+ const binary_hex = rest[0];
644
+ const sample_id = rest[1] || 'cli';
645
+ return output(await request('POST', '/v1/kasbah-os/binary/analyze', { binary_hex, sample_id }));
646
+ }
647
+ // v4.0 pipeline
648
+ if (sub === 'pipeline') return output(await request('POST', '/v1/kasbah-os/pipeline/run',
649
+ { binary_hex: rest[0], target_info: rest[1] ? JSON.parse(rest.slice(1).join(' ')) : {} }));
650
+ // v4.0 audit
651
+ if (sub === 'audit') {
652
+ if (rest[0] === 'report') return output(await request('GET', '/v1/kasbah-os/audit/report'));
653
+ if (rest[0] === 'chain') return output(await request('GET', '/v1/kasbah-os/audit/chain'));
654
+ if (rest[0] === 'event') {
655
+ return output(await request('POST', '/v1/kasbah-os/audit/event', {
656
+ engine: rest[1] || 'cli', event_type: rest[2] || 'manual', data: rest[3] ? JSON.parse(rest[3]) : {}
657
+ }));
658
+ }
659
+ if (rest[0] === 'trust') return output(await request('POST', '/v1/kasbah-os/audit/trust',
660
+ { engine: rest[1] || 'bridge' }));
661
+ }
662
+ // v4.0 merkle
663
+ if (sub === 'merkle') {
664
+ if (rest[0] === 'build') return output(await request('POST', '/v1/kasbah-os/merkle/build',
665
+ { entries: rest.slice(1) }));
666
+ if (rest[0] === 'verify') return output(await request('POST', '/v1/kasbah-os/merkle/verify',
667
+ { root_hex: rest[1], leaf_hex: rest[2], proof_hex: rest.slice(3) }));
668
+ }
669
+ // v4.0 system
670
+ if (sub === 'modules') return output(await request('GET', '/v1/kasbah-os/modules'));
671
+ if (sub === 'ping') return output(await request('GET', '/v1/kasbah-os/ping'));
672
+ // v4.0 governance
673
+ if (sub === 'governance') {
674
+ if (rest[0] === 'health') return output(await request('GET', '/v1/kasbah-os/governance/health'));
675
+ if (rest[0] === 'process') {
676
+ const msgs = rest[1] ? JSON.parse(rest[1]) : [];
677
+ return output(await request('POST', '/v1/kasbah-os/governance/process', {
678
+ messages: msgs, tools: [], clearance: parseInt(rest[2]) || 2
679
+ }));
680
+ }
681
+ if (rest[0] === 'approve') {
682
+ return output(await request('POST', '/v1/kasbah-os/governance/approve/' + (rest[1] || ''));
683
+ }
684
+ }
685
+ }
686
+
687
+ if (cmd === 'pipeline' && sub === 'unified') {
688
+ return output(await request('POST', '/v1/pipeline/unified', { text: rest.join(' ') }));
689
+ }
690
+
691
+ if (cmd === 'mcp' && sub === 'servers') return output(await request('GET', '/v1/mcp/servers'));
692
+ if (cmd === 'mcp' && sub === 'teams') return output(await request('GET', '/v1/mcp/servers'));
693
+ if (cmd === 'mcp' && sub === 'policies') return output(await request('GET', '/v1/mcp/policies'));
694
+
695
+ if (cmd === 'keys') return output(await request('GET', '/v1/keys'));
696
+ if (cmd === 'models') return output(await request('GET', '/v1/models'));
697
+
698
+ // Cost tracking
699
+ if (cmd === 'dashboard') {
700
+ const { CostTracker } = require('../kasbah-sdk/src/cost-tracker');
701
+ const tracker = new CostTracker();
702
+
703
+ if (sub === 'daily' || !sub) {
704
+ const report = tracker.getDailyReport();
705
+ return output(report);
706
+ } else if (sub === 'monthly') {
707
+ const report = tracker.getMonthlyReport();
708
+ return output(report);
709
+ } else if (sub === 'session') {
710
+ const report = tracker.getSessionReport();
711
+ return output(report);
712
+ } else if (sub === 'savings') {
713
+ const savings = tracker.calculateRoutingSavings();
714
+ return output(savings);
715
+ }
716
+ console.error('Usage: kasbah dashboard [daily|monthly|session|savings]');
717
+ process.exit(2);
718
+ }
719
+
720
+ if (cmd === 'status') {
721
+ const { CostTracker } = require('../kasbah-sdk/src/cost-tracker');
722
+ const tracker = new CostTracker();
723
+ const stats = tracker.getAllTimeStats();
724
+ const today = tracker.getDailyReport();
725
+
726
+ const status = {
727
+ allTime: stats,
728
+ today: {
729
+ cost: today.totalCost,
730
+ calls: today.totalCalls,
731
+ tokens: today.totalTokens
732
+ },
733
+ savings: tracker.calculateRoutingSavings()
734
+ };
735
+
736
+ if (EMIT_JSON) return output(status);
737
+
738
+ console.log(c('bold', '\nKasbah Status'));
739
+ console.log(c('dim', '═'.repeat(50)));
740
+ console.log('All-time stats:');
741
+ console.log(` Calls: ${stats.totalCalls}`);
742
+ console.log(` Tokens: ${stats.totalTokens}`);
743
+ console.log(` Cost: $${stats.totalCost.toFixed(2)}`);
744
+ console.log(` Avg/day: $${stats.avgDailyCost}`);
745
+ console.log('');
746
+ console.log('Today:');
747
+ console.log(` Cost: $${today.totalCost.toFixed(4)}`);
748
+ console.log(` Calls: ${today.totalCalls}`);
749
+ console.log(` Tokens: ${today.totalTokens}`);
750
+ console.log('');
751
+ console.log('Routing savings (vs Opus):');
752
+ for (const [model, saving] of Object.entries(status.savings || {})) {
753
+ console.log(` ${model}: ${saving.pctCheaper}% cheaper`);
754
+ }
755
+ console.log('');
756
+ }
757
+
758
+ console.error('Unknown command: ' + cmd);
759
+ usage();
760
+ }
761
+
762
+ main().catch((e) => {
763
+ if (EMIT_JSON) console.log(JSON.stringify({ error: e.message, status: e.status, body: e.body }, null, 2));
764
+ else console.error(c('red', '✕ ') + e.message + (e.status ? c('dim', ' (HTTP ' + e.status + ')') : ''));
765
+ process.exit(1);
766
+ });