pentesting 0.14.1 → 0.16.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/dist/main.js ADDED
@@ -0,0 +1,3103 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/platform/tui/main.tsx
4
+ import { render } from "ink";
5
+ import { Command } from "commander";
6
+ import chalk from "chalk";
7
+ import gradient from "gradient-string";
8
+
9
+ // src/platform/tui/app.tsx
10
+ import { useState, useEffect, useCallback, useRef } from "react";
11
+ import { Box as Box2, Text as Text2, useInput, useApp, Static } from "ink";
12
+ import TextInput from "ink-text-input";
13
+ import Spinner from "ink-spinner";
14
+
15
+ // src/shared/constants/agent.ts
16
+ var AGENT_LIMITS = {
17
+ /** Maximum number of iterations before stopping */
18
+ MAX_ITERATIONS: 50,
19
+ /** Maximum number of retries for failed tool execution */
20
+ MAX_RETRIES: 3,
21
+ /** Maximum tokens for LLM response */
22
+ MAX_TOKENS: 4096
23
+ };
24
+ var DISPLAY_LIMITS = {
25
+ /** Maximum characters for console output summary */
26
+ OUTPUT_SUMMARY: 200,
27
+ /** Maximum characters for thought display */
28
+ THOUGHT_SNIPPET: 100,
29
+ /** Maximum characters for tool input display */
30
+ TOOL_INPUT_SNIPPET: 100,
31
+ /** Maximum characters for final response preview */
32
+ RESPONSE_PREVIEW: 200,
33
+ /** Maximum items to display in summary lists */
34
+ SUMMARY_LIST_ITEMS: 10,
35
+ /** Maximum items to display in compact lists */
36
+ COMPACT_LIST_ITEMS: 7
37
+ };
38
+ var ID_LENGTH = 7;
39
+ var ID_RADIX = 36;
40
+ var APP_NAME = "Pentest AI";
41
+ var APP_VERSION = "0.15.0";
42
+ var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
43
+
44
+ // src/shared/constants/protocol.ts
45
+ var PHASES = {
46
+ RECON: "recon",
47
+ VULN_ANALYSIS: "vulnerability_analysis",
48
+ EXPLOIT: "exploit",
49
+ POST_EXPLOIT: "post_exploitation",
50
+ PRIV_ESC: "privilege_escalation",
51
+ LATERAL: "lateral_movement",
52
+ PERSISTENCE: "persistence",
53
+ EXFIL: "exfiltration",
54
+ REPORT: "report"
55
+ };
56
+ var AGENT_ROLES = {
57
+ ORCHESTRATOR: "orchestrator",
58
+ RECON: "recon",
59
+ WEB: "web",
60
+ EXPLOTER: "exploit",
61
+ DATABASE: "database",
62
+ INFRA: "infra"
63
+ };
64
+ var SERVICES = {
65
+ HTTP: "http",
66
+ HTTPS: "https",
67
+ SSH: "ssh",
68
+ FTP: "ftp",
69
+ SMB: "smb",
70
+ AD: "ad",
71
+ MYSQL: "mysql",
72
+ MSSQL: "mssql",
73
+ POSTGRES: "postgresql",
74
+ MONGODB: "mongodb",
75
+ REDIS: "redis",
76
+ ELASTIC: "elasticsearch",
77
+ API: "api"
78
+ };
79
+ var SERVICE_CATEGORIES = {
80
+ NETWORK: "network",
81
+ WEB: "web",
82
+ DATABASE: "database",
83
+ AD: "ad",
84
+ EMAIL: "email",
85
+ REMOTE_ACCESS: "remote_access",
86
+ FILE_SHARING: "file_sharing",
87
+ CLOUD: "cloud",
88
+ CONTAINER: "container",
89
+ API: "api",
90
+ WIRELESS: "wireless",
91
+ ICS: "ics"
92
+ };
93
+ var APPROVAL_LEVELS = {
94
+ AUTO: "auto",
95
+ CONFIRM: "confirm",
96
+ REVIEW: "review",
97
+ BLOCK: "block"
98
+ };
99
+ var DANGER_LEVELS = {
100
+ PASSIVE: "passive",
101
+ ACTIVE: "active",
102
+ EXPLOIT: "exploit"
103
+ };
104
+ var TOOL_NAMES = {
105
+ // Basic Tools
106
+ RUN_CMD: "run_cmd",
107
+ READ_FILE: "read_file",
108
+ WRITE_FILE: "write_file",
109
+ // Mid-level Tools
110
+ PARSE_NMAP: "parse_nmap",
111
+ SEARCH_CVE: "search_cve",
112
+ WEB_SEARCH: "web_search",
113
+ EXTRACT_URLS: "extract_urls",
114
+ // High-level / Engine Tools
115
+ SPAWN_SUB: "spawn_sub",
116
+ ADD_FINDING: "add_finding",
117
+ UPDATE_TODO: "update_todo",
118
+ GET_STATE: "get_state",
119
+ SET_SCOPE: "set_scope",
120
+ ADD_TARGET: "add_target",
121
+ ASK_USER: "ask_user",
122
+ HELP: "help",
123
+ // Database Specialized
124
+ SQLMAP_BASIC: "sqlmap_basic",
125
+ SQLMAP_ADVANCED: "sqlmap_advanced",
126
+ MYSQL_ENUM: "mysql_enum",
127
+ POSTGRES_ENUM: "postgres_enum",
128
+ REDIS_ENUM: "redis_enum",
129
+ DB_BRUTE: "db_brute_common",
130
+ // Network Specialized
131
+ NMAP_QUICK: "nmap_quick",
132
+ NMAP_FULL: "nmap_full",
133
+ RUSTSCAN: "rustscan_fast",
134
+ // Web Specialized
135
+ HTTP_FINGERPRINT: "http_fingerprint",
136
+ WAF_DETECT: "waf_detect",
137
+ DIRSEARCH: "dirsearch",
138
+ NUCLEI_WEB: "nuclei_web",
139
+ // AD Specialized
140
+ BLOODHOUND_COLLECT: "bloodhound_collect",
141
+ KERBEROAST: "kerberoast",
142
+ LDAP_ENUM: "ldap_enum",
143
+ // API Specialized
144
+ API_DISCOVER: "api_discover",
145
+ GRAPHQL_INTROSPECT: "graphql_introspect",
146
+ SWAGGER_PARSE: "swagger_parse",
147
+ API_FUZZ: "api_fuzz",
148
+ // Cloud Specialized
149
+ AWS_S3_CHECK: "aws_s3_check",
150
+ CLOUD_META_CHECK: "cloud_meta_check",
151
+ // Container Specialized
152
+ DOCKER_PS: "docker_ps",
153
+ KUBE_GET_PODS: "kube_get_pods",
154
+ // Service Enumeration
155
+ SMTP_ENUM: "smtp_enum",
156
+ FTP_ENUM: "ftp_enum",
157
+ SMB_ENUM: "smb_enum",
158
+ MODBUS_ENUM: "modbus_enum",
159
+ SSH_ENUM: "ssh_enum",
160
+ RDP_ENUM: "rdp_enum",
161
+ WIFI_SCAN: "wifi_scan"
162
+ };
163
+ var TODO_STATUSES = {
164
+ PENDING: "pending",
165
+ IN_PROGRESS: "in_progress",
166
+ DONE: "done",
167
+ SKIPPED: "skipped"
168
+ };
169
+ var TODO_ACTIONS = {
170
+ ADD: "add",
171
+ COMPLETE: "complete",
172
+ REMOVE: "remove"
173
+ };
174
+ var CORE_BINARIES = {
175
+ NMAP: "nmap",
176
+ MASSCAN: "masscan",
177
+ NUCLEI: "nuclei",
178
+ NIKTO: "nikto",
179
+ FFUF: "ffuf",
180
+ GOBUSTER: "gobuster",
181
+ SQLMAP: "sqlmap",
182
+ METASPLOIT: "msfconsole"
183
+ };
184
+ var PRIORITIES = {
185
+ HIGH: "high",
186
+ MEDIUM: "medium",
187
+ LOW: "low"
188
+ };
189
+ var NOISE_LEVELS = {
190
+ LOW: "low",
191
+ MEDIUM: "medium",
192
+ HIGH: "high"
193
+ };
194
+
195
+ // src/engine/state.ts
196
+ var SharedState = class {
197
+ data;
198
+ constructor() {
199
+ this.data = {
200
+ engagement: null,
201
+ targets: /* @__PURE__ */ new Map(),
202
+ findings: [],
203
+ loot: [],
204
+ todo: [],
205
+ actionLog: [],
206
+ currentPhase: PHASES.RECON
207
+ };
208
+ }
209
+ // Engagement
210
+ setEngagement(engagement) {
211
+ this.data.engagement = engagement;
212
+ }
213
+ getEngagement() {
214
+ return this.data.engagement;
215
+ }
216
+ // Scope
217
+ getScope() {
218
+ return this.data.engagement?.scope || null;
219
+ }
220
+ setScope(scope) {
221
+ if (this.data.engagement) {
222
+ this.data.engagement.scope = scope;
223
+ } else {
224
+ this.data.engagement = {
225
+ id: Math.random().toString(36).slice(2, 10),
226
+ name: "auto-engagement",
227
+ client: "unknown",
228
+ scope,
229
+ phase: this.data.currentPhase,
230
+ startedAt: Date.now()
231
+ };
232
+ }
233
+ }
234
+ // Targets
235
+ addTarget(target) {
236
+ this.data.targets.set(target.ip, target);
237
+ }
238
+ getTarget(ip) {
239
+ return this.data.targets.get(ip);
240
+ }
241
+ getAllTargets() {
242
+ return Array.from(this.data.targets.values());
243
+ }
244
+ getTargets() {
245
+ return this.data.targets;
246
+ }
247
+ /**
248
+ * Update target with service fingerprint
249
+ */
250
+ addServiceFingerprint(ip, fingerprint) {
251
+ const target = this.getTarget(ip);
252
+ if (target) {
253
+ if (!target.services) {
254
+ target.services = [];
255
+ }
256
+ target.services.push(fingerprint);
257
+ target.primaryCategory = this.getMostCommonCategory(target.services);
258
+ }
259
+ }
260
+ /**
261
+ * Get most common service category for target
262
+ */
263
+ getMostCommonCategory(services) {
264
+ const counts = /* @__PURE__ */ new Map();
265
+ for (const s of services) {
266
+ counts.set(s.category, (counts.get(s.category) || 0) + 1);
267
+ }
268
+ let max = 0;
269
+ let result = SERVICE_CATEGORIES.NETWORK;
270
+ for (const [cat, count] of counts) {
271
+ if (count > max) {
272
+ max = count;
273
+ result = cat;
274
+ }
275
+ }
276
+ return result;
277
+ }
278
+ /**
279
+ * Get targets by category
280
+ */
281
+ getTargetsByCategory(category) {
282
+ return this.getAllTargets().filter(
283
+ (t) => t.primaryCategory === category || t.services?.some((s) => s.category === category)
284
+ );
285
+ }
286
+ /**
287
+ * Get high-risk targets
288
+ */
289
+ getHighRiskTargets() {
290
+ const highRisk = [SERVICE_CATEGORIES.ICS, SERVICE_CATEGORIES.AD, SERVICE_CATEGORIES.DATABASE, SERVICE_CATEGORIES.CLOUD, SERVICE_CATEGORIES.CONTAINER];
291
+ return this.getAllTargets().filter(
292
+ (t) => highRisk.includes(t.primaryCategory || SERVICE_CATEGORIES.NETWORK)
293
+ );
294
+ }
295
+ // Findings
296
+ addFinding(finding) {
297
+ this.data.findings.push(finding);
298
+ }
299
+ getFindings() {
300
+ return this.data.findings;
301
+ }
302
+ getFindingsBySeverity(severity) {
303
+ return this.data.findings.filter((f) => f.severity === severity);
304
+ }
305
+ /**
306
+ * Get findings by category
307
+ */
308
+ getFindingsByCategory(category) {
309
+ return this.data.findings.filter((f) => f.category === category);
310
+ }
311
+ /**
312
+ * Get findings by attack tactic
313
+ */
314
+ getFindingsByTactic(tactic) {
315
+ return this.data.findings.filter((f) => f.attackPattern === tactic);
316
+ }
317
+ /**
318
+ * Get verified findings only
319
+ */
320
+ getVerifiedFindings() {
321
+ return this.data.findings.filter((f) => f.verified);
322
+ }
323
+ /**
324
+ * Get findings with available exploits
325
+ */
326
+ getExploitableFindings() {
327
+ return this.data.findings.filter((f) => f.exploitAvailable === true);
328
+ }
329
+ /**
330
+ * Get chainable findings (attack chains)
331
+ */
332
+ getChainableFindings() {
333
+ const chains = /* @__PURE__ */ new Map();
334
+ for (const f of this.data.findings) {
335
+ if (f.chainable && f.chainable.length > 0) {
336
+ chains.set(f.id, [f, ...this.data.findings.filter((o) => f.chainable?.includes(o.id))]);
337
+ }
338
+ }
339
+ return chains;
340
+ }
341
+ // Loot
342
+ addLoot(loot) {
343
+ this.data.loot.push(loot);
344
+ }
345
+ getLoot() {
346
+ return this.data.loot;
347
+ }
348
+ /**
349
+ * Get loot by category
350
+ */
351
+ getLootByCategory(category) {
352
+ return this.data.loot.filter((l) => l.category === category);
353
+ }
354
+ /**
355
+ * Get loot by type
356
+ */
357
+ getLootByType(type) {
358
+ return this.data.loot.filter((l) => l.type === type);
359
+ }
360
+ /**
361
+ * Get crackable loot (hashes, tickets)
362
+ */
363
+ getCrackableLoot() {
364
+ return this.data.loot.filter((l) => l.crackable === true && !l.cracked);
365
+ }
366
+ // TODO List Management
367
+ addTodo(content, priority = "medium") {
368
+ const id = Math.random().toString(ID_RADIX).substring(ID_LENGTH);
369
+ const todo = {
370
+ id,
371
+ content,
372
+ status: TODO_STATUSES.PENDING,
373
+ priority
374
+ };
375
+ this.data.todo.push(todo);
376
+ return id;
377
+ }
378
+ updateTodo(id, updates) {
379
+ const index = this.data.todo.findIndex((t) => t.id === id);
380
+ if (index !== -1) {
381
+ this.data.todo[index] = { ...this.data.todo[index], ...updates };
382
+ }
383
+ }
384
+ getTodo() {
385
+ return this.data.todo;
386
+ }
387
+ completeTodo(id) {
388
+ this.updateTodo(id, { status: TODO_STATUSES.DONE });
389
+ }
390
+ // Action Log
391
+ logAction(action) {
392
+ const log = {
393
+ id: Math.random().toString(ID_RADIX).substring(ID_LENGTH),
394
+ timestamp: Date.now(),
395
+ ...action
396
+ };
397
+ this.data.actionLog.push(log);
398
+ }
399
+ getRecentActions(count = DISPLAY_LIMITS.COMPACT_LIST_ITEMS) {
400
+ return this.data.actionLog.slice(-count);
401
+ }
402
+ getActionLog() {
403
+ return this.data.actionLog;
404
+ }
405
+ // Phase
406
+ setPhase(phase) {
407
+ this.data.currentPhase = phase;
408
+ }
409
+ getPhase() {
410
+ return this.data.currentPhase;
411
+ }
412
+ // Export to prompt (minimal summary)
413
+ toPrompt() {
414
+ const lines = [];
415
+ if (this.data.engagement) {
416
+ lines.push(`Engagement: ${this.data.engagement.name} (${this.data.engagement.client})`);
417
+ }
418
+ const scope = this.getScope();
419
+ if (scope) {
420
+ lines.push(`Scope: CIDR=[${scope.allowedCidrs.join(", ")}] Domains=[${scope.allowedDomains.join(", ")}]`);
421
+ }
422
+ const targets = this.getAllTargets();
423
+ if (targets.length > 0) {
424
+ const byCategory = /* @__PURE__ */ new Map();
425
+ for (const t of targets) {
426
+ const cat = t.primaryCategory || SERVICE_CATEGORIES.NETWORK;
427
+ if (!byCategory.has(cat)) {
428
+ byCategory.set(cat, []);
429
+ }
430
+ byCategory.get(cat).push(t);
431
+ }
432
+ lines.push(`Targets (${targets.length}):`);
433
+ for (const [cat, catTargets] of byCategory) {
434
+ lines.push(` [${cat}] ${catTargets.length} hosts`);
435
+ for (const t of catTargets.slice(0, 3)) {
436
+ const ports = t.ports.map((p) => `${p.port}/${p.service}`).join(", ");
437
+ lines.push(` ${t.ip}${t.hostname ? ` (${t.hostname})` : ""}: ${ports || "unknown"}`);
438
+ }
439
+ }
440
+ }
441
+ const findings = this.getFindings();
442
+ if (findings.length > 0) {
443
+ const bySeverity = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
444
+ for (const f of findings) {
445
+ bySeverity[f.severity]++;
446
+ }
447
+ lines.push(`Findings: ${findings.length} total (crit:${bySeverity.critical} high:${bySeverity.high} medium:${bySeverity.medium})`);
448
+ const criticalHigh = findings.filter((f) => f.severity === "critical" || f.severity === "high");
449
+ if (criticalHigh.length > 0) {
450
+ lines.push(` Critical/High:`);
451
+ for (const f of criticalHigh.slice(0, 5)) {
452
+ lines.push(` [${f.severity.toUpperCase()}] ${f.title} (${f.category || "general"})`);
453
+ }
454
+ }
455
+ }
456
+ const loot = this.getLoot();
457
+ if (loot.length > 0) {
458
+ const byType = /* @__PURE__ */ new Map();
459
+ for (const l of loot) {
460
+ byType.set(l.type, (byType.get(l.type) || 0) + 1);
461
+ }
462
+ lines.push(`Loot: ${loot.length} items (${Array.from(byType.entries()).map(([t, c]) => `${t}:${c}`).join(", ")})`);
463
+ }
464
+ const todo = this.getTodo();
465
+ if (todo.length > 0) {
466
+ lines.push(`TODO (${todo.length}):`);
467
+ for (const t of todo.slice(0, DISPLAY_LIMITS.COMPACT_LIST_ITEMS)) {
468
+ const status = t.status === "done" ? "[x]" : t.status === "in_progress" ? "[->]" : "[ ]";
469
+ lines.push(` ${status} ${t.content} (${t.priority})`);
470
+ }
471
+ }
472
+ lines.push(`Phase: ${this.getPhase()}`);
473
+ return lines.join("\n");
474
+ }
475
+ };
476
+
477
+ // src/engine/events.ts
478
+ var AgentEventEmitter = class {
479
+ listeners = /* @__PURE__ */ new Map();
480
+ /**
481
+ * Subscribe to events
482
+ */
483
+ on(eventType, listener) {
484
+ if (!this.listeners.has(eventType)) {
485
+ this.listeners.set(eventType, []);
486
+ }
487
+ this.listeners.get(eventType).push(listener);
488
+ }
489
+ /**
490
+ * Subscribe to all events
491
+ */
492
+ onAny(listener) {
493
+ const allKey = "*";
494
+ if (!this.listeners.has(allKey)) {
495
+ this.listeners.set(allKey, []);
496
+ }
497
+ this.listeners.get(allKey).push(listener);
498
+ }
499
+ /**
500
+ * Unsubscribe from events
501
+ */
502
+ off(eventType, listener) {
503
+ const listeners = this.listeners.get(eventType);
504
+ if (listeners) {
505
+ const index = listeners.indexOf(listener);
506
+ if (index > -1) {
507
+ listeners.splice(index, 1);
508
+ }
509
+ }
510
+ }
511
+ /**
512
+ * Emit an event
513
+ */
514
+ emit(event) {
515
+ const listeners = this.listeners.get(event.type);
516
+ if (listeners) {
517
+ for (const listener of listeners) {
518
+ listener(event);
519
+ }
520
+ }
521
+ const anyListeners = this.listeners.get("*");
522
+ if (anyListeners) {
523
+ for (const listener of anyListeners) {
524
+ listener(event);
525
+ }
526
+ }
527
+ }
528
+ /**
529
+ * Remove all listeners
530
+ */
531
+ removeAllListeners() {
532
+ this.listeners.clear();
533
+ }
534
+ /**
535
+ * Get listener count for an event type
536
+ */
537
+ listenerCount(eventType) {
538
+ return this.listeners.get(eventType)?.length || 0;
539
+ }
540
+ };
541
+
542
+ // src/engine/scope.ts
543
+ var ScopeGuard = class {
544
+ constructor(state) {
545
+ this.state = state;
546
+ }
547
+ /**
548
+ * Check if a tool call is within allowed scope
549
+ */
550
+ check(toolCall) {
551
+ const passiveTools = [
552
+ TOOL_NAMES.READ_FILE,
553
+ TOOL_NAMES.SEARCH_CVE,
554
+ TOOL_NAMES.PARSE_NMAP,
555
+ TOOL_NAMES.WEB_SEARCH,
556
+ TOOL_NAMES.ADD_FINDING,
557
+ TOOL_NAMES.UPDATE_TODO,
558
+ TOOL_NAMES.GET_STATE,
559
+ TOOL_NAMES.HELP
560
+ ];
561
+ if (passiveTools.includes(toolCall.name)) {
562
+ return { allowed: true };
563
+ }
564
+ const scope = this.state.getScope();
565
+ if (!scope) {
566
+ return { allowed: false, reason: "No scope defined. Use /scope to set allowed targets." };
567
+ }
568
+ let command = "";
569
+ if (toolCall.name === TOOL_NAMES.RUN_CMD) {
570
+ command = String(toolCall.input.command || "");
571
+ }
572
+ if (!command) {
573
+ return { allowed: true };
574
+ }
575
+ const targets = this.extractTargets(command);
576
+ const violations = [];
577
+ for (const target of targets) {
578
+ if (!this.isTargetInScope(target)) {
579
+ violations.push(target);
580
+ }
581
+ }
582
+ if (violations.length > 0) {
583
+ return {
584
+ allowed: false,
585
+ reason: `Target(s) outside approved scope: ${violations.join(", ")}`,
586
+ violations
587
+ };
588
+ }
589
+ return { allowed: true };
590
+ }
591
+ /**
592
+ * Public helper to check if a specific target is in scope
593
+ */
594
+ isTargetInScope(target) {
595
+ const scope = this.state.getScope();
596
+ if (!scope) return false;
597
+ if (scope.exclusions.some((e) => target === e || target.endsWith(`.${e}`))) {
598
+ return false;
599
+ }
600
+ if (scope.allowedDomains.some((d) => target === d)) {
601
+ return true;
602
+ }
603
+ if (scope.allowedCidrs.some((c) => this.matchesCidr(target, c))) {
604
+ return true;
605
+ }
606
+ return false;
607
+ }
608
+ /**
609
+ * Simple CIDR matching (IP-only for now)
610
+ */
611
+ matchesCidr(target, cidr) {
612
+ if (target === cidr) return true;
613
+ if (cidr.includes("/")) {
614
+ const [network, mask] = cidr.split("/");
615
+ if (mask === "24") {
616
+ const networkPrefix = network.split(".").slice(0, 3).join(".");
617
+ const targetPrefix = target.split(".").slice(0, 3).join(".");
618
+ return networkPrefix === targetPrefix;
619
+ }
620
+ if (mask === "8") {
621
+ const networkPrefix = network.split(".")[0];
622
+ const targetPrefix = target.split(".")[0];
623
+ return networkPrefix === targetPrefix;
624
+ }
625
+ if (mask === "16") {
626
+ const networkPrefix = network.split(".").slice(0, 2).join(".");
627
+ const targetPrefix = target.split(".").slice(0, 2).join(".");
628
+ return networkPrefix === targetPrefix;
629
+ }
630
+ }
631
+ return target === cidr;
632
+ }
633
+ /**
634
+ * Extract IPs and Domains from string
635
+ */
636
+ extractTargets(text) {
637
+ const targets = /* @__PURE__ */ new Set();
638
+ const ipv4Regex = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
639
+ (text.match(ipv4Regex) || []).forEach((ip) => targets.add(ip));
640
+ const domainRegex = /\b[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}\b/gi;
641
+ (text.match(domainRegex) || []).forEach((d) => targets.add(d.toLowerCase()));
642
+ return targets;
643
+ }
644
+ };
645
+
646
+ // src/engine/approval.ts
647
+ var CATEGORY_APPROVAL = {
648
+ [SERVICE_CATEGORIES.NETWORK]: APPROVAL_LEVELS.CONFIRM,
649
+ [SERVICE_CATEGORIES.WEB]: APPROVAL_LEVELS.CONFIRM,
650
+ [SERVICE_CATEGORIES.DATABASE]: APPROVAL_LEVELS.REVIEW,
651
+ [SERVICE_CATEGORIES.AD]: APPROVAL_LEVELS.REVIEW,
652
+ [SERVICE_CATEGORIES.EMAIL]: APPROVAL_LEVELS.CONFIRM,
653
+ [SERVICE_CATEGORIES.REMOTE_ACCESS]: APPROVAL_LEVELS.REVIEW,
654
+ [SERVICE_CATEGORIES.FILE_SHARING]: APPROVAL_LEVELS.CONFIRM,
655
+ [SERVICE_CATEGORIES.CLOUD]: APPROVAL_LEVELS.REVIEW,
656
+ [SERVICE_CATEGORIES.CONTAINER]: APPROVAL_LEVELS.REVIEW,
657
+ [SERVICE_CATEGORIES.API]: APPROVAL_LEVELS.CONFIRM,
658
+ [SERVICE_CATEGORIES.WIRELESS]: APPROVAL_LEVELS.REVIEW,
659
+ [SERVICE_CATEGORIES.ICS]: APPROVAL_LEVELS.BLOCK
660
+ };
661
+ function getApprovalLevel(toolCall) {
662
+ if (toolCall.metadata?.approval) return toolCall.metadata.approval;
663
+ if (toolCall.metadata?.category) {
664
+ const categoryLevel = CATEGORY_APPROVAL[toolCall.metadata.category];
665
+ if (categoryLevel === APPROVAL_LEVELS.BLOCK) return APPROVAL_LEVELS.BLOCK;
666
+ }
667
+ const tool = toolCall.name;
668
+ const input = toolCall.input;
669
+ if (tool === TOOL_NAMES.RUN_CMD) {
670
+ const command = String(input.command || "").toLowerCase();
671
+ if (["whois", "dig", "curl -i"].some((p) => command.includes(p))) {
672
+ return APPROVAL_LEVELS.AUTO;
673
+ }
674
+ if ([CORE_BINARIES.NMAP, CORE_BINARIES.FFUF, CORE_BINARIES.NUCLEI].some((p) => command.includes(p))) {
675
+ if (toolCall.metadata?.category && CATEGORY_APPROVAL[toolCall.metadata.category] === APPROVAL_LEVELS.REVIEW) {
676
+ return APPROVAL_LEVELS.REVIEW;
677
+ }
678
+ return APPROVAL_LEVELS.CONFIRM;
679
+ }
680
+ if ([CORE_BINARIES.SQLMAP, CORE_BINARIES.METASPLOIT, "impacket"].some((p) => command.includes(p))) {
681
+ return APPROVAL_LEVELS.REVIEW;
682
+ }
683
+ }
684
+ const autoTools = [TOOL_NAMES.READ_FILE, TOOL_NAMES.SEARCH_CVE, TOOL_NAMES.PARSE_NMAP, TOOL_NAMES.WEB_SEARCH, TOOL_NAMES.ADD_FINDING];
685
+ if (autoTools.includes(tool)) return APPROVAL_LEVELS.AUTO;
686
+ return APPROVAL_LEVELS.CONFIRM;
687
+ }
688
+ var ApprovalGate = class {
689
+ constructor(autoApprove = false) {
690
+ this.autoApprove = autoApprove;
691
+ }
692
+ /**
693
+ * Set auto-approve mode
694
+ */
695
+ setAutoApprove(enabled) {
696
+ this.autoApprove = enabled;
697
+ }
698
+ async request(toolCall) {
699
+ if (this.autoApprove) return { approved: true, reason: "Auto-approve enabled" };
700
+ const level = getApprovalLevel(toolCall);
701
+ if (level === APPROVAL_LEVELS.AUTO) return { approved: true };
702
+ if (level === APPROVAL_LEVELS.BLOCK) return { approved: false, reason: "Policy blocked execution" };
703
+ return { approved: true, reason: `Auto-approved in non-interactive: ${toolCall.name}` };
704
+ }
705
+ };
706
+
707
+ // src/engine/tools-base.ts
708
+ import { execFileSync } from "child_process";
709
+ import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
710
+ import { join } from "path";
711
+ var DEFAULT_COMMAND_TIMEOUT = 3e4;
712
+ async function runCommand(command, args = [], options = {}) {
713
+ try {
714
+ const parts = command.trim().split(/\s+/);
715
+ const execPath = parts[0];
716
+ const execArgs = [...parts.slice(1), ...args];
717
+ const result = execFileSync(execPath, execArgs, {
718
+ encoding: "utf-8",
719
+ stdio: "pipe",
720
+ timeout: DEFAULT_COMMAND_TIMEOUT,
721
+ ...options
722
+ });
723
+ return {
724
+ success: true,
725
+ output: result.toString().trim()
726
+ };
727
+ } catch (error) {
728
+ return {
729
+ success: false,
730
+ output: "",
731
+ error: error.message || String(error)
732
+ };
733
+ }
734
+ }
735
+ async function readFileContent(filePath) {
736
+ try {
737
+ if (!existsSync(filePath)) {
738
+ return {
739
+ success: false,
740
+ output: "",
741
+ error: `File not found: ${filePath}`
742
+ };
743
+ }
744
+ const content = readFileSync(filePath, "utf-8");
745
+ return {
746
+ success: true,
747
+ output: content
748
+ };
749
+ } catch (error) {
750
+ return {
751
+ success: false,
752
+ output: "",
753
+ error: error.message || String(error)
754
+ };
755
+ }
756
+ }
757
+ async function writeFileContent(filePath, content) {
758
+ try {
759
+ const dir = join(filePath, "..");
760
+ if (!existsSync(dir)) {
761
+ mkdirSync(dir, { recursive: true });
762
+ }
763
+ writeFileSync(filePath, content, "utf-8");
764
+ return {
765
+ success: true,
766
+ output: `Written to ${filePath}`
767
+ };
768
+ } catch (error) {
769
+ return {
770
+ success: false,
771
+ output: "",
772
+ error: error.message || String(error)
773
+ };
774
+ }
775
+ }
776
+
777
+ // src/engine/tools-mid.ts
778
+ import { execFileSync as execFileSync2 } from "child_process";
779
+ async function parseNmap(xmlPath) {
780
+ try {
781
+ const fileResult = await readFileContent(xmlPath);
782
+ if (!fileResult.success) {
783
+ return fileResult;
784
+ }
785
+ const xmlContent = fileResult.output;
786
+ const results = {
787
+ targets: [],
788
+ summary: { totalTargets: 0, openPorts: 0, servicesFound: 0 }
789
+ };
790
+ const hostRegex = /<host[^>]*>[\s\S]*?<\/host>/g;
791
+ const hosts = xmlContent.match(hostRegex) || [];
792
+ for (const hostBlock of hosts) {
793
+ const ipMatch = hostBlock.match(/<address[^>]*addr="([^"]+)"/);
794
+ const ip = ipMatch ? ipMatch[1] : "";
795
+ const hostnameMatch = hostBlock.match(/<hostname[^>]*name="([^"]+)"/);
796
+ const hostname = hostnameMatch ? hostnameMatch[1] : void 0;
797
+ const ports = [];
798
+ const portRegex = /<port[^>]*protocol="([^"]*)"[^>]*portid="(\d+)">[\s\S]*?<\/port>/g;
799
+ let portMatch;
800
+ while ((portMatch = portRegex.exec(hostBlock)) !== null) {
801
+ const protocol = portMatch[1];
802
+ const port = parseInt(portMatch[2]);
803
+ const stateMatch = portMatch[0].match(/<state[^>]*state="([^"]+)"/);
804
+ const state = stateMatch ? stateMatch[1] : "";
805
+ const serviceMatch = portMatch[0].match(/<service[^>]*name="([^"]*)"(?:[^>]*version="([^"]*)")?/);
806
+ const service = serviceMatch ? serviceMatch[1] : void 0;
807
+ const version = serviceMatch && serviceMatch[2] ? serviceMatch[2] : void 0;
808
+ if (state === "open") {
809
+ ports.push({ port, protocol, state, service, version });
810
+ results.summary.openPorts++;
811
+ if (service) results.summary.servicesFound++;
812
+ }
813
+ }
814
+ if (ip) {
815
+ results.targets.push({ ip, hostname, ports });
816
+ results.summary.totalTargets++;
817
+ }
818
+ }
819
+ return {
820
+ success: true,
821
+ output: JSON.stringify(results, null, 2)
822
+ };
823
+ } catch (error) {
824
+ return {
825
+ success: false,
826
+ output: "",
827
+ error: error.message || String(error)
828
+ };
829
+ }
830
+ }
831
+ async function searchCVE(service, version) {
832
+ try {
833
+ return searchExploitDB(service, version);
834
+ } catch (error) {
835
+ return {
836
+ success: false,
837
+ output: "",
838
+ error: error.message || String(error)
839
+ };
840
+ }
841
+ }
842
+ async function searchExploitDB(service, version) {
843
+ try {
844
+ const query = version ? `${service} ${version}` : service;
845
+ try {
846
+ const output = execFileSync2("searchsploit", [query, "--color", "never"], {
847
+ encoding: "utf-8",
848
+ stdio: ["ignore", "pipe", "pipe"],
849
+ timeout: 1e4
850
+ });
851
+ const lines = output.trim().split("\n").slice(0, 20);
852
+ return {
853
+ success: true,
854
+ output: lines.join("\n") || `No exploits found for ${query}`
855
+ };
856
+ } catch (e) {
857
+ const stderr = String(e.stderr || "");
858
+ const stdout = String(e.stdout || "");
859
+ if (stderr.includes("No results")) {
860
+ return {
861
+ success: true,
862
+ output: `No exploits found for ${query}`
863
+ };
864
+ }
865
+ return {
866
+ success: true,
867
+ output: stdout || `No exploits found for ${query}`
868
+ };
869
+ }
870
+ } catch (error) {
871
+ return {
872
+ success: false,
873
+ output: "",
874
+ error: error.message || String(error)
875
+ };
876
+ }
877
+ }
878
+ async function webSearch(query, engine = "duckduckgo") {
879
+ try {
880
+ let results = [];
881
+ if (engine === "duckduckgo") {
882
+ const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
883
+ try {
884
+ const html = execFileSync2("curl", ["-s", "-L", "-A", "Mozilla/5.0", url], {
885
+ encoding: "utf-8",
886
+ stdio: ["ignore", "pipe", "pipe"],
887
+ timeout: 15e3
888
+ });
889
+ const titleRegex = /class="result__a"[^>]*>([^<]+)</g;
890
+ const matches = html.match(titleRegex) || [];
891
+ results = matches.map((m) => m.replace(/class="result__a"[^>]*>/, "")).slice(0, 10);
892
+ } catch (e) {
893
+ results = [`Web search failed: ${e.message}`];
894
+ }
895
+ } else {
896
+ results = ["Search engine not supported. Use: duckduckgo"];
897
+ }
898
+ const formatted = results.map((r, i) => `${i + 1}. ${r}`).join("\n");
899
+ return {
900
+ success: true,
901
+ output: formatted || `No results found for: ${query}`
902
+ };
903
+ } catch (error) {
904
+ return {
905
+ success: false,
906
+ output: "",
907
+ error: error.message || String(error)
908
+ };
909
+ }
910
+ }
911
+ async function extractURLs(text) {
912
+ try {
913
+ const urlRegex = /https?:\/\/[^\s<>"{}|\\^`\[\]]+/g;
914
+ const urls = text.match(urlRegex) || [];
915
+ const uniqueURLs = [...new Set(urls)];
916
+ return {
917
+ success: true,
918
+ output: JSON.stringify({
919
+ count: uniqueURLs.length,
920
+ urls: uniqueURLs
921
+ }, null, 2)
922
+ };
923
+ } catch (error) {
924
+ return {
925
+ success: false,
926
+ output: "",
927
+ error: error.message || String(error)
928
+ };
929
+ }
930
+ }
931
+
932
+ // src/engine/tools.ts
933
+ var ToolRegistry = class {
934
+ constructor(state, scopeGuard, approvalGate, events, subAgentExecutor) {
935
+ this.state = state;
936
+ this.scopeGuard = scopeGuard;
937
+ this.approvalGate = approvalGate;
938
+ this.events = events;
939
+ this.subAgentExecutor = subAgentExecutor;
940
+ this.registerLowLevelTools();
941
+ this.registerMidLevelTools();
942
+ this.registerHighLevelTools();
943
+ }
944
+ tools = /* @__PURE__ */ new Map();
945
+ /**
946
+ * Get all registered tools
947
+ */
948
+ getAll() {
949
+ return Array.from(this.tools.values());
950
+ }
951
+ /**
952
+ * Get tool by name
953
+ */
954
+ getTool(name) {
955
+ return this.tools.get(name);
956
+ }
957
+ /**
958
+ * Execute tool with full pipeline
959
+ */
960
+ async execute(toolCall) {
961
+ const tool = this.getTool(toolCall.name);
962
+ if (!tool) {
963
+ return {
964
+ success: false,
965
+ output: "",
966
+ error: `Unknown tool: ${toolCall.name}`
967
+ };
968
+ }
969
+ const scopeResult = this.scopeGuard.check(toolCall);
970
+ if (!scopeResult.allowed) {
971
+ return {
972
+ success: false,
973
+ output: "",
974
+ error: scopeResult.reason
975
+ };
976
+ }
977
+ const approval = await this.approvalGate.request(toolCall);
978
+ if (!approval.approved) {
979
+ this.state.logAction({
980
+ tool: toolCall.name,
981
+ command: JSON.stringify(toolCall.input),
982
+ approval: "denied",
983
+ noiseLevel: "none",
984
+ outputSummary: approval.reason || "Denied"
985
+ });
986
+ return {
987
+ success: false,
988
+ output: "",
989
+ error: approval.reason || "Execution denied"
990
+ };
991
+ }
992
+ const result = await tool.execute(toolCall.input);
993
+ this.state.logAction({
994
+ tool: toolCall.name,
995
+ command: JSON.stringify(toolCall.input),
996
+ approval: approval.approved ? "auto" : "user_confirmed",
997
+ noiseLevel: getNoiseLevel(toolCall),
998
+ outputSummary: result.output.slice(0, 200)
999
+ });
1000
+ return result;
1001
+ }
1002
+ /**
1003
+ * Register low-level tools
1004
+ */
1005
+ registerLowLevelTools() {
1006
+ this.tools.set(TOOL_NAMES.RUN_CMD, {
1007
+ name: TOOL_NAMES.RUN_CMD,
1008
+ description: "Execute shell command safely. Use for reconnaissance, scanning, and exploitation.",
1009
+ parameters: {
1010
+ command: { type: "string", description: 'The shell command to execute (e.g., "nmap -sV 10.0.0.1")' }
1011
+ },
1012
+ required: ["command"],
1013
+ execute: async (params) => {
1014
+ const command = params.command;
1015
+ return await runCommand(command, []);
1016
+ }
1017
+ });
1018
+ this.tools.set(TOOL_NAMES.READ_FILE, {
1019
+ name: TOOL_NAMES.READ_FILE,
1020
+ description: "Read local file content (logs, configs, evidence)",
1021
+ parameters: {
1022
+ path: { type: "string", description: "Path to the file" }
1023
+ },
1024
+ required: ["path"],
1025
+ execute: async (params) => {
1026
+ const filePath = params.path;
1027
+ return await readFileContent(filePath);
1028
+ }
1029
+ });
1030
+ this.tools.set(TOOL_NAMES.WRITE_FILE, {
1031
+ name: TOOL_NAMES.WRITE_FILE,
1032
+ description: "Write content to file (creates parent directories if needed)",
1033
+ parameters: {
1034
+ path: { type: "string", description: "Absolute path to the file" },
1035
+ content: { type: "string", description: "File content" }
1036
+ },
1037
+ required: ["path", "content"],
1038
+ execute: async (params) => {
1039
+ const filePath = params.path;
1040
+ const content = params.content;
1041
+ return await writeFileContent(filePath, content);
1042
+ }
1043
+ });
1044
+ }
1045
+ /**
1046
+ * Register mid-level tools
1047
+ */
1048
+ registerMidLevelTools() {
1049
+ this.tools.set(TOOL_NAMES.PARSE_NMAP, {
1050
+ name: TOOL_NAMES.PARSE_NMAP,
1051
+ description: "Parse nmap XML output to structured JSON",
1052
+ parameters: {
1053
+ path: { type: "string", description: "Path to nmap XML output file" }
1054
+ },
1055
+ required: ["path"],
1056
+ execute: async (params) => {
1057
+ const xmlPath = params.path;
1058
+ return await parseNmap(xmlPath);
1059
+ }
1060
+ });
1061
+ this.tools.set(TOOL_NAMES.SEARCH_CVE, {
1062
+ name: TOOL_NAMES.SEARCH_CVE,
1063
+ description: "Search CVE and Exploit-DB for vulnerabilities",
1064
+ parameters: {
1065
+ service: { type: "string", description: 'Service name (e.g., "apache", "ssh")' },
1066
+ version: { type: "string", description: "Version number (optional)" }
1067
+ },
1068
+ required: ["service"],
1069
+ execute: async (params) => {
1070
+ const service = params.service;
1071
+ const version = params.version;
1072
+ return await searchCVE(service, version);
1073
+ }
1074
+ });
1075
+ this.tools.set(TOOL_NAMES.WEB_SEARCH, {
1076
+ name: TOOL_NAMES.WEB_SEARCH,
1077
+ description: "Search web for reconnaissance info",
1078
+ parameters: {
1079
+ query: { type: "string", description: "Search query" }
1080
+ },
1081
+ required: ["query"],
1082
+ execute: async (params) => {
1083
+ const query = params.query;
1084
+ const engine = params.engine || "duckduckgo";
1085
+ return await webSearch(query, engine);
1086
+ }
1087
+ });
1088
+ this.tools.set(TOOL_NAMES.EXTRACT_URLS, {
1089
+ name: TOOL_NAMES.EXTRACT_URLS,
1090
+ description: "Extract and deduplicate URLs from text",
1091
+ parameters: {
1092
+ text: { type: "string", description: "Input text containing URLs" }
1093
+ },
1094
+ required: ["text"],
1095
+ execute: async (params) => {
1096
+ const text = params.text;
1097
+ return await extractURLs(text);
1098
+ }
1099
+ });
1100
+ }
1101
+ /**
1102
+ * Register high-level tools
1103
+ */
1104
+ registerHighLevelTools() {
1105
+ this.tools.set(TOOL_NAMES.SPAWN_SUB, {
1106
+ name: TOOL_NAMES.SPAWN_SUB,
1107
+ description: "Spawn a sub-agent with specialized prompt",
1108
+ parameters: {
1109
+ task: { type: "string", description: "Specific task for sub-agent" },
1110
+ agent_type: { type: "string", description: "Sub-agent type (recon, web, exploit)" }
1111
+ },
1112
+ required: ["task"],
1113
+ execute: async (params) => {
1114
+ const task = params.task;
1115
+ const agentType = params.agent_type || AGENT_ROLES.RECON;
1116
+ if (!this.subAgentExecutor) {
1117
+ return { success: false, output: "SubAgentExecutor not initialized in registry" };
1118
+ }
1119
+ const systemPrompt = this.getSpecializedPrompt(agentType, task);
1120
+ const result = await this.subAgentExecutor.executeSubTask(task, systemPrompt);
1121
+ return {
1122
+ success: result.completed,
1123
+ output: result.output
1124
+ };
1125
+ }
1126
+ });
1127
+ this.tools.set(TOOL_NAMES.ADD_FINDING, {
1128
+ name: TOOL_NAMES.ADD_FINDING,
1129
+ description: "Add a security finding",
1130
+ parameters: {
1131
+ title: { type: "string", description: "Finding title" },
1132
+ severity: { type: "string", description: "Severity (info, low, medium, high, critical)" },
1133
+ affected: { type: "array", items: { type: "string" }, description: "Affected host:port" },
1134
+ description: { type: "string", description: "Finding detail" }
1135
+ },
1136
+ required: ["title", "severity"],
1137
+ execute: async (params) => {
1138
+ this.state.addFinding({
1139
+ id: Math.random().toString(ID_RADIX).substring(ID_LENGTH),
1140
+ title: params.title,
1141
+ severity: params.severity,
1142
+ affected: params.affected || [],
1143
+ description: params.description || "",
1144
+ evidence: [],
1145
+ verified: false,
1146
+ remediation: "",
1147
+ foundAt: Date.now()
1148
+ });
1149
+ return {
1150
+ success: true,
1151
+ output: `Finding added: ${params.title}`
1152
+ };
1153
+ }
1154
+ });
1155
+ this.tools.set(TOOL_NAMES.UPDATE_TODO, {
1156
+ name: TOOL_NAMES.UPDATE_TODO,
1157
+ description: "Update plan / TODO list",
1158
+ parameters: {
1159
+ action: { type: "string", description: "add, complete" },
1160
+ content: { type: "string", description: "Task content" }
1161
+ },
1162
+ required: ["action"],
1163
+ execute: async (params) => {
1164
+ if (params.action === TODO_ACTIONS.ADD) {
1165
+ this.state.addTodo(params.content, params.priority);
1166
+ } else if (params.action === TODO_ACTIONS.COMPLETE) {
1167
+ this.state.completeTodo(params.id);
1168
+ }
1169
+ return {
1170
+ success: true,
1171
+ output: "TODO updated"
1172
+ };
1173
+ }
1174
+ });
1175
+ this.tools.set(TOOL_NAMES.GET_STATE, {
1176
+ name: TOOL_NAMES.GET_STATE,
1177
+ description: "Get current engagement state summary",
1178
+ parameters: {},
1179
+ execute: async (params) => {
1180
+ return {
1181
+ success: true,
1182
+ output: this.state.toPrompt()
1183
+ };
1184
+ }
1185
+ });
1186
+ this.tools.set(TOOL_NAMES.SET_SCOPE, {
1187
+ name: TOOL_NAMES.SET_SCOPE,
1188
+ description: "Set engagement scope",
1189
+ parameters: {
1190
+ allowed: { type: "array", items: { type: "string" }, description: "Allowed CIDRs or domains" },
1191
+ exclusions: { type: "array", items: { type: "string" }, description: "Excluded targets" }
1192
+ },
1193
+ required: ["allowed"],
1194
+ execute: async (params) => {
1195
+ const allowed = params.allowed || [];
1196
+ const exclusions = params.exclusions || [];
1197
+ this.state.setScope({
1198
+ allowedCidrs: allowed.filter((a) => a.includes("/")),
1199
+ allowedDomains: allowed.filter((a) => !a.includes("/")),
1200
+ exclusions,
1201
+ noDOS: true,
1202
+ noSocial: true
1203
+ });
1204
+ return {
1205
+ success: true,
1206
+ output: `Scope set: ${allowed.length} targets`
1207
+ };
1208
+ }
1209
+ });
1210
+ this.tools.set(TOOL_NAMES.ADD_TARGET, {
1211
+ name: TOOL_NAMES.ADD_TARGET,
1212
+ description: "Add target to engagement",
1213
+ parameters: {
1214
+ ip: { type: "string", description: "IP address" },
1215
+ hostname: { type: "string", description: "Hostname (optional)" }
1216
+ },
1217
+ required: ["ip"],
1218
+ execute: async (params) => {
1219
+ const ip = params.ip;
1220
+ const hostname = params.hostname;
1221
+ this.state.addTarget({
1222
+ ip,
1223
+ hostname,
1224
+ ports: [],
1225
+ tags: [],
1226
+ firstSeen: Date.now()
1227
+ });
1228
+ return {
1229
+ success: true,
1230
+ output: `Target added: ${ip}`
1231
+ };
1232
+ }
1233
+ });
1234
+ this.tools.set(TOOL_NAMES.ASK_USER, {
1235
+ name: TOOL_NAMES.ASK_USER,
1236
+ description: "Ask the user a question and wait for input. Use when you need clarification, authorization, or additional information.",
1237
+ parameters: {
1238
+ question: { type: "string", description: "Question to ask" }
1239
+ },
1240
+ required: ["question"],
1241
+ execute: async (params) => {
1242
+ const question = params.question;
1243
+ return {
1244
+ success: true,
1245
+ output: `[ASK_USER] ${question}
1246
+ (Waiting for user response via TUI input)`
1247
+ };
1248
+ }
1249
+ });
1250
+ }
1251
+ /**
1252
+ * Get specialized prompt for sub-agent type
1253
+ * Opus4.7: Agents are prompts, not code
1254
+ */
1255
+ getSpecializedPrompt(agentType, task) {
1256
+ const prompts = {
1257
+ [AGENT_ROLES.RECON]: `You are a RECONNAISSANCE specialist.
1258
+ Your task: ${task}
1259
+ Focus on: Information gathering, OSINT, passive reconnaissance, target discovery
1260
+ Return: Structured target and port information for the main agent.`,
1261
+ [AGENT_ROLES.WEB]: `You are a WEB APPLICATION specialist.
1262
+ Your task: ${task}
1263
+ Focus on: Web app discovery, enumeration, vulnerability scanning
1264
+ Return: Web application structure, endpoints, vulnerabilities found.`,
1265
+ [AGENT_ROLES.EXPLOTER]: `You are an EXPLOITATION specialist.
1266
+ Your task: ${task}
1267
+ Focus on: Exploit research, proof-of-concept testing
1268
+ Return: Exploit results, shell access, credentials obtained.`
1269
+ };
1270
+ return prompts[agentType] || prompts[AGENT_ROLES.RECON];
1271
+ }
1272
+ };
1273
+ function getNoiseLevel(toolCall) {
1274
+ if (toolCall.name === TOOL_NAMES.RUN_CMD) {
1275
+ const command = String(toolCall.input.command || "").toLowerCase();
1276
+ if (command.includes(CORE_BINARIES.NMAP) || command.includes(CORE_BINARIES.MASSCAN)) return NOISE_LEVELS.HIGH;
1277
+ if (command.includes(CORE_BINARIES.NUCLEI) || command.includes(CORE_BINARIES.NIKTO)) return NOISE_LEVELS.HIGH;
1278
+ if (command.includes(CORE_BINARIES.FFUF) || command.includes(CORE_BINARIES.GOBUSTER)) return NOISE_LEVELS.MEDIUM;
1279
+ }
1280
+ return NOISE_LEVELS.LOW;
1281
+ }
1282
+
1283
+ // src/domains/registry.ts
1284
+ import { join as join2, dirname } from "path";
1285
+ import { fileURLToPath } from "url";
1286
+ var __dirname = dirname(fileURLToPath(import.meta.url));
1287
+ var DOMAINS = {
1288
+ [SERVICE_CATEGORIES.NETWORK]: {
1289
+ id: SERVICE_CATEGORIES.NETWORK,
1290
+ name: "Network Infrastructure",
1291
+ description: "Vulnerability scanning, port mapping, and network service exploitation.",
1292
+ promptPath: join2(__dirname, "network/prompt.md")
1293
+ },
1294
+ [SERVICE_CATEGORIES.WEB]: {
1295
+ id: SERVICE_CATEGORIES.WEB,
1296
+ name: "Web Application",
1297
+ description: "Web app security testing, injection attacks, and auth bypass.",
1298
+ promptPath: join2(__dirname, "web/prompt.md")
1299
+ },
1300
+ [SERVICE_CATEGORIES.DATABASE]: {
1301
+ id: SERVICE_CATEGORIES.DATABASE,
1302
+ name: "Database Security",
1303
+ description: "SQL injection, database enumeration, and data extraction.",
1304
+ promptPath: join2(__dirname, "database/prompt.md")
1305
+ },
1306
+ [SERVICE_CATEGORIES.AD]: {
1307
+ id: SERVICE_CATEGORIES.AD,
1308
+ name: "Active Directory",
1309
+ description: "Kerberos, LDAP, and Windows domain privilege escalation.",
1310
+ promptPath: join2(__dirname, "ad/prompt.md")
1311
+ },
1312
+ [SERVICE_CATEGORIES.EMAIL]: {
1313
+ id: SERVICE_CATEGORIES.EMAIL,
1314
+ name: "Email Services",
1315
+ description: "SMTP, IMAP, POP3 security and user enumeration.",
1316
+ promptPath: join2(__dirname, "email/prompt.md")
1317
+ },
1318
+ [SERVICE_CATEGORIES.REMOTE_ACCESS]: {
1319
+ id: SERVICE_CATEGORIES.REMOTE_ACCESS,
1320
+ name: "Remote Access",
1321
+ description: "SSH, RDP, VNC and other remote control protocols.",
1322
+ promptPath: join2(__dirname, "remote-access/prompt.md")
1323
+ },
1324
+ [SERVICE_CATEGORIES.FILE_SHARING]: {
1325
+ id: SERVICE_CATEGORIES.FILE_SHARING,
1326
+ name: "File Sharing",
1327
+ description: "SMB, NFS, FTP and shared resource security.",
1328
+ promptPath: join2(__dirname, "file-sharing/prompt.md")
1329
+ },
1330
+ [SERVICE_CATEGORIES.CLOUD]: {
1331
+ id: SERVICE_CATEGORIES.CLOUD,
1332
+ name: "Cloud Infrastructure",
1333
+ description: "AWS, Azure, and GCP security and misconfiguration.",
1334
+ promptPath: join2(__dirname, "cloud/prompt.md")
1335
+ },
1336
+ [SERVICE_CATEGORIES.CONTAINER]: {
1337
+ id: SERVICE_CATEGORIES.CONTAINER,
1338
+ name: "Container Systems",
1339
+ description: "Docker and Kubernetes security testing.",
1340
+ promptPath: join2(__dirname, "container/prompt.md")
1341
+ },
1342
+ [SERVICE_CATEGORIES.API]: {
1343
+ id: SERVICE_CATEGORIES.API,
1344
+ name: "API Security",
1345
+ description: "REST, GraphQL, and SOAP API security testing.",
1346
+ promptPath: join2(__dirname, "api/prompt.md")
1347
+ },
1348
+ [SERVICE_CATEGORIES.WIRELESS]: {
1349
+ id: SERVICE_CATEGORIES.WIRELESS,
1350
+ name: "Wireless Networks",
1351
+ description: "WiFi and Bluetooth security testing.",
1352
+ promptPath: join2(__dirname, "wireless/prompt.md")
1353
+ },
1354
+ [SERVICE_CATEGORIES.ICS]: {
1355
+ id: SERVICE_CATEGORIES.ICS,
1356
+ name: "Industrial Systems",
1357
+ description: "Critical infrastructure - Modbus, DNP3, ENIP.",
1358
+ promptPath: join2(__dirname, "ics/prompt.md")
1359
+ }
1360
+ };
1361
+
1362
+ // src/engine/tools-registry.ts
1363
+ var PORT_CATEGORY_MAP = {
1364
+ // Web
1365
+ 80: SERVICE_CATEGORIES.WEB,
1366
+ 443: SERVICE_CATEGORIES.WEB,
1367
+ 8080: SERVICE_CATEGORIES.WEB,
1368
+ 8443: SERVICE_CATEGORIES.WEB,
1369
+ 8e3: SERVICE_CATEGORIES.WEB,
1370
+ 8888: SERVICE_CATEGORIES.WEB,
1371
+ // Database
1372
+ 1433: SERVICE_CATEGORIES.DATABASE,
1373
+ // MSSQL
1374
+ 3306: SERVICE_CATEGORIES.DATABASE,
1375
+ // MySQL
1376
+ 5432: SERVICE_CATEGORIES.DATABASE,
1377
+ // PostgreSQL
1378
+ 27017: SERVICE_CATEGORIES.DATABASE,
1379
+ // MongoDB
1380
+ 6379: SERVICE_CATEGORIES.DATABASE,
1381
+ // Redis
1382
+ 9042: SERVICE_CATEGORIES.DATABASE,
1383
+ // Cassandra
1384
+ 9200: SERVICE_CATEGORIES.DATABASE,
1385
+ // Elasticsearch
1386
+ // Active Directory
1387
+ 88: SERVICE_CATEGORIES.AD,
1388
+ // Kerberos
1389
+ 389: SERVICE_CATEGORIES.AD,
1390
+ // LDAP
1391
+ 636: SERVICE_CATEGORIES.AD,
1392
+ // LDAPS
1393
+ 3268: SERVICE_CATEGORIES.AD,
1394
+ // LDAP GC
1395
+ 3269: SERVICE_CATEGORIES.AD,
1396
+ // LDAP GC SSL
1397
+ 445: SERVICE_CATEGORIES.AD,
1398
+ // SMB (also file_sharing)
1399
+ // Email
1400
+ 25: SERVICE_CATEGORIES.EMAIL,
1401
+ // SMTP
1402
+ 587: SERVICE_CATEGORIES.EMAIL,
1403
+ // SMTP Submission
1404
+ 465: SERVICE_CATEGORIES.EMAIL,
1405
+ // SMTPS
1406
+ 110: SERVICE_CATEGORIES.EMAIL,
1407
+ // POP3
1408
+ 143: SERVICE_CATEGORIES.EMAIL,
1409
+ // IMAP
1410
+ 993: SERVICE_CATEGORIES.EMAIL,
1411
+ // IMAPS
1412
+ 995: SERVICE_CATEGORIES.EMAIL,
1413
+ // POP3S
1414
+ // Remote Access
1415
+ 22: SERVICE_CATEGORIES.REMOTE_ACCESS,
1416
+ // SSH
1417
+ 3389: SERVICE_CATEGORIES.REMOTE_ACCESS,
1418
+ // RDP
1419
+ 5900: SERVICE_CATEGORIES.REMOTE_ACCESS,
1420
+ // VNC
1421
+ 5901: SERVICE_CATEGORIES.REMOTE_ACCESS,
1422
+ // VNC
1423
+ // File Sharing
1424
+ 21: SERVICE_CATEGORIES.FILE_SHARING,
1425
+ // FTP
1426
+ 139: SERVICE_CATEGORIES.FILE_SHARING,
1427
+ // NetBIOS
1428
+ 2049: SERVICE_CATEGORIES.FILE_SHARING,
1429
+ // NFS
1430
+ 111: SERVICE_CATEGORIES.FILE_SHARING,
1431
+ // RPC
1432
+ // Container
1433
+ 2375: SERVICE_CATEGORIES.CONTAINER,
1434
+ // Docker API
1435
+ 2376: SERVICE_CATEGORIES.CONTAINER,
1436
+ // Docker API TLS
1437
+ 5e3: SERVICE_CATEGORIES.CONTAINER,
1438
+ // Docker Registry
1439
+ // Cloud (detected via banner, common on 443)
1440
+ // API
1441
+ 3e3: SERVICE_CATEGORIES.API,
1442
+ 5001: SERVICE_CATEGORIES.API,
1443
+ 8001: SERVICE_CATEGORIES.API,
1444
+ // Wireless
1445
+ // ICS
1446
+ 502: SERVICE_CATEGORIES.ICS,
1447
+ // Modbus
1448
+ 102: SERVICE_CATEGORIES.ICS,
1449
+ // ISO-TSAP
1450
+ 2e4: SERVICE_CATEGORIES.ICS,
1451
+ // DNP3
1452
+ 44818: SERVICE_CATEGORIES.ICS
1453
+ // Ethernet/IP
1454
+ };
1455
+ var SERVICE_CATEGORY_MAP = {
1456
+ // Web
1457
+ [SERVICES.HTTP]: SERVICE_CATEGORIES.WEB,
1458
+ [SERVICES.HTTPS]: SERVICE_CATEGORIES.WEB,
1459
+ "http-proxy": SERVICE_CATEGORIES.WEB,
1460
+ "ssl/http": SERVICE_CATEGORIES.WEB,
1461
+ "nginx": SERVICE_CATEGORIES.WEB,
1462
+ "apache": SERVICE_CATEGORIES.WEB,
1463
+ "iis": SERVICE_CATEGORIES.WEB,
1464
+ // Database
1465
+ [SERVICES.MYSQL]: SERVICE_CATEGORIES.DATABASE,
1466
+ [SERVICES.MSSQL]: SERVICE_CATEGORIES.DATABASE,
1467
+ [SERVICES.POSTGRES]: SERVICE_CATEGORIES.DATABASE,
1468
+ [SERVICES.MONGODB]: SERVICE_CATEGORIES.DATABASE,
1469
+ [SERVICES.REDIS]: SERVICE_CATEGORIES.DATABASE,
1470
+ [SERVICES.ELASTIC]: SERVICE_CATEGORIES.DATABASE,
1471
+ "cassandra": SERVICE_CATEGORIES.DATABASE,
1472
+ // Active Directory
1473
+ "kerberos": SERVICE_CATEGORIES.AD,
1474
+ "ldap": SERVICE_CATEGORIES.AD,
1475
+ "ldaps": SERVICE_CATEGORIES.AD,
1476
+ "microsoft-ds": SERVICE_CATEGORIES.AD,
1477
+ "netbios-ssn": SERVICE_CATEGORIES.AD,
1478
+ // Email
1479
+ "smtp": SERVICE_CATEGORIES.EMAIL,
1480
+ "pop3": SERVICE_CATEGORIES.EMAIL,
1481
+ "imap": SERVICE_CATEGORIES.EMAIL,
1482
+ // Remote Access
1483
+ [SERVICES.SSH]: SERVICE_CATEGORIES.REMOTE_ACCESS,
1484
+ "ms-wbt-server": SERVICE_CATEGORIES.REMOTE_ACCESS,
1485
+ // RDP
1486
+ "vnc": SERVICE_CATEGORIES.REMOTE_ACCESS,
1487
+ // File Sharing
1488
+ [SERVICES.FTP]: SERVICE_CATEGORIES.FILE_SHARING,
1489
+ "ftp-data": SERVICE_CATEGORIES.FILE_SHARING,
1490
+ "nfs": SERVICE_CATEGORIES.FILE_SHARING,
1491
+ "rpcbind": SERVICE_CATEGORIES.FILE_SHARING,
1492
+ // Container
1493
+ "docker": SERVICE_CATEGORIES.CONTAINER,
1494
+ "docker-swap": SERVICE_CATEGORIES.CONTAINER,
1495
+ // Cloud APIs
1496
+ "aws-s3": SERVICE_CATEGORIES.CLOUD,
1497
+ "azure-storage": SERVICE_CATEGORIES.CLOUD,
1498
+ // Wireless
1499
+ // ICS
1500
+ "modbus": SERVICE_CATEGORIES.ICS,
1501
+ "iso-tsap": SERVICE_CATEGORIES.ICS,
1502
+ "dnp3": SERVICE_CATEGORIES.ICS,
1503
+ "enip": SERVICE_CATEGORIES.ICS
1504
+ };
1505
+ var CATEGORY_APPROVAL2 = {
1506
+ [SERVICE_CATEGORIES.NETWORK]: APPROVAL_LEVELS.CONFIRM,
1507
+ [SERVICE_CATEGORIES.WEB]: APPROVAL_LEVELS.CONFIRM,
1508
+ [SERVICE_CATEGORIES.DATABASE]: APPROVAL_LEVELS.REVIEW,
1509
+ [SERVICE_CATEGORIES.AD]: APPROVAL_LEVELS.REVIEW,
1510
+ [SERVICE_CATEGORIES.EMAIL]: APPROVAL_LEVELS.CONFIRM,
1511
+ [SERVICE_CATEGORIES.REMOTE_ACCESS]: APPROVAL_LEVELS.REVIEW,
1512
+ [SERVICE_CATEGORIES.FILE_SHARING]: APPROVAL_LEVELS.CONFIRM,
1513
+ [SERVICE_CATEGORIES.CLOUD]: APPROVAL_LEVELS.REVIEW,
1514
+ [SERVICE_CATEGORIES.CONTAINER]: APPROVAL_LEVELS.REVIEW,
1515
+ [SERVICE_CATEGORIES.API]: APPROVAL_LEVELS.CONFIRM,
1516
+ [SERVICE_CATEGORIES.WIRELESS]: APPROVAL_LEVELS.REVIEW,
1517
+ [SERVICE_CATEGORIES.ICS]: APPROVAL_LEVELS.BLOCK
1518
+ // Requires explicit written authorization
1519
+ };
1520
+ var CATEGORY_DESCRIPTIONS = Object.entries(DOMAINS).reduce((acc, [id, info]) => {
1521
+ acc[id] = info.description;
1522
+ return acc;
1523
+ }, {});
1524
+ var CategorizedToolRegistry = class extends ToolRegistry {
1525
+ categories;
1526
+ constructor(state, scopeGuard, approvalGate, events, subAgentExecutor) {
1527
+ super(state, scopeGuard, approvalGate, events, subAgentExecutor);
1528
+ this.categories = /* @__PURE__ */ new Map();
1529
+ this.initializeCategories();
1530
+ }
1531
+ /**
1532
+ * Initialize all tool categories with core tools from parent ToolRegistry
1533
+ *
1534
+ * Every service category gets the base tools (run_cmd, read_file, write_file, etc.)
1535
+ * Sub-agents need at least these core tools to function.
1536
+ * spawn_sub is explicitly excluded to prevent recursion (1-level only).
1537
+ */
1538
+ initializeCategories() {
1539
+ const coreToolNames = [
1540
+ TOOL_NAMES.RUN_CMD,
1541
+ TOOL_NAMES.READ_FILE,
1542
+ TOOL_NAMES.WRITE_FILE,
1543
+ TOOL_NAMES.PARSE_NMAP,
1544
+ TOOL_NAMES.SEARCH_CVE,
1545
+ TOOL_NAMES.WEB_SEARCH,
1546
+ TOOL_NAMES.EXTRACT_URLS,
1547
+ TOOL_NAMES.ADD_FINDING,
1548
+ TOOL_NAMES.UPDATE_TODO,
1549
+ TOOL_NAMES.GET_STATE,
1550
+ TOOL_NAMES.ADD_TARGET,
1551
+ TOOL_NAMES.ASK_USER
1552
+ ];
1553
+ const coreTools = [];
1554
+ for (const name of coreToolNames) {
1555
+ const tool = this.getTool(name);
1556
+ if (tool) {
1557
+ coreTools.push(tool);
1558
+ }
1559
+ }
1560
+ for (const category of Object.keys(CATEGORY_DESCRIPTIONS)) {
1561
+ this.categories.set(category, {
1562
+ name: category,
1563
+ description: CATEGORY_DESCRIPTIONS[category],
1564
+ tools: [...coreTools],
1565
+ // Each category gets a copy of core tools
1566
+ subAgentPrompt: "",
1567
+ // Loaded from Domain Registry or prompt files at runtime
1568
+ dangerLevel: this.getDangerLevel(category),
1569
+ defaultApproval: CATEGORY_APPROVAL2[category],
1570
+ commonPorts: this.getCommonPorts(category),
1571
+ commonServices: this.getCommonServices(category)
1572
+ });
1573
+ }
1574
+ }
1575
+ /**
1576
+ * Get danger level for category
1577
+ */
1578
+ getDangerLevel(category) {
1579
+ const passive = [SERVICE_CATEGORIES.NETWORK];
1580
+ const active = [SERVICE_CATEGORIES.WEB, SERVICE_CATEGORIES.API, SERVICE_CATEGORIES.EMAIL, SERVICE_CATEGORIES.FILE_SHARING];
1581
+ if (passive.includes(category)) return DANGER_LEVELS.PASSIVE;
1582
+ if (active.includes(category)) return DANGER_LEVELS.ACTIVE;
1583
+ return DANGER_LEVELS.EXPLOIT;
1584
+ }
1585
+ /**
1586
+ * Get common ports for category
1587
+ */
1588
+ getCommonPorts(category) {
1589
+ const ports = {
1590
+ [SERVICE_CATEGORIES.NETWORK]: [1, 7, 9, 21, 22, 23, 25, 53, 79, 80, 110, 111, 135, 139, 143, 443, 445, 993, 995, 1723, 3306, 3389, 5432, 5900, 8080],
1591
+ [SERVICE_CATEGORIES.WEB]: [80, 443, 8e3, 8080, 8443, 8888],
1592
+ [SERVICE_CATEGORIES.DATABASE]: [1433, 3306, 5432, 6379, 9042, 9200, 27017, 1521],
1593
+ [SERVICE_CATEGORIES.AD]: [88, 135, 139, 389, 445, 636, 3268, 3269, 5722],
1594
+ [SERVICE_CATEGORIES.EMAIL]: [25, 110, 143, 465, 587, 993, 995],
1595
+ [SERVICE_CATEGORIES.REMOTE_ACCESS]: [22, 23, 3389, 5900, 5901],
1596
+ [SERVICE_CATEGORIES.FILE_SHARING]: [21, 139, 445, 2049, 111],
1597
+ [SERVICE_CATEGORIES.CLOUD]: [443],
1598
+ // Detected via banner
1599
+ [SERVICE_CATEGORIES.CONTAINER]: [2375, 2376, 5e3],
1600
+ [SERVICE_CATEGORIES.API]: [3e3, 5e3, 5001, 8e3, 8001, 8080],
1601
+ [SERVICE_CATEGORIES.WIRELESS]: [],
1602
+ [SERVICE_CATEGORIES.ICS]: [102, 502, 44818, 2e4]
1603
+ };
1604
+ return ports[category] || [];
1605
+ }
1606
+ /**
1607
+ * Get common services for category
1608
+ */
1609
+ getCommonServices(category) {
1610
+ const services = {
1611
+ [SERVICE_CATEGORIES.NETWORK]: [SERVICES.FTP, SERVICES.SSH, "telnet", "smtp", SERVICES.HTTP, "pop3", "imap", SERVICES.HTTPS],
1612
+ [SERVICE_CATEGORIES.WEB]: [SERVICES.HTTP, SERVICES.HTTPS, "nginx", "apache", "iis"],
1613
+ [SERVICE_CATEGORIES.DATABASE]: [SERVICES.MYSQL, SERVICES.MSSQL, SERVICES.POSTGRES, SERVICES.MONGODB, SERVICES.REDIS, SERVICES.ELASTIC],
1614
+ [SERVICE_CATEGORIES.AD]: ["kerberos", "ldap", "microsoft-ds", "netbios-ssn"],
1615
+ [SERVICE_CATEGORIES.EMAIL]: ["smtp", "pop3", "imap"],
1616
+ [SERVICE_CATEGORIES.REMOTE_ACCESS]: [SERVICES.SSH, "ms-wbt-server", "vnc"],
1617
+ [SERVICE_CATEGORIES.FILE_SHARING]: [SERVICES.FTP, "microsoft-ds", "nfs"],
1618
+ [SERVICE_CATEGORIES.CLOUD]: [SERVICES.HTTP, SERVICES.HTTPS],
1619
+ // Detected via banner analysis
1620
+ [SERVICE_CATEGORIES.CONTAINER]: ["docker"],
1621
+ [SERVICE_CATEGORIES.API]: [SERVICES.HTTP, SERVICES.HTTPS],
1622
+ [SERVICE_CATEGORIES.WIRELESS]: [],
1623
+ [SERVICE_CATEGORIES.ICS]: ["modbus", "iso-tsap", "dnp3", "enip"]
1624
+ };
1625
+ return services[category] || [];
1626
+ }
1627
+ /**
1628
+ * Get all tools in a category
1629
+ */
1630
+ getByCategory(category) {
1631
+ return this.categories.get(category)?.tools || [];
1632
+ }
1633
+ /**
1634
+ * Get category definition
1635
+ */
1636
+ getCategory(category) {
1637
+ return this.categories.get(category);
1638
+ }
1639
+ /**
1640
+ * Get all categories
1641
+ */
1642
+ getAllCategories() {
1643
+ return Array.from(this.categories.values());
1644
+ }
1645
+ /**
1646
+ * Suggest tools based on discovered target
1647
+ */
1648
+ suggestTools(target) {
1649
+ const suggestions = [];
1650
+ for (const port of target.ports) {
1651
+ const category = this.detectCategoryFromPort(port.port, port.service);
1652
+ if (category && !suggestions.find((s) => s.category === category)) {
1653
+ const catTools = this.getByCategory(category);
1654
+ suggestions.push({
1655
+ category,
1656
+ tools: catTools.map((t) => t.name)
1657
+ });
1658
+ }
1659
+ }
1660
+ if (target.hostname) {
1661
+ const cloudCat = this.detectCloudProvider(target.hostname);
1662
+ if (cloudCat && !suggestions.find((s) => s.category === cloudCat)) {
1663
+ const catTools = this.getByCategory(cloudCat);
1664
+ suggestions.push({
1665
+ category: cloudCat,
1666
+ tools: catTools.map((t) => t.name)
1667
+ });
1668
+ }
1669
+ }
1670
+ return suggestions;
1671
+ }
1672
+ /**
1673
+ * Detect category from port number and service name
1674
+ */
1675
+ detectCategoryFromPort(port, serviceName) {
1676
+ if (PORT_CATEGORY_MAP[port]) {
1677
+ return PORT_CATEGORY_MAP[port];
1678
+ }
1679
+ if (serviceName && SERVICE_CATEGORY_MAP[serviceName.toLowerCase()]) {
1680
+ return SERVICE_CATEGORY_MAP[serviceName.toLowerCase()];
1681
+ }
1682
+ if (port >= 8e3 && port <= 9e3) return SERVICE_CATEGORIES.WEB;
1683
+ if (port >= 3e3 && port <= 3500) return SERVICE_CATEGORIES.API;
1684
+ return null;
1685
+ }
1686
+ /**
1687
+ * Detect cloud provider from hostname
1688
+ */
1689
+ detectCloudProvider(hostname) {
1690
+ const cloudProviders = [
1691
+ "amazonaws.com",
1692
+ "aws",
1693
+ "s3.amazonaws.com",
1694
+ "azure.com",
1695
+ "azurewebsites.net",
1696
+ "windows.net",
1697
+ "gcp",
1698
+ "googleusercontent.com",
1699
+ "appspot.com",
1700
+ "digitalocean.com",
1701
+ "do.co",
1702
+ "herokuapp.com",
1703
+ "vercel.app",
1704
+ "netlify.app"
1705
+ ];
1706
+ const lowerHostname = hostname.toLowerCase();
1707
+ for (const provider of cloudProviders) {
1708
+ if (lowerHostname.includes(provider)) {
1709
+ return SERVICE_CATEGORIES.CLOUD;
1710
+ }
1711
+ }
1712
+ return null;
1713
+ }
1714
+ /**
1715
+ * Suggest sub-agent type for target
1716
+ */
1717
+ suggestSubAgent(target) {
1718
+ const suggestions = this.suggestTools(target);
1719
+ const priority = [
1720
+ SERVICE_CATEGORIES.ICS,
1721
+ // Critical - block
1722
+ SERVICE_CATEGORIES.AD,
1723
+ // High value - review
1724
+ SERVICE_CATEGORIES.DATABASE,
1725
+ // High value - review
1726
+ SERVICE_CATEGORIES.CLOUD,
1727
+ // High value - review
1728
+ SERVICE_CATEGORIES.CONTAINER,
1729
+ // Escalation - review
1730
+ SERVICE_CATEGORIES.REMOTE_ACCESS,
1731
+ // Access - review
1732
+ SERVICE_CATEGORIES.WEB,
1733
+ // Common - confirm
1734
+ SERVICE_CATEGORIES.API,
1735
+ // Common - confirm
1736
+ SERVICE_CATEGORIES.EMAIL,
1737
+ // Info - confirm
1738
+ SERVICE_CATEGORIES.FILE_SHARING,
1739
+ // Access - confirm
1740
+ SERVICE_CATEGORIES.NETWORK,
1741
+ // Basic - confirm
1742
+ SERVICE_CATEGORIES.WIRELESS
1743
+ // Special - review
1744
+ ];
1745
+ for (const cat of priority) {
1746
+ if (suggestions.find((s) => s.category === cat)) {
1747
+ return cat;
1748
+ }
1749
+ }
1750
+ return PHASES.RECON;
1751
+ }
1752
+ /**
1753
+ * Get approval level for category
1754
+ */
1755
+ getApprovalForCategory(category) {
1756
+ return CATEGORY_APPROVAL2[category];
1757
+ }
1758
+ /**
1759
+ * Check if category requires special handling
1760
+ */
1761
+ isHighRiskCategory(category) {
1762
+ return [SERVICE_CATEGORIES.ICS, SERVICE_CATEGORIES.AD, SERVICE_CATEGORIES.DATABASE, SERVICE_CATEGORIES.CLOUD, SERVICE_CATEGORIES.CONTAINER].includes(category);
1763
+ }
1764
+ /**
1765
+ * Get service fingerprint from port data
1766
+ */
1767
+ fingerprintService(port) {
1768
+ const category = this.detectCategoryFromPort(port.port, port.service);
1769
+ if (!category) return null;
1770
+ return {
1771
+ port: port.port,
1772
+ service: port.service,
1773
+ version: port.version,
1774
+ banner: port.notes[0] || void 0,
1775
+ category,
1776
+ confidence: port.version ? 0.9 : 0.7
1777
+ };
1778
+ }
1779
+ /**
1780
+ * Get category description
1781
+ */
1782
+ getCategoryDescription(category) {
1783
+ return CATEGORY_DESCRIPTIONS[category];
1784
+ }
1785
+ };
1786
+
1787
+ // src/shared/utils/logger.ts
1788
+ var COLORS = {
1789
+ reset: "\x1B[0m",
1790
+ dim: "\x1B[2m",
1791
+ red: "\x1B[31m",
1792
+ yellow: "\x1B[33m",
1793
+ green: "\x1B[32m",
1794
+ blue: "\x1B[34m",
1795
+ magenta: "\x1B[35m",
1796
+ cyan: "\x1B[36m"
1797
+ };
1798
+ var levelColors = {
1799
+ [0 /* DEBUG */]: COLORS.dim,
1800
+ [1 /* INFO */]: COLORS.green,
1801
+ [2 /* WARN */]: COLORS.yellow,
1802
+ [3 /* ERROR */]: COLORS.red,
1803
+ [999 /* SILENT */]: COLORS.reset
1804
+ };
1805
+ var levelNames = {
1806
+ [0 /* DEBUG */]: "DEBUG",
1807
+ [1 /* INFO */]: "INFO",
1808
+ [2 /* WARN */]: "WARN",
1809
+ [3 /* ERROR */]: "ERROR",
1810
+ [999 /* SILENT */]: "SILENT"
1811
+ };
1812
+ var Logger = class {
1813
+ constructor(component, config = {}) {
1814
+ this.component = component;
1815
+ this.config = {
1816
+ minLevel: config.minLevel ?? 1 /* INFO */,
1817
+ includeTimestamp: config.includeTimestamp ?? true,
1818
+ includeComponent: config.includeComponent ?? true,
1819
+ colorOutput: config.colorOutput ?? true,
1820
+ outputToFile: config.outputToFile ?? false,
1821
+ logFilePath: config.logFilePath
1822
+ };
1823
+ }
1824
+ config;
1825
+ logBuffer = [];
1826
+ maxBufferSize = 1e3;
1827
+ /**
1828
+ * Set minimum log level
1829
+ */
1830
+ setMinLevel(level) {
1831
+ this.config.minLevel = level;
1832
+ }
1833
+ /**
1834
+ * Log at a specific level
1835
+ */
1836
+ log(level, message, data) {
1837
+ if (level < this.config.minLevel) {
1838
+ return;
1839
+ }
1840
+ const entry = {
1841
+ timestamp: Date.now(),
1842
+ level,
1843
+ component: this.component,
1844
+ message,
1845
+ data
1846
+ };
1847
+ this.logBuffer.push(entry);
1848
+ if (this.logBuffer.length > this.maxBufferSize) {
1849
+ this.logBuffer.shift();
1850
+ }
1851
+ const formatted = this.formatEntry(entry);
1852
+ console.log(formatted);
1853
+ }
1854
+ /**
1855
+ * Format a log entry for output
1856
+ */
1857
+ formatEntry(entry) {
1858
+ const parts = [];
1859
+ if (this.config.includeTimestamp) {
1860
+ const date = new Date(entry.timestamp);
1861
+ const ts = date.toISOString().split("T")[1].slice(0, -1);
1862
+ parts.push(this.config.colorOutput ? COLORS.dim + ts + COLORS.reset : ts);
1863
+ }
1864
+ const levelName = levelNames[entry.level];
1865
+ const levelColor = this.config.colorOutput ? levelColors[entry.level] : "";
1866
+ parts.push(levelColor + `[${levelName}]` + (this.config.colorOutput ? COLORS.reset : ""));
1867
+ if (this.config.includeComponent) {
1868
+ const comp = this.config.colorOutput ? COLORS.cyan + entry.component + COLORS.reset : entry.component;
1869
+ parts.push(`[${comp}]`);
1870
+ }
1871
+ parts.push(entry.message);
1872
+ if (entry.data) {
1873
+ const dataStr = Object.entries(entry.data).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
1874
+ parts.push(this.config.colorOutput ? COLORS.dim + dataStr + COLORS.reset : dataStr);
1875
+ }
1876
+ return parts.join(" ");
1877
+ }
1878
+ /**
1879
+ * Debug level logging
1880
+ */
1881
+ debug(message, data) {
1882
+ this.log(0 /* DEBUG */, message, data);
1883
+ }
1884
+ /**
1885
+ * Info level logging
1886
+ */
1887
+ info(message, data) {
1888
+ this.log(1 /* INFO */, message, data);
1889
+ }
1890
+ /**
1891
+ * Warning level logging
1892
+ */
1893
+ warn(message, data) {
1894
+ this.log(2 /* WARN */, message, data);
1895
+ }
1896
+ /**
1897
+ * Error level logging
1898
+ */
1899
+ error(message, data) {
1900
+ this.log(3 /* ERROR */, message, data);
1901
+ }
1902
+ /**
1903
+ * Get all log entries
1904
+ */
1905
+ getLogs() {
1906
+ return [...this.logBuffer];
1907
+ }
1908
+ /**
1909
+ * Get logs by level
1910
+ */
1911
+ getLogsByLevel(minLevel) {
1912
+ return this.logBuffer.filter((entry) => entry.level >= minLevel);
1913
+ }
1914
+ /**
1915
+ * Clear log buffer
1916
+ */
1917
+ clearLogs() {
1918
+ this.logBuffer = [];
1919
+ }
1920
+ /**
1921
+ * Export logs to string
1922
+ */
1923
+ exportLogs() {
1924
+ return this.logBuffer.map((entry) => this.formatEntry(entry)).join("\n");
1925
+ }
1926
+ };
1927
+ var agentLogger = new Logger("Agent", {
1928
+ minLevel: 1 /* INFO */,
1929
+ colorOutput: true
1930
+ });
1931
+
1932
+ // src/shared/utils/config.ts
1933
+ import path from "path";
1934
+ import { fileURLToPath as fileURLToPath2 } from "url";
1935
+ var __filename = fileURLToPath2(import.meta.url);
1936
+ var __dirname2 = path.dirname(__filename);
1937
+ function getApiKey() {
1938
+ return process.env.PENTEST_API_KEY || "";
1939
+ }
1940
+ function getBaseUrl() {
1941
+ return process.env.PENTEST_BASE_URL || void 0;
1942
+ }
1943
+ function getModel() {
1944
+ return process.env.PENTEST_MODEL || "";
1945
+ }
1946
+
1947
+ // src/engine/llm.ts
1948
+ import Anthropic from "@anthropic-ai/sdk";
1949
+ var LLMClient = class {
1950
+ client;
1951
+ model;
1952
+ constructor() {
1953
+ this.client = new Anthropic({
1954
+ apiKey: getApiKey(),
1955
+ baseURL: getBaseUrl(),
1956
+ dangerouslyAllowBrowser: true
1957
+ });
1958
+ this.model = getModel();
1959
+ }
1960
+ /**
1961
+ * Generate response from messages and tools (non-streaming)
1962
+ */
1963
+ async generateResponse(messages, tools, systemPrompt) {
1964
+ const response = await this.client.messages.create({
1965
+ model: this.model,
1966
+ max_tokens: 4096,
1967
+ system: systemPrompt,
1968
+ messages: messages.filter((m) => m.role !== "system"),
1969
+ tools
1970
+ });
1971
+ const textBlock = response.content.find((b) => b.type === "text");
1972
+ const toolBlocks = response.content.filter((b) => b.type === "tool_use");
1973
+ return {
1974
+ content: textBlock?.type === "text" ? textBlock.text : "",
1975
+ toolCalls: toolBlocks.map((b) => ({
1976
+ id: b.id,
1977
+ name: b.name,
1978
+ input: b.input
1979
+ })),
1980
+ rawResponse: response
1981
+ };
1982
+ }
1983
+ /**
1984
+ * Generate response with streaming support for real-time thinking display
1985
+ * This is like opencode's reasoning stream handling
1986
+ */
1987
+ async generateResponseStream(messages, tools, systemPrompt, callbacks) {
1988
+ const stream = await this.client.messages.create({
1989
+ model: this.model,
1990
+ max_tokens: 4096,
1991
+ system: systemPrompt,
1992
+ messages: messages.filter((m) => m.role !== "system"),
1993
+ tools,
1994
+ stream: true
1995
+ });
1996
+ let fullContent = "";
1997
+ const toolCallsMap = /* @__PURE__ */ new Map();
1998
+ callbacks?.onThinkingStart?.(systemPrompt ? "processing" : "default", 0);
1999
+ for await (const event of stream) {
2000
+ switch (event.type) {
2001
+ case "content_block_start":
2002
+ if (event.content_block.type === "text") {
2003
+ } else if (event.content_block.type === "tool_use") {
2004
+ const toolId = event.content_block.id;
2005
+ toolCallsMap.set(toolId, { id: toolId, name: "", input: {} });
2006
+ }
2007
+ break;
2008
+ case "content_block_delta":
2009
+ if (event.delta.type === "text_delta") {
2010
+ const text = event.delta.text;
2011
+ fullContent += text;
2012
+ callbacks?.onThinkingDelta?.(text);
2013
+ } else if (event.delta.type === "input_json_delta") {
2014
+ }
2015
+ break;
2016
+ case "content_block_stop":
2017
+ break;
2018
+ case "message_start":
2019
+ break;
2020
+ case "message_delta":
2021
+ break;
2022
+ case "message_stop":
2023
+ break;
2024
+ }
2025
+ }
2026
+ callbacks?.onThinkingEnd?.();
2027
+ const toolCalls = Array.from(toolCallsMap.values());
2028
+ return {
2029
+ content: fullContent,
2030
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
2031
+ rawResponse: null
2032
+ };
2033
+ }
2034
+ /**
2035
+ * Get current model name
2036
+ */
2037
+ getModelName() {
2038
+ return this.model;
2039
+ }
2040
+ };
2041
+ var llmInstance = null;
2042
+ function getLLMClient() {
2043
+ if (!llmInstance) {
2044
+ llmInstance = new LLMClient();
2045
+ }
2046
+ return llmInstance;
2047
+ }
2048
+
2049
+ // src/agents/core-agent.ts
2050
+ var CoreAgent = class {
2051
+ llm;
2052
+ state;
2053
+ events;
2054
+ toolRegistry;
2055
+ agentType;
2056
+ maxIterations;
2057
+ constructor(agentType, state, events, toolRegistry, maxIterations) {
2058
+ this.agentType = agentType;
2059
+ this.state = state;
2060
+ this.events = events;
2061
+ this.toolRegistry = toolRegistry;
2062
+ this.llm = getLLMClient();
2063
+ this.maxIterations = maxIterations || AGENT_LIMITS.MAX_ITERATIONS;
2064
+ }
2065
+ /**
2066
+ * The core loop: think -> act -> observe
2067
+ */
2068
+ async run(task, systemPrompt) {
2069
+ const messages = [{ role: "user", content: task }];
2070
+ let toolsExecutedCount = 0;
2071
+ for (let iteration = 0; iteration < this.maxIterations; iteration++) {
2072
+ const result = await this.step(iteration, messages, systemPrompt);
2073
+ toolsExecutedCount += result.toolsExecuted;
2074
+ if (result.completed) {
2075
+ return {
2076
+ output: result.output,
2077
+ iterations: iteration + 1,
2078
+ toolsExecuted: toolsExecutedCount,
2079
+ completed: true
2080
+ };
2081
+ }
2082
+ }
2083
+ return {
2084
+ output: "Max iterations reached",
2085
+ iterations: this.maxIterations,
2086
+ toolsExecuted: toolsExecutedCount,
2087
+ completed: false
2088
+ };
2089
+ }
2090
+ /**
2091
+ * Execute a single iteration step with real-time thinking display
2092
+ */
2093
+ async step(iteration, messages, systemPrompt) {
2094
+ const phase = this.state.getPhase();
2095
+ this.emitThinkingStart(phase, iteration);
2096
+ this.emitThink(iteration);
2097
+ const response = await this.llm.generateResponseStream(
2098
+ messages,
2099
+ this.getToolSchemas(),
2100
+ systemPrompt,
2101
+ {
2102
+ onThinkingStart: (phase2, iter) => {
2103
+ },
2104
+ onThinkingDelta: (content) => {
2105
+ this.emitThinkingDelta(content, phase);
2106
+ },
2107
+ onThinkingEnd: () => {
2108
+ this.emitThinkingEnd(phase);
2109
+ }
2110
+ }
2111
+ );
2112
+ messages.push({ role: "assistant", content: response.content });
2113
+ if (!response.toolCalls || response.toolCalls.length === 0) {
2114
+ this.emitComplete(response.content, iteration, 0);
2115
+ return { output: response.content, toolsExecuted: 0, completed: true };
2116
+ }
2117
+ const results = await this.processToolCalls(response.toolCalls);
2118
+ this.addToolResultsToMessages(messages, results);
2119
+ return { output: "", toolsExecuted: results.length, completed: false };
2120
+ }
2121
+ emitThink(iteration) {
2122
+ this.events.emit({
2123
+ type: "think",
2124
+ timestamp: Date.now(),
2125
+ data: {
2126
+ thought: `${this.agentType} agent iteration ${iteration + 1}: Decision making`,
2127
+ phase: this.state.getPhase()
2128
+ }
2129
+ });
2130
+ }
2131
+ /**
2132
+ * Emit thinking start event - like opencode's reasoning-start
2133
+ */
2134
+ emitThinkingStart(phase, iteration) {
2135
+ this.events.emit({
2136
+ type: "thinking_start",
2137
+ timestamp: Date.now(),
2138
+ data: {
2139
+ phase,
2140
+ iteration
2141
+ }
2142
+ });
2143
+ }
2144
+ /**
2145
+ * Emit thinking delta event - like opencode's reasoning-delta
2146
+ * This provides real-time transparency of the model's thinking process
2147
+ */
2148
+ emitThinkingDelta(content, phase) {
2149
+ this.events.emit({
2150
+ type: "thinking_delta",
2151
+ timestamp: Date.now(),
2152
+ data: {
2153
+ content,
2154
+ phase
2155
+ }
2156
+ });
2157
+ }
2158
+ /**
2159
+ * Emit thinking end event - like opencode's reasoning-end
2160
+ */
2161
+ emitThinkingEnd(phase) {
2162
+ this.events.emit({
2163
+ type: "thinking_end",
2164
+ timestamp: Date.now(),
2165
+ data: {
2166
+ phase
2167
+ }
2168
+ });
2169
+ }
2170
+ emitComplete(output, iteration, toolsExecuted) {
2171
+ this.events.emit({
2172
+ type: "complete",
2173
+ timestamp: Date.now(),
2174
+ data: {
2175
+ finalOutput: output,
2176
+ iterations: iteration + 1,
2177
+ toolsExecuted
2178
+ }
2179
+ });
2180
+ }
2181
+ addToolResultsToMessages(messages, results) {
2182
+ for (const res of results) {
2183
+ messages.push({
2184
+ role: "user",
2185
+ content: [
2186
+ {
2187
+ type: "tool_result",
2188
+ tool_use_id: res.toolCallId,
2189
+ content: res.output,
2190
+ is_error: !!res.error
2191
+ }
2192
+ ]
2193
+ });
2194
+ }
2195
+ }
2196
+ async processToolCalls(toolCalls) {
2197
+ const results = [];
2198
+ for (const call of toolCalls) {
2199
+ const toolName = call.name;
2200
+ const toolInput = call.input;
2201
+ try {
2202
+ const result = await this.toolRegistry.execute({ name: toolName, input: toolInput });
2203
+ results.push({ toolCallId: call.id, output: result.output, error: result.error });
2204
+ } catch (error) {
2205
+ results.push({ toolCallId: call.id, output: String(error), error: String(error) });
2206
+ }
2207
+ }
2208
+ return results;
2209
+ }
2210
+ getToolSchemas() {
2211
+ return this.toolRegistry.getAll().map((t) => ({
2212
+ name: t.name,
2213
+ description: t.description,
2214
+ input_schema: {
2215
+ type: "object",
2216
+ properties: t.parameters,
2217
+ required: t.required || []
2218
+ }
2219
+ }));
2220
+ }
2221
+ };
2222
+
2223
+ // src/agents/prompt-builder.ts
2224
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
2225
+ import { join as join3, dirname as dirname2 } from "path";
2226
+ import { fileURLToPath as fileURLToPath3 } from "url";
2227
+ var __dirname3 = dirname2(fileURLToPath3(import.meta.url));
2228
+ var PromptBuilder = class {
2229
+ state;
2230
+ promptDir;
2231
+ constructor(state) {
2232
+ this.state = state;
2233
+ this.promptDir = join3(__dirname3, "../shared/prompts");
2234
+ }
2235
+ /**
2236
+ * Build complete prompt for LLM iteration
2237
+ */
2238
+ build(userInput, phase) {
2239
+ return [
2240
+ this.loadFragment("base.md"),
2241
+ this.loadPhasePrompt(phase),
2242
+ this.getScopeFragment(),
2243
+ this.getStateFragment(),
2244
+ this.getTodoFragment(),
2245
+ `User Context: ${userInput}`
2246
+ ].join("\n\n");
2247
+ }
2248
+ loadFragment(filename) {
2249
+ const path2 = join3(this.promptDir, filename);
2250
+ if (!existsSync2(path2)) return "";
2251
+ return readFileSync2(path2, "utf-8");
2252
+ }
2253
+ loadPhasePrompt(phase) {
2254
+ const path2 = join3(this.promptDir, "phases", `${phase}.md`);
2255
+ return existsSync2(path2) ? `<phase-instructions phase="${phase}">
2256
+ ${readFileSync2(path2, "utf-8")}
2257
+ </phase-instructions>` : "";
2258
+ }
2259
+ getScopeFragment() {
2260
+ const scope = this.state.getScope();
2261
+ if (!scope) return "<scope>NO SCOPE DEFINED. STOP.</scope>";
2262
+ return `<scope type="ABSOLUTE_CONSTRAINT">
2263
+ Authorized CIDR: ${scope.allowedCidrs.join(", ")}
2264
+ Authorized Domains: ${scope.allowedDomains.join(", ")}
2265
+ Exclusions: ${scope.exclusions.join(", ") || "none"}
2266
+ NoDoS: ${scope.noDOS} | NoSocial: ${scope.noSocial}
2267
+ </scope>`;
2268
+ }
2269
+ getStateFragment() {
2270
+ return `<current-state>
2271
+ ${this.state.toPrompt()}
2272
+ </current-state>`;
2273
+ }
2274
+ getTodoFragment() {
2275
+ const todo = this.state.getTodo();
2276
+ const list = todo.map((t) => `[${t.status === "done" ? "x" : " "}] ${t.content} (${t.priority})`).join("\n");
2277
+ return `<todo>
2278
+ ${list || "Create initial plan"}
2279
+ </todo>`;
2280
+ }
2281
+ };
2282
+
2283
+ // src/agents/sub-agent-executor.ts
2284
+ var SubAgentExecutor = class extends CoreAgent {
2285
+ constructor(state, events, toolRegistry) {
2286
+ super(AGENT_ROLES.RECON, state, events, toolRegistry);
2287
+ }
2288
+ /**
2289
+ * Entry point for executing a sub-task
2290
+ */
2291
+ async executeSubTask(task, systemPrompt) {
2292
+ console.log(`[SubAgent] Executing specialized task: ${task.slice(0, 50)}...`);
2293
+ return await this.run(task, systemPrompt);
2294
+ }
2295
+ };
2296
+
2297
+ // src/agents/main-agent.ts
2298
+ var MainAgent = class extends CoreAgent {
2299
+ promptBuilder;
2300
+ approvalGate;
2301
+ scopeGuard;
2302
+ userInput = "";
2303
+ constructor() {
2304
+ const state = new SharedState();
2305
+ const events = new AgentEventEmitter();
2306
+ const approvalGate = new ApprovalGate(false);
2307
+ const scopeGuard = new ScopeGuard(state);
2308
+ const subAgentExecutor = new SubAgentExecutor(state, events, null);
2309
+ const toolRegistry = new CategorizedToolRegistry(state, scopeGuard, approvalGate, events, subAgentExecutor);
2310
+ subAgentExecutor.toolRegistry = toolRegistry;
2311
+ super(AGENT_ROLES.ORCHESTRATOR, state, events, toolRegistry);
2312
+ this.approvalGate = approvalGate;
2313
+ this.scopeGuard = scopeGuard;
2314
+ this.promptBuilder = new PromptBuilder(state);
2315
+ }
2316
+ /**
2317
+ * Public entry point for running the agent with a simple string input
2318
+ */
2319
+ async execute(userInput) {
2320
+ this.userInput = userInput;
2321
+ this.emitStart(userInput);
2322
+ this.initializeTask();
2323
+ const result = await this.run(userInput, this.getCurrentPrompt());
2324
+ return result.output;
2325
+ }
2326
+ /**
2327
+ * Override CoreAgent.run to enforce dynamic prompt injection per iteration
2328
+ * But keep the signature compatible with CoreAgent
2329
+ */
2330
+ async run(task, _systemPrompt) {
2331
+ return super.run(task, _systemPrompt);
2332
+ }
2333
+ /**
2334
+ * Override step to ensure prompt is always fresh with latest state
2335
+ */
2336
+ async step(iteration, messages, _unusedPrompt) {
2337
+ const dynamicPrompt = this.getCurrentPrompt();
2338
+ const result = await super.step(iteration, messages, dynamicPrompt);
2339
+ this.emitStateChange();
2340
+ return result;
2341
+ }
2342
+ // --- Internal Helpers (Refactored for Readability) ---
2343
+ initializeTask() {
2344
+ if (this.state.getTodo().length === 0) {
2345
+ this.state.addTodo("Initial reconnaissance and target discovery", PRIORITIES.HIGH);
2346
+ }
2347
+ }
2348
+ getCurrentPrompt() {
2349
+ const phase = this.state.getPhase() || PHASES.RECON;
2350
+ return this.promptBuilder.build(this.userInput, phase);
2351
+ }
2352
+ emitStart(userInput) {
2353
+ this.events.emit({
2354
+ type: "start",
2355
+ timestamp: Date.now(),
2356
+ data: { userInput, phase: this.state.getPhase() }
2357
+ });
2358
+ }
2359
+ emitStateChange() {
2360
+ this.events.emit({
2361
+ type: "state_change",
2362
+ timestamp: Date.now(),
2363
+ data: {
2364
+ phase: this.state.getPhase(),
2365
+ targets: this.state.getTargets().size,
2366
+ findings: this.state.getFindings().length,
2367
+ todo: this.state.getTodo().length,
2368
+ loot: this.state.getLoot().length
2369
+ }
2370
+ });
2371
+ }
2372
+ // --- Public API Surface ---
2373
+ setAutoApprove(enabled) {
2374
+ this.approvalGate.setAutoApprove(enabled);
2375
+ }
2376
+ getState() {
2377
+ return this.state;
2378
+ }
2379
+ getPhase() {
2380
+ return this.state.getPhase() || PHASES.RECON;
2381
+ }
2382
+ getEventEmitter() {
2383
+ return this.events;
2384
+ }
2385
+ setScope(allowed, exclusions = []) {
2386
+ this.state.setScope({
2387
+ allowedCidrs: allowed.filter((a) => a.includes("/")),
2388
+ allowedDomains: allowed.filter((a) => !a.includes("/")),
2389
+ exclusions,
2390
+ noDOS: true,
2391
+ noSocial: true
2392
+ });
2393
+ }
2394
+ addTarget(ip) {
2395
+ this.state.addTarget({
2396
+ ip,
2397
+ ports: [],
2398
+ tags: [],
2399
+ firstSeen: Date.now(),
2400
+ hostname: ""
2401
+ });
2402
+ this.emitStateChange();
2403
+ }
2404
+ };
2405
+
2406
+ // src/shared/constants/thought.ts
2407
+ var THOUGHT_TYPE = {
2408
+ THINKING: "thinking",
2409
+ // LLM text streaming
2410
+ REASONING: "reasoning",
2411
+ // LLM extended thinking
2412
+ PLANNING: "planning",
2413
+ // Strategic planning
2414
+ OBSERVATION: "observation",
2415
+ // Observing results
2416
+ HYPOTHESIS: "hypothesis",
2417
+ // Forming hypothesis
2418
+ REFLECTION: "reflection",
2419
+ // Self-reflection
2420
+ ACTION: "action",
2421
+ // Taking action
2422
+ RESULT: "result",
2423
+ // Action result
2424
+ STUCK: "stuck",
2425
+ // Detected stuck state
2426
+ BREAKTHROUGH: "breakthrough"
2427
+ // Found breakthrough
2428
+ };
2429
+
2430
+ // src/shared/constants/theme.ts
2431
+ var THEME = {
2432
+ // Backgrounds (deep dark with blue tint)
2433
+ bg: {
2434
+ primary: "#050505",
2435
+ // Deepest black
2436
+ secondary: "#0a0c10",
2437
+ // Dark void
2438
+ tertiary: "#0f172a",
2439
+ // Slate dark
2440
+ elevated: "#1e293b",
2441
+ // Bright slate
2442
+ input: "#020617"
2443
+ // Midnight
2444
+ },
2445
+ // Text colors
2446
+ text: {
2447
+ primary: "#f8fafc",
2448
+ // Near white
2449
+ secondary: "#94a3b8",
2450
+ // Muted slate
2451
+ muted: "#64748b",
2452
+ // Blue-gray
2453
+ accent: "#38bdf8",
2454
+ // Sky blue
2455
+ highlight: "#ffffff"
2456
+ // Pure white
2457
+ },
2458
+ // Status colors
2459
+ status: {
2460
+ success: "#22c55e",
2461
+ // Green
2462
+ warning: "#f59e0b",
2463
+ // Amber
2464
+ error: "#ef4444",
2465
+ // Red
2466
+ info: "#3b82f6",
2467
+ // Blue
2468
+ running: "#0ea5e9",
2469
+ // Sky
2470
+ pending: "#64748b"
2471
+ // Slate
2472
+ },
2473
+ // Severity colors
2474
+ semantic: {
2475
+ critical: "#7f1d1d",
2476
+ // Dark red
2477
+ high: "#ef4444",
2478
+ // Red
2479
+ medium: "#f97316",
2480
+ // Orange
2481
+ low: "#eab308",
2482
+ // Yellow
2483
+ info: "#3b82f6"
2484
+ // Blue
2485
+ },
2486
+ // Border colors
2487
+ border: {
2488
+ default: "#1e293b",
2489
+ focus: "#38bdf8",
2490
+ error: "#ef4444",
2491
+ success: "#22c55e"
2492
+ },
2493
+ // Phase colors
2494
+ phase: {
2495
+ recon: "#94a3b8",
2496
+ enum: "#38bdf8",
2497
+ vuln: "#f59e0b",
2498
+ exploit: "#ef4444",
2499
+ privesc: "#8b5cf6",
2500
+ persist: "#22c55e",
2501
+ report: "#64748b"
2502
+ },
2503
+ // Accent colors
2504
+ accent: {
2505
+ pink: "#f472b6",
2506
+ rose: "#fb7185",
2507
+ fuchsia: "#e879f9",
2508
+ purple: "#a78bfa",
2509
+ violet: "#8b5cf6",
2510
+ indigo: "#818cf8",
2511
+ blue: "#60a5fa",
2512
+ cyan: "#22d3ee",
2513
+ teal: "#2dd4bf",
2514
+ emerald: "#34d399",
2515
+ green: "#4ade80",
2516
+ lime: "#a3e635",
2517
+ yellow: "#facc15",
2518
+ amber: "#fbbf24",
2519
+ orange: "#fb923c",
2520
+ red: "#f87171"
2521
+ },
2522
+ // Gradients
2523
+ gradient: {
2524
+ cyber: ["#00d4ff", "#00ff9f"],
2525
+ danger: ["#ef4444", "#7f1d1d"],
2526
+ success: ["#22c55e", "#14532d"],
2527
+ gold: ["#f59e0b", "#78350f"],
2528
+ royal: ["#818cf8", "#312e81"]
2529
+ },
2530
+ // Spinner color (Sky blue)
2531
+ spinner: "#38bdf8",
2532
+ // Identity color
2533
+ identity: "#38bdf8"
2534
+ };
2535
+ var ASCII_BANNER = `
2536
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557
2537
+ \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
2538
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557
2539
+ \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
2540
+ \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
2541
+ \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
2542
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2543
+ A U T O N O M O U S S E C U R I T Y A G E N T
2544
+ `;
2545
+ var THOUGHT_LABELS = {
2546
+ [THOUGHT_TYPE.THINKING]: "[think]",
2547
+ [THOUGHT_TYPE.REASONING]: "[reason]",
2548
+ [THOUGHT_TYPE.PLANNING]: "[plan]",
2549
+ [THOUGHT_TYPE.OBSERVATION]: "[observe]",
2550
+ [THOUGHT_TYPE.HYPOTHESIS]: "[hypothesis]",
2551
+ [THOUGHT_TYPE.REFLECTION]: "[reflect]",
2552
+ [THOUGHT_TYPE.ACTION]: "[action]",
2553
+ [THOUGHT_TYPE.RESULT]: "[result]",
2554
+ [THOUGHT_TYPE.STUCK]: "[stuck]",
2555
+ [THOUGHT_TYPE.BREAKTHROUGH]: "[!]"
2556
+ };
2557
+
2558
+ // src/platform/tui/components/footer.tsx
2559
+ import { Box, Text } from "ink";
2560
+ import { jsx, jsxs } from "react/jsx-runtime";
2561
+ var Footer = ({ phase, targets, findings, todo, elapsedTime, isProcessing }) => {
2562
+ return /* @__PURE__ */ jsxs(
2563
+ Box,
2564
+ {
2565
+ paddingX: 1,
2566
+ marginTop: 0,
2567
+ justifyContent: "space-between",
2568
+ children: [
2569
+ /* @__PURE__ */ jsxs(Box, { gap: 2, children: [
2570
+ /* @__PURE__ */ jsxs(Text, { color: THEME.text.accent, children: [
2571
+ "Phase: ",
2572
+ /* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: phase })
2573
+ ] }),
2574
+ /* @__PURE__ */ jsxs(Text, { color: THEME.text.secondary, children: [
2575
+ "Targets: ",
2576
+ /* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: targets })
2577
+ ] }),
2578
+ /* @__PURE__ */ jsxs(Text, { color: THEME.status.warning, children: [
2579
+ "Findings: ",
2580
+ /* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: findings })
2581
+ ] }),
2582
+ /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
2583
+ "Tasks: ",
2584
+ /* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: todo })
2585
+ ] })
2586
+ ] }),
2587
+ /* @__PURE__ */ jsxs(Box, { children: [
2588
+ /* @__PURE__ */ jsx(Text, { color: isProcessing ? THEME.status.running : THEME.text.muted, children: isProcessing ? "\u25CF Running " : "\u25CB Idle " }),
2589
+ /* @__PURE__ */ jsxs(Text, { color: THEME.text.secondary, children: [
2590
+ Math.floor(elapsedTime / 60),
2591
+ "m ",
2592
+ Math.floor(elapsedTime % 60),
2593
+ "s"
2594
+ ] })
2595
+ ] })
2596
+ ]
2597
+ }
2598
+ );
2599
+ };
2600
+ var footer_default = Footer;
2601
+
2602
+ // src/platform/tui/app.tsx
2603
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2604
+ var App = ({ autoApprove = false, target }) => {
2605
+ const { exit } = useApp();
2606
+ const [messages, setMessages] = useState([]);
2607
+ const [input, setInput] = useState("");
2608
+ const [isProcessing, setIsProcessing] = useState(false);
2609
+ const [currentStatus, setCurrentStatus] = useState("");
2610
+ const [elapsedTime, setElapsedTime] = useState(0);
2611
+ const [thinking, setThinking] = useState({ isActive: false, content: "", phase: "" });
2612
+ const [stats, setStats] = useState({
2613
+ phase: "init",
2614
+ targets: 0,
2615
+ findings: 0,
2616
+ todo: 0
2617
+ });
2618
+ const [agent] = useState(() => {
2619
+ const ag = new MainAgent();
2620
+ if (autoApprove) {
2621
+ ag.setAutoApprove(true);
2622
+ }
2623
+ if (target) {
2624
+ ag.addTarget(target);
2625
+ ag.setScope([target]);
2626
+ }
2627
+ return ag;
2628
+ });
2629
+ const startTimeRef = useRef(0);
2630
+ const timerRef = useRef(null);
2631
+ const startTimer = useCallback(() => {
2632
+ startTimeRef.current = Date.now();
2633
+ timerRef.current = setInterval(() => {
2634
+ setElapsedTime(Math.floor((Date.now() - startTimeRef.current) / 1e3));
2635
+ }, 1e3);
2636
+ }, []);
2637
+ const stopTimer = useCallback(() => {
2638
+ if (timerRef.current) {
2639
+ clearInterval(timerRef.current);
2640
+ timerRef.current = null;
2641
+ }
2642
+ const duration = Math.floor((Date.now() - startTimeRef.current) / 100) / 10;
2643
+ setElapsedTime(0);
2644
+ return duration;
2645
+ }, []);
2646
+ const addMessage = useCallback((type, content) => {
2647
+ const id = Math.random().toString(36).substring(7);
2648
+ setMessages((prev) => [...prev, { id, type, content, timestamp: /* @__PURE__ */ new Date() }]);
2649
+ }, []);
2650
+ useEffect(() => {
2651
+ if (target) {
2652
+ addMessage("system", `Target: ${target}`);
2653
+ }
2654
+ if (autoApprove) {
2655
+ addMessage("system", "YOLO Mode: Auto-approving all tool executions");
2656
+ }
2657
+ const events = agent.getEventEmitter();
2658
+ events.on("thinking_start", (event) => {
2659
+ setThinking({ isActive: true, content: "", phase: event.data.phase });
2660
+ });
2661
+ events.on("thinking_delta", (event) => {
2662
+ setThinking((prev) => {
2663
+ const newContent = prev.content + event.data.content;
2664
+ return {
2665
+ ...prev,
2666
+ content: newContent.slice(-500)
2667
+ // Keep only last 500 chars for display
2668
+ };
2669
+ });
2670
+ });
2671
+ events.on("thinking_end", (event) => {
2672
+ setThinking((prev) => ({ ...prev, isActive: false }));
2673
+ });
2674
+ events.on("tool_call", (event) => {
2675
+ const data = event.data;
2676
+ setCurrentStatus(`Executing: ${data.toolName}`);
2677
+ let inputStr = "";
2678
+ try {
2679
+ if (data.input) {
2680
+ const str = JSON.stringify(data.input);
2681
+ inputStr = str.length > 50 ? str.substring(0, 47) + "..." : str;
2682
+ }
2683
+ } catch (e) {
2684
+ }
2685
+ addMessage("tool", ` \u23BF ${data.toolName} ${inputStr}`);
2686
+ });
2687
+ events.on("tool_result", (event) => {
2688
+ const data = event.data;
2689
+ const icon = data.success ? "\u2713" : "\u2717";
2690
+ const output = data.output || "";
2691
+ const preview = output.slice(0, 300).replace(/\n/g, " ");
2692
+ const more = output.length > 300 ? "..." : "";
2693
+ addMessage("result", ` ${icon} ${preview}${more}`);
2694
+ });
2695
+ events.on("think", (event) => {
2696
+ const data = event.data;
2697
+ const thought = data.thought.length > 100 ? data.thought.substring(0, 97) + "..." : data.thought;
2698
+ setCurrentStatus(thought);
2699
+ });
2700
+ events.on("complete", (event) => {
2701
+ const data = event.data;
2702
+ addMessage("system", `\u2713 Complete (${data.toolsExecuted} tools)`);
2703
+ });
2704
+ events.on("error", (event) => {
2705
+ const data = event.data;
2706
+ addMessage("error", data.message || "An error occurred");
2707
+ });
2708
+ const updateStats = () => {
2709
+ const state = agent.getState();
2710
+ setStats({
2711
+ phase: agent.getPhase(),
2712
+ targets: state.getTargets().size,
2713
+ findings: state.getFindings().length,
2714
+ todo: state.getTodo().length
2715
+ });
2716
+ };
2717
+ events.on("state_change", updateStats);
2718
+ events.on("start", updateStats);
2719
+ events.on("complete", updateStats);
2720
+ updateStats();
2721
+ return () => {
2722
+ if (timerRef.current) clearInterval(timerRef.current);
2723
+ events.off("state_change", updateStats);
2724
+ events.off("start", updateStats);
2725
+ events.off("complete", updateStats);
2726
+ };
2727
+ }, [agent, target, autoApprove, addMessage, startTimer]);
2728
+ const handleExit = useCallback(async () => {
2729
+ setCurrentStatus("Exiting");
2730
+ if (timerRef.current) {
2731
+ clearInterval(timerRef.current);
2732
+ }
2733
+ exit();
2734
+ }, [exit]);
2735
+ useEffect(() => {
2736
+ const onExit = () => {
2737
+ handleExit();
2738
+ };
2739
+ process.on("SIGINT", onExit);
2740
+ process.on("SIGTERM", onExit);
2741
+ return () => {
2742
+ process.off("SIGINT", onExit);
2743
+ process.off("SIGTERM", onExit);
2744
+ };
2745
+ }, [handleExit, exit]);
2746
+ const handleSubmit = useCallback(async (value) => {
2747
+ const trimmed = value.trim();
2748
+ if (!trimmed) return;
2749
+ setInput("");
2750
+ addMessage("user", trimmed);
2751
+ if (trimmed.startsWith("/")) {
2752
+ const [cmd, ...args] = trimmed.slice(1).split(" ");
2753
+ switch (cmd) {
2754
+ case "help":
2755
+ case "h":
2756
+ addMessage("system", `
2757
+ \u2500\u2500 Commands \u2500\u2500
2758
+ /target <ip> Set target
2759
+ /start [goal] Start autonomous pentest
2760
+ /stop Stop operation
2761
+ /findings Show findings
2762
+ /status Show status
2763
+ /clear Clear screen
2764
+ /exit Exit
2765
+
2766
+ \u2500\u2500 Examples \u2500\u2500
2767
+ /target 192.168.1.1
2768
+ /start "Recon the target"
2769
+ /findings
2770
+ `);
2771
+ return;
2772
+ case "target":
2773
+ case "t":
2774
+ if (args[0]) {
2775
+ agent.addTarget(args[0]);
2776
+ agent.setScope([args[0]]);
2777
+ addMessage("system", `Target \u2192 ${args[0]}`);
2778
+ } else {
2779
+ addMessage("system", "Usage: /target <ip>");
2780
+ }
2781
+ return;
2782
+ case "start":
2783
+ case "s":
2784
+ if (!agent.getState().getTargets().size) {
2785
+ addMessage("error", "Set target first: /target <ip>");
2786
+ return;
2787
+ }
2788
+ setIsProcessing(true);
2789
+ startTimer();
2790
+ setCurrentStatus("Initializing");
2791
+ addMessage("system", `Starting: ${args.join(" ") || "Perform comprehensive penetration testing"}`);
2792
+ try {
2793
+ await agent.execute(args.join(" ") || "Perform comprehensive penetration testing");
2794
+ } catch (e) {
2795
+ addMessage("error", e instanceof Error ? e.message : String(e));
2796
+ }
2797
+ stopTimer();
2798
+ setIsProcessing(false);
2799
+ setCurrentStatus("");
2800
+ return;
2801
+ case "stop":
2802
+ addMessage("system", "Stopping...");
2803
+ setIsProcessing(false);
2804
+ setCurrentStatus("");
2805
+ return;
2806
+ case "findings":
2807
+ case "f":
2808
+ const findings = agent.getState().getFindings();
2809
+ if (findings.length === 0) {
2810
+ addMessage("system", "No findings.");
2811
+ } else {
2812
+ addMessage("system", `--- ${findings.length} Findings ---`);
2813
+ findings.forEach((f) => addMessage("system", `[${f.severity}] ${f.title}`));
2814
+ }
2815
+ return;
2816
+ case "status":
2817
+ const state = agent.getState();
2818
+ addMessage("system", `Status:
2819
+ Phase: ${agent.getPhase()}
2820
+ Targets: ${state.getTargets().size}
2821
+ Findings: ${state.getFindings().length}
2822
+ TODO: ${state.getTodo().length}
2823
+ `);
2824
+ return;
2825
+ case "clear":
2826
+ case "c":
2827
+ setMessages([]);
2828
+ return;
2829
+ case "exit":
2830
+ case "quit":
2831
+ case "q":
2832
+ await handleExit();
2833
+ return;
2834
+ default:
2835
+ addMessage("error", `Unknown command: /${cmd}`);
2836
+ return;
2837
+ }
2838
+ }
2839
+ setIsProcessing(true);
2840
+ startTimer();
2841
+ setCurrentStatus("Thinking");
2842
+ try {
2843
+ const response = await agent.execute(trimmed);
2844
+ addMessage("assistant", response);
2845
+ } catch (e) {
2846
+ addMessage("error", e instanceof Error ? e.message : String(e));
2847
+ } finally {
2848
+ stopTimer();
2849
+ setIsProcessing(false);
2850
+ setCurrentStatus("");
2851
+ }
2852
+ }, [agent, addMessage, startTimer, stopTimer, handleExit]);
2853
+ useInput((input2, key) => {
2854
+ if (key.ctrl && input2 === "c") {
2855
+ if (isProcessing) {
2856
+ setIsProcessing(false);
2857
+ stopTimer();
2858
+ setCurrentStatus("");
2859
+ addMessage("system", "Interrupted");
2860
+ } else {
2861
+ exit();
2862
+ }
2863
+ return;
2864
+ }
2865
+ });
2866
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
2867
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginBottom: 1, flexGrow: 1, children: /* @__PURE__ */ jsx2(Static, { items: messages, children: (msg) => {
2868
+ const colors = {
2869
+ user: THEME.text.accent,
2870
+ assistant: THEME.text.primary,
2871
+ system: THEME.text.muted,
2872
+ error: THEME.status.error,
2873
+ tool: THEME.status.running,
2874
+ result: THEME.text.muted,
2875
+ thinking: THEME.status.warning
2876
+ };
2877
+ const prefixes = {
2878
+ user: "\u276F",
2879
+ assistant: ">",
2880
+ system: "\u2022",
2881
+ error: "\u2717",
2882
+ tool: " \u23BF",
2883
+ result: " \u2713",
2884
+ thinking: "\u{1F9E0}"
2885
+ };
2886
+ return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: colors[msg.type], dimColor: msg.type === "result", children: [
2887
+ prefixes[msg.type],
2888
+ " ",
2889
+ msg.content
2890
+ ] }) }, msg.id);
2891
+ } }) }),
2892
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 0, children: [
2893
+ thinking.isActive && thinking.content && /* @__PURE__ */ jsxs2(Box2, { marginBottom: 1, children: [
2894
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.status.running, children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
2895
+ /* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
2896
+ " ",
2897
+ "Thinking: "
2898
+ ] }),
2899
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.text.secondary, dimColor: true, children: thinking.content.slice(-200) })
2900
+ ] }),
2901
+ isProcessing && !thinking.isActive && /* @__PURE__ */ jsxs2(Box2, { marginBottom: 1, children: [
2902
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.spinner, children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
2903
+ /* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
2904
+ " ",
2905
+ currentStatus || "Processing"
2906
+ ] })
2907
+ ] }),
2908
+ /* @__PURE__ */ jsx2(
2909
+ Box2,
2910
+ {
2911
+ borderStyle: "single",
2912
+ borderColor: THEME.border.default,
2913
+ paddingX: 1,
2914
+ children: /* @__PURE__ */ jsxs2(Box2, { children: [
2915
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.text.secondary, children: "\u25B8" }),
2916
+ /* @__PURE__ */ jsx2(Text2, { children: " " }),
2917
+ /* @__PURE__ */ jsx2(
2918
+ TextInput,
2919
+ {
2920
+ value: input,
2921
+ onChange: setInput,
2922
+ onSubmit: handleSubmit,
2923
+ placeholder: "Message or /help..."
2924
+ }
2925
+ )
2926
+ ] })
2927
+ }
2928
+ ),
2929
+ /* @__PURE__ */ jsx2(
2930
+ footer_default,
2931
+ {
2932
+ phase: stats.phase,
2933
+ targets: stats.targets,
2934
+ findings: stats.findings,
2935
+ todo: stats.todo,
2936
+ elapsedTime,
2937
+ isProcessing
2938
+ }
2939
+ )
2940
+ ] })
2941
+ ] });
2942
+ };
2943
+ var app_default = App;
2944
+
2945
+ // src/shared/constants/_shared/signal.const.ts
2946
+ var EXIT_CODE = {
2947
+ SUCCESS: 0,
2948
+ ERROR: 1,
2949
+ // Unix convention: 128 + signal number
2950
+ SIGINT: 130,
2951
+ // 128 + 2
2952
+ SIGTERM: 143,
2953
+ // 128 + 15
2954
+ SIGKILL: 137
2955
+ // 128 + 9
2956
+ };
2957
+
2958
+ // src/platform/tui/main.tsx
2959
+ import { jsx as jsx3 } from "react/jsx-runtime";
2960
+ var program = new Command();
2961
+ program.name("pentesting").version(APP_VERSION).description(APP_DESCRIPTION).option("--dangerously-skip-permissions", "Skip all permission prompts (dangerous!)").option("-t, --target <target>", "Set initial target");
2962
+ program.command("interactive", { isDefault: true }).alias("i").description("Start interactive TUI mode").action(async () => {
2963
+ const opts = program.opts();
2964
+ const skipPermissions = opts.dangerouslySkipPermissions || false;
2965
+ console.clear();
2966
+ console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
2967
+ console.log(
2968
+ " " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.text.accent)("Type /help for commands") + "\n"
2969
+ );
2970
+ if (skipPermissions) {
2971
+ console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions"));
2972
+ console.log(chalk.hex(THEME.status.error)("[!] All tool executions will be auto-approved!\n"));
2973
+ }
2974
+ const { waitUntilExit } = render(
2975
+ /* @__PURE__ */ jsx3(
2976
+ app_default,
2977
+ {
2978
+ autoApprove: skipPermissions,
2979
+ target: opts.target
2980
+ }
2981
+ )
2982
+ );
2983
+ await waitUntilExit();
2984
+ });
2985
+ program.command("run <objective>").alias("r").description("Run a single objective and exit").option("-o, --output <file>", "Output file for results").option("--max-steps <n>", "Maximum number of steps", "50").action(async (objective, options) => {
2986
+ const opts = program.opts();
2987
+ const skipPermissions = opts.dangerouslySkipPermissions || false;
2988
+ console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
2989
+ if (skipPermissions) {
2990
+ console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions\n"));
2991
+ }
2992
+ console.log(chalk.hex(THEME.text.accent)(`[target] Objective: ${objective}
2993
+ `));
2994
+ const agent = new MainAgent();
2995
+ if (skipPermissions) {
2996
+ agent.setAutoApprove(true);
2997
+ }
2998
+ if (opts.target) {
2999
+ agent.addTarget(opts.target);
3000
+ agent.setScope([opts.target]);
3001
+ }
3002
+ const shutdown = async (exitCode = 0) => {
3003
+ process.exit(exitCode);
3004
+ };
3005
+ process.on("SIGINT", () => shutdown(EXIT_CODE.SIGINT));
3006
+ process.on("SIGTERM", () => shutdown(EXIT_CODE.SIGTERM));
3007
+ try {
3008
+ const result = await agent.execute(objective);
3009
+ console.log(chalk.hex(THEME.status.success)("\n[+] Assessment complete!\n"));
3010
+ console.log(result);
3011
+ if (options.output) {
3012
+ const fs = await import("fs/promises");
3013
+ await fs.writeFile(options.output, JSON.stringify({ result }, null, 2));
3014
+ console.log(chalk.hex(THEME.text.accent)(`
3015
+ [+] Report saved to: ${options.output}`));
3016
+ }
3017
+ await shutdown(0);
3018
+ } catch (error) {
3019
+ const errorMessage = error instanceof Error ? error.message : String(error);
3020
+ console.error(chalk.hex(THEME.status.error)(`
3021
+ [-] Failed: ${errorMessage}`));
3022
+ await shutdown(1);
3023
+ }
3024
+ });
3025
+ program.command("scan <target>").description("Quick scan a target").option("-s, --scan-type <type>", "Scan type (quick|full|stealth|service|vuln)", "quick").option("-p, --ports <ports>", "Specific ports to scan").action(async (target, options) => {
3026
+ const opts = program.opts();
3027
+ const skipPermissions = opts.dangerouslySkipPermissions || false;
3028
+ console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
3029
+ console.log(chalk.hex(THEME.text.accent)(`
3030
+ [scan] Target: ${target} (${options.scanType})
3031
+ `));
3032
+ const agent = new MainAgent();
3033
+ if (skipPermissions) {
3034
+ agent.setAutoApprove(true);
3035
+ }
3036
+ agent.addTarget(target);
3037
+ agent.setScope([target]);
3038
+ const shutdown = async (exitCode = 0) => {
3039
+ process.exit(exitCode);
3040
+ };
3041
+ process.on("SIGINT", () => shutdown(EXIT_CODE.SIGINT));
3042
+ process.on("SIGTERM", () => shutdown(EXIT_CODE.SIGTERM));
3043
+ try {
3044
+ await agent.execute(`Perform a ${options.scanType} scan on ${target}${options.ports ? ` focusing on ports ${options.ports}` : ""}. Analyze the results and identify potential vulnerabilities.`);
3045
+ console.log(chalk.hex(THEME.status.success)("[+] Scan complete!"));
3046
+ await shutdown(0);
3047
+ } catch (error) {
3048
+ const errorMessage = error instanceof Error ? error.message : String(error);
3049
+ console.error(chalk.hex(THEME.status.error)(`[-] Scan failed: ${errorMessage}`));
3050
+ await shutdown(1);
3051
+ }
3052
+ });
3053
+ program.command("help-extended").description("Show extended help with examples").action(() => {
3054
+ console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
3055
+ console.log(`
3056
+ ${chalk.hex(THEME.text.accent)(APP_NAME + " - Autonomous Penetration Testing AI")}
3057
+
3058
+ ${chalk.hex(THEME.status.warning)("Usage:")}
3059
+
3060
+ ${chalk.hex(THEME.status.success)("$ pentesting")} Start interactive mode
3061
+ ${chalk.hex(THEME.status.success)("$ pentesting -t 192.168.1.1")} Start with target
3062
+ ${chalk.hex(THEME.status.success)("$ pentesting --dangerously-skip-permissions")} Auto-approve all tools
3063
+
3064
+ ${chalk.hex(THEME.status.warning)("Commands:")}
3065
+
3066
+ ${chalk.hex(THEME.text.accent)("pentesting")} Interactive TUI mode
3067
+ ${chalk.hex(THEME.text.accent)("pentesting run <objective>")} Run single objective
3068
+ ${chalk.hex(THEME.text.accent)("pentesting scan <target>")} Quick scan target
3069
+
3070
+ ${chalk.hex(THEME.status.warning)("Options:")}
3071
+
3072
+ ${chalk.hex(THEME.text.accent)("--dangerously-skip-permissions")} Skip all permission prompts
3073
+ ${chalk.hex(THEME.text.accent)("-t, --target <ip>")} Set target
3074
+ ${chalk.hex(THEME.text.accent)("-o, --output <file>")} Save results to file
3075
+
3076
+ ${chalk.hex(THEME.status.warning)("Interactive Commands:")}
3077
+
3078
+ ${chalk.hex(THEME.text.accent)("/target <ip>")} Set target
3079
+ ${chalk.hex(THEME.text.accent)("/start")} Start autonomous mode
3080
+ ${chalk.hex(THEME.text.accent)("/config")} Manage configuration
3081
+ ${chalk.hex(THEME.text.accent)("/hint <text>")} Provide hint
3082
+ ${chalk.hex(THEME.text.accent)("/findings")} Show findings
3083
+ ${chalk.hex(THEME.text.accent)("/reset")} Reset session
3084
+
3085
+ ${chalk.hex(THEME.status.warning)("Examples:")}
3086
+
3087
+ ${chalk.hex(THEME.text.muted)("# Full autonomous mode")}
3088
+ $ pentesting --dangerously-skip-permissions -t 10.10.10.5
3089
+
3090
+ ${chalk.hex(THEME.text.muted)("# Run specific objective")}
3091
+ $ pentesting run "Find SQL injection" -t http://target.com -o report.json
3092
+
3093
+ ${chalk.hex(THEME.text.muted)("# Quick vulnerability scan")}
3094
+ $ pentesting scan 192.168.1.1 -s vuln
3095
+
3096
+ ${chalk.hex(THEME.status.warning)("Environment:")}
3097
+
3098
+ ${chalk.hex(THEME.text.accent)("PENTEST_API_KEY")} Required - LLM API key
3099
+ ${chalk.hex(THEME.text.accent)("PENTEST_BASE_URL")} Optional - AI API base URL
3100
+ ${chalk.hex(THEME.text.accent)("PENTEST_MODEL")} Optional - Model override
3101
+ `);
3102
+ });
3103
+ program.parse();