pentesting 0.56.1 → 0.56.3

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.
Files changed (2) hide show
  1. package/dist/main.js +164 -49
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -711,7 +711,7 @@ var INPUT_PROMPT_PATTERNS = [
711
711
 
712
712
  // src/shared/constants/agent.ts
713
713
  var APP_NAME = "Pentest AI";
714
- var APP_VERSION = "0.56.1";
714
+ var APP_VERSION = "0.56.3";
715
715
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
716
716
  var LLM_ROLES = {
717
717
  SYSTEM: "system",
@@ -768,7 +768,7 @@ import gradient from "gradient-string";
768
768
 
769
769
  // src/platform/tui/app.tsx
770
770
  import { useState as useState8, useCallback as useCallback11, useRef as useRef10 } from "react";
771
- import { Box as Box18, useApp, useStdout as useStdout2 } from "ink";
771
+ import { Box as Box18, useApp, useStdout as useStdout4 } from "ink";
772
772
 
773
773
  // src/platform/tui/hooks/useAgent.ts
774
774
  import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef3 } from "react";
@@ -14911,7 +14911,10 @@ var formatFlags = (flags) => {
14911
14911
  if (!flags.length) return "";
14912
14912
  const lines = [];
14913
14913
  const total = flags.length;
14914
- lines.push(`\u2500\u2500\u2500 Captured (${total}) \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`);
14914
+ const label = `\u2500\u2500\u2500 Captured (${total}) `;
14915
+ const cols = process.stdout?.columns ?? 80;
14916
+ const fillWidth = Math.max(3, cols - 2 - label.length);
14917
+ lines.push(`${label}${"\u2500".repeat(fillWidth)}`);
14915
14918
  lines.push("");
14916
14919
  flags.forEach((flag, i) => {
14917
14920
  lines.push(` ${(i + 1).toString().padStart(2, " ")}. ${flag}`);
@@ -14920,6 +14923,21 @@ var formatFlags = (flags) => {
14920
14923
  };
14921
14924
 
14922
14925
  // src/platform/tui/hooks/commands/formatters/graph-summary.ts
14926
+ var getBoxWidth = () => Math.max(30, (process.stdout?.columns ?? 80) - 2);
14927
+ var boxLine = (content, width) => {
14928
+ const inner = width - 2;
14929
+ const truncated = content.length > inner ? content.slice(0, inner - 1) + "\u2026" : content;
14930
+ return `\u2502 ${truncated.padEnd(inner - 1)}`;
14931
+ };
14932
+ var formatTargetLine = (target) => {
14933
+ if (target.ports.length === 0) return target.ip;
14934
+ const portList = target.ports.map((p) => `${p.port}`).join(", ");
14935
+ return `${target.ip} \u2192 ${target.ip}:${portList}`;
14936
+ };
14937
+ var formatServiceLabel = (target, port) => {
14938
+ const svc = port.version ? `${port.service}/${port.version}` : port.service;
14939
+ return `${target.ip}:${port.port} (${svc})`;
14940
+ };
14923
14941
  var formatFindingsWithFlags = (findings, flags) => {
14924
14942
  const findingsOutput = formatFindings(findings);
14925
14943
  const flagsOutput = formatFlags(flags);
@@ -14929,40 +14947,109 @@ ${flagsOutput}`;
14929
14947
  }
14930
14948
  return findingsOutput;
14931
14949
  };
14932
- var formatGraphWithSummary = (_graphASCII, findings, flags) => {
14950
+ var formatGraphWithSummary = (state, findings, flags) => {
14933
14951
  const lines = [];
14934
- lines.push("\u250C\u2500\u2500\u2500 Attack Graph \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\u2510");
14935
- lines.push(`\u2502 \u{1F5A5} 1 \u26A0 ${findings.length} \u2699 1`);
14936
- lines.push("\u2502");
14937
- lines.push("\u2502 \u{1F5A5} HOST (1)");
14938
- lines.push(`\u2502 \u25CB 138.2.89.94 \u2192 138.2.89.94:443`);
14939
- lines.push("\u2502");
14940
- lines.push(`\u2502 \u26A0 VULNERABILITY (${findings.length})`);
14941
- const sortedFindings = [...findings].sort((a, b) => b.confidence - a.confidence).slice(0, 5);
14942
- for (const f of sortedFindings) {
14943
- const icon = confIcon(f.confidence);
14944
- const cat = f.category ? ` \u2502 ${f.category}` : "";
14945
- lines.push(`\u2502 \u25CB ${icon} ${f.title.slice(0, 60)}${f.title.length > 60 ? "..." : ""}`);
14946
- lines.push(`\u2502 ${confLabel(f.confidence).toUpperCase()} \u2502 ${f.severity.toUpperCase()}${cat}`);
14952
+ const w = getBoxWidth();
14953
+ const innerDash = w - 2;
14954
+ const targets = state.getAllTargets();
14955
+ const graphStats = state.attackGraph.getStats();
14956
+ const allNodes = state.attackGraph.getAllNodes();
14957
+ const hostNodes = allNodes.filter((n) => n.type === NODE_TYPE.HOST);
14958
+ const serviceNodes = allNodes.filter((n) => n.type === NODE_TYPE.SERVICE);
14959
+ const vulnNodes = allNodes.filter((n) => n.type === NODE_TYPE.VULNERABILITY);
14960
+ const hostCount = hostNodes.length || targets.length;
14961
+ const serviceCount = serviceNodes.length || targets.reduce((sum, t) => sum + t.ports.length, 0);
14962
+ const vulnCount = findings.length;
14963
+ const topTitle = "\u2500\u2500\u2500 Attack Graph ";
14964
+ lines.push(`\u250C${topTitle}${"\u2500".repeat(Math.max(0, innerDash - topTitle.length))}\u2510`);
14965
+ lines.push(boxLine(`\u{1F5A5} ${hostCount} \u26A0 ${vulnCount} \u2699 ${serviceCount}`, w));
14966
+ lines.push(boxLine("", w));
14967
+ lines.push(boxLine(`\u{1F5A5} HOST (${hostCount})`, w));
14968
+ if (targets.length > 0) {
14969
+ for (const t of targets.slice(0, 5)) {
14970
+ lines.push(boxLine(` \u25CB ${formatTargetLine(t)}`, w));
14971
+ }
14972
+ if (targets.length > 5) {
14973
+ lines.push(boxLine(` ... and ${targets.length - 5} more hosts`, w));
14974
+ }
14975
+ } else if (hostNodes.length > 0) {
14976
+ for (const node of hostNodes.slice(0, 5)) {
14977
+ lines.push(boxLine(` \u25CB ${node.label}`, w));
14978
+ }
14979
+ if (hostNodes.length > 5) {
14980
+ lines.push(boxLine(` ... and ${hostNodes.length - 5} more hosts`, w));
14981
+ }
14982
+ } else {
14983
+ lines.push(boxLine(" (no hosts discovered)", w));
14947
14984
  }
14948
- if (findings.length > 5) {
14949
- lines.push(`\u2502 ... and ${findings.length - 5} more findings`);
14985
+ lines.push(boxLine("", w));
14986
+ lines.push(boxLine(`\u26A0 VULNERABILITY (${vulnCount})`, w));
14987
+ if (findings.length > 0) {
14988
+ const sortedFindings = [...findings].sort((a, b) => b.confidence - a.confidence).slice(0, 5);
14989
+ for (const f of sortedFindings) {
14990
+ const icon = confIcon(f.confidence);
14991
+ const cat = f.category ? ` \u2502 ${f.category}` : "";
14992
+ lines.push(boxLine(` \u25CB ${icon} ${f.title.slice(0, 60)}${f.title.length > 60 ? "..." : ""}`, w));
14993
+ lines.push(boxLine(` ${confLabel(f.confidence).toUpperCase()} \u2502 ${f.severity.toUpperCase()}${cat}`, w));
14994
+ }
14995
+ if (findings.length > 5) {
14996
+ lines.push(boxLine(` ... and ${findings.length - 5} more findings`, w));
14997
+ }
14998
+ } else if (vulnNodes.length > 0) {
14999
+ for (const node of vulnNodes.slice(0, 5)) {
15000
+ lines.push(boxLine(` \u25CB ${node.label}`, w));
15001
+ }
15002
+ if (vulnNodes.length > 5) {
15003
+ lines.push(boxLine(` ... and ${vulnNodes.length - 5} more`, w));
15004
+ }
15005
+ } else {
15006
+ lines.push(boxLine(" (no vulnerabilities found)", w));
14950
15007
  }
14951
15008
  const cveFindings = findings.filter((f) => f.title.includes("CVE"));
14952
15009
  if (cveFindings.length > 0) {
14953
- lines.push(`\u2502 \u25CB CVE search: https nginx/1.24.0 (Ubuntu) -> Apache CouchDB 3.5.1`);
15010
+ const cveList = cveFindings.slice(0, 3).map((f) => f.title.match(/CVE-\d{4}-\d+/)?.[0] ?? f.title).join(", ");
15011
+ const suffix = cveFindings.length > 3 ? ` (+${cveFindings.length - 3} more)` : "";
15012
+ lines.push(boxLine(` \u25CB CVE found: ${cveList}${suffix}`, w));
15013
+ }
15014
+ lines.push(boxLine("", w));
15015
+ lines.push(boxLine(`\u2699 SERVICE (${serviceCount})`, w));
15016
+ if (targets.length > 0) {
15017
+ let shownServices = 0;
15018
+ const maxShow = 5;
15019
+ for (const t of targets) {
15020
+ for (const p of t.ports) {
15021
+ if (shownServices >= maxShow) break;
15022
+ lines.push(boxLine(` \u25CB ${formatServiceLabel(t, p)}`, w));
15023
+ shownServices++;
15024
+ }
15025
+ if (shownServices >= maxShow) break;
15026
+ }
15027
+ const totalServices = targets.reduce((sum, t) => sum + t.ports.length, 0);
15028
+ if (totalServices > maxShow) {
15029
+ lines.push(boxLine(` ... and ${totalServices - maxShow} more services`, w));
15030
+ }
15031
+ } else if (serviceNodes.length > 0) {
15032
+ for (const node of serviceNodes.slice(0, 5)) {
15033
+ lines.push(boxLine(` \u25CB ${node.label}`, w));
15034
+ }
15035
+ if (serviceNodes.length > 5) {
15036
+ lines.push(boxLine(` ... and ${serviceNodes.length - 5} more`, w));
15037
+ }
15038
+ } else {
15039
+ lines.push(boxLine(" (no services discovered)", w));
14954
15040
  }
14955
- lines.push("\u2502");
14956
- lines.push("\u2502 \u2699 SERVICE (1)");
14957
- lines.push(`\u2502 \u25CB 138.2.89.94:443 (nginx/1.24.0 (Ubuntu) -> Apache CouchDB 3.5.1) \u2192 CVE search: https nginx/1.24.0 (Ubuntu) -> Apache CouchDB 3.5.1`);
14958
15041
  if (flags.length > 0) {
14959
- lines.push("\u2502");
14960
- lines.push(`\u2502 \u{1F3F4} Captured: ${flags.length}`);
14961
- }
14962
- lines.push("\u2502");
14963
- 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");
14964
- lines.push(`\u2502 Nodes: ${findings.length + 2} | Edges: 2 | Succeeded: 0 | Failed: 0 | Chains: 0`);
14965
- lines.push("\u2514\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\u2518");
15042
+ lines.push(boxLine("", w));
15043
+ lines.push(boxLine(`\u{1F3F4} Captured: ${flags.length}`, w));
15044
+ }
15045
+ lines.push(boxLine("", w));
15046
+ const midTitle = "\u2500\u2500\u2500 Summary ";
15047
+ lines.push(`\u251C${midTitle}${"\u2500".repeat(Math.max(0, innerDash - midTitle.length))}\u2524`);
15048
+ lines.push(boxLine(
15049
+ `Nodes: ${graphStats.nodes} | Edges: ${graphStats.edges} | Succeeded: ${graphStats.succeeded} | Failed: ${graphStats.failed} | Chains: ${graphStats.chains}`,
15050
+ w
15051
+ ));
15052
+ lines.push(`\u2514${"\u2500".repeat(innerDash)}\u2518`);
14966
15053
  return lines.join("\n");
14967
15054
  };
14968
15055
 
@@ -15020,7 +15107,7 @@ ${procData.stdout || "(no output)"}
15020
15107
  const flags = state.getFlags();
15021
15108
  const graphASCII = state.attackGraph.toASCII();
15022
15109
  if (state.attackGraph.isEmpty() && (findings.length > 0 || flags.length > 0)) {
15023
- ctx.showModal("graph", formatGraphWithSummary(graphASCII, findings, flags));
15110
+ ctx.showModal("graph", formatGraphWithSummary(state, findings, flags));
15024
15111
  } else {
15025
15112
  let output = graphASCII;
15026
15113
  if (flags.length > 0) {
@@ -15035,7 +15122,7 @@ ${procData.stdout || "(no output)"}
15035
15122
  const flags = state.getFlags();
15036
15123
  const graphASCII = state.attackGraph.toASCII();
15037
15124
  if (state.attackGraph.isEmpty() && (findings.length > 0 || flags.length > 0)) {
15038
- ctx.showModal("graph", formatGraphWithSummary(graphASCII, findings, flags));
15125
+ ctx.showModal("graph", formatGraphWithSummary(state, findings, flags));
15039
15126
  } else {
15040
15127
  let output = graphASCII;
15041
15128
  if (flags.length > 0) {
@@ -16160,17 +16247,21 @@ var AutocompletePreview = ({
16160
16247
  };
16161
16248
 
16162
16249
  // src/platform/tui/components/input/SecretInputArea.tsx
16163
- import { Box as Box12, Text as Text14 } from "ink";
16250
+ import { Box as Box12, Text as Text14, useStdout } from "ink";
16164
16251
  import TextInput from "ink-text-input";
16165
16252
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
16253
+ var OUTER_PADDING = 2;
16166
16254
  var SecretInputArea = ({
16167
16255
  inputRequest,
16168
16256
  secretInput,
16169
16257
  setSecretInput,
16170
16258
  onSecretSubmit
16171
16259
  }) => {
16260
+ const { stdout } = useStdout();
16261
+ const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING);
16262
+ const borderLine = "\u2501".repeat(borderWidth);
16172
16263
  return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
16173
- /* @__PURE__ */ jsx14(Box12, { children: /* @__PURE__ */ jsx14(Text14, { color: THEME.yellow, children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }) }),
16264
+ /* @__PURE__ */ jsx14(Box12, { children: /* @__PURE__ */ jsx14(Text14, { color: THEME.yellow, children: borderLine }) }),
16174
16265
  /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, children: [
16175
16266
  /* @__PURE__ */ jsxs12(Box12, { children: [
16176
16267
  /* @__PURE__ */ jsx14(Text14, { color: THEME.yellow, bold: true, children: "\u{1F512} " }),
@@ -16190,14 +16281,15 @@ var SecretInputArea = ({
16190
16281
  )
16191
16282
  ] })
16192
16283
  ] }),
16193
- /* @__PURE__ */ jsx14(Box12, { children: /* @__PURE__ */ jsx14(Text14, { color: THEME.yellow, children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }) })
16284
+ /* @__PURE__ */ jsx14(Box12, { children: /* @__PURE__ */ jsx14(Text14, { color: THEME.yellow, children: borderLine }) })
16194
16285
  ] });
16195
16286
  };
16196
16287
 
16197
16288
  // src/platform/tui/components/input/NormalInputArea.tsx
16198
- import { Box as Box13, Text as Text15 } from "ink";
16289
+ import { Box as Box13, Text as Text15, useStdout as useStdout2 } from "ink";
16199
16290
  import TextInput2 from "ink-text-input";
16200
16291
  import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
16292
+ var OUTER_PADDING2 = 2;
16201
16293
  var NormalInputArea = ({
16202
16294
  inputKey,
16203
16295
  value,
@@ -16205,8 +16297,11 @@ var NormalInputArea = ({
16205
16297
  onSubmit,
16206
16298
  placeholder
16207
16299
  }) => {
16300
+ const { stdout } = useStdout2();
16301
+ const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING2);
16302
+ const borderLine = "\u2500".repeat(borderWidth);
16208
16303
  return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
16209
- /* @__PURE__ */ jsx15(Box13, { children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, color: THEME.dimGray, children: "\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\u2500\u2500\u2500\u2500\u2500" }) }),
16304
+ /* @__PURE__ */ jsx15(Box13, { children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, color: THEME.dimGray, children: borderLine }) }),
16210
16305
  /* @__PURE__ */ jsxs13(Box13, { paddingX: 1, children: [
16211
16306
  /* @__PURE__ */ jsx15(Text15, { color: THEME.primary, children: "\u276F " }),
16212
16307
  /* @__PURE__ */ jsx15(
@@ -16220,7 +16315,7 @@ var NormalInputArea = ({
16220
16315
  inputKey
16221
16316
  )
16222
16317
  ] }),
16223
- /* @__PURE__ */ jsx15(Box13, { children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, color: THEME.dimGray, children: "\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\u2500\u2500\u2500\u2500\u2500" }) })
16318
+ /* @__PURE__ */ jsx15(Box13, { children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, color: THEME.dimGray, children: borderLine }) })
16224
16319
  ] });
16225
16320
  };
16226
16321
 
@@ -16287,6 +16382,27 @@ var ChatInput = memo10(({
16287
16382
  setSelectedIdx(0);
16288
16383
  setInputKey((k) => k + 1);
16289
16384
  }, []);
16385
+ const onSubmitRef = useRef8(onSubmit);
16386
+ onSubmitRef.current = onSubmit;
16387
+ const wrappedOnSubmit = useCallback9((val) => {
16388
+ if (showPreviewRef.current) {
16389
+ const sug = suggestionsRef.current;
16390
+ if (!sug.length) {
16391
+ onSubmitRef.current(val);
16392
+ return;
16393
+ }
16394
+ const best = sug[Math.min(selectedIdxRef.current, sug.length - 1)];
16395
+ if (best.args) {
16396
+ completeCommand(selectedIdxRef.current);
16397
+ } else {
16398
+ const completed = `/${best.name}`;
16399
+ onChangeRef.current(completed);
16400
+ onSubmitRef.current(completed);
16401
+ }
16402
+ return;
16403
+ }
16404
+ onSubmitRef.current(val);
16405
+ }, [completeCommand]);
16290
16406
  useInput3(useCallback9((_input, key) => {
16291
16407
  if (inputRequestRef.current.status === "active") return;
16292
16408
  const sug = suggestionsRef.current;
@@ -16330,7 +16446,7 @@ var ChatInput = memo10(({
16330
16446
  inputKey,
16331
16447
  value,
16332
16448
  onChange,
16333
- onSubmit,
16449
+ onSubmit: wrappedOnSubmit,
16334
16450
  placeholder
16335
16451
  }
16336
16452
  )
@@ -16361,6 +16477,7 @@ var Footer = memo11(({ phase, targets, findings, todo, elapsedTime, isProcessing
16361
16477
  return /* @__PURE__ */ jsxs15(
16362
16478
  Box15,
16363
16479
  {
16480
+ width: "100%",
16364
16481
  paddingX: 1,
16365
16482
  justifyContent: "space-between",
16366
16483
  overflow: "hidden",
@@ -16412,7 +16529,7 @@ var footer_default = Footer;
16412
16529
 
16413
16530
  // src/platform/tui/components/Modal.tsx
16414
16531
  import { useMemo as useMemo2, memo as memo12, useCallback as useCallback10, useRef as useRef9 } from "react";
16415
- import { Box as Box16, Text as Text18, useStdout, useInput as useInput4 } from "ink";
16532
+ import { Box as Box16, Text as Text18, useStdout as useStdout3, useInput as useInput4 } from "ink";
16416
16533
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
16417
16534
  var MODAL_TITLES = {
16418
16535
  findings: "\u25C8 FINDINGS \u25C8",
@@ -16430,7 +16547,7 @@ var Modal = memo12(({
16430
16547
  onScrollRef.current = onScroll;
16431
16548
  const onCloseRef = useRef9(onClose);
16432
16549
  onCloseRef.current = onClose;
16433
- const { stdout } = useStdout();
16550
+ const { stdout } = useStdout3();
16434
16551
  const terminalHeight = stdout?.rows ?? 24;
16435
16552
  const terminalWidth = (stdout?.columns ?? 80) - 4;
16436
16553
  const maxHeight = Math.max(1, terminalHeight - 6);
@@ -16464,13 +16581,11 @@ var Modal = memo12(({
16464
16581
  width: terminalWidth,
16465
16582
  height: terminalHeight,
16466
16583
  children: [
16467
- /* @__PURE__ */ jsx18(Box16, { justifyContent: "center", marginBottom: 0, children: /* @__PURE__ */ jsxs16(Text18, { color: THEME.cyan, bold: true, children: [
16468
- "\u2500".repeat(10),
16469
- " ",
16470
- MODAL_TITLES[type],
16471
- " ",
16472
- "\u2500".repeat(10)
16473
- ] }) }),
16584
+ /* @__PURE__ */ jsx18(Box16, { justifyContent: "center", marginBottom: 0, children: /* @__PURE__ */ jsx18(Text18, { color: THEME.cyan, bold: true, children: (() => {
16585
+ const title = MODAL_TITLES[type];
16586
+ const sideWidth = Math.max(3, Math.floor((terminalWidth - title.length - 2) / 2));
16587
+ return `${"\u2500".repeat(sideWidth)} ${title} ${"\u2500".repeat(sideWidth)}`;
16588
+ })() }) }),
16474
16589
  /* @__PURE__ */ jsx18(
16475
16590
  Box16,
16476
16591
  {
@@ -16575,7 +16690,7 @@ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
16575
16690
  var MODEL_NAME = getModel() || DEFAULT_MODEL;
16576
16691
  var App = ({ autoApprove = false, target }) => {
16577
16692
  const { exit } = useApp();
16578
- const { stdout } = useStdout2();
16693
+ const { stdout } = useStdout4();
16579
16694
  const terminalWidth = stdout?.columns ?? 80;
16580
16695
  const [input, setInput] = useState8("");
16581
16696
  const [secretInput, setSecretInput] = useState8("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pentesting",
3
- "version": "0.56.1",
3
+ "version": "0.56.3",
4
4
  "description": "Autonomous Penetration Testing AI Agent",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",