@pensar/apex 0.0.27 → 0.0.28-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -66454,13 +66454,200 @@ Remember: You are a focused penetration testing agent assigned a specific target
66454
66454
  import { exec } from "child_process";
66455
66455
  import { promisify } from "util";
66456
66456
  import {
66457
- writeFileSync,
66457
+ writeFileSync as writeFileSync2,
66458
66458
  appendFileSync,
66459
- readdirSync,
66459
+ readdirSync as readdirSync2,
66460
+ readFileSync as readFileSync2,
66461
+ existsSync as existsSync5
66462
+ } from "fs";
66463
+ import { join as join2 } from "path";
66464
+
66465
+ // src/core/agent/sessions/index.ts
66466
+ import {
66467
+ mkdirSync,
66468
+ existsSync as existsSync3,
66469
+ writeFileSync,
66460
66470
  readFileSync,
66461
- existsSync as existsSync3
66471
+ readdirSync,
66472
+ statSync,
66473
+ rmSync
66462
66474
  } from "fs";
66463
66475
  import { join } from "path";
66476
+ import { homedir } from "os";
66477
+
66478
+ // src/core/services/rateLimiter/index.ts
66479
+ function sleep(ms) {
66480
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
66481
+ }
66482
+
66483
+ class RateLimiter {
66484
+ tokens;
66485
+ lastRefillTime;
66486
+ rps;
66487
+ bucketSize;
66488
+ msPerToken;
66489
+ queue;
66490
+ constructor(config3) {
66491
+ this.rps = config3?.requestsPerSecond;
66492
+ this.bucketSize = this.rps ? 1 : 0;
66493
+ this.tokens = this.bucketSize;
66494
+ this.lastRefillTime = performance.now();
66495
+ this.msPerToken = this.rps ? 1000 / this.rps : undefined;
66496
+ this.queue = Promise.resolve();
66497
+ }
66498
+ async acquireSlot() {
66499
+ if (!this.rps || !this.msPerToken)
66500
+ return;
66501
+ const previousPromise = this.queue;
66502
+ let resolveCurrentRequest;
66503
+ this.queue = new Promise((resolve4) => {
66504
+ resolveCurrentRequest = resolve4;
66505
+ });
66506
+ await previousPromise;
66507
+ try {
66508
+ const now2 = performance.now();
66509
+ this.refill(now2);
66510
+ if (this.tokens < 1) {
66511
+ const waitTime = (1 - this.tokens) * this.msPerToken;
66512
+ await sleep(waitTime);
66513
+ const nowAfterSleep = performance.now();
66514
+ this.refill(nowAfterSleep);
66515
+ }
66516
+ this.tokens -= 1;
66517
+ } finally {
66518
+ resolveCurrentRequest();
66519
+ }
66520
+ }
66521
+ refill(now2) {
66522
+ if (this.tokens >= this.bucketSize) {
66523
+ this.lastRefillTime = now2;
66524
+ return;
66525
+ }
66526
+ const elapsed = now2 - this.lastRefillTime;
66527
+ const tokensToAdd = elapsed / this.msPerToken;
66528
+ this.tokens = Math.min(this.bucketSize, this.tokens + tokensToAdd);
66529
+ this.lastRefillTime = now2;
66530
+ }
66531
+ isEnabled() {
66532
+ return this.rps !== undefined;
66533
+ }
66534
+ }
66535
+
66536
+ // src/core/agent/sessions/index.ts
66537
+ var DEFAULT_OFFENSIVE_HEADERS = {
66538
+ "User-Agent": "pensar-apex"
66539
+ };
66540
+ function generateSessionId(prefix) {
66541
+ const timestamp = Date.now().toString(36);
66542
+ return `${prefix ? `${prefix}-` : ""}${timestamp}`;
66543
+ }
66544
+ function getPensarDir() {
66545
+ return join(homedir(), ".pensar");
66546
+ }
66547
+ function getExecutionsDir() {
66548
+ return join(getPensarDir(), "executions");
66549
+ }
66550
+ function createSession(target, objective, prefix, config3) {
66551
+ const sessionId = generateSessionId(prefix);
66552
+ const rootPath = join(getExecutionsDir(), sessionId);
66553
+ const findingsPath = join(rootPath, "findings");
66554
+ const scratchpadPath = join(rootPath, "scratchpad");
66555
+ const logsPath = join(rootPath, "logs");
66556
+ ensureDirectoryExists(rootPath);
66557
+ ensureDirectoryExists(findingsPath);
66558
+ ensureDirectoryExists(scratchpadPath);
66559
+ ensureDirectoryExists(logsPath);
66560
+ const session = {
66561
+ id: sessionId,
66562
+ rootPath,
66563
+ findingsPath,
66564
+ scratchpadPath,
66565
+ logsPath,
66566
+ target,
66567
+ objective: objective ?? "",
66568
+ startTime: new Date().toISOString(),
66569
+ config: config3
66570
+ };
66571
+ if (config3?.rateLimiter) {
66572
+ session._rateLimiter = new RateLimiter(config3.rateLimiter);
66573
+ }
66574
+ const metadataPath = join(rootPath, "session.json");
66575
+ writeFileSync(metadataPath, JSON.stringify(session, null, 2));
66576
+ const readmePath = join(rootPath, "README.md");
66577
+ const readme = `# Penetration Test Session
66578
+
66579
+ **Session ID:** ${sessionId}
66580
+ **Target:** ${target}
66581
+ **Objective:** ${objective}
66582
+ **Started:** ${session.startTime}
66583
+
66584
+ ## Directory Structure
66585
+
66586
+ - \`findings/\` - Security findings and vulnerabilities
66587
+ - \`scratchpad/\` - Notes and temporary data during testing
66588
+ - \`logs/\` - Execution logs and command outputs
66589
+ - \`session.json\` - Session metadata
66590
+
66591
+ ## Findings
66592
+
66593
+ Security findings will be documented in the \`findings/\` directory as individual files.
66594
+
66595
+ ## Status
66596
+
66597
+ Testing in progress...
66598
+ `;
66599
+ writeFileSync(readmePath, readme);
66600
+ return session;
66601
+ }
66602
+ function ensureDirectoryExists(path3) {
66603
+ if (!existsSync3(path3)) {
66604
+ mkdirSync(path3, { recursive: true });
66605
+ }
66606
+ }
66607
+ function getSession(sessionId) {
66608
+ const sessionPath = join(getExecutionsDir(), sessionId);
66609
+ const metadataPath = join(sessionPath, "session.json");
66610
+ if (!existsSync3(metadataPath)) {
66611
+ return null;
66612
+ }
66613
+ const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"));
66614
+ return metadata;
66615
+ }
66616
+ function extractTimestamp(sessionId) {
66617
+ const parts = sessionId.split("-");
66618
+ const timestampBase36 = parts[parts.length - 1];
66619
+ return parseInt(timestampBase36 || "", 36);
66620
+ }
66621
+ function listSessions() {
66622
+ const executionsDir = getExecutionsDir();
66623
+ if (!existsSync3(executionsDir)) {
66624
+ return [];
66625
+ }
66626
+ const entries = readdirSync(executionsDir);
66627
+ const directories = entries.filter((entry) => {
66628
+ const fullPath = join(executionsDir, entry);
66629
+ return statSync(fullPath).isDirectory();
66630
+ });
66631
+ return directories.sort((a, b) => {
66632
+ const timestampA = extractTimestamp(a);
66633
+ const timestampB = extractTimestamp(b);
66634
+ return timestampB - timestampA;
66635
+ });
66636
+ }
66637
+ function getOffensiveHeaders(session) {
66638
+ const config3 = session.config?.offensiveHeaders;
66639
+ if (!config3 || config3.mode === "none") {
66640
+ return;
66641
+ }
66642
+ if (config3.mode === "default") {
66643
+ return DEFAULT_OFFENSIVE_HEADERS;
66644
+ }
66645
+ if (config3.mode === "custom" && config3.headers) {
66646
+ return config3.headers;
66647
+ }
66648
+ return;
66649
+ }
66650
+ // src/core/agent/tools.ts
66464
66651
  var execAsync = promisify(exec);
66465
66652
  var PayloadSchema = exports_external.object({
66466
66653
  payload: exports_external.string(),
@@ -67554,7 +67741,7 @@ FINDING STRUCTURE:
67554
67741
  const safeTitle = finding.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").substring(0, 50);
67555
67742
  const findingId = `${timestamp.split("T")[0]}-${safeTitle}`;
67556
67743
  const filename = `${findingId}.md`;
67557
- const filepath = join(session.findingsPath, filename);
67744
+ const filepath = join2(session.findingsPath, filename);
67558
67745
  const markdown = `# ${finding.title}
67559
67746
 
67560
67747
  **Severity:** ${finding.severity}
@@ -67588,8 +67775,8 @@ ${finding.references}` : ""}
67588
67775
 
67589
67776
  *This finding was automatically documented by the Pensar penetration testing agent.*
67590
67777
  `;
67591
- writeFileSync(filepath, markdown);
67592
- const summaryPath = join(session.rootPath, "findings-summary.md");
67778
+ writeFileSync2(filepath, markdown);
67779
+ const summaryPath = join2(session.rootPath, "findings-summary.md");
67593
67780
  const summaryEntry = `- [${finding.severity}] ${finding.title} - \`findings/${filename}\`
67594
67781
  `;
67595
67782
  try {
@@ -67603,7 +67790,7 @@ ${finding.references}` : ""}
67603
67790
  ## All Findings
67604
67791
 
67605
67792
  `;
67606
- writeFileSync(summaryPath, header + summaryEntry);
67793
+ writeFileSync2(summaryPath, header + summaryEntry);
67607
67794
  }
67608
67795
  return {
67609
67796
  success: true,
@@ -67642,7 +67829,7 @@ The scratchpad is session-specific and helps maintain context during long assess
67642
67829
  execute: async ({ note, category }) => {
67643
67830
  try {
67644
67831
  const timestamp = new Date().toISOString();
67645
- const scratchpadFile = join(session.scratchpadPath, "notes.md");
67832
+ const scratchpadFile = join2(session.scratchpadPath, "notes.md");
67646
67833
  const entry = `## ${category.toUpperCase()} - ${timestamp}
67647
67834
 
67648
67835
  ${note}
@@ -67661,7 +67848,7 @@ ${note}
67661
67848
  ---
67662
67849
 
67663
67850
  `;
67664
- writeFileSync(scratchpadFile, header + entry);
67851
+ writeFileSync2(scratchpadFile, header + entry);
67665
67852
  }
67666
67853
  return {
67667
67854
  success: true,
@@ -67788,11 +67975,11 @@ The report will be saved as 'pentest-report.md' in the session root directory.`,
67788
67975
  MEDIUM: 0,
67789
67976
  LOW: 0
67790
67977
  };
67791
- if (existsSync3(session.findingsPath)) {
67792
- const findingFiles = readdirSync(session.findingsPath).filter((f) => f.endsWith(".json"));
67978
+ if (existsSync5(session.findingsPath)) {
67979
+ const findingFiles = readdirSync2(session.findingsPath).filter((f) => f.endsWith(".json"));
67793
67980
  for (const file2 of findingFiles) {
67794
- const filePath = join(session.findingsPath, file2);
67795
- const content = readFileSync(filePath, "utf-8");
67981
+ const filePath = join2(session.findingsPath, file2);
67982
+ const content = readFileSync2(filePath, "utf-8");
67796
67983
  const severityMatch = content.match(/\*\*Severity:\*\*\s+(CRITICAL|HIGH|MEDIUM|LOW)/);
67797
67984
  const titleMatch = content.match(/^#\s+(.+)$/m);
67798
67985
  if (severityMatch && titleMatch) {
@@ -67817,14 +68004,14 @@ The report will be saved as 'pentest-report.md' in the session root directory.`,
67817
68004
  const totalFindings = findings.length;
67818
68005
  const criticalAndHigh = severityCounts.CRITICAL + severityCounts.HIGH;
67819
68006
  let scratchpadNotes = "";
67820
- const scratchpadFile = join(session.scratchpadPath, "notes.md");
67821
- if (existsSync3(scratchpadFile)) {
67822
- scratchpadNotes = readFileSync(scratchpadFile, "utf-8");
68007
+ const scratchpadFile = join2(session.scratchpadPath, "notes.md");
68008
+ if (existsSync5(scratchpadFile)) {
68009
+ scratchpadNotes = readFileSync2(scratchpadFile, "utf-8");
67823
68010
  }
67824
68011
  let testResultsSummary = "";
67825
- const testResultsFile = join(session.scratchpadPath, "test-results.jsonl");
67826
- if (existsSync3(testResultsFile)) {
67827
- const testLines = readFileSync(testResultsFile, "utf-8").split(`
68012
+ const testResultsFile = join2(session.scratchpadPath, "test-results.jsonl");
68013
+ if (existsSync5(testResultsFile)) {
68014
+ const testLines = readFileSync2(testResultsFile, "utf-8").split(`
67828
68015
  `).filter((l) => l.trim());
67829
68016
  const testResults = testLines.map((line) => {
67830
68017
  try {
@@ -67998,25 +68185,25 @@ This report should be treated as confidential and distributed only to authorized
67998
68185
  *Report generated by Pensar Penetration Testing Agent*
67999
68186
  *Session: ${session.id}*
68000
68187
  `;
68001
- const reportPath = join(session.rootPath, "pentest-report.md");
68002
- writeFileSync(reportPath, report);
68003
- const readmePath = join(session.rootPath, "README.md");
68004
- if (existsSync3(readmePath)) {
68005
- let readme = readFileSync(readmePath, "utf-8");
68188
+ const reportPath = join2(session.rootPath, "pentest-report.md");
68189
+ writeFileSync2(reportPath, report);
68190
+ const readmePath = join2(session.rootPath, "README.md");
68191
+ if (existsSync5(readmePath)) {
68192
+ let readme = readFileSync2(readmePath, "utf-8");
68006
68193
  readme = readme.replace("Testing in progress...", `Testing completed on ${endDate.toLocaleString()}
68007
68194
 
68008
68195
  **Final Report:** \`pentest-report.md\``);
68009
- writeFileSync(readmePath, readme);
68196
+ writeFileSync2(readmePath, readme);
68010
68197
  }
68011
- const metadataPath = join(session.rootPath, "session.json");
68012
- if (existsSync3(metadataPath)) {
68013
- const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"));
68198
+ const metadataPath = join2(session.rootPath, "session.json");
68199
+ if (existsSync5(metadataPath)) {
68200
+ const metadata = JSON.parse(readFileSync2(metadataPath, "utf-8"));
68014
68201
  metadata.endTime = endTime;
68015
68202
  metadata.duration = duration3;
68016
68203
  metadata.status = "completed";
68017
68204
  metadata.totalFindings = totalFindings;
68018
68205
  metadata.severityCounts = severityCounts;
68019
- writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
68206
+ writeFileSync2(metadataPath, JSON.stringify(metadata, null, 2));
68020
68207
  }
68021
68208
  return {
68022
68209
  success: true,
@@ -68057,7 +68244,7 @@ async function recordTestResultCore(session, params) {
68057
68244
  evidence: params.evidence || "",
68058
68245
  confidence: params.confidence || "high"
68059
68246
  };
68060
- const testResultsPath = join(session.scratchpadPath, "test-results.jsonl");
68247
+ const testResultsPath = join2(session.scratchpadPath, "test-results.jsonl");
68061
68248
  const resultLine = JSON.stringify(testResult) + `
68062
68249
  `;
68063
68250
  appendFileSync(testResultsPath, resultLine);
@@ -68468,8 +68655,8 @@ Use this when:
68468
68655
  }),
68469
68656
  execute: async ({ objective }) => {
68470
68657
  try {
68471
- const testResultsPath = join(session.scratchpadPath, "test-results.jsonl");
68472
- if (!existsSync3(testResultsPath)) {
68658
+ const testResultsPath = join2(session.scratchpadPath, "test-results.jsonl");
68659
+ if (!existsSync5(testResultsPath)) {
68473
68660
  return {
68474
68661
  success: true,
68475
68662
  totalTests: 0,
@@ -68478,7 +68665,7 @@ Use this when:
68478
68665
  suggestions: objective ? `Based on objective "${objective}", consider testing relevant parameters with test_parameter tool.` : "Use test_parameter to test parameters for vulnerabilities."
68479
68666
  };
68480
68667
  }
68481
- const fileContent = readFileSync(testResultsPath, "utf-8");
68668
+ const fileContent = readFileSync2(testResultsPath, "utf-8");
68482
68669
  const testResults = fileContent.trim().split(`
68483
68670
  `).filter((line) => line.trim()).map((line) => JSON.parse(line));
68484
68671
  const parametersTested = new Set;
@@ -68735,7 +68922,7 @@ ${discovered.map((d) => `- ${d.endpoint} [${d.method}] → HTTP ${d.status}`).jo
68735
68922
  Not found: ${range.max - range.min + 1 - discovered.length} endpoints returned 404`;
68736
68923
  try {
68737
68924
  const timestamp = new Date().toISOString();
68738
- const scratchpadFile = join(session.scratchpadPath, "notes.md");
68925
+ const scratchpadFile = join2(session.scratchpadPath, "notes.md");
68739
68926
  const entry = `## RESULT - ${timestamp}
68740
68927
 
68741
68928
  ${note}
@@ -68754,7 +68941,7 @@ ${note}
68754
68941
  ---
68755
68942
 
68756
68943
  `;
68757
- writeFileSync(scratchpadFile, header + entry);
68944
+ writeFileSync2(scratchpadFile, header + entry);
68758
68945
  }
68759
68946
  } catch (err) {
68760
68947
  console.error("Failed to record to scratchpad:", err);
@@ -68771,7 +68958,53 @@ ${note}
68771
68958
  }
68772
68959
  });
68773
68960
  }
68961
+ function wrapCommandWithHeaders(command, headers) {
68962
+ const userAgent = headers["User-Agent"];
68963
+ if (!userAgent)
68964
+ return command;
68965
+ let wrapped = command;
68966
+ if (command.includes("curl") && !command.includes("User-Agent") && !command.includes("-A")) {
68967
+ wrapped = wrapped.replace(/\bcurl(\s+)/g, `curl -A "${userAgent}" $1`);
68968
+ }
68969
+ if (command.includes("nikto") && !command.includes("-useragent")) {
68970
+ wrapped = wrapped.replace(/\bnikto\s+/, `nikto -useragent "${userAgent}" `);
68971
+ }
68972
+ if (command.includes("nmap") && command.includes("--script") && /--script[= ](.*?http.*?)/.test(command) && !command.includes("http.useragent")) {
68973
+ if (command.includes("--script-args")) {
68974
+ wrapped = wrapped.replace(/--script-args\s+([^\s]+)/, `--script-args $1,http.useragent="${userAgent}"`);
68975
+ } else {
68976
+ wrapped = `${wrapped} --script-args http.useragent="${userAgent}"`;
68977
+ }
68978
+ }
68979
+ if (command.includes("gobuster") && !command.includes("-a ") && !command.includes("--useragent")) {
68980
+ wrapped = wrapped.replace(/\bgobuster\s+/, `gobuster -a "${userAgent}" `);
68981
+ }
68982
+ if (command.includes("ffuf") && !command.includes("User-Agent:")) {
68983
+ wrapped = wrapped.replace(/\bffuf\s+/, `ffuf -H "User-Agent: ${userAgent}" `);
68984
+ }
68985
+ if (command.includes("sqlmap") && !command.includes("--user-agent")) {
68986
+ wrapped = wrapped.replace(/\bsqlmap\s+/, `sqlmap --user-agent="${userAgent}" `);
68987
+ }
68988
+ if (command.includes("wfuzz") && !command.includes("-H") && !command.includes("User-Agent")) {
68989
+ wrapped = wrapped.replace(/\bwfuzz\s+/, `wfuzz -H "User-Agent: ${userAgent}" `);
68990
+ }
68991
+ if (command.includes("dirb") && !command.includes("-a")) {
68992
+ wrapped = wrapped.replace(/\bdirb\s+/, `dirb -a "${userAgent}" `);
68993
+ }
68994
+ if (command.includes("wpscan") && !command.includes("--user-agent")) {
68995
+ wrapped = wrapped.replace(/\bwpscan\s+/, `wpscan --user-agent "${userAgent}" `);
68996
+ }
68997
+ if (command.includes("nuclei") && !command.includes("-H")) {
68998
+ wrapped = wrapped.replace(/\bnuclei\s+/, `nuclei -H "User-Agent: ${userAgent}" `);
68999
+ }
69000
+ if (command.includes("httpx") && !command.includes("-H")) {
69001
+ wrapped = wrapped.replace(/\bhttpx\s+/, `httpx -H "User-Agent: ${userAgent}" `);
69002
+ }
69003
+ return wrapped;
69004
+ }
68774
69005
  function createPentestTools(session, model, toolOverride) {
69006
+ const offensiveHeaders = getOffensiveHeaders(session);
69007
+ const rateLimiter = session._rateLimiter;
68775
69008
  const executeCommand = tool({
68776
69009
  name: "execute_command",
68777
69010
  description: `Execute a shell command for penetration testing activities.
@@ -68816,6 +69049,9 @@ IMPORTANT: Always analyze results and adjust your approach based on findings.`,
68816
69049
  inputSchema: ExecuteCommandInput,
68817
69050
  execute: async ({ command, timeout = 30000, toolCallDescription }) => {
68818
69051
  try {
69052
+ if (rateLimiter) {
69053
+ await rateLimiter.acquireSlot();
69054
+ }
68819
69055
  if (toolOverride?.execute_command) {
68820
69056
  return toolOverride.execute_command({
68821
69057
  command,
@@ -68823,7 +69059,8 @@ IMPORTANT: Always analyze results and adjust your approach based on findings.`,
68823
69059
  toolCallDescription
68824
69060
  });
68825
69061
  }
68826
- const { stdout, stderr } = await execAsync(command, {
69062
+ const finalCommand = offensiveHeaders ? wrapCommandWithHeaders(command, offensiveHeaders) : command;
69063
+ const { stdout, stderr } = await execAsync(finalCommand, {
68827
69064
  timeout,
68828
69065
  maxBuffer: 10 * 1024 * 1024
68829
69066
  });
@@ -68833,7 +69070,7 @@ IMPORTANT: Always analyze results and adjust your approach based on findings.`,
68833
69070
 
68834
69071
  (truncated) call the command again with grep / tail to paginate the response` || "(no output)",
68835
69072
  stderr: stderr || "",
68836
- command,
69073
+ command: finalCommand,
68837
69074
  error: ""
68838
69075
  };
68839
69076
  } catch (error46) {
@@ -68871,6 +69108,9 @@ COMMON TESTING PATTERNS:
68871
69108
  inputSchema: HttpRequestInput,
68872
69109
  execute: async ({ url: url2, method, headers, body, followRedirects, timeout, toolCallDescription }) => {
68873
69110
  try {
69111
+ if (rateLimiter) {
69112
+ await rateLimiter.acquireSlot();
69113
+ }
68874
69114
  if (toolOverride?.http_request) {
68875
69115
  return toolOverride.http_request({
68876
69116
  url: url2,
@@ -68886,7 +69126,10 @@ COMMON TESTING PATTERNS:
68886
69126
  const timeoutId = setTimeout(() => controller.abort(), timeout);
68887
69127
  const response = await fetch(url2, {
68888
69128
  method,
68889
- headers: headers || {},
69129
+ headers: {
69130
+ ...offensiveHeaders || {},
69131
+ ...headers || {}
69132
+ },
68890
69133
  body: body || undefined,
68891
69134
  redirect: followRedirects ? "follow" : "manual",
68892
69135
  signal: controller.signal
@@ -68945,102 +69188,6 @@ COMMON TESTING PATTERNS:
68945
69188
  };
68946
69189
  }
68947
69190
 
68948
- // src/core/agent/sessions/index.ts
68949
- import {
68950
- mkdirSync,
68951
- existsSync as existsSync5,
68952
- writeFileSync as writeFileSync2,
68953
- readFileSync as readFileSync2,
68954
- readdirSync as readdirSync2,
68955
- statSync,
68956
- rmSync
68957
- } from "fs";
68958
- import { join as join2 } from "path";
68959
- import { homedir } from "os";
68960
- function generateSessionId(prefix) {
68961
- const timestamp = Date.now().toString(36);
68962
- return `${prefix ? `${prefix}-` : ""}${timestamp}`;
68963
- }
68964
- function getPensarDir() {
68965
- return join2(homedir(), ".pensar");
68966
- }
68967
- function getExecutionsDir() {
68968
- return join2(getPensarDir(), "executions");
68969
- }
68970
- function createSession(target, objective, prefix) {
68971
- const sessionId = generateSessionId(prefix);
68972
- const rootPath = join2(getExecutionsDir(), sessionId);
68973
- const findingsPath = join2(rootPath, "findings");
68974
- const scratchpadPath = join2(rootPath, "scratchpad");
68975
- const logsPath = join2(rootPath, "logs");
68976
- ensureDirectoryExists(rootPath);
68977
- ensureDirectoryExists(findingsPath);
68978
- ensureDirectoryExists(scratchpadPath);
68979
- ensureDirectoryExists(logsPath);
68980
- const session = {
68981
- id: sessionId,
68982
- rootPath,
68983
- findingsPath,
68984
- scratchpadPath,
68985
- logsPath,
68986
- target,
68987
- objective: objective ?? "",
68988
- startTime: new Date().toISOString()
68989
- };
68990
- const metadataPath = join2(rootPath, "session.json");
68991
- writeFileSync2(metadataPath, JSON.stringify(session, null, 2));
68992
- const readmePath = join2(rootPath, "README.md");
68993
- const readme = `# Penetration Test Session
68994
-
68995
- **Session ID:** ${sessionId}
68996
- **Target:** ${target}
68997
- **Objective:** ${objective}
68998
- **Started:** ${session.startTime}
68999
-
69000
- ## Directory Structure
69001
-
69002
- - \`findings/\` - Security findings and vulnerabilities
69003
- - \`scratchpad/\` - Notes and temporary data during testing
69004
- - \`logs/\` - Execution logs and command outputs
69005
- - \`session.json\` - Session metadata
69006
-
69007
- ## Findings
69008
-
69009
- Security findings will be documented in the \`findings/\` directory as individual files.
69010
-
69011
- ## Status
69012
-
69013
- Testing in progress...
69014
- `;
69015
- writeFileSync2(readmePath, readme);
69016
- return session;
69017
- }
69018
- function ensureDirectoryExists(path3) {
69019
- if (!existsSync5(path3)) {
69020
- mkdirSync(path3, { recursive: true });
69021
- }
69022
- }
69023
- function getSession(sessionId) {
69024
- const sessionPath = join2(getExecutionsDir(), sessionId);
69025
- const metadataPath = join2(sessionPath, "session.json");
69026
- if (!existsSync5(metadataPath)) {
69027
- return null;
69028
- }
69029
- const metadata = JSON.parse(readFileSync2(metadataPath, "utf-8"));
69030
- return metadata;
69031
- }
69032
- function listSessions() {
69033
- const executionsDir = getExecutionsDir();
69034
- if (!existsSync5(executionsDir)) {
69035
- return [];
69036
- }
69037
- const entries = readdirSync2(executionsDir);
69038
- return entries.filter((entry) => {
69039
- const fullPath = join2(executionsDir, entry);
69040
- return statSync(fullPath).isDirectory();
69041
- });
69042
- }
69043
-
69044
69191
  // src/core/agent/utils.ts
69045
69192
  import { readFileSync as readFileSync3, existsSync as existsSync6 } from "fs";
69046
69193
  import { execSync } from "child_process";
@@ -69855,9 +70002,10 @@ function runAgent(opts) {
69855
70002
  silent,
69856
70003
  authConfig,
69857
70004
  toolOverride,
69858
- messages
70005
+ messages,
70006
+ sessionConfig
69859
70007
  } = opts;
69860
- const session = opts.session || createSession(target, objective);
70008
+ const session = opts.session || createSession(target, objective, undefined, sessionConfig);
69861
70009
  const pocsPath = join4(session.rootPath, "pocs");
69862
70010
  if (!existsSync9(pocsPath)) {
69863
70011
  mkdirSync4(pocsPath, { recursive: true });
@@ -71613,9 +71761,128 @@ import fs5 from "fs";
71613
71761
 
71614
71762
  // src/core/messages/index.ts
71615
71763
  import fs4 from "fs";
71764
+
71765
+ // src/core/messages/types.ts
71766
+ var ToolMessageObject = exports_external.object({
71767
+ role: exports_external.literal("tool"),
71768
+ status: exports_external.enum(["pending", "completed"]),
71769
+ toolCallId: exports_external.string(),
71770
+ content: exports_external.string(),
71771
+ args: exports_external.record(exports_external.string(), exports_external.any()),
71772
+ toolName: exports_external.string(),
71773
+ createdAt: exports_external.coerce.date()
71774
+ });
71775
+ var SystemModelMessageObject = exports_external.object({
71776
+ role: exports_external.literal("system"),
71777
+ content: exports_external.string(),
71778
+ createdAt: exports_external.coerce.date(),
71779
+ providerOptions: exports_external.record(exports_external.string(), exports_external.any()).optional()
71780
+ });
71781
+ var TextPartObject = exports_external.object({
71782
+ type: exports_external.literal("text"),
71783
+ text: exports_external.string(),
71784
+ providerOptions: exports_external.record(exports_external.string(), exports_external.any()).optional()
71785
+ });
71786
+ var FilePartObject = exports_external.object({
71787
+ type: exports_external.literal("file"),
71788
+ data: exports_external.union([
71789
+ exports_external.string(),
71790
+ exports_external.instanceof(Uint8Array),
71791
+ exports_external.instanceof(ArrayBuffer),
71792
+ exports_external.instanceof(Buffer),
71793
+ exports_external.url()
71794
+ ]),
71795
+ filename: exports_external.string().optional(),
71796
+ mediaType: exports_external.string(),
71797
+ providerOptions: exports_external.record(exports_external.string(), exports_external.any()).optional()
71798
+ });
71799
+ var ReasoningPartObject = exports_external.object({
71800
+ type: exports_external.literal("reasoning"),
71801
+ text: exports_external.string(),
71802
+ providerOptions: exports_external.record(exports_external.string(), exports_external.any()).optional()
71803
+ });
71804
+ var ToolCallPartObject = exports_external.object({
71805
+ type: exports_external.literal("tool-call"),
71806
+ toolCallId: exports_external.string(),
71807
+ toolName: exports_external.string(),
71808
+ input: exports_external.unknown(),
71809
+ providerOptions: exports_external.record(exports_external.string(), exports_external.any()).optional(),
71810
+ providerExecuted: exports_external.boolean().optional()
71811
+ });
71812
+ var ToolResultOutputObject = exports_external.discriminatedUnion("type", [
71813
+ exports_external.object({
71814
+ type: exports_external.literal("text"),
71815
+ value: exports_external.string()
71816
+ }),
71817
+ exports_external.object({
71818
+ type: exports_external.literal("json"),
71819
+ value: exports_external.any()
71820
+ }),
71821
+ exports_external.object({
71822
+ type: exports_external.literal("error-text"),
71823
+ value: exports_external.string()
71824
+ }),
71825
+ exports_external.object({
71826
+ type: exports_external.literal("error-json"),
71827
+ value: exports_external.any()
71828
+ }),
71829
+ exports_external.object({
71830
+ type: exports_external.literal("content"),
71831
+ value: exports_external.array(exports_external.discriminatedUnion("type", [
71832
+ exports_external.object({
71833
+ type: exports_external.literal("text"),
71834
+ text: exports_external.string()
71835
+ }),
71836
+ exports_external.object({
71837
+ type: exports_external.literal("media"),
71838
+ data: exports_external.string(),
71839
+ mediaType: exports_external.string()
71840
+ })
71841
+ ]))
71842
+ })
71843
+ ]);
71844
+ var ToolResultPartObject = exports_external.object({
71845
+ type: exports_external.literal("tool-result"),
71846
+ toolCallId: exports_external.string(),
71847
+ toolName: exports_external.string(),
71848
+ output: ToolResultOutputObject,
71849
+ providerOptions: exports_external.record(exports_external.string(), exports_external.any()).optional()
71850
+ });
71851
+ var AssistantModelMessageObject = exports_external.object({
71852
+ role: exports_external.literal("assistant"),
71853
+ content: exports_external.union([
71854
+ exports_external.string(),
71855
+ exports_external.array(exports_external.discriminatedUnion("type", [
71856
+ TextPartObject,
71857
+ FilePartObject,
71858
+ ReasoningPartObject,
71859
+ ToolCallPartObject,
71860
+ ToolResultPartObject
71861
+ ]))
71862
+ ]),
71863
+ createdAt: exports_external.coerce.date(),
71864
+ providerOptions: exports_external.record(exports_external.string(), exports_external.any()).optional()
71865
+ });
71866
+ var UserModelMessageObject = exports_external.object({
71867
+ role: exports_external.literal("user"),
71868
+ content: exports_external.union([
71869
+ exports_external.string(),
71870
+ exports_external.array(exports_external.discriminatedUnion("type", [TextPartObject, FilePartObject]))
71871
+ ]),
71872
+ createdAt: exports_external.coerce.date(),
71873
+ providerOptions: exports_external.record(exports_external.string(), exports_external.any()).optional()
71874
+ });
71875
+ var ModelMessageObject = exports_external.discriminatedUnion("role", [
71876
+ SystemModelMessageObject,
71877
+ UserModelMessageObject,
71878
+ AssistantModelMessageObject,
71879
+ ToolMessageObject
71880
+ ]);
71881
+
71882
+ // src/core/messages/index.ts
71616
71883
  function getMessages(session) {
71617
71884
  const messages = fs4.readFileSync(session.rootPath + "/messages.json", "utf8");
71618
- return JSON.parse(messages);
71885
+ return ModelMessageObject.array().parse(JSON.parse(messages));
71619
71886
  }
71620
71887
  function saveMessages(session, messages) {
71621
71888
  fs4.writeFileSync(session.rootPath + "/messages.json", JSON.stringify(messages, null, 2));
@@ -71643,6 +71910,15 @@ function PentestAgentDisplay() {
71643
71910
  const [isCompleted, setIsCompleted] = import_react20.useState(false);
71644
71911
  const [sessionPath, setSessionPath] = import_react20.useState("");
71645
71912
  const [abortController, setAbortController] = import_react20.useState(null);
71913
+ const [rps, setRps] = import_react20.useState("");
71914
+ const [headerMode, setHeaderMode] = import_react20.useState("default");
71915
+ const [customHeaders, setCustomHeaders] = import_react20.useState({});
71916
+ const [headerInputName, setHeaderInputName] = import_react20.useState("");
71917
+ const [headerInputValue, setHeaderInputValue] = import_react20.useState("");
71918
+ const [selectedHeaderIndex, setSelectedHeaderIndex] = import_react20.useState(0);
71919
+ const [headerEditMode, setHeaderEditMode] = import_react20.useState("list");
71920
+ const [inputError, setInputError] = import_react20.useState("");
71921
+ const [editingHeaderKey, setEditingHeaderKey] = import_react20.useState(null);
71646
71922
  const { closePentest } = useCommand();
71647
71923
  const {
71648
71924
  model,
@@ -71652,7 +71928,8 @@ function PentestAgentDisplay() {
71652
71928
  isExecuting,
71653
71929
  setIsExecuting
71654
71930
  } = useAgent();
71655
- const inputs = ["target", "objective"];
71931
+ const inputs = headerMode === "custom" && headerEditMode === "input" ? ["target", "objective", "rps", "headerMode", "headerInputName", "headerInputValue"] : ["target", "objective", "rps", "headerMode"];
71932
+ const headerEntries = Object.entries(customHeaders);
71656
71933
  useKeyboard((key) => {
71657
71934
  if (key.name === "escape") {
71658
71935
  closePentest();
@@ -71671,6 +71948,105 @@ function PentestAgentDisplay() {
71671
71948
  if (hasStarted) {
71672
71949
  return;
71673
71950
  }
71951
+ if (focusedIndex === 3 && key.name === "up") {
71952
+ if (headerMode === "custom" && headerEditMode === "list") {
71953
+ if (headerEntries.length > 0) {
71954
+ setSelectedHeaderIndex((prev) => (prev - 1 + headerEntries.length) % headerEntries.length);
71955
+ }
71956
+ } else {
71957
+ const modes = ["none", "default", "custom"];
71958
+ const currentIndex = modes.indexOf(headerMode);
71959
+ const newMode = modes[(currentIndex - 1 + 3) % 3];
71960
+ if (newMode)
71961
+ setHeaderMode(newMode);
71962
+ }
71963
+ return;
71964
+ }
71965
+ if (focusedIndex === 3 && key.name === "down") {
71966
+ if (headerMode === "custom" && headerEditMode === "list") {
71967
+ if (headerEntries.length > 0) {
71968
+ setSelectedHeaderIndex((prev) => (prev + 1) % headerEntries.length);
71969
+ }
71970
+ } else {
71971
+ const modes = ["none", "default", "custom"];
71972
+ const currentIndex = modes.indexOf(headerMode);
71973
+ const newMode = modes[(currentIndex + 1) % 3];
71974
+ if (newMode)
71975
+ setHeaderMode(newMode);
71976
+ }
71977
+ return;
71978
+ }
71979
+ if (headerMode === "custom" && focusedIndex === 3) {
71980
+ if (key.name === "a" && headerEditMode === "list") {
71981
+ setHeaderEditMode("input");
71982
+ setHeaderInputName("");
71983
+ setHeaderInputValue("");
71984
+ setInputError("");
71985
+ setEditingHeaderKey(null);
71986
+ setFocusedIndex(3);
71987
+ return;
71988
+ }
71989
+ if (key.name === "e" && headerEditMode === "list" && headerEntries.length > 0) {
71990
+ const entry = headerEntries[selectedHeaderIndex];
71991
+ if (entry) {
71992
+ const [key2, value] = entry;
71993
+ setHeaderInputName(key2);
71994
+ setHeaderInputValue(value);
71995
+ setEditingHeaderKey(key2);
71996
+ setHeaderEditMode("input");
71997
+ setInputError("");
71998
+ setFocusedIndex(3);
71999
+ }
72000
+ return;
72001
+ }
72002
+ if (key.name === "d" && headerEditMode === "list" && headerEntries.length > 0) {
72003
+ const entry = headerEntries[selectedHeaderIndex];
72004
+ if (entry) {
72005
+ const [keyToDelete] = entry;
72006
+ const newHeaders = { ...customHeaders };
72007
+ delete newHeaders[keyToDelete];
72008
+ setCustomHeaders(newHeaders);
72009
+ setSelectedHeaderIndex(Math.max(0, selectedHeaderIndex - 1));
72010
+ }
72011
+ return;
72012
+ }
72013
+ if (key.name === "escape" && headerEditMode === "input") {
72014
+ setHeaderEditMode("list");
72015
+ setHeaderInputName("");
72016
+ setHeaderInputValue("");
72017
+ setInputError("");
72018
+ setEditingHeaderKey(null);
72019
+ setFocusedIndex(2);
72020
+ return;
72021
+ }
72022
+ }
72023
+ if (headerMode === "custom" && focusedIndex === 4 && key.name === "return") {
72024
+ const headerName = headerInputName.trim();
72025
+ const headerValue = headerInputValue.trim();
72026
+ if (!headerName) {
72027
+ setInputError("Header name cannot be empty");
72028
+ return;
72029
+ }
72030
+ if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(headerName)) {
72031
+ setInputError("Invalid header name - Use only letters, numbers, and hyphens");
72032
+ return;
72033
+ }
72034
+ if (editingHeaderKey && editingHeaderKey !== headerName) {
72035
+ const newHeaders = { ...customHeaders };
72036
+ delete newHeaders[editingHeaderKey];
72037
+ newHeaders[headerName] = headerValue;
72038
+ setCustomHeaders(newHeaders);
72039
+ } else {
72040
+ setCustomHeaders({ ...customHeaders, [headerName]: headerValue });
72041
+ }
72042
+ setHeaderEditMode("list");
72043
+ setHeaderInputName("");
72044
+ setHeaderInputValue("");
72045
+ setInputError("");
72046
+ setEditingHeaderKey(null);
72047
+ setFocusedIndex(2);
72048
+ return;
72049
+ }
71674
72050
  if (key.name === "tab" && !key.shift) {
71675
72051
  setFocusedIndex((prev) => (prev + 1) % inputs.length);
71676
72052
  return;
@@ -71679,9 +72055,21 @@ function PentestAgentDisplay() {
71679
72055
  setFocusedIndex((prev) => (prev - 1 + inputs.length) % inputs.length);
71680
72056
  return;
71681
72057
  }
71682
- if (key.name === "return") {
72058
+ if (key.name === "return" && focusedIndex < 4) {
71683
72059
  if (target && objective) {
71684
- beginExecution();
72060
+ const parsedRps = rps ? parseInt(rps, 10) : undefined;
72061
+ const sessionConfig = {
72062
+ offensiveHeaders: {
72063
+ mode: headerMode,
72064
+ headers: headerMode === "custom" ? customHeaders : undefined
72065
+ },
72066
+ ...parsedRps && !isNaN(parsedRps) && parsedRps > 0 && {
72067
+ rateLimiter: {
72068
+ requestsPerSecond: parsedRps
72069
+ }
72070
+ }
72071
+ };
72072
+ beginExecution(sessionConfig);
71685
72073
  }
71686
72074
  }
71687
72075
  });
@@ -71695,7 +72083,7 @@ function PentestAgentDisplay() {
71695
72083
  });
71696
72084
  }
71697
72085
  }
71698
- async function beginExecution() {
72086
+ async function beginExecution(sessionConfig) {
71699
72087
  if (target && objective && !hasStarted) {
71700
72088
  setHasStarted(true);
71701
72089
  setThinking(true);
@@ -71708,6 +72096,7 @@ function PentestAgentDisplay() {
71708
72096
  objective,
71709
72097
  model: model.id,
71710
72098
  abortSignal: controller.signal,
72099
+ sessionConfig,
71711
72100
  onStepFinish: ({ usage }) => {
71712
72101
  const stepTokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
71713
72102
  setTokenCount(stepTokens);
@@ -71901,6 +72290,244 @@ Path: ${result.session.rootPath}`,
71901
72290
  onInput: setObjective,
71902
72291
  focused: focusedIndex === 1
71903
72292
  }, undefined, false, undefined, this),
72293
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Input, {
72294
+ label: "Rate Limit (RPS)",
72295
+ description: "Max requests/sec (leave empty for unlimited)",
72296
+ placeholder: "15 (recommended)",
72297
+ value: rps,
72298
+ onInput: setRps,
72299
+ focused: focusedIndex === 2
72300
+ }, undefined, false, undefined, this),
72301
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72302
+ border: true,
72303
+ width: "100%",
72304
+ borderStyle: "heavy",
72305
+ backgroundColor: "black",
72306
+ borderColor: focusedIndex === 3 ? "green" : "gray",
72307
+ flexDirection: "column",
72308
+ padding: 1,
72309
+ children: [
72310
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72311
+ fg: "green",
72312
+ children: "Offensive Request Headers"
72313
+ }, undefined, false, undefined, this),
72314
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72315
+ fg: "gray",
72316
+ children: "Configure headers for HTTP tools (curl, nmap, nikto, etc.)"
72317
+ }, undefined, false, undefined, this),
72318
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72319
+ fg: "gray",
72320
+ children: "─────────────────────────────────────────────────"
72321
+ }, undefined, false, undefined, this),
72322
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72323
+ flexDirection: "column",
72324
+ children: [
72325
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72326
+ fg: headerMode === "none" ? "green" : "gray",
72327
+ children: [
72328
+ headerMode === "none" ? "●" : "○",
72329
+ " None ",
72330
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72331
+ fg: "gray",
72332
+ children: "(no custom headers)"
72333
+ }, undefined, false, undefined, this)
72334
+ ]
72335
+ }, undefined, true, undefined, this),
72336
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72337
+ fg: headerMode === "default" ? "green" : "gray",
72338
+ children: [
72339
+ headerMode === "default" ? "●" : "○",
72340
+ " Default ",
72341
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72342
+ fg: "gray",
72343
+ children: "(User-Agent: pensar-apex)"
72344
+ }, undefined, false, undefined, this)
72345
+ ]
72346
+ }, undefined, true, undefined, this),
72347
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72348
+ fg: headerMode === "custom" ? "green" : "gray",
72349
+ children: [
72350
+ headerMode === "custom" ? "●" : "○",
72351
+ " Custom ",
72352
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72353
+ fg: "gray",
72354
+ children: "(define headers below)"
72355
+ }, undefined, false, undefined, this)
72356
+ ]
72357
+ }, undefined, true, undefined, this)
72358
+ ]
72359
+ }, undefined, true, undefined, this),
72360
+ headerMode === "custom" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72361
+ flexDirection: "column",
72362
+ children: [
72363
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72364
+ fg: "gray",
72365
+ children: "─────────────────────────────────────────────────"
72366
+ }, undefined, false, undefined, this),
72367
+ headerEditMode === "list" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72368
+ flexDirection: "column",
72369
+ children: headerEntries.length > 0 ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
72370
+ children: [
72371
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72372
+ fg: "green",
72373
+ children: "Configured Headers:"
72374
+ }, undefined, false, undefined, this),
72375
+ headerEntries.map(([key, value], index) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72376
+ fg: index === selectedHeaderIndex ? "green" : "gray",
72377
+ children: [
72378
+ index === selectedHeaderIndex ? "▸" : " ",
72379
+ " • ",
72380
+ key,
72381
+ ": ",
72382
+ value
72383
+ ]
72384
+ }, key, true, undefined, this))
72385
+ ]
72386
+ }, undefined, true, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72387
+ fg: "gray",
72388
+ children: "No headers configured. Press [A] to add."
72389
+ }, undefined, false, undefined, this)
72390
+ }, undefined, false, undefined, this),
72391
+ headerEditMode === "input" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72392
+ flexDirection: "column",
72393
+ children: [
72394
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72395
+ fg: "green",
72396
+ children: editingHeaderKey ? "Edit Header" : "Add Header"
72397
+ }, undefined, false, undefined, this),
72398
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72399
+ flexDirection: "row",
72400
+ children: [
72401
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72402
+ fg: "gray",
72403
+ children: "Name: "
72404
+ }, undefined, false, undefined, this),
72405
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
72406
+ value: headerInputName,
72407
+ onInput: setHeaderInputName,
72408
+ placeholder: "User-Agent",
72409
+ focused: focusedIndex === 3,
72410
+ width: 40,
72411
+ backgroundColor: "black"
72412
+ }, undefined, false, undefined, this)
72413
+ ]
72414
+ }, undefined, true, undefined, this),
72415
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72416
+ flexDirection: "row",
72417
+ children: [
72418
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72419
+ fg: "gray",
72420
+ children: "Value: "
72421
+ }, undefined, false, undefined, this),
72422
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
72423
+ value: headerInputValue,
72424
+ onInput: setHeaderInputValue,
72425
+ placeholder: "pensar-apex_client123",
72426
+ focused: focusedIndex === 4,
72427
+ width: 40,
72428
+ backgroundColor: "black"
72429
+ }, undefined, false, undefined, this)
72430
+ ]
72431
+ }, undefined, true, undefined, this),
72432
+ inputError && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72433
+ fg: "red",
72434
+ children: inputError
72435
+ }, undefined, false, undefined, this)
72436
+ ]
72437
+ }, undefined, true, undefined, this),
72438
+ headerEntries.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72439
+ flexDirection: "column",
72440
+ children: [
72441
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72442
+ fg: "gray",
72443
+ children: "─────────────────────────────────────────────────"
72444
+ }, undefined, false, undefined, this),
72445
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72446
+ fg: "green",
72447
+ children: "Preview:"
72448
+ }, undefined, false, undefined, this),
72449
+ headerEntries.map(([key, value]) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72450
+ fg: "gray",
72451
+ children: [
72452
+ key,
72453
+ ": ",
72454
+ value
72455
+ ]
72456
+ }, key, true, undefined, this))
72457
+ ]
72458
+ }, undefined, true, undefined, this),
72459
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72460
+ fg: "gray",
72461
+ children: "─────────────────────────────────────────────────"
72462
+ }, undefined, false, undefined, this),
72463
+ headerEditMode === "list" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72464
+ fg: "gray",
72465
+ children: [
72466
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72467
+ fg: "green",
72468
+ children: "[A]"
72469
+ }, undefined, false, undefined, this),
72470
+ " Add ",
72471
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72472
+ fg: "green",
72473
+ children: "[E]"
72474
+ }, undefined, false, undefined, this),
72475
+ " Edit ",
72476
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72477
+ fg: "green",
72478
+ children: "[D]"
72479
+ }, undefined, false, undefined, this),
72480
+ " Delete ",
72481
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72482
+ fg: "green",
72483
+ children: "[↑↓]"
72484
+ }, undefined, false, undefined, this),
72485
+ " Navigate"
72486
+ ]
72487
+ }, undefined, true, undefined, this),
72488
+ headerEditMode === "input" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72489
+ fg: "gray",
72490
+ children: [
72491
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72492
+ fg: "green",
72493
+ children: "[TAB]"
72494
+ }, undefined, false, undefined, this),
72495
+ " Next Field ",
72496
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72497
+ fg: "green",
72498
+ children: "[ENTER]"
72499
+ }, undefined, false, undefined, this),
72500
+ " Save ",
72501
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72502
+ fg: "green",
72503
+ children: "[ESC]"
72504
+ }, undefined, false, undefined, this),
72505
+ " Cancel"
72506
+ ]
72507
+ }, undefined, true, undefined, this)
72508
+ ]
72509
+ }, undefined, true, undefined, this),
72510
+ headerMode !== "custom" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
72511
+ flexDirection: "column",
72512
+ children: [
72513
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72514
+ fg: "gray",
72515
+ children: "─────────────────────────────────────────────────"
72516
+ }, undefined, false, undefined, this),
72517
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
72518
+ fg: "gray",
72519
+ children: [
72520
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
72521
+ fg: "green",
72522
+ children: "[↑↓]"
72523
+ }, undefined, false, undefined, this),
72524
+ " Select mode"
72525
+ ]
72526
+ }, undefined, true, undefined, this)
72527
+ ]
72528
+ }, undefined, true, undefined, this)
72529
+ ]
72530
+ }, undefined, true, undefined, this),
71904
72531
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
71905
72532
  flexDirection: "row",
71906
72533
  width: "100%",
@@ -74073,12 +74700,13 @@ function runAgent3(opts) {
74073
74700
  model,
74074
74701
  onStepFinish,
74075
74702
  abortSignal,
74703
+ sessionConfig,
74076
74704
  onSubagentSpawn,
74077
74705
  onSubagentMessage,
74078
74706
  onSubagentComplete,
74079
74707
  session: sessionProp
74080
74708
  } = opts;
74081
- const session = sessionProp || createSession(target);
74709
+ const session = sessionProp || createSession(target, undefined, undefined, sessionConfig);
74082
74710
  const logger = new Logger(session);
74083
74711
  logger.log(`Created thorough pentest session: ${session.id}`);
74084
74712
  logger.log(`Session path: ${session.rootPath}`);
@@ -74673,7 +75301,7 @@ function useThoroughPentestAgent() {
74673
75301
  }
74674
75302
  }
74675
75303
  }
74676
- async function beginExecution() {
75304
+ async function beginExecution(sessionConfig) {
74677
75305
  if (target && !hasStarted) {
74678
75306
  setHasStarted(true);
74679
75307
  setThinking(true);
@@ -74685,6 +75313,7 @@ function useThoroughPentestAgent() {
74685
75313
  target,
74686
75314
  model: model.id,
74687
75315
  abortSignal: controller.signal,
75316
+ sessionConfig,
74688
75317
  onStepFinish: ({ usage }) => {
74689
75318
  const stepTokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
74690
75319
  setTokenCount(stepTokens);
@@ -74892,6 +75521,16 @@ Mode: Pentest (Orchestrator)`,
74892
75521
  function ThoroughPentestAgentDisplay() {
74893
75522
  const [focusedIndex, setFocusedIndex] = import_react23.useState(0);
74894
75523
  const { closeThoroughPentest } = useCommand();
75524
+ const [rps, setRps] = import_react23.useState("");
75525
+ const [headerMode, setHeaderMode] = import_react23.useState("default");
75526
+ const [customHeaders, setCustomHeaders] = import_react23.useState({});
75527
+ const [headerInputName, setHeaderInputName] = import_react23.useState("");
75528
+ const [headerInputValue, setHeaderInputValue] = import_react23.useState("");
75529
+ const [selectedHeaderIndex, setSelectedHeaderIndex] = import_react23.useState(0);
75530
+ const [headerEditMode, setHeaderEditMode] = import_react23.useState("list");
75531
+ const [inputError, setInputError] = import_react23.useState("");
75532
+ const [editingHeaderKey, setEditingHeaderKey] = import_react23.useState(null);
75533
+ const [inputFocusField, setInputFocusField] = import_react23.useState("name");
74895
75534
  const {
74896
75535
  target,
74897
75536
  setTarget,
@@ -74905,7 +75544,8 @@ function ThoroughPentestAgentDisplay() {
74905
75544
  beginExecution,
74906
75545
  openReport
74907
75546
  } = useThoroughPentestAgent();
74908
- const inputs = ["target", "objective"];
75547
+ const inputs = headerMode === "custom" && headerEditMode === "input" ? ["target", "rps", "headerMode", "headerInputName", "headerInputValue"] : ["target", "rps", "headerMode"];
75548
+ const headerEntries = Object.entries(customHeaders);
74909
75549
  useKeyboard((key) => {
74910
75550
  if (key.name === "escape") {
74911
75551
  closeThoroughPentest();
@@ -74924,6 +75564,107 @@ function ThoroughPentestAgentDisplay() {
74924
75564
  if (hasStarted) {
74925
75565
  return;
74926
75566
  }
75567
+ if (focusedIndex === 2 && key.name === "up") {
75568
+ if (headerMode === "custom" && headerEditMode === "list") {
75569
+ if (headerEntries.length > 0) {
75570
+ setSelectedHeaderIndex((prev) => (prev - 1 + headerEntries.length) % headerEntries.length);
75571
+ }
75572
+ } else {
75573
+ const modes = ["none", "default", "custom"];
75574
+ const currentIndex = modes.indexOf(headerMode);
75575
+ const newMode = modes[(currentIndex - 1 + 3) % 3];
75576
+ if (newMode)
75577
+ setHeaderMode(newMode);
75578
+ }
75579
+ return;
75580
+ }
75581
+ if (focusedIndex === 2 && key.name === "down") {
75582
+ if (headerMode === "custom" && headerEditMode === "list") {
75583
+ if (headerEntries.length > 0) {
75584
+ setSelectedHeaderIndex((prev) => (prev + 1) % headerEntries.length);
75585
+ }
75586
+ } else {
75587
+ const modes = ["none", "default", "custom"];
75588
+ const currentIndex = modes.indexOf(headerMode);
75589
+ const newMode = modes[(currentIndex + 1) % 3];
75590
+ if (newMode)
75591
+ setHeaderMode(newMode);
75592
+ }
75593
+ return;
75594
+ }
75595
+ if (headerMode === "custom" && focusedIndex === 2) {
75596
+ if (key.name === "a" && headerEditMode === "list") {
75597
+ setHeaderEditMode("input");
75598
+ setHeaderInputName("");
75599
+ setHeaderInputValue("");
75600
+ setInputError("");
75601
+ setEditingHeaderKey(null);
75602
+ setInputFocusField("name");
75603
+ setFocusedIndex(2);
75604
+ return;
75605
+ }
75606
+ if (key.name === "e" && headerEditMode === "list" && headerEntries.length > 0) {
75607
+ const entry = headerEntries[selectedHeaderIndex];
75608
+ if (entry) {
75609
+ const [key2, value] = entry;
75610
+ setHeaderInputName(key2);
75611
+ setHeaderInputValue(value);
75612
+ setEditingHeaderKey(key2);
75613
+ setHeaderEditMode("input");
75614
+ setInputError("");
75615
+ setInputFocusField("name");
75616
+ setFocusedIndex(2);
75617
+ }
75618
+ return;
75619
+ }
75620
+ if (key.name === "d" && headerEditMode === "list" && headerEntries.length > 0) {
75621
+ const entry = headerEntries[selectedHeaderIndex];
75622
+ if (entry) {
75623
+ const [keyToDelete] = entry;
75624
+ const newHeaders = { ...customHeaders };
75625
+ delete newHeaders[keyToDelete];
75626
+ setCustomHeaders(newHeaders);
75627
+ setSelectedHeaderIndex(Math.max(0, selectedHeaderIndex - 1));
75628
+ }
75629
+ return;
75630
+ }
75631
+ if (key.name === "escape" && headerEditMode === "input") {
75632
+ setHeaderEditMode("list");
75633
+ setHeaderInputName("");
75634
+ setHeaderInputValue("");
75635
+ setInputError("");
75636
+ setEditingHeaderKey(null);
75637
+ setFocusedIndex(1);
75638
+ return;
75639
+ }
75640
+ }
75641
+ if (headerMode === "custom" && focusedIndex === 3 && key.name === "return") {
75642
+ const headerName = headerInputName.trim();
75643
+ const headerValue = headerInputValue.trim();
75644
+ if (!headerName) {
75645
+ setInputError("Header name cannot be empty");
75646
+ return;
75647
+ }
75648
+ if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(headerName)) {
75649
+ setInputError("Invalid header name - Use only letters, numbers, and hyphens");
75650
+ return;
75651
+ }
75652
+ if (editingHeaderKey && editingHeaderKey !== headerName) {
75653
+ const newHeaders = { ...customHeaders };
75654
+ delete newHeaders[editingHeaderKey];
75655
+ newHeaders[headerName] = headerValue;
75656
+ setCustomHeaders(newHeaders);
75657
+ } else {
75658
+ setCustomHeaders({ ...customHeaders, [headerName]: headerValue });
75659
+ }
75660
+ setHeaderEditMode("list");
75661
+ setHeaderInputName("");
75662
+ setHeaderInputValue("");
75663
+ setInputError("");
75664
+ setEditingHeaderKey(null);
75665
+ setFocusedIndex(1);
75666
+ return;
75667
+ }
74927
75668
  if (key.name === "tab" && !key.shift) {
74928
75669
  setFocusedIndex((prev) => (prev + 1) % inputs.length);
74929
75670
  return;
@@ -74932,9 +75673,21 @@ function ThoroughPentestAgentDisplay() {
74932
75673
  setFocusedIndex((prev) => (prev - 1 + inputs.length) % inputs.length);
74933
75674
  return;
74934
75675
  }
74935
- if (key.name === "return") {
75676
+ if (key.name === "return" && focusedIndex < 3) {
74936
75677
  if (target) {
74937
- beginExecution();
75678
+ const parsedRps = rps ? parseInt(rps, 10) : undefined;
75679
+ const sessionConfig = {
75680
+ offensiveHeaders: {
75681
+ mode: headerMode,
75682
+ headers: headerMode === "custom" ? customHeaders : undefined
75683
+ },
75684
+ ...parsedRps && !isNaN(parsedRps) && parsedRps > 0 && {
75685
+ rateLimiter: {
75686
+ requestsPerSecond: parsedRps
75687
+ }
75688
+ }
75689
+ };
75690
+ beginExecution(sessionConfig);
74938
75691
  }
74939
75692
  }
74940
75693
  });
@@ -75050,6 +75803,244 @@ function ThoroughPentestAgentDisplay() {
75050
75803
  onInput: setTarget,
75051
75804
  focused: focusedIndex === 0
75052
75805
  }, undefined, false, undefined, this),
75806
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Input, {
75807
+ label: "Rate Limit (RPS)",
75808
+ description: "Max requests/sec (leave empty for unlimited)",
75809
+ placeholder: "15 (recommended)",
75810
+ value: rps,
75811
+ onInput: setRps,
75812
+ focused: focusedIndex === 1
75813
+ }, undefined, false, undefined, this),
75814
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75815
+ border: true,
75816
+ width: "100%",
75817
+ borderStyle: "heavy",
75818
+ backgroundColor: "black",
75819
+ borderColor: focusedIndex === 2 ? "green" : "gray",
75820
+ flexDirection: "column",
75821
+ padding: 1,
75822
+ children: [
75823
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75824
+ fg: "green",
75825
+ children: "Offensive Request Headers"
75826
+ }, undefined, false, undefined, this),
75827
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75828
+ fg: "gray",
75829
+ children: "Configure headers for HTTP tools (curl, nmap, nikto, etc.)"
75830
+ }, undefined, false, undefined, this),
75831
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75832
+ fg: "gray",
75833
+ children: "─────────────────────────────────────────────────"
75834
+ }, undefined, false, undefined, this),
75835
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75836
+ flexDirection: "column",
75837
+ children: [
75838
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75839
+ fg: headerMode === "none" ? "green" : "gray",
75840
+ children: [
75841
+ headerMode === "none" ? "●" : "○",
75842
+ " None ",
75843
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75844
+ fg: "gray",
75845
+ children: "(no custom headers)"
75846
+ }, undefined, false, undefined, this)
75847
+ ]
75848
+ }, undefined, true, undefined, this),
75849
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75850
+ fg: headerMode === "default" ? "green" : "gray",
75851
+ children: [
75852
+ headerMode === "default" ? "●" : "○",
75853
+ " Default ",
75854
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75855
+ fg: "gray",
75856
+ children: "(User-Agent: pensar-apex)"
75857
+ }, undefined, false, undefined, this)
75858
+ ]
75859
+ }, undefined, true, undefined, this),
75860
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75861
+ fg: headerMode === "custom" ? "green" : "gray",
75862
+ children: [
75863
+ headerMode === "custom" ? "●" : "○",
75864
+ " Custom ",
75865
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75866
+ fg: "gray",
75867
+ children: "(define headers below)"
75868
+ }, undefined, false, undefined, this)
75869
+ ]
75870
+ }, undefined, true, undefined, this)
75871
+ ]
75872
+ }, undefined, true, undefined, this),
75873
+ headerMode === "custom" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75874
+ flexDirection: "column",
75875
+ children: [
75876
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75877
+ fg: "gray",
75878
+ children: "─────────────────────────────────────────────────"
75879
+ }, undefined, false, undefined, this),
75880
+ headerEditMode === "list" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75881
+ flexDirection: "column",
75882
+ children: headerEntries.length > 0 ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
75883
+ children: [
75884
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75885
+ fg: "green",
75886
+ children: "Configured Headers:"
75887
+ }, undefined, false, undefined, this),
75888
+ headerEntries.map(([key, value], index) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75889
+ fg: index === selectedHeaderIndex ? "green" : "gray",
75890
+ children: [
75891
+ index === selectedHeaderIndex ? "▸" : " ",
75892
+ " • ",
75893
+ key,
75894
+ ": ",
75895
+ value
75896
+ ]
75897
+ }, key, true, undefined, this))
75898
+ ]
75899
+ }, undefined, true, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75900
+ fg: "gray",
75901
+ children: "No headers configured. Press [A] to add."
75902
+ }, undefined, false, undefined, this)
75903
+ }, undefined, false, undefined, this),
75904
+ headerEditMode === "input" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75905
+ flexDirection: "column",
75906
+ children: [
75907
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75908
+ fg: "green",
75909
+ children: editingHeaderKey ? "Edit Header" : "Add Header"
75910
+ }, undefined, false, undefined, this),
75911
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75912
+ flexDirection: "row",
75913
+ children: [
75914
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75915
+ fg: "gray",
75916
+ children: "Name: "
75917
+ }, undefined, false, undefined, this),
75918
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
75919
+ value: headerInputName,
75920
+ onInput: setHeaderInputName,
75921
+ placeholder: "User-Agent",
75922
+ focused: focusedIndex === 2,
75923
+ width: 40,
75924
+ backgroundColor: "black"
75925
+ }, undefined, false, undefined, this)
75926
+ ]
75927
+ }, undefined, true, undefined, this),
75928
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75929
+ flexDirection: "row",
75930
+ children: [
75931
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75932
+ fg: "gray",
75933
+ children: "Value: "
75934
+ }, undefined, false, undefined, this),
75935
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
75936
+ value: headerInputValue,
75937
+ onInput: setHeaderInputValue,
75938
+ placeholder: "pensar-apex_client123",
75939
+ focused: focusedIndex === 3,
75940
+ width: 40,
75941
+ backgroundColor: "black"
75942
+ }, undefined, false, undefined, this)
75943
+ ]
75944
+ }, undefined, true, undefined, this),
75945
+ inputError && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75946
+ fg: "red",
75947
+ children: inputError
75948
+ }, undefined, false, undefined, this)
75949
+ ]
75950
+ }, undefined, true, undefined, this),
75951
+ headerEntries.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75952
+ flexDirection: "column",
75953
+ children: [
75954
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75955
+ fg: "gray",
75956
+ children: "─────────────────────────────────────────────────"
75957
+ }, undefined, false, undefined, this),
75958
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75959
+ fg: "green",
75960
+ children: "Preview:"
75961
+ }, undefined, false, undefined, this),
75962
+ headerEntries.map(([key, value]) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75963
+ fg: "gray",
75964
+ children: [
75965
+ key,
75966
+ ": ",
75967
+ value
75968
+ ]
75969
+ }, key, true, undefined, this))
75970
+ ]
75971
+ }, undefined, true, undefined, this),
75972
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75973
+ fg: "gray",
75974
+ children: "─────────────────────────────────────────────────"
75975
+ }, undefined, false, undefined, this),
75976
+ headerEditMode === "list" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
75977
+ fg: "gray",
75978
+ children: [
75979
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75980
+ fg: "green",
75981
+ children: "[A]"
75982
+ }, undefined, false, undefined, this),
75983
+ " Add ",
75984
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75985
+ fg: "green",
75986
+ children: "[E]"
75987
+ }, undefined, false, undefined, this),
75988
+ " Edit ",
75989
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75990
+ fg: "green",
75991
+ children: "[D]"
75992
+ }, undefined, false, undefined, this),
75993
+ " Delete ",
75994
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
75995
+ fg: "green",
75996
+ children: "[↑↓]"
75997
+ }, undefined, false, undefined, this),
75998
+ " Navigate"
75999
+ ]
76000
+ }, undefined, true, undefined, this),
76001
+ headerEditMode === "input" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76002
+ fg: "gray",
76003
+ children: [
76004
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76005
+ fg: "green",
76006
+ children: "[TAB]"
76007
+ }, undefined, false, undefined, this),
76008
+ " Next Field ",
76009
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76010
+ fg: "green",
76011
+ children: "[ENTER]"
76012
+ }, undefined, false, undefined, this),
76013
+ " Save ",
76014
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76015
+ fg: "green",
76016
+ children: "[ESC]"
76017
+ }, undefined, false, undefined, this),
76018
+ " Cancel"
76019
+ ]
76020
+ }, undefined, true, undefined, this)
76021
+ ]
76022
+ }, undefined, true, undefined, this),
76023
+ headerMode !== "custom" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
76024
+ flexDirection: "column",
76025
+ children: [
76026
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76027
+ fg: "gray",
76028
+ children: "─────────────────────────────────────────────────"
76029
+ }, undefined, false, undefined, this),
76030
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76031
+ fg: "gray",
76032
+ children: [
76033
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76034
+ fg: "green",
76035
+ children: "[↑↓]"
76036
+ }, undefined, false, undefined, this),
76037
+ " Select mode"
76038
+ ]
76039
+ }, undefined, true, undefined, this)
76040
+ ]
76041
+ }, undefined, true, undefined, this)
76042
+ ]
76043
+ }, undefined, true, undefined, this),
75053
76044
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75054
76045
  flexDirection: "row",
75055
76046
  width: "100%",