pentesting 0.43.0 → 0.44.1

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 CHANGED
@@ -10,7 +10,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
10
  import { render } from "ink";
11
11
  import { Command } from "commander";
12
12
  import chalk from "chalk";
13
- import gradient from "gradient-string";
14
13
 
15
14
  // src/platform/tui/app.tsx
16
15
  import { useState as useState4, useCallback as useCallback4, useEffect as useEffect4, useRef as useRef4 } from "react";
@@ -307,14 +306,12 @@ var ORPHAN_PROCESS_NAMES = [
307
306
  "dnsspoof",
308
307
  "tcpdump",
309
308
  "tshark",
310
- "socat",
311
- "nc",
312
- "python"
309
+ "socat"
313
310
  ];
314
311
 
315
312
  // src/shared/constants/agent.ts
316
313
  var APP_NAME = "Pentest AI";
317
- var APP_VERSION = "0.43.0";
314
+ var APP_VERSION = "0.44.1";
318
315
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
319
316
  var LLM_ROLES = {
320
317
  SYSTEM: "system",
@@ -342,6 +339,146 @@ var SENSITIVE_INPUT_TYPES = [
342
339
  INPUT_TYPES.CREDENTIAL
343
340
  ];
344
341
 
342
+ // src/shared/utils/time-strategy.ts
343
+ var PHASE_RATIOS = {
344
+ /** Sprint ends at 25% of total time */
345
+ SPRINT_END: 0.25,
346
+ /** Exploit phase ends at 50% of total time */
347
+ EXPLOIT_END: 0.5,
348
+ /** Creative phase ends at 75% of total time */
349
+ CREATIVE_END: 0.75
350
+ // Harvest: 75%-100%
351
+ };
352
+ var DEFAULT_DURATION_SEC = 86400;
353
+ var MIN_DURATION_SEC = 300;
354
+ var MAX_DURATION_SEC = 604800;
355
+ var MS_PER_SEC = 1e3;
356
+ function getSessionDurationMs() {
357
+ const envValue = process.env.PENTEST_DURATION;
358
+ if (envValue) {
359
+ const parsed = parseInt(envValue, 10);
360
+ if (!isNaN(parsed) && parsed > 0) {
361
+ const clamped = Math.max(MIN_DURATION_SEC, Math.min(MAX_DURATION_SEC, parsed));
362
+ return clamped * MS_PER_SEC;
363
+ }
364
+ }
365
+ return DEFAULT_DURATION_SEC * MS_PER_SEC;
366
+ }
367
+ function computeDeadline(startedAt) {
368
+ return startedAt + getSessionDurationMs();
369
+ }
370
+ function formatDuration(totalSec) {
371
+ if (totalSec <= 0) return "0s";
372
+ const h = Math.floor(totalSec / 3600);
373
+ const m = Math.floor(totalSec % 3600 / 60);
374
+ const s = Math.floor(totalSec % 60);
375
+ if (h > 0 && m > 0) return `${h}h${m}m`;
376
+ if (h > 0) return `${h}h`;
377
+ if (m > 0 && s > 0) return `${m}m${s}s`;
378
+ if (m > 0) return `${m}m`;
379
+ return `${s}s`;
380
+ }
381
+ function getTimeAdaptiveStrategy(elapsedMs, deadlineMs) {
382
+ let totalMs;
383
+ let remainingMs;
384
+ if (deadlineMs > 0) {
385
+ remainingMs = Math.max(0, deadlineMs - Date.now());
386
+ totalMs = elapsedMs + remainingMs;
387
+ } else {
388
+ totalMs = getSessionDurationMs();
389
+ remainingMs = Math.max(0, totalMs - elapsedMs);
390
+ }
391
+ if (totalMs <= 0) totalMs = 1;
392
+ const progress = Math.min(1, elapsedMs / totalMs);
393
+ const remainingSec = Math.floor(remainingMs / MS_PER_SEC);
394
+ const totalDurationSec = Math.floor(totalMs / MS_PER_SEC);
395
+ const elapsedStr = formatDuration(Math.floor(elapsedMs / MS_PER_SEC));
396
+ const remainStr = formatDuration(remainingSec);
397
+ const totalStr = formatDuration(totalDurationSec);
398
+ const pctStr = `${Math.floor(progress * 100)}%`;
399
+ if (progress >= PHASE_RATIOS.CREATIVE_END) {
400
+ return {
401
+ phase: "harvest",
402
+ label: "HARVEST",
403
+ urgency: "critical",
404
+ progress,
405
+ totalDurationSec,
406
+ remainingSec,
407
+ directive: [
408
+ `\u{1F3C1} HARVEST MODE [${pctStr} elapsed, ${remainStr} of ${totalStr} remaining]:`,
409
+ "- STOP exploring new attack vectors. Exploit what you HAVE.",
410
+ "- Submit ALL discovered flags immediately",
411
+ "- Check obvious flag locations: /root/flag.txt, user.txt, env vars, DB tables",
412
+ "- Re-check previously accessed systems for missed flags/proof",
413
+ "- Collect and record ALL evidence and proof files",
414
+ "- Write final report with all findings",
415
+ "- Credential spray ALL discovered creds on ALL remaining services",
416
+ remainingSec <= 300 ? "- \u26A0\uFE0F FINAL 5 MINUTES \u2014 submit everything NOW, stop all scans" : "- Focus on highest-confidence paths only"
417
+ ].join("\n")
418
+ };
419
+ }
420
+ if (progress >= PHASE_RATIOS.EXPLOIT_END) {
421
+ return {
422
+ phase: "creative",
423
+ label: "CREATIVE",
424
+ urgency: "high",
425
+ progress,
426
+ totalDurationSec,
427
+ remainingSec,
428
+ directive: [
429
+ `\u{1F52C} CREATIVE MODE [${pctStr} elapsed, ${remainStr} of ${totalStr} remaining]:`,
430
+ "- Try advanced techniques: chained exploits, custom tools, race conditions",
431
+ "- Binary analysis for SUID/custom binaries, kernel exploits for privesc",
432
+ "- Protocol-level attacks: SMB relay, Kerberoasting, ADCS abuse",
433
+ '- Search for latest bypasses: web_search("{defense} bypass {year}")',
434
+ "- If stuck >5min on any vector, SWITCH immediately",
435
+ "- Start preparing evidence collection for final phase",
436
+ "- If all targets are compromised \u2192 skip to harvest immediately"
437
+ ].join("\n")
438
+ };
439
+ }
440
+ if (progress >= PHASE_RATIOS.SPRINT_END) {
441
+ return {
442
+ phase: "exploit",
443
+ label: "EXPLOIT",
444
+ urgency: "medium",
445
+ progress,
446
+ totalDurationSec,
447
+ remainingSec,
448
+ directive: [
449
+ `\u{1F3AF} EXPLOIT MODE [${pctStr} elapsed, ${remainStr} of ${totalStr} remaining]:`,
450
+ "- Concentrate on discovered vectors with highest success probability",
451
+ "- Run parallel exploitation attempts on multiple targets",
452
+ "- Search for CVE PoCs for every identified service+version",
453
+ "- Chain: credentials \u2192 spray everywhere, LFI \u2192 config \u2192 DB creds",
454
+ "- Keep background scans running for new targets",
455
+ "- Time-box each vector: 10min max then switch",
456
+ "- If all vectors exhausted early \u2192 move to creative techniques immediately"
457
+ ].join("\n")
458
+ };
459
+ }
460
+ return {
461
+ phase: "sprint",
462
+ label: "SPRINT",
463
+ urgency: "low",
464
+ progress,
465
+ totalDurationSec,
466
+ remainingSec,
467
+ directive: [
468
+ `\u26A1 SPRINT MODE [${pctStr} elapsed, ${remainStr} of ${totalStr} remaining]:`,
469
+ "- RustScan first for fast port discovery \u2192 then nmap -Pn -sV -sC on found ports",
470
+ "- ALWAYS use nmap -Pn (no exceptions \u2014 firewalls block ICMP)",
471
+ "- Run ALL scans in parallel (rustscan, nmap UDP, web fuzzing, CVE search)",
472
+ "- Try default/weak credentials on every discovered service IMMEDIATELY",
473
+ "- Check for exposed files (.env, .git, backup.sql, phpinfo)",
474
+ "- Anonymous access: FTP, SMB null session, Redis no auth, MongoDB",
475
+ "- OSINT: web_search for target intel, shodan, github repos",
476
+ "- Goal: Maximum attack surface discovery + instant wins",
477
+ "- \u26A1 If recon completes early \u2192 ATTACK IMMEDIATELY, do not wait for this phase to end"
478
+ ].join("\n")
479
+ };
480
+ }
481
+
345
482
  // src/shared/utils/id.ts
346
483
  var ID_DEFAULT_RADIX = 36;
347
484
  var ID_DEFAULT_LENGTH = 6;
@@ -2027,7 +2164,7 @@ async function cleanupAllProcesses() {
2027
2164
  backgroundProcesses.clear();
2028
2165
  for (const name of ORPHAN_PROCESS_NAMES) {
2029
2166
  try {
2030
- execSync2(`pkill -f "${name}" 2>/dev/null || true`, { stdio: "ignore", timeout: SYSTEM_LIMITS.PROCESS_OP_TIMEOUT_MS });
2167
+ execSync2(`pkill -x "${name}" 2>/dev/null || true`, { stdio: "ignore", timeout: SYSTEM_LIMITS.PROCESS_OP_TIMEOUT_MS });
2031
2168
  } catch {
2032
2169
  }
2033
2170
  }
@@ -2652,6 +2789,22 @@ var AttackGraph = class {
2652
2789
  if (nodeCount >= GRAPH_LIMITS.ASCII_MAX_NODES) break;
2653
2790
  const sIcon = statusIcons[node.status] || "?";
2654
2791
  const fail = node.status === NODE_STATUS.FAILED && node.failReason ? ` \u2014 ${node.failReason}` : "";
2792
+ let detail = "";
2793
+ if (type === NODE_TYPE.CREDENTIAL) {
2794
+ const source = node.data.source ? ` (from: ${node.data.source})` : "";
2795
+ detail = source;
2796
+ } else if (type === NODE_TYPE.VULNERABILITY) {
2797
+ const sev = node.data.severity ? ` [${String(node.data.severity).toUpperCase()}]` : "";
2798
+ const exploit = node.data.hasExploit ? " [EXPLOIT]" : "";
2799
+ const target = node.data.target ? ` \u2192 ${node.data.target}` : "";
2800
+ detail = `${sev}${exploit}${target}`;
2801
+ } else if (type === NODE_TYPE.ACCESS) {
2802
+ const via = node.data.via ? ` via ${node.data.via}` : "";
2803
+ detail = via;
2804
+ } else if (type === NODE_TYPE.SERVICE) {
2805
+ const ver = node.data.version ? ` (${node.data.version})` : "";
2806
+ detail = ver;
2807
+ }
2655
2808
  const outEdges = this.edges.filter((e) => e.from === node.id);
2656
2809
  const edgeStr = outEdges.length > 0 ? ` \u2192 ${outEdges.map((e) => {
2657
2810
  const target = this.nodes.get(e.to);
@@ -2659,10 +2812,19 @@ var AttackGraph = class {
2659
2812
  const eStatus = e.status === EDGE_STATUS.FAILED ? " \u2717" : e.status === EDGE_STATUS.SUCCEEDED ? " \u2713" : "";
2660
2813
  return `${eName}${eStatus}`;
2661
2814
  }).join(", ")}` : "";
2662
- lines.push(`\u2502 ${sIcon} ${node.label}${fail}${edgeStr}`);
2815
+ lines.push(`\u2502 ${sIcon} ${node.label}${detail}${fail}${edgeStr}`);
2663
2816
  nodeCount++;
2664
2817
  }
2665
2818
  }
2819
+ const succeededNodes = Array.from(this.nodes.values()).filter((n) => n.status === NODE_STATUS.SUCCEEDED);
2820
+ if (succeededNodes.length > 0) {
2821
+ lines.push(`\u2502`);
2822
+ lines.push(`\u251C\u2500\u2500\u2500 Exploited \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\u2524`);
2823
+ for (const n of succeededNodes) {
2824
+ const via = n.data.via ? ` via ${n.data.via}` : "";
2825
+ lines.push(`\u2502 \u25CF ${n.label}${via}`);
2826
+ }
2827
+ }
2666
2828
  const stats = this.getStats();
2667
2829
  lines.push(`\u2502`);
2668
2830
  lines.push(`\u251C\u2500\u2500\u2500 Summary \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\u2524`);
@@ -3183,6 +3345,7 @@ var SharedState = class {
3183
3345
  // ─── Improvement #7: Dynamic Technique Library ────────────────
3184
3346
  dynamicTechniques = new DynamicTechniqueLibrary();
3185
3347
  constructor() {
3348
+ const now = Date.now();
3186
3349
  this.data = {
3187
3350
  engagement: null,
3188
3351
  targets: /* @__PURE__ */ new Map(),
@@ -3196,8 +3359,10 @@ var SharedState = class {
3196
3359
  ctfMode: true,
3197
3360
  // CTF mode ON by default
3198
3361
  flags: [],
3199
- startedAt: Date.now(),
3200
- deadlineAt: 0,
3362
+ startedAt: now,
3363
+ // Auto-configure from PENTEST_DURATION env (seconds).
3364
+ // Defaults to 24h if not set. Can be overridden via setDeadline().
3365
+ deadlineAt: computeDeadline(now),
3201
3366
  challengeAnalysis: null
3202
3367
  };
3203
3368
  }
@@ -3206,6 +3371,7 @@ var SharedState = class {
3206
3371
  * Used by /clear command to start a fresh session without recreating the agent.
3207
3372
  */
3208
3373
  reset() {
3374
+ const now = Date.now();
3209
3375
  this.data = {
3210
3376
  engagement: null,
3211
3377
  targets: /* @__PURE__ */ new Map(),
@@ -3218,8 +3384,8 @@ var SharedState = class {
3218
3384
  missionChecklist: [],
3219
3385
  ctfMode: true,
3220
3386
  flags: [],
3221
- startedAt: Date.now(),
3222
- deadlineAt: 0,
3387
+ startedAt: now,
3388
+ deadlineAt: computeDeadline(now),
3223
3389
  challengeAnalysis: null
3224
3390
  };
3225
3391
  this.attackGraph.reset();
@@ -3404,24 +3570,28 @@ var SharedState = class {
3404
3570
  if (this.data.deadlineAt === 0) return 0;
3405
3571
  return Math.max(0, this.data.deadlineAt - Date.now());
3406
3572
  }
3407
- /** Get time status string for prompt injection */
3573
+ /** Get time status string for prompt injection (ratio-based, aligned with PHASE_RATIOS) */
3408
3574
  getTimeStatus() {
3409
3575
  const elapsed = this.getElapsedMs();
3410
3576
  const mins = Math.floor(elapsed / 6e4);
3411
3577
  let status = `\u23F1 Elapsed: ${mins}min`;
3412
3578
  if (this.data.deadlineAt > 0) {
3413
3579
  const remainingMs = this.getRemainingMs();
3580
+ const totalMs = elapsed + remainingMs;
3581
+ const progress = totalMs > 0 ? elapsed / totalMs : 1;
3414
3582
  const remainingMins = Math.floor(remainingMs / 6e4);
3415
- if (remainingMins <= 0) {
3583
+ if (remainingMs <= 0) {
3416
3584
  status += " | \u26A0\uFE0F TIME IS UP \u2014 submit any flags you have NOW";
3417
- } else if (remainingMins <= 15) {
3585
+ } else if (progress >= 0.95) {
3418
3586
  status += ` | \u{1F534} ${remainingMins}min LEFT \u2014 FINAL PUSH: submit flags, check obvious locations, env vars, DBs`;
3419
- } else if (remainingMins <= 30) {
3420
- status += ` | \u{1F7E1} ${remainingMins}min LEFT \u2014 wrap up current vector, ensure all findings are captured`;
3421
- } else if (remainingMins <= 60) {
3422
- status += ` | \u{1F7E0} ${remainingMins}min LEFT \u2014 focus on highest-probability vectors only`;
3587
+ } else if (progress >= 0.75) {
3588
+ status += ` | \u{1F7E1} ${remainingMins}min LEFT \u2014 HARVEST: wrap up, collect evidence, submit flags`;
3589
+ } else if (progress >= 0.5) {
3590
+ status += ` | \u{1F7E0} ${remainingMins}min LEFT \u2014 CREATIVE: advanced techniques, switch if stuck`;
3591
+ } else if (progress >= 0.25) {
3592
+ status += ` | \u{1F535} ${remainingMins}min LEFT \u2014 EXPLOIT: focus on discovered vectors`;
3423
3593
  } else {
3424
- status += ` | \u{1F7E2} ${remainingMins}min remaining`;
3594
+ status += ` | \u{1F7E2} ${remainingMins}min remaining \u2014 SPRINT: recon + quick wins`;
3425
3595
  }
3426
3596
  }
3427
3597
  return status;
@@ -4912,26 +5082,31 @@ Detail: ${detail}
4912
5082
  },
4913
5083
  {
4914
5084
  name: TOOL_NAMES.ADD_FINDING,
4915
- description: "Add a security finding. Always include attackPattern for MITRE ATT&CK mapping.",
5085
+ description: `Add a security finding with full details.
5086
+ ALWAYS provide: description (HOW you exploited it, step-by-step), evidence (actual command output proving success), and attackPattern (MITRE ATT&CK tactic).
5087
+ Findings without evidence are marked as UNVERIFIED and have low credibility.`,
4916
5088
  parameters: {
4917
- title: { type: "string", description: "Finding title" },
5089
+ title: { type: "string", description: 'Concise finding title (e.g., "Path Traversal via /download endpoint")' },
4918
5090
  severity: { type: "string", description: "Severity: critical, high, medium, low, info" },
4919
- affected: { type: "array", items: { type: "string" }, description: "Affected host:port" },
5091
+ affected: { type: "array", items: { type: "string" }, description: 'Affected host:port or URLs (e.g., ["ctf.example.com:443/download"])' },
5092
+ description: { type: "string", description: "Detailed description: what the vulnerability is, how you exploited it step-by-step, what access it gives, and the impact. Include credentials found, methods used, and exploitation chain." },
5093
+ evidence: { type: "array", items: { type: "string" }, description: 'Actual command outputs proving the finding (e.g., ["curl output showing /etc/passwd", "uid=0(root) gid=0(root)"]). Copy real output here.' },
4920
5094
  attackPattern: { type: "string", description: "MITRE ATT&CK tactic: initial_access, execution, persistence, privilege_escalation, defense_evasion, credential_access, discovery, lateral_movement, collection, exfiltration, command_and_control, impact" }
4921
5095
  },
4922
- required: ["title", "severity"],
5096
+ required: ["title", "severity", "description", "evidence"],
4923
5097
  execute: async (p) => {
4924
5098
  const evidence = p.evidence || [];
4925
5099
  const title = p.title;
4926
5100
  const severity = p.severity;
4927
5101
  const affected = p.affected || [];
5102
+ const description = p.description || "";
4928
5103
  const validation = validateFinding(evidence, severity);
4929
5104
  state.addFinding({
4930
5105
  id: generateId(AGENT_LIMITS.ID_RADIX, AGENT_LIMITS.ID_LENGTH),
4931
5106
  title,
4932
5107
  severity,
4933
5108
  affected,
4934
- description: p.description || "",
5109
+ description,
4935
5110
  evidence,
4936
5111
  isVerified: validation.isVerified,
4937
5112
  remediation: "",
@@ -8686,6 +8861,11 @@ var NETWORK_ERROR_CODES = {
8686
8861
  ENOTFOUND: "ENOTFOUND",
8687
8862
  CONNECT_TIMEOUT: "UND_ERR_CONNECT_TIMEOUT"
8688
8863
  };
8864
+ var TRANSIENT_NETWORK_ERRORS = [
8865
+ NETWORK_ERROR_CODES.ECONNRESET,
8866
+ NETWORK_ERROR_CODES.ETIMEDOUT,
8867
+ NETWORK_ERROR_CODES.ENOTFOUND
8868
+ ];
8689
8869
  var LLMError = class extends Error {
8690
8870
  /** Structured error information */
8691
8871
  errorInfo;
@@ -8696,8 +8876,11 @@ var LLMError = class extends Error {
8696
8876
  }
8697
8877
  };
8698
8878
  function classifyError(error) {
8879
+ if (error === null || error === void 0) {
8880
+ return { type: LLM_ERROR_TYPES.UNKNOWN, message: String(error), isRetryable: false, suggestedAction: "Analyze error" };
8881
+ }
8699
8882
  const e = error;
8700
- const statusCode = e.status || e.statusCode;
8883
+ const statusCode = e?.status || e?.statusCode;
8701
8884
  const errorMessage = e?.error?.error?.message || e?.error?.message || e?.message || String(error);
8702
8885
  if (statusCode === HTTP_STATUS.RATE_LIMIT || errorMessage.toLowerCase().includes("rate limit")) {
8703
8886
  return { type: LLM_ERROR_TYPES.RATE_LIMIT, message: errorMessage, statusCode, isRetryable: true, suggestedAction: "Wait and retry" };
@@ -8708,7 +8891,7 @@ function classifyError(error) {
8708
8891
  if (statusCode === HTTP_STATUS.BAD_REQUEST) {
8709
8892
  return { type: LLM_ERROR_TYPES.INVALID_REQUEST, message: errorMessage, statusCode, isRetryable: false, suggestedAction: "Modify request" };
8710
8893
  }
8711
- if (e.code === NETWORK_ERROR_CODES.ECONNRESET || e.code === NETWORK_ERROR_CODES.ETIMEDOUT || e.code === NETWORK_ERROR_CODES.ENOTFOUND) {
8894
+ if (e.code && TRANSIENT_NETWORK_ERRORS.includes(e.code)) {
8712
8895
  return { type: LLM_ERROR_TYPES.NETWORK_ERROR, message: errorMessage, isRetryable: true, suggestedAction: "Check network" };
8713
8896
  }
8714
8897
  if (errorMessage.toLowerCase().includes("timeout") || e.code === NETWORK_ERROR_CODES.CONNECT_TIMEOUT) {
@@ -10317,100 +10500,6 @@ var INITIAL_TASKS = {
10317
10500
  RECON: "Initial reconnaissance and target discovery"
10318
10501
  };
10319
10502
 
10320
- // src/shared/utils/time-strategy.ts
10321
- var TIME_CONSTANTS = {
10322
- /** Milliseconds per minute */
10323
- MS_PER_MINUTE: 6e4,
10324
- /** Minutes threshold for sprint-to-exploit switch (no deadline mode) */
10325
- SPRINT_MINUTES: 10,
10326
- /** Deadline percentage thresholds for phase transitions */
10327
- PHASE_SPRINT: 0.25,
10328
- PHASE_EXPLOIT: 0.5,
10329
- PHASE_CREATIVE: 0.75
10330
- };
10331
- function getTimeAdaptiveStrategy(elapsedMs, deadlineMs) {
10332
- if (deadlineMs === 0) {
10333
- const mins = Math.floor(elapsedMs / TIME_CONSTANTS.MS_PER_MINUTE);
10334
- if (mins < TIME_CONSTANTS.SPRINT_MINUTES) {
10335
- return {
10336
- phase: "sprint",
10337
- label: "SPRINT",
10338
- urgency: "low",
10339
- directive: `\u26A1 SPRINT MODE (${mins}min elapsed): Parallel reconnaissance. Full port scan + service enumeration + default credentials. Attack immediately on any finding.`
10340
- };
10341
- }
10342
- return {
10343
- phase: "exploit",
10344
- label: "EXPLOIT",
10345
- urgency: "low",
10346
- directive: `\u{1F3AF} EXPLOIT MODE (${mins}min elapsed): Focus on discovered vectors. Chain findings. Build custom tools if needed.`
10347
- };
10348
- }
10349
- const totalMs = deadlineMs - (deadlineMs > Date.now() ? Date.now() - elapsedMs : 0);
10350
- const remainingMs = Math.max(0, deadlineMs - Date.now());
10351
- const pct = totalMs > 0 ? 1 - remainingMs / totalMs : 1;
10352
- const remainingMins = Math.floor(remainingMs / TIME_CONSTANTS.MS_PER_MINUTE);
10353
- if (pct < TIME_CONSTANTS.PHASE_SPRINT) {
10354
- return {
10355
- phase: "sprint",
10356
- label: "SPRINT",
10357
- urgency: "low",
10358
- directive: [
10359
- `\u26A1 SPRINT MODE (${remainingMins}min remaining):`,
10360
- "- Run ALL scans in parallel (nmap TCP/UDP, web fuzzing, CVE search)",
10361
- "- Try default/weak credentials on every discovered service IMMEDIATELY",
10362
- "- Check for exposed files (.env, .git, backup.sql, phpinfo)",
10363
- "- Anonymous access: FTP, SMB null session, Redis no auth, MongoDB",
10364
- "- Goal: Maximum attack surface discovery + instant wins"
10365
- ].join("\n")
10366
- };
10367
- }
10368
- if (pct < TIME_CONSTANTS.PHASE_EXPLOIT) {
10369
- return {
10370
- phase: "exploit",
10371
- label: "EXPLOIT",
10372
- urgency: "medium",
10373
- directive: [
10374
- `\u{1F3AF} EXPLOIT MODE (${remainingMins}min remaining):`,
10375
- "- Concentrate on discovered vectors with highest success probability",
10376
- "- Run parallel exploitation attempts on multiple targets",
10377
- "- Search for CVE PoCs for every identified service+version",
10378
- "- Chain: credentials \u2192 spray everywhere, LFI \u2192 config \u2192 DB creds",
10379
- "- Keep background scans running for new targets"
10380
- ].join("\n")
10381
- };
10382
- }
10383
- if (pct < TIME_CONSTANTS.PHASE_CREATIVE) {
10384
- return {
10385
- phase: "creative",
10386
- label: "CREATIVE",
10387
- urgency: "high",
10388
- directive: [
10389
- `\u{1F52C} CREATIVE MODE (${remainingMins}min remaining):`,
10390
- "- Try advanced techniques: chained exploits, custom tools, race conditions",
10391
- "- Binary analysis for SUID/custom binaries, kernel exploits for privesc",
10392
- "- Protocol-level attacks: SMB relay, Kerberoasting, ADCS abuse",
10393
- '- Search for latest bypasses: web_search("{defense} bypass 2025")',
10394
- "- If stuck >5min on any vector, SWITCH immediately"
10395
- ].join("\n")
10396
- };
10397
- }
10398
- return {
10399
- phase: "harvest",
10400
- label: "HARVEST",
10401
- urgency: "critical",
10402
- directive: [
10403
- `\u{1F3C1} HARVEST MODE (${remainingMins}min remaining \u2014 FINAL PUSH):`,
10404
- "- STOP exploring new vectors. Focus on what you HAVE.",
10405
- "- Submit ALL discovered flags immediately",
10406
- "- Check obvious flag locations: /root/flag.txt, env vars, DB tables",
10407
- "- Re-check previously accessed systems for missed flags",
10408
- "- Collect and record ALL evidence and proof files",
10409
- "- Write final report with all findings"
10410
- ].join("\n")
10411
- };
10412
- }
10413
-
10414
10503
  // src/shared/constants/service-ports.ts
10415
10504
  var SERVICE_PORTS = {
10416
10505
  SSH: 22,
@@ -10960,12 +11049,13 @@ var STRATEGIST_LIMITS = {
10960
11049
  * Full state can be 20K+; Strategist needs summary, not everything. */
10961
11050
  MAX_INPUT_CHARS: 15e3,
10962
11051
  /** Maximum characters for the Strategist's response.
10963
- * WHY: Directives should be terse and actionable (~500-800 tokens).
10964
- * Longer = more cost, less focus. */
10965
- MAX_OUTPUT_CHARS: 3e3,
11052
+ * WHY: Directives should be terse and actionable (~800-1500 tokens).
11053
+ * Enhanced format includes SITUATION, priorities, EXHAUSTED, and SEARCH ORDERS. */
11054
+ MAX_OUTPUT_CHARS: 5e3,
10966
11055
  /** Maximum lines in the directive output.
10967
- * WHY: Forces concise, prioritized directives. */
10968
- MAX_DIRECTIVE_LINES: 40
11056
+ * WHY: Forces concise, prioritized directives while allowing
11057
+ * structured format (priorities + exhausted + search orders). */
11058
+ MAX_DIRECTIVE_LINES: 60
10969
11059
  };
10970
11060
 
10971
11061
  // src/agents/strategist.ts
@@ -11134,11 +11224,14 @@ NOTE: This directive is from ${age}min ago (Strategist call failed this turn). V
11134
11224
  this.totalCalls = 0;
11135
11225
  }
11136
11226
  };
11137
- var FALLBACK_SYSTEM_PROMPT = `You are a penetration testing strategist.
11138
- Analyze the situation and write a precise tactical directive for the attack agent.
11139
- Be specific: name exact tools, commands, and parameters.
11140
- Maximum 30 lines. Prioritize by probability of success.
11141
- NEVER suggest approaches listed in the failed attempts section.`;
11227
+ var FALLBACK_SYSTEM_PROMPT = `You are an elite autonomous penetration testing STRATEGIST \u2014 a red team tactical commander.
11228
+ Analyze the engagement state and issue precise attack orders for the execution agent.
11229
+ Format: SITUATION line, numbered PRIORITY items with ACTION/SEARCH/SUCCESS/FALLBACK/CHAIN fields, EXHAUSTED list, and SEARCH ORDERS.
11230
+ Be surgically specific: name exact tools, commands, parameters, and wordlists. The agent copy-pastes your commands.
11231
+ Include mandatory web_search directives for every unknown service/version.
11232
+ Detect stalls (repeated failures, no progress) and force completely different attack vectors.
11233
+ Chain every finding: "If X works \u2192 immediately do Y \u2192 which enables Z."
11234
+ Maximum 50 lines. Zero preamble. Direct imperatives only. Never repeat failed approaches.`;
11142
11235
 
11143
11236
  // src/agents/main-agent.ts
11144
11237
  var MainAgent = class extends CoreAgent {
@@ -11319,7 +11412,7 @@ var AgentFactory = class {
11319
11412
  };
11320
11413
 
11321
11414
  // src/platform/tui/utils/format.ts
11322
- var formatDuration = (ms) => {
11415
+ var formatDuration2 = (ms) => {
11323
11416
  const totalSec = ms / 1e3;
11324
11417
  if (totalSec < 60) return `${totalSec.toFixed(1)}s`;
11325
11418
  const minutes = Math.floor(totalSec / 60);
@@ -11333,7 +11426,7 @@ var formatTokens = (count) => {
11333
11426
  };
11334
11427
  var formatMeta = (ms, tokens) => {
11335
11428
  const parts = [];
11336
- if (ms > 0) parts.push(formatDuration(ms));
11429
+ if (ms > 0) parts.push(formatDuration2(ms));
11337
11430
  if (tokens > 0) parts.push(`\u2191 ${formatTokens(tokens)} tokens`);
11338
11431
  return parts.length > 0 ? `(${parts.join(" \xB7 ")})` : "";
11339
11432
  };
@@ -11367,135 +11460,74 @@ var formatInlineStatus = () => {
11367
11460
  import { useState, useRef, useCallback } from "react";
11368
11461
 
11369
11462
  // src/shared/constants/theme.ts
11463
+ var HEX = {
11464
+ primary: "#FF968A",
11465
+ pink: "#FFAEAE",
11466
+ peach: "#FFC5BF"
11467
+ };
11468
+ var COLORS = {
11469
+ // Primary accent - coral/salmon (ANSI 210 is light coral)
11470
+ primary: "ansi256(210)",
11471
+ // Pastel pink (ANSI 217 is light pink)
11472
+ pink: "ansi256(217)",
11473
+ // Pastel peach (ANSI 216 is light salmon)
11474
+ peach: "ansi256(216)",
11475
+ // Warning yellow (ANSI 222 is light goldenrod)
11476
+ yellow: "ansi256(222)",
11477
+ // Orange (ANSI 215)
11478
+ orange: "ansi256(215)",
11479
+ // Bright white for main text
11480
+ white: "white",
11481
+ // Light gray for secondary text (ANSI 252 is light gray)
11482
+ gray: "ansi256(252)",
11483
+ // Bright red for errors
11484
+ red: "red"
11485
+ };
11370
11486
  var THEME = {
11371
- // Backgrounds (deep dark with blue tint)
11487
+ ...COLORS,
11372
11488
  bg: {
11373
11489
  primary: "#050505",
11374
- // Deepest black
11375
- secondary: "#0a0c10",
11376
- // Dark void
11377
- tertiary: "#0f172a",
11378
- // Slate dark
11379
- elevated: "#1e293b",
11380
- // Bright slate
11381
11490
  input: "#020617"
11382
- // Midnight
11383
11491
  },
11384
- // Text colors
11385
11492
  text: {
11386
- primary: "#f8fafc",
11387
- // Near white
11388
- secondary: "#cbd5e1",
11389
- // Brighter slate
11390
- muted: "#94a3b8",
11391
- // Lighter blue-gray
11392
- accent: "#1d4ed8",
11393
- // Blue 700 - deep blue
11394
- highlight: "#ffffff"
11395
- // Pure white
11493
+ primary: COLORS.white,
11494
+ // AI responses - main text
11495
+ secondary: COLORS.gray,
11496
+ // Secondary info
11497
+ muted: COLORS.gray,
11498
+ // Metadata, hints
11499
+ accent: COLORS.gray
11500
+ // Very subtle
11396
11501
  },
11397
- // Status colors (deep blue-focused)
11398
11502
  status: {
11399
- success: "#10b981",
11400
- // Emerald
11401
- warning: "#f59e0b",
11402
- // Amber
11403
- error: "#ef4444",
11404
- // Red
11405
- info: "#1d4ed8",
11406
- // Blue 700
11407
- running: "#1e40af",
11408
- // Blue 800 (AI activity)
11409
- pending: "#64748b"
11410
- // Slate
11503
+ success: COLORS.gray,
11504
+ // Keep it subtle
11505
+ warning: COLORS.yellow,
11506
+ // Pastel yellow
11507
+ error: COLORS.red,
11508
+ // Bright red for errors
11509
+ running: COLORS.gray
11510
+ // Processing indicator
11411
11511
  },
11412
- // Severity colors
11413
- semantic: {
11414
- critical: "#7f1d1d",
11415
- // Dark red
11416
- high: "#ef4444",
11417
- // Red
11418
- medium: "#f97316",
11419
- // Orange
11420
- low: "#eab308",
11421
- // Yellow
11422
- info: "#1d4ed8"
11423
- // Blue 700
11424
- },
11425
- // Border colors
11426
11512
  border: {
11427
- default: "#1e293b",
11428
- focus: "#3b82f6",
11429
- // Blue 500 (UI decorative)
11430
- error: "#ef4444",
11431
- success: "#22c55e"
11432
- },
11433
- // Phase colors (deep blue-focused)
11434
- phase: {
11435
- recon: "#94a3b8",
11436
- enum: "#1d4ed8",
11437
- // Blue 700 (phase indicator)
11438
- vuln: "#f59e0b",
11439
- exploit: "#ef4444",
11440
- privesc: "#8b5cf6",
11441
- persist: "#22c55e",
11442
- report: "#64748b"
11513
+ // ANSI 241 is a medium slate gray for borders
11514
+ default: "ansi256(241)",
11515
+ focus: COLORS.primary,
11516
+ error: COLORS.red
11443
11517
  },
11444
- // Accent colors (NO cyan/teal - pure blue palette)
11445
- accent: {
11446
- pink: "#f472b6",
11447
- rose: "#fb7185",
11448
- fuchsia: "#e879f9",
11449
- purple: "#a78bfa",
11450
- violet: "#8b5cf6",
11451
- indigo: "#818cf8",
11452
- blue: "#1d4ed8",
11453
- // Blue 700 - primary accent
11454
- emerald: "#34d399",
11455
- green: "#4ade80",
11456
- lime: "#a3e635",
11457
- yellow: "#facc15",
11458
- amber: "#fbbf24",
11459
- orange: "#fb923c",
11460
- red: "#f87171"
11461
- },
11462
- // Gradients (deep blue-focused)
11463
11518
  gradient: {
11464
- cyber: [
11465
- "#3b82f6",
11466
- // Blue 500
11467
- "#3584f4",
11468
- "#2f86f2",
11469
- "#2988f0",
11470
- "#238aee",
11471
- "#1d8cec",
11472
- // Mid blue
11473
- "#1d7ad8",
11474
- "#1d78c6",
11475
- "#1d76b4",
11476
- "#1d74a2",
11477
- "#1e40af"
11478
- // Blue 800
11479
- ],
11480
- danger: ["#ef4444", "#7f1d1d"],
11481
- success: ["#22c55e", "#14532d"],
11482
- gold: ["#f59e0b", "#78350f"],
11483
- royal: ["#818cf8", "#312e81"]
11519
+ // Hex colors required for gradient-string
11520
+ cyber: [HEX.primary, HEX.pink, HEX.peach]
11484
11521
  },
11485
- // Spinner color (deep blue — UI feedback)
11486
- spinner: "#3b82f6",
11487
- // Blue 500
11488
- // Identity color (branded accent - deep blue)
11489
- identity: "#1d4ed8"
11490
- // Blue 700
11522
+ spinner: COLORS.primary
11491
11523
  };
11492
11524
  var ASCII_BANNER = `
11493
- ____ __ __ _
11494
- / __ \\___ ____ / /____ _______/ /_(_)___ ____ _
11495
- / /_/ / _ \\/ __ \\/ __/ _ \\/ ___/ __/ / / __ \\/ __ \`/
11496
- / ____/ __/ / / / /_/ __(__ ) /_/ / / / / / /_/ /
11497
- /_/ \\___/_/ /_/\\__/\\___/____/\\__/_/_/_/ /_/\\__, /
11498
- /____/
11525
+ \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
11526
+ \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
11527
+ \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
11528
+ \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
11529
+ \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
11530
+ \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
11499
11531
  `;
11500
11532
  var ICONS = {
11501
11533
  // Status
@@ -11509,7 +11541,7 @@ var ICONS = {
11509
11541
  target: "\u25C8",
11510
11542
  // Diamond for target
11511
11543
  scan: "\u25CE",
11512
- exploit: "\u26A1",
11544
+ exploit: "\u2607",
11513
11545
  shell: "\u276F",
11514
11546
  // Progress
11515
11547
  pending: "\u25CB",
@@ -11521,9 +11553,9 @@ var ICONS = {
11521
11553
  medium: "\u25C7",
11522
11554
  low: "\u25E6",
11523
11555
  // Security
11524
- lock: "\u{1F512}",
11525
- unlock: "\u{1F513}",
11526
- key: "\u{1F511}",
11556
+ lock: "\u22A1",
11557
+ unlock: "\u2B1A",
11558
+ key: "\u26B7",
11527
11559
  flag: "\u2691",
11528
11560
  // Simple flag
11529
11561
  pwned: "\u25C8"
@@ -11573,14 +11605,22 @@ var TUI_DISPLAY_LIMITS = {
11573
11605
  };
11574
11606
  var MESSAGE_STYLES = {
11575
11607
  colors: {
11576
- user: THEME.text.accent,
11577
- assistant: THEME.text.primary,
11578
- ai: THEME.text.primary,
11579
- system: THEME.text.muted,
11580
- error: THEME.status.error,
11581
- tool: THEME.status.running,
11582
- result: THEME.text.muted,
11583
- status: THEME.accent.blue
11608
+ user: THEME.white,
11609
+ // User input - bright white
11610
+ assistant: THEME.white,
11611
+ // AI response - white
11612
+ ai: THEME.white,
11613
+ // AI response - white
11614
+ system: THEME.gray,
11615
+ // System - light gray
11616
+ error: THEME.red,
11617
+ // Errors - red
11618
+ tool: THEME.gray,
11619
+ // Tool commands - light gray
11620
+ result: THEME.gray,
11621
+ // Tool results - light gray
11622
+ status: THEME.primary
11623
+ // Status - pastel red accent
11584
11624
  },
11585
11625
  prefixes: {
11586
11626
  user: "\u276F",
@@ -11641,21 +11681,9 @@ var useAgentState = () => {
11641
11681
  const [isProcessing, setIsProcessing] = useState(false);
11642
11682
  const [currentStatus, setCurrentStatus] = useState("");
11643
11683
  const [elapsedTime, setElapsedTime] = useState(0);
11644
- const [retryState, setRetryState] = useState({
11645
- isRetrying: false,
11646
- attempt: 0,
11647
- maxRetries: 0,
11648
- delayMs: 0,
11649
- error: "",
11650
- countdown: 0
11651
- });
11684
+ const [retryState, setRetryState] = useState({ status: "idle" });
11652
11685
  const [currentTokens, setCurrentTokens] = useState(0);
11653
- const [inputRequest, setInputRequest] = useState({
11654
- isActive: false,
11655
- prompt: "",
11656
- isPassword: false,
11657
- resolve: null
11658
- });
11686
+ const [inputRequest, setInputRequest] = useState({ status: "inactive" });
11659
11687
  const [stats, setStats] = useState({ phase: DEFAULTS.INIT_PHASE, targets: 0, findings: 0, todo: 0 });
11660
11688
  const lastResponseMetaRef = useRef(null);
11661
11689
  const startTimeRef = useRef(0);
@@ -11796,7 +11824,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
11796
11824
  const isPassword = /password|passphrase/i.test(p);
11797
11825
  const inputType = /sudo/i.test(p) ? "sudo_password" : isPassword ? "password" : "text";
11798
11826
  setInputRequest({
11799
- isActive: true,
11827
+ status: "active",
11800
11828
  prompt: p.trim(),
11801
11829
  isPassword,
11802
11830
  inputType,
@@ -11810,7 +11838,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
11810
11838
  const isPassword = hiddenTypes.includes(request.type);
11811
11839
  const displayPrompt = buildCredentialPrompt(request);
11812
11840
  setInputRequest({
11813
- isActive: true,
11841
+ status: "active",
11814
11842
  prompt: displayPrompt,
11815
11843
  isPassword,
11816
11844
  inputType: request.type,
@@ -11891,7 +11919,7 @@ function handleRetry(e, addMessage, setRetryState, retryCountdownRef, retryCount
11891
11919
  const retryNum = retryCountRef.current;
11892
11920
  addMessage("system", `\u27F3 Retry #${retryNum} \xB7 ${e.data.error} \xB7 waiting ${delaySec}s...`);
11893
11921
  setRetryState({
11894
- isRetrying: true,
11922
+ status: "retrying",
11895
11923
  attempt: retryNum,
11896
11924
  maxRetries: e.data.maxRetries,
11897
11925
  delayMs: e.data.delayMs,
@@ -11903,10 +11931,10 @@ function handleRetry(e, addMessage, setRetryState, retryCountdownRef, retryCount
11903
11931
  retryCountdownRef.current = setInterval(() => {
11904
11932
  remaining -= 1;
11905
11933
  if (remaining <= 0) {
11906
- setRetryState((prev) => ({ ...prev, isRetrying: false, countdown: 0 }));
11934
+ setRetryState({ status: "idle" });
11907
11935
  if (retryCountdownRef.current) clearInterval(retryCountdownRef.current);
11908
11936
  } else {
11909
- setRetryState((prev) => ({ ...prev, countdown: remaining }));
11937
+ setRetryState((prev) => prev.status === "retrying" ? { ...prev, countdown: remaining } : prev);
11910
11938
  }
11911
11939
  }, 1e3);
11912
11940
  }
@@ -12001,9 +12029,9 @@ var useAgent = (shouldAutoApprove, target) => {
12001
12029
  inputRequestRef.current = inputRequest;
12002
12030
  const cancelInputRequest = useCallback2(() => {
12003
12031
  const ir = inputRequestRef.current;
12004
- if (ir.isActive && ir.resolve) {
12032
+ if (ir.status === "active") {
12005
12033
  ir.resolve(null);
12006
- setInputRequest({ isActive: false, prompt: "", isPassword: false, resolve: null });
12034
+ setInputRequest({ status: "inactive" });
12007
12035
  addMessage("system", "Input cancelled");
12008
12036
  }
12009
12037
  }, [setInputRequest, addMessage]);
@@ -12045,7 +12073,7 @@ import { Box as Box2, Text as Text2, Static } from "ink";
12045
12073
  // src/platform/tui/components/inline-status.tsx
12046
12074
  import { Box, Text } from "ink";
12047
12075
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12048
- function formatDuration2(ms) {
12076
+ function formatDuration3(ms) {
12049
12077
  const seconds = Math.floor(ms / 1e3);
12050
12078
  if (seconds < 60) return `${seconds}s`;
12051
12079
  const minutes = Math.floor(seconds / 60);
@@ -12057,43 +12085,43 @@ function formatDuration2(ms) {
12057
12085
  }
12058
12086
  function getRoleColor(role) {
12059
12087
  const roleColors = {
12060
- listener: THEME.accent.blue,
12061
- active_shell: THEME.accent.green,
12062
- server: THEME.accent.blue,
12063
- sniffer: THEME.accent.amber,
12064
- spoofer: THEME.accent.orange,
12065
- proxy: THEME.accent.purple,
12066
- callback: THEME.accent.pink,
12067
- background: THEME.text.muted
12088
+ listener: THEME.primary,
12089
+ active_shell: THEME.primary,
12090
+ server: THEME.gray,
12091
+ sniffer: THEME.yellow,
12092
+ spoofer: THEME.yellow,
12093
+ proxy: THEME.primary,
12094
+ callback: THEME.primary,
12095
+ background: THEME.gray
12068
12096
  };
12069
- return roleColors[role] || THEME.text.secondary;
12097
+ return roleColors[role] || THEME.gray;
12070
12098
  }
12071
12099
  function StatusIndicator({ running, exitCode }) {
12072
12100
  if (running) {
12073
- return /* @__PURE__ */ jsxs(Text, { color: THEME.status.running, children: [
12101
+ return /* @__PURE__ */ jsxs(Text, { color: THEME.primary, children: [
12074
12102
  ICONS.running,
12075
12103
  " "
12076
12104
  ] });
12077
12105
  }
12078
12106
  if (exitCode === 0) {
12079
- return /* @__PURE__ */ jsxs(Text, { color: THEME.status.success, children: [
12107
+ return /* @__PURE__ */ jsxs(Text, { color: THEME.gray, children: [
12080
12108
  ICONS.success,
12081
12109
  " "
12082
12110
  ] });
12083
12111
  }
12084
- return /* @__PURE__ */ jsxs(Text, { color: THEME.status.error, children: [
12112
+ return /* @__PURE__ */ jsxs(Text, { color: THEME.red, children: [
12085
12113
  ICONS.error,
12086
12114
  " "
12087
12115
  ] });
12088
12116
  }
12089
12117
  function ProcessRow({ proc, compact }) {
12090
- const duration = formatDuration2(proc.durationMs);
12118
+ const duration = formatDuration3(proc.durationMs);
12091
12119
  const port = proc.listeningPort ? `:${proc.listeningPort}` : "";
12092
12120
  const purpose = proc.purpose || proc.description || "";
12093
12121
  const truncatedPurpose = compact && purpose.length > TUI_DISPLAY_LIMITS.purposeMaxLength ? purpose.slice(0, TUI_DISPLAY_LIMITS.purposeTruncated) + "..." : purpose;
12094
12122
  return /* @__PURE__ */ jsxs(Box, { children: [
12095
12123
  /* @__PURE__ */ jsx(StatusIndicator, { running: proc.running, exitCode: proc.exitCode }),
12096
- /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
12124
+ /* @__PURE__ */ jsxs(Text, { color: THEME.gray, children: [
12097
12125
  "[",
12098
12126
  proc.id,
12099
12127
  "]"
@@ -12103,14 +12131,14 @@ function ProcessRow({ proc, compact }) {
12103
12131
  proc.role,
12104
12132
  port
12105
12133
  ] }),
12106
- /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
12134
+ /* @__PURE__ */ jsxs(Text, { color: THEME.gray, children: [
12107
12135
  " (",
12108
12136
  duration,
12109
12137
  ")"
12110
12138
  ] }),
12111
12139
  truncatedPurpose && /* @__PURE__ */ jsxs(Fragment, { children: [
12112
- /* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: " - " }),
12113
- /* @__PURE__ */ jsx(Text, { color: THEME.text.secondary, children: truncatedPurpose })
12140
+ /* @__PURE__ */ jsx(Text, { color: THEME.gray, children: " - " }),
12141
+ /* @__PURE__ */ jsx(Text, { color: THEME.gray, children: truncatedPurpose })
12114
12142
  ] })
12115
12143
  ] });
12116
12144
  }
@@ -12121,18 +12149,18 @@ var InlineStatus = ({
12121
12149
  compact = true
12122
12150
  }) => {
12123
12151
  if (processes.length === 0 && zombies.length === 0) {
12124
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: "\u2022 No active background processes" }) });
12152
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: THEME.gray, children: "\u2022 No active background processes" }) });
12125
12153
  }
12126
12154
  const running = processes.filter((p) => p.running);
12127
12155
  const stopped = processes.filter((p) => !p.running);
12128
12156
  const healthColor = {
12129
- healthy: THEME.status.success,
12130
- warning: THEME.status.warning,
12131
- critical: THEME.status.error
12157
+ healthy: THEME.gray,
12158
+ warning: THEME.yellow,
12159
+ critical: THEME.red
12132
12160
  }[health];
12133
12161
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
12134
12162
  running.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
12135
- /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, bold: true, children: [
12163
+ /* @__PURE__ */ jsxs(Text, { color: THEME.gray, bold: true, children: [
12136
12164
  ICONS.running,
12137
12165
  " Active (",
12138
12166
  running.length,
@@ -12141,46 +12169,46 @@ var InlineStatus = ({
12141
12169
  running.map((proc) => /* @__PURE__ */ jsx(ProcessRow, { proc, compact }, proc.id))
12142
12170
  ] }),
12143
12171
  stopped.length > 0 && !compact && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: running.length > 0 ? 1 : 0, children: [
12144
- /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
12172
+ /* @__PURE__ */ jsxs(Text, { color: THEME.gray, children: [
12145
12173
  ICONS.completed,
12146
12174
  " Completed (",
12147
12175
  stopped.length,
12148
12176
  ")"
12149
12177
  ] }),
12150
12178
  stopped.slice(0, TUI_DISPLAY_LIMITS.maxStoppedProcesses).map((proc) => /* @__PURE__ */ jsx(ProcessRow, { proc, compact }, proc.id)),
12151
- stopped.length > TUI_DISPLAY_LIMITS.maxStoppedProcesses && /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
12179
+ stopped.length > TUI_DISPLAY_LIMITS.maxStoppedProcesses && /* @__PURE__ */ jsxs(Text, { color: THEME.gray, children: [
12152
12180
  " ... and ",
12153
12181
  stopped.length - TUI_DISPLAY_LIMITS.maxStoppedProcesses,
12154
12182
  " more"
12155
12183
  ] })
12156
12184
  ] }),
12157
12185
  zombies.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
12158
- /* @__PURE__ */ jsxs(Text, { color: THEME.status.warning, children: [
12186
+ /* @__PURE__ */ jsxs(Text, { color: THEME.yellow, children: [
12159
12187
  ICONS.warning,
12160
12188
  " Zombie Processes (",
12161
12189
  zombies.length,
12162
12190
  ")"
12163
12191
  ] }),
12164
12192
  zombies.map((z) => /* @__PURE__ */ jsxs(Box, { children: [
12165
- /* @__PURE__ */ jsxs(Text, { color: THEME.status.error, children: [
12193
+ /* @__PURE__ */ jsxs(Text, { color: THEME.red, children: [
12166
12194
  " ",
12167
12195
  ICONS.error,
12168
12196
  " "
12169
12197
  ] }),
12170
- /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
12198
+ /* @__PURE__ */ jsxs(Text, { color: THEME.gray, children: [
12171
12199
  "[",
12172
12200
  z.processId,
12173
12201
  "] "
12174
12202
  ] }),
12175
- /* @__PURE__ */ jsxs(Text, { color: THEME.status.warning, children: [
12203
+ /* @__PURE__ */ jsxs(Text, { color: THEME.yellow, children: [
12176
12204
  z.orphanedChildren.length,
12177
12205
  " orphaned children"
12178
12206
  ] })
12179
12207
  ] }, z.processId)),
12180
- /* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: " Run /cleanup to terminate" })
12208
+ /* @__PURE__ */ jsx(Text, { color: THEME.gray, children: " Run /cleanup to terminate" })
12181
12209
  ] }),
12182
12210
  /* @__PURE__ */ jsxs(Box, { marginTop: running.length > 0 ? 1 : 0, children: [
12183
- /* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: "Health: " }),
12211
+ /* @__PURE__ */ jsx(Text, { color: THEME.gray, children: "Health: " }),
12184
12212
  /* @__PURE__ */ jsx(Text, { color: healthColor, bold: true, children: health.toUpperCase() })
12185
12213
  ] })
12186
12214
  ] });
@@ -12234,7 +12262,7 @@ import { useState as useState3, useEffect as useEffect3, memo as memo2 } from "r
12234
12262
  import { Text as Text3 } from "ink";
12235
12263
  import { jsx as jsx3 } from "react/jsx-runtime";
12236
12264
  var FRAMES = ["\u2669", "\u266A", "\u266B", "\u266C", "\u266B", "\u266A"];
12237
- var INTERVAL = 600;
12265
+ var INTERVAL = 120;
12238
12266
  var MusicSpinner = memo2(({ color }) => {
12239
12267
  const [index, setIndex] = useState3(0);
12240
12268
  useEffect3(() => {
@@ -12260,29 +12288,29 @@ var StatusDisplay = memo3(({
12260
12288
  };
12261
12289
  const meta = formatMeta(elapsedTime * 1e3, currentTokens);
12262
12290
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 0, children: [
12263
- retryState.isRetrying && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12264
- /* @__PURE__ */ jsx4(Text4, { color: THEME.status.warning, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.status.warning }) }),
12265
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.status.warning, children: [
12291
+ retryState.status === "retrying" && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12292
+ /* @__PURE__ */ jsx4(Text4, { color: THEME.yellow, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.yellow }) }),
12293
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.yellow, children: [
12266
12294
  " \u27F3 Retry #",
12267
12295
  retryState.attempt
12268
12296
  ] }),
12269
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.text.muted, children: [
12297
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.gray, children: [
12270
12298
  " \xB7 ",
12271
12299
  truncateError(retryState.error)
12272
12300
  ] }),
12273
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.accent.blue, bold: true, children: [
12301
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.primary, bold: true, children: [
12274
12302
  " \xB7 ",
12275
12303
  retryState.countdown,
12276
12304
  "s"
12277
12305
  ] })
12278
12306
  ] }),
12279
- isProcessing && !retryState.isRetrying && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12280
- /* @__PURE__ */ jsx4(Text4, { color: THEME.spinner, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.spinner }) }),
12281
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.text.muted, children: [
12307
+ isProcessing && retryState.status !== "retrying" && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12308
+ /* @__PURE__ */ jsx4(Text4, { color: THEME.primary, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.primary }) }),
12309
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.gray, children: [
12282
12310
  " ",
12283
12311
  currentStatus || "Processing"
12284
12312
  ] }),
12285
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.text.muted, children: [
12313
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.gray, children: [
12286
12314
  " ",
12287
12315
  meta
12288
12316
  ] })
@@ -12325,25 +12353,28 @@ var ChatInput = memo4(({
12325
12353
  const onChangeRef = useRef3(onChange);
12326
12354
  onChangeRef.current = onChange;
12327
12355
  useInput(useCallback3((_input, key) => {
12328
- if (inputRequestRef.current.isActive) return;
12356
+ if (inputRequestRef.current.status === "active") return;
12329
12357
  if (key.tab && isSlashModeRef.current && !hasArgsRef.current && suggestionsRef.current.length > 0) {
12330
12358
  const best = suggestionsRef.current[0];
12331
12359
  const argsHint = best.args ? " " : "";
12332
- onChangeRef.current(`/${best.name}${argsHint}`);
12360
+ const completed = `/${best.name}${argsHint}`;
12361
+ onChangeRef.current("");
12362
+ setTimeout(() => onChangeRef.current(completed), 0);
12333
12363
  }
12334
12364
  }, []));
12335
12365
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
12336
- showPreview && /* @__PURE__ */ jsx5(
12366
+ /* @__PURE__ */ jsx5(
12337
12367
  Box4,
12338
12368
  {
12339
12369
  flexDirection: "column",
12340
- borderStyle: "single",
12341
- borderColor: THEME.border.default,
12342
- paddingX: 1,
12370
+ borderStyle: showPreview ? "single" : void 0,
12371
+ borderColor: showPreview ? THEME.border.default : void 0,
12372
+ paddingX: showPreview ? 1 : 0,
12343
12373
  marginBottom: 0,
12344
- children: suggestions.map((cmd, i) => {
12374
+ height: showPreview ? void 0 : 0,
12375
+ children: showPreview && suggestions.map((cmd, i) => {
12345
12376
  const isFirst = i === 0;
12346
- const nameColor = isFirst ? THEME.text.accent : THEME.text.secondary;
12377
+ const nameColor = isFirst ? THEME.white : THEME.gray;
12347
12378
  const aliasText = cmd.alias ? ` /${cmd.alias}` : "";
12348
12379
  const argsText = cmd.args ? ` ${cmd.args}` : "";
12349
12380
  return /* @__PURE__ */ jsxs4(Box4, { children: [
@@ -12351,13 +12382,13 @@ var ChatInput = memo4(({
12351
12382
  "/",
12352
12383
  cmd.name
12353
12384
  ] }),
12354
- /* @__PURE__ */ jsx5(Text5, { color: THEME.text.muted, children: argsText }),
12355
- aliasText && /* @__PURE__ */ jsx5(Text5, { color: THEME.text.muted, children: aliasText }),
12356
- /* @__PURE__ */ jsxs4(Text5, { color: THEME.text.muted, children: [
12385
+ /* @__PURE__ */ jsx5(Text5, { color: THEME.gray, children: argsText }),
12386
+ aliasText && /* @__PURE__ */ jsx5(Text5, { color: THEME.gray, children: aliasText }),
12387
+ /* @__PURE__ */ jsxs4(Text5, { color: THEME.gray, children: [
12357
12388
  " \u2014 ",
12358
12389
  cmd.description
12359
12390
  ] }),
12360
- isFirst && /* @__PURE__ */ jsx5(Text5, { color: THEME.accent.blue, children: " [Tab]" })
12391
+ isFirst && /* @__PURE__ */ jsx5(Text5, { color: THEME.primary, children: " [Tab]" })
12361
12392
  ] }, cmd.name);
12362
12393
  })
12363
12394
  }
@@ -12366,11 +12397,11 @@ var ChatInput = memo4(({
12366
12397
  Box4,
12367
12398
  {
12368
12399
  borderStyle: "single",
12369
- borderColor: inputRequest.isActive ? THEME.status.warning : THEME.border.default,
12400
+ borderColor: inputRequest.status === "active" ? THEME.yellow : THEME.border.default,
12370
12401
  paddingX: 1,
12371
- children: inputRequest.isActive ? /* @__PURE__ */ jsxs4(Box4, { children: [
12372
- /* @__PURE__ */ jsx5(Text5, { color: THEME.status.warning, children: "[auth]" }),
12373
- /* @__PURE__ */ jsxs4(Text5, { color: THEME.text.muted, children: [
12402
+ children: inputRequest.status === "active" ? /* @__PURE__ */ jsxs4(Box4, { children: [
12403
+ /* @__PURE__ */ jsx5(Text5, { color: THEME.yellow, children: "[auth]" }),
12404
+ /* @__PURE__ */ jsxs4(Text5, { color: THEME.gray, children: [
12374
12405
  " ",
12375
12406
  inputRequest.prompt
12376
12407
  ] }),
@@ -12385,7 +12416,7 @@ var ChatInput = memo4(({
12385
12416
  }
12386
12417
  )
12387
12418
  ] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
12388
- /* @__PURE__ */ jsx5(Text5, { color: THEME.text.secondary, children: "\u25B8" }),
12419
+ /* @__PURE__ */ jsx5(Text5, { color: THEME.primary, children: "\u25B8" }),
12389
12420
  /* @__PURE__ */ jsx5(Text5, { children: " " }),
12390
12421
  /* @__PURE__ */ jsx5(
12391
12422
  TextInput,
@@ -12425,26 +12456,26 @@ var Footer = memo5(({ phase, targets, findings, todo, elapsedTime, isProcessing
12425
12456
  justifyContent: "space-between",
12426
12457
  children: [
12427
12458
  /* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
12428
- /* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
12459
+ /* @__PURE__ */ jsxs5(Text6, { color: THEME.gray, children: [
12429
12460
  "Phase: ",
12430
- /* @__PURE__ */ jsx6(Text6, { color: THEME.accent.blue, children: phase })
12461
+ /* @__PURE__ */ jsx6(Text6, { color: THEME.white, children: phase })
12431
12462
  ] }),
12432
- /* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
12463
+ /* @__PURE__ */ jsxs5(Text6, { color: THEME.gray, children: [
12433
12464
  "Targets: ",
12434
- /* @__PURE__ */ jsx6(Text6, { color: THEME.text.secondary, children: targets })
12465
+ /* @__PURE__ */ jsx6(Text6, { color: THEME.white, children: targets })
12435
12466
  ] }),
12436
- /* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
12467
+ /* @__PURE__ */ jsxs5(Text6, { color: THEME.gray, children: [
12437
12468
  "Findings: ",
12438
- /* @__PURE__ */ jsx6(Text6, { color: THEME.status.warning, children: findings })
12469
+ /* @__PURE__ */ jsx6(Text6, { color: THEME.white, children: findings })
12439
12470
  ] }),
12440
- /* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
12471
+ /* @__PURE__ */ jsxs5(Text6, { color: THEME.gray, children: [
12441
12472
  "Tasks: ",
12442
- /* @__PURE__ */ jsx6(Text6, { color: THEME.text.secondary, children: todo })
12473
+ /* @__PURE__ */ jsx6(Text6, { color: THEME.white, children: todo })
12443
12474
  ] })
12444
12475
  ] }),
12445
12476
  /* @__PURE__ */ jsxs5(Box5, { children: [
12446
- /* @__PURE__ */ jsx6(Text6, { color: isProcessing ? THEME.status.running : THEME.text.muted, children: isProcessing ? "Running " : "Idle " }),
12447
- /* @__PURE__ */ jsx6(Text6, { color: THEME.text.secondary, children: formatElapsed(elapsedTime) })
12477
+ /* @__PURE__ */ jsx6(Text6, { color: isProcessing ? THEME.primary : THEME.gray, children: isProcessing ? "Running " : "Idle " }),
12478
+ /* @__PURE__ */ jsx6(Text6, { color: THEME.white, children: formatElapsed(elapsedTime) })
12448
12479
  ] })
12449
12480
  ]
12450
12481
  }
@@ -12485,7 +12516,7 @@ var App = ({ autoApprove = false, target }) => {
12485
12516
  inputRequestRef.current = inputRequest;
12486
12517
  const handleExit = useCallback4(() => {
12487
12518
  const ir = inputRequestRef.current;
12488
- if (ir.isActive && ir.resolve) ir.resolve(null);
12519
+ if (ir.status === "active") ir.resolve(null);
12489
12520
  cleanupAllProcesses().catch(() => {
12490
12521
  });
12491
12522
  exit();
@@ -12549,8 +12580,29 @@ var App = ({ autoApprove = false, target }) => {
12549
12580
  addMessage("system", "No findings.");
12550
12581
  break;
12551
12582
  }
12552
- addMessage("system", `--- ${findings.length} Findings ---`);
12553
- findings.forEach((f) => addMessage("system", `[${f.severity}] ${f.title}${f.attackPattern ? ` (ATT&CK: ${f.attackPattern})` : ""}`));
12583
+ const findingLines = [`\u2500\u2500\u2500 ${findings.length} Findings \u2500\u2500\u2500`, ""];
12584
+ findings.forEach((f, i) => {
12585
+ const verified = f.isVerified ? `[${ICONS.success}] Verified` : `[${ICONS.warning}] Unverified`;
12586
+ const atk = f.attackPattern ? ` | ATT&CK: ${f.attackPattern}` : "";
12587
+ findingLines.push(`[${i + 1}] [${f.severity.toUpperCase()}] ${f.title}`);
12588
+ findingLines.push(` ${verified}${atk}`);
12589
+ if (f.affected.length > 0) {
12590
+ findingLines.push(` Affected: ${f.affected.join(", ")}`);
12591
+ }
12592
+ if (f.description) {
12593
+ findingLines.push(` ${f.description}`);
12594
+ }
12595
+ if (f.evidence.length > 0) {
12596
+ findingLines.push(` Evidence:`);
12597
+ f.evidence.slice(0, 3).forEach((e) => {
12598
+ const preview = e.length > 120 ? e.slice(0, 120) + "..." : e;
12599
+ findingLines.push(` \u25B8 ${preview}`);
12600
+ });
12601
+ if (f.evidence.length > 3) findingLines.push(` ... +${f.evidence.length - 3} more`);
12602
+ }
12603
+ findingLines.push("");
12604
+ });
12605
+ addMessage("system", findingLines.join("\n"));
12554
12606
  break;
12555
12607
  case UI_COMMANDS.ASSETS:
12556
12608
  case UI_COMMANDS.ASSETS_SHORT:
@@ -12614,17 +12666,17 @@ ${procData.stdout || "(no output)"}
12614
12666
  }, [addMessage, executeTask, handleCommand]);
12615
12667
  const handleSecretSubmit = useCallback4((value) => {
12616
12668
  const ir = inputRequestRef.current;
12617
- if (!ir.isActive || !ir.resolve) return;
12669
+ if (ir.status !== "active") return;
12618
12670
  const displayText = ir.isPassword ? "\u2022".repeat(value.length) : value;
12619
12671
  const promptLabel = ir.prompt || "Input";
12620
12672
  addMessage("system", `\u21B3 ${promptLabel} ${displayText}`);
12621
12673
  ir.resolve(value);
12622
- setInputRequest({ isActive: false, prompt: "", isPassword: false, resolve: null });
12674
+ setInputRequest({ status: "inactive" });
12623
12675
  setSecretInput("");
12624
12676
  }, [addMessage, setInputRequest]);
12625
12677
  useInput2(useCallback4((ch, key) => {
12626
12678
  if (key.escape) {
12627
- if (inputRequestRef.current.isActive) cancelInputRequest();
12679
+ if (inputRequestRef.current.status === "active") cancelInputRequest();
12628
12680
  else if (isProcessingRef.current) abort();
12629
12681
  }
12630
12682
  if (key.ctrl && ch === "c") handleExit();
@@ -12711,9 +12763,9 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
12711
12763
  const opts = program.opts();
12712
12764
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12713
12765
  console.clear();
12714
- console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
12766
+ console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12715
12767
  console.log(
12716
- " " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.accent.blue)("Type /help for commands") + "\n"
12768
+ " " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.primary)("Type /help for commands") + "\n"
12717
12769
  );
12718
12770
  if (skipPermissions) {
12719
12771
  console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions"));
@@ -12733,11 +12785,11 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
12733
12785
  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", String(CLI_DEFAULT.MAX_STEPS)).action(async (objective, options) => {
12734
12786
  const opts = program.opts();
12735
12787
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12736
- console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
12788
+ console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12737
12789
  if (skipPermissions) {
12738
12790
  console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions\n"));
12739
12791
  }
12740
- console.log(chalk.hex(THEME.accent.blue)(`[target] Objective: ${objective}
12792
+ console.log(chalk.hex(THEME.primary)(`[target] Objective: ${objective}
12741
12793
  `));
12742
12794
  const agent = AgentFactory.createMainAgent(skipPermissions);
12743
12795
  if (skipPermissions) {
@@ -12759,7 +12811,7 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
12759
12811
  if (options.output) {
12760
12812
  const fs = await import("fs/promises");
12761
12813
  await fs.writeFile(options.output, JSON.stringify({ result: result2 }, null, 2));
12762
- console.log(chalk.hex(THEME.accent.blue)(`
12814
+ console.log(chalk.hex(THEME.primary)(`
12763
12815
  [+] Report saved to: ${options.output}`));
12764
12816
  }
12765
12817
  await shutdown(0);
@@ -12773,8 +12825,8 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
12773
12825
  program.command("scan <target>").description("Quick scan a target").option("-s, --scan-type <type>", `Scan type (${CLI_SCAN_TYPES.join("|")})`, CLI_DEFAULT.SCAN_TYPE).option("-p, --ports <ports>", "Specific ports to scan").action(async (target, options) => {
12774
12826
  const opts = program.opts();
12775
12827
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12776
- console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
12777
- console.log(chalk.hex(THEME.accent.blue)(`
12828
+ console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12829
+ console.log(chalk.hex(THEME.primary)(`
12778
12830
  [scan] Target: ${target} (${options.scanType})
12779
12831
  `));
12780
12832
  const agent = AgentFactory.createMainAgent(skipPermissions);
@@ -12796,11 +12848,11 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
12796
12848
  }
12797
12849
  });
12798
12850
  program.command("help-extended").description("Show extended help with examples").action(() => {
12799
- console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
12851
+ console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12800
12852
  console.log(`
12801
- ${chalk.hex(THEME.accent.blue)(APP_NAME + " - Autonomous Penetration Testing AI")}
12853
+ ${chalk.hex(THEME.primary)(APP_NAME + " - Autonomous Penetration Testing AI")}
12802
12854
 
12803
- ${chalk.hex(THEME.status.warning)("Usage:")}
12855
+ ${chalk.hex(THEME.status.warning)("Usage:")}
12804
12856
 
12805
12857
  ${chalk.hex(THEME.status.success)("$ pentesting")} Start interactive mode
12806
12858
  ${chalk.hex(THEME.status.success)("$ pentesting -t 192.168.1.1")} Start with target
@@ -12808,24 +12860,24 @@ ${chalk.hex(THEME.status.warning)("Usage:")}
12808
12860
 
12809
12861
  ${chalk.hex(THEME.status.warning)("Commands:")}
12810
12862
 
12811
- ${chalk.hex(THEME.accent.blue)("pentesting")} Interactive TUI mode
12812
- ${chalk.hex(THEME.accent.blue)("pentesting run <objective>")} Run single objective
12813
- ${chalk.hex(THEME.accent.blue)("pentesting scan <target>")} Quick scan target
12863
+ ${chalk.hex(THEME.primary)("pentesting")} Interactive TUI mode
12864
+ ${chalk.hex(THEME.primary)("pentesting run <objective>")} Run single objective
12865
+ ${chalk.hex(THEME.primary)("pentesting scan <target>")} Quick scan target
12814
12866
 
12815
12867
  ${chalk.hex(THEME.status.warning)("Options:")}
12816
12868
 
12817
- ${chalk.hex(THEME.accent.blue)("--dangerously-skip-permissions")} Skip all permission prompts
12818
- ${chalk.hex(THEME.accent.blue)("-t, --target <ip>")} Set target
12819
- ${chalk.hex(THEME.accent.blue)("-o, --output <file>")} Save results to file
12869
+ ${chalk.hex(THEME.primary)("--dangerously-skip-permissions")} Skip all permission prompts
12870
+ ${chalk.hex(THEME.primary)("-t, --target <ip>")} Set target
12871
+ ${chalk.hex(THEME.primary)("-o, --output <file>")} Save results to file
12820
12872
 
12821
12873
  ${chalk.hex(THEME.status.warning)("Interactive Commands:")}
12822
12874
 
12823
- ${chalk.hex(THEME.accent.blue)("/target <ip>")} Set target
12824
- ${chalk.hex(THEME.accent.blue)("/start")} Start autonomous mode
12825
- ${chalk.hex(THEME.accent.blue)("/config")} Manage configuration
12826
- ${chalk.hex(THEME.accent.blue)("/hint <text>")} Provide hint
12827
- ${chalk.hex(THEME.accent.blue)("/findings")} Show findings
12828
- ${chalk.hex(THEME.accent.blue)("/reset")} Reset session
12875
+ ${chalk.hex(THEME.primary)("/target <ip>")} Set target
12876
+ ${chalk.hex(THEME.primary)("/start")} Start autonomous mode
12877
+ ${chalk.hex(THEME.primary)("/config")} Manage configuration
12878
+ ${chalk.hex(THEME.primary)("/hint <text>")} Provide hint
12879
+ ${chalk.hex(THEME.primary)("/findings")} Show findings
12880
+ ${chalk.hex(THEME.primary)("/reset")} Reset session
12829
12881
 
12830
12882
  ${chalk.hex(THEME.status.warning)("Examples:")}
12831
12883
 
@@ -12840,9 +12892,9 @@ ${chalk.hex(THEME.status.warning)("Examples:")}
12840
12892
 
12841
12893
  ${chalk.hex(THEME.status.warning)("Environment:")}
12842
12894
 
12843
- ${chalk.hex(THEME.accent.blue)("PENTEST_API_KEY")} Required - LLM API key
12844
- ${chalk.hex(THEME.accent.blue)("PENTEST_BASE_URL")} Optional - AI API base URL
12845
- ${chalk.hex(THEME.accent.blue)("PENTEST_MODEL")} Optional - Model override
12895
+ ${chalk.hex(THEME.primary)("PENTEST_API_KEY")} Required - LLM API key
12896
+ ${chalk.hex(THEME.primary)("PENTEST_BASE_URL")} Optional - AI API base URL
12897
+ ${chalk.hex(THEME.primary)("PENTEST_MODEL")} Optional - Model override
12846
12898
  `);
12847
12899
  });
12848
12900
  program.parse();