pentesting 0.7.40 → 0.7.43

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/index.js +319 -91
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -2440,7 +2440,7 @@ var HookExecutor = class extends EventEmitter {
2440
2440
  }
2441
2441
  // Execute single hook
2442
2442
  async executeHook(hook, context) {
2443
- return new Promise((resolve) => {
2443
+ return new Promise((resolve2) => {
2444
2444
  const timeout = hook.timeout || 1e4;
2445
2445
  let command = hook.command;
2446
2446
  if (command.startsWith("./")) {
@@ -2479,7 +2479,7 @@ var HookExecutor = class extends EventEmitter {
2479
2479
  decision = "modify";
2480
2480
  reason = output.split("MODIFY:")[1]?.split("\n")[0]?.trim() || "";
2481
2481
  }
2482
- resolve({
2482
+ resolve2({
2483
2483
  success: code === 0,
2484
2484
  output,
2485
2485
  decision,
@@ -2487,7 +2487,7 @@ var HookExecutor = class extends EventEmitter {
2487
2487
  });
2488
2488
  });
2489
2489
  proc.on("error", (error) => {
2490
- resolve({
2490
+ resolve2({
2491
2491
  success: false,
2492
2492
  output: error.message,
2493
2493
  decision: "proceed"
@@ -2545,7 +2545,7 @@ var MCPClient = class extends EventEmitter2 {
2545
2545
  }
2546
2546
  // Start MCP server
2547
2547
  async connect() {
2548
- return new Promise((resolve, reject) => {
2548
+ return new Promise((resolve2, reject) => {
2549
2549
  const { command, args, env } = this.config;
2550
2550
  this.process = spawn3(command, args || [], {
2551
2551
  env: { ...process.env, ...env },
@@ -2576,7 +2576,7 @@ var MCPClient = class extends EventEmitter2 {
2576
2576
  this.process.on("spawn", () => {
2577
2577
  this.connected = true;
2578
2578
  this.emit("connected", { server: this.serverName });
2579
- resolve();
2579
+ resolve2();
2580
2580
  });
2581
2581
  this.process.on("close", (code) => {
2582
2582
  this.connected = false;
@@ -2596,8 +2596,8 @@ var MCPClient = class extends EventEmitter2 {
2596
2596
  method,
2597
2597
  params
2598
2598
  };
2599
- return new Promise((resolve, reject) => {
2600
- this.pendingRequests.set(id, { resolve, reject });
2599
+ return new Promise((resolve2, reject) => {
2600
+ this.pendingRequests.set(id, { resolve: resolve2, reject });
2601
2601
  const line = JSON.stringify(request) + "\n";
2602
2602
  this.process.stdin.write(line, (error) => {
2603
2603
  if (error) {
@@ -2947,11 +2947,11 @@ var ApprovalManager = class extends EventEmitter3 {
2947
2947
  };
2948
2948
  this.pendingRequests.set(request.id, request);
2949
2949
  this.emit(APPROVAL_EVENT.REQUEST, request);
2950
- return new Promise((resolve) => {
2950
+ return new Promise((resolve2) => {
2951
2951
  const timeout = setTimeout(() => {
2952
2952
  this.pendingRequests.delete(request.id);
2953
2953
  this.emit(APPROVAL_EVENT.TIMEOUT, request.id);
2954
- resolve("deny");
2954
+ resolve2("deny");
2955
2955
  }, this.defaultTimeout);
2956
2956
  const responseHandler = (response) => {
2957
2957
  if (response.requestId === request.id) {
@@ -2969,7 +2969,7 @@ var ApprovalManager = class extends EventEmitter3 {
2969
2969
  respondedAt: (/* @__PURE__ */ new Date()).toISOString()
2970
2970
  });
2971
2971
  }
2972
- resolve("approve");
2972
+ resolve2("approve");
2973
2973
  } else if (response.decision === "deny_always") {
2974
2974
  this.autoDeniedTools.add(toolName);
2975
2975
  const pendingForSameTool = Array.from(this.pendingRequests.entries()).filter(([_, req]) => req.toolName === toolName);
@@ -2981,9 +2981,9 @@ var ApprovalManager = class extends EventEmitter3 {
2981
2981
  respondedAt: (/* @__PURE__ */ new Date()).toISOString()
2982
2982
  });
2983
2983
  }
2984
- resolve("deny");
2984
+ resolve2("deny");
2985
2985
  } else {
2986
- resolve(response.decision);
2986
+ resolve2(response.decision);
2987
2987
  }
2988
2988
  }
2989
2989
  };
@@ -3033,7 +3033,7 @@ function getApprovalManager(options) {
3033
3033
  import { spawn as spawn4 } from "child_process";
3034
3034
  async function searchDuckDuckGo(query, options = {}) {
3035
3035
  const { maxResults = 10, timeout = 3e4 } = options;
3036
- return new Promise((resolve) => {
3036
+ return new Promise((resolve2) => {
3037
3037
  const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
3038
3038
  const proc = spawn4("curl", [
3039
3039
  "-s",
@@ -3067,10 +3067,10 @@ async function searchDuckDuckGo(query, options = {}) {
3067
3067
  snippet: snippets[i] || ""
3068
3068
  });
3069
3069
  }
3070
- resolve(results);
3070
+ resolve2(results);
3071
3071
  });
3072
3072
  proc.on("error", () => {
3073
- resolve([]);
3073
+ resolve2([]);
3074
3074
  });
3075
3075
  });
3076
3076
  }
@@ -3130,7 +3130,7 @@ async function withRetry(fn, options = {}) {
3130
3130
  }
3131
3131
  const delay = calculateDelay(attempt, opts);
3132
3132
  opts.onRetry?.(attempt + 1, lastError, delay);
3133
- await new Promise((resolve) => setTimeout(resolve, delay));
3133
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
3134
3134
  }
3135
3135
  }
3136
3136
  throw lastError ?? new Error("Retry failed with no error");
@@ -3250,6 +3250,184 @@ var ContextManager = class {
3250
3250
  }
3251
3251
  };
3252
3252
 
3253
+ // src/agents/spec-loader.ts
3254
+ import * as fs3 from "fs";
3255
+ import * as path3 from "path";
3256
+ import * as yaml from "yaml";
3257
+ import { fileURLToPath } from "url";
3258
+ var __filename = fileURLToPath(import.meta.url);
3259
+ var __dirname = path3.dirname(__filename);
3260
+ var SPECS_DIR = path3.join(__dirname, "specs");
3261
+ function loadAgentSpec(specName) {
3262
+ const specPath = path3.join(SPECS_DIR, `${specName}.yaml`);
3263
+ if (!fs3.existsSync(specPath)) {
3264
+ throw new Error(`Agent spec not found: ${specPath}`);
3265
+ }
3266
+ const content = fs3.readFileSync(specPath, "utf-8");
3267
+ const spec = yaml.parse(content);
3268
+ return resolveAgentSpec(spec, specPath);
3269
+ }
3270
+ function resolveAgentSpec(spec, specPath) {
3271
+ const specDir = path3.dirname(specPath);
3272
+ let resolved = {
3273
+ name: spec.agent.name,
3274
+ description: spec.agent.description || "",
3275
+ systemPrompt: "",
3276
+ tools: spec.agent.tools || [],
3277
+ excludeTools: spec.agent.exclude_tools || [],
3278
+ subagents: spec.agent.subagents || {},
3279
+ switchingRules: spec.switching?.rules || []
3280
+ };
3281
+ if (spec.agent.extends) {
3282
+ const basePath = path3.resolve(specDir, spec.agent.extends);
3283
+ const baseContent = fs3.readFileSync(basePath, "utf-8");
3284
+ const baseSpec = yaml.parse(baseContent);
3285
+ const baseResolved = resolveAgentSpec(baseSpec, basePath);
3286
+ resolved = {
3287
+ ...baseResolved,
3288
+ name: spec.agent.name || baseResolved.name,
3289
+ description: spec.agent.description || baseResolved.description,
3290
+ tools: spec.agent.tools || baseResolved.tools,
3291
+ excludeTools: [...baseResolved.excludeTools, ...spec.agent.exclude_tools || []],
3292
+ subagents: { ...baseResolved.subagents, ...spec.agent.subagents },
3293
+ switchingRules: spec.switching?.rules || baseResolved.switchingRules
3294
+ };
3295
+ }
3296
+ if (spec.agent.system_prompt) {
3297
+ resolved.systemPrompt = spec.agent.system_prompt;
3298
+ } else if (spec.agent.system_prompt_path) {
3299
+ const promptPath = path3.resolve(specDir, spec.agent.system_prompt_path);
3300
+ if (fs3.existsSync(promptPath)) {
3301
+ resolved.systemPrompt = fs3.readFileSync(promptPath, "utf-8");
3302
+ }
3303
+ }
3304
+ resolved.tools = resolved.tools.filter((t) => !resolved.excludeTools.includes(t));
3305
+ return resolved;
3306
+ }
3307
+ var SpecOrchestrator = class {
3308
+ currentAgent;
3309
+ agents = /* @__PURE__ */ new Map();
3310
+ context = {};
3311
+ constructor() {
3312
+ this.currentAgent = loadAgentSpec("default");
3313
+ this.agents.set("default", this.currentAgent);
3314
+ for (const [name] of Object.entries(this.currentAgent.subagents)) {
3315
+ try {
3316
+ const spec = loadAgentSpec(name);
3317
+ this.agents.set(name, spec);
3318
+ } catch {
3319
+ }
3320
+ }
3321
+ }
3322
+ /**
3323
+ * Get current active agent
3324
+ */
3325
+ getCurrentAgent() {
3326
+ return this.currentAgent;
3327
+ }
3328
+ /**
3329
+ * Get current agent's system prompt
3330
+ */
3331
+ getSystemPrompt() {
3332
+ return this.currentAgent.systemPrompt;
3333
+ }
3334
+ /**
3335
+ * Update context for agent switching decisions
3336
+ */
3337
+ updateContext(key, value) {
3338
+ this.context[key] = value;
3339
+ this.evaluateSwitching();
3340
+ }
3341
+ /**
3342
+ * Manually switch to a specific agent
3343
+ */
3344
+ switchTo(agentName) {
3345
+ if (this.agents.has(agentName)) {
3346
+ this.currentAgent = this.agents.get(agentName);
3347
+ return true;
3348
+ }
3349
+ try {
3350
+ const spec = loadAgentSpec(agentName);
3351
+ this.agents.set(agentName, spec);
3352
+ this.currentAgent = spec;
3353
+ return true;
3354
+ } catch {
3355
+ return false;
3356
+ }
3357
+ }
3358
+ /**
3359
+ * Evaluate switching rules and auto-switch if needed
3360
+ */
3361
+ evaluateSwitching() {
3362
+ for (const rule of this.currentAgent.switchingRules) {
3363
+ if (this.evaluateCondition(rule.condition)) {
3364
+ this.switchTo(rule.agent);
3365
+ break;
3366
+ }
3367
+ }
3368
+ }
3369
+ /**
3370
+ * Evaluate a condition string against current context
3371
+ */
3372
+ evaluateCondition(condition) {
3373
+ try {
3374
+ const ctx = this.context;
3375
+ const checks = {
3376
+ target_set: !!ctx.target,
3377
+ vulnerability_found: (ctx.vulnerabilities || []).length > 0,
3378
+ shell_obtained: !!ctx.shellObtained,
3379
+ hash_found: (ctx.hashes || []).length > 0
3380
+ };
3381
+ const phaseMatch = condition.match(/phase\s*==\s*['"](\w+)['"]/);
3382
+ if (phaseMatch) {
3383
+ checks[`phase_${phaseMatch[1]}`] = ctx.phase === phaseMatch[1];
3384
+ }
3385
+ const portMatches = condition.matchAll(/port_(\d+)_open/g);
3386
+ const openPorts = ctx.openPorts || [];
3387
+ for (const match of portMatches) {
3388
+ checks[`port_${match[1]}_open`] = openPorts.includes(parseInt(match[1]));
3389
+ }
3390
+ const tokens = condition.split(/\s*(&&|\|\|)\s*/);
3391
+ let result = true;
3392
+ let operator = "&&";
3393
+ for (const token of tokens) {
3394
+ if (token === "&&" || token === "||") {
3395
+ operator = token;
3396
+ continue;
3397
+ }
3398
+ let tokenResult = false;
3399
+ for (const [key, value] of Object.entries(checks)) {
3400
+ if (token.includes(key.replace("_", " ")) || token.includes(key)) {
3401
+ tokenResult = value;
3402
+ break;
3403
+ }
3404
+ }
3405
+ if (operator === "&&") {
3406
+ result = result && tokenResult;
3407
+ } else {
3408
+ result = result || tokenResult;
3409
+ }
3410
+ }
3411
+ return result;
3412
+ } catch {
3413
+ return false;
3414
+ }
3415
+ }
3416
+ /**
3417
+ * Get available subagents for current agent
3418
+ */
3419
+ getSubagents() {
3420
+ return this.currentAgent.subagents;
3421
+ }
3422
+ /**
3423
+ * Get tools available for current agent
3424
+ */
3425
+ getTools() {
3426
+ return this.currentAgent.tools;
3427
+ }
3428
+ };
3429
+ var specOrchestrator = new SpecOrchestrator();
3430
+
3253
3431
  // src/agents/index.ts
3254
3432
  var TARGET_EXPLORER = {
3255
3433
  name: "target-explorer",
@@ -4263,6 +4441,9 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
4263
4441
  tools;
4264
4442
  builtinAgents = BUILTIN_AGENTS;
4265
4443
  currentAgent = null;
4444
+ // YAML-based agent orchestration
4445
+ specOrchestrator = null;
4446
+ currentSpec = null;
4266
4447
  // Integrated systems
4267
4448
  hookExecutor;
4268
4449
  mcpManager;
@@ -4299,13 +4480,22 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
4299
4480
  this.state = this.createInitialState();
4300
4481
  this.initSystems();
4301
4482
  }
4302
- // Initialize systems (hooks, MCP)
4483
+ // Initialize systems (hooks, MCP, spec orchestrator)
4303
4484
  async initSystems() {
4304
4485
  try {
4305
4486
  this.emit(AGENT_EVENT.PLUGINS_LOADED, { agents: this.builtinAgents.length });
4306
4487
  await this.hookExecutor.initialize();
4307
4488
  this.emit(AGENT_EVENT.HOOKS_LOADED);
4308
4489
  this.emit(AGENT_EVENT.COMMANDS_LOADED, { commands: BUILTIN_COMMANDS.length });
4490
+ try {
4491
+ this.specOrchestrator = new SpecOrchestrator();
4492
+ this.currentSpec = this.specOrchestrator.getCurrentAgent();
4493
+ this.emit(AGENT_EVENT.PLUGINS_LOADED, {
4494
+ agents: this.builtinAgents.length,
4495
+ yamlAgents: this.specOrchestrator.getSubagents()
4496
+ });
4497
+ } catch {
4498
+ }
4309
4499
  } catch {
4310
4500
  }
4311
4501
  }
@@ -4576,7 +4766,20 @@ ${prompt}`
4576
4766
  * Automatically switch to the best agent for the current phase
4577
4767
  */
4578
4768
  autoSwitchAgentForPhase(phaseId) {
4579
- const phaseToAgentMap = {
4769
+ const phaseToYamlAgentMap = {
4770
+ [PHASE_ID.RECON]: "recon",
4771
+ [PHASE_ID.SCAN]: "recon",
4772
+ [PHASE_ID.ENUM]: "web",
4773
+ [PHASE_ID.VULN]: "exploit",
4774
+ [PHASE_ID.EXPLOIT]: "exploit",
4775
+ [PHASE_ID.PRIVESC]: "privesc",
4776
+ [PHASE_ID.PIVOT]: "privesc",
4777
+ [PHASE_ID.PERSIST]: "privesc",
4778
+ [PHASE_ID.EXFIL]: "crypto",
4779
+ [PHASE_ID.REPORT]: "recon"
4780
+ // fallback
4781
+ };
4782
+ const phaseToBuiltinAgentMap = {
4580
4783
  [PHASE_ID.RECON]: "target-explorer",
4581
4784
  [PHASE_ID.SCAN]: "target-explorer",
4582
4785
  [PHASE_ID.ENUM]: "target-explorer",
@@ -4588,13 +4791,30 @@ ${prompt}`
4588
4791
  [PHASE_ID.EXFIL]: "forensics-analyst",
4589
4792
  [PHASE_ID.REPORT]: "finding-reviewer"
4590
4793
  };
4591
- const agentName = phaseToAgentMap[phaseId];
4794
+ if (this.specOrchestrator) {
4795
+ const yamlAgentName = phaseToYamlAgentMap[phaseId];
4796
+ if (yamlAgentName) {
4797
+ this.specOrchestrator.updateContext("phase", phaseId);
4798
+ if (this.specOrchestrator.switchTo(yamlAgentName)) {
4799
+ this.currentSpec = this.specOrchestrator.getCurrentAgent();
4800
+ this.emit(AGENT_EVENT.AGENT_SWITCH, {
4801
+ name: this.currentSpec.name,
4802
+ description: this.currentSpec.description,
4803
+ type: "yaml-spec"
4804
+ });
4805
+ this.think(THOUGHT_TYPE.OBSERVATION, `Switched to ${this.currentSpec.name} agent (YAML spec) for ${phaseId} phase`);
4806
+ return;
4807
+ }
4808
+ }
4809
+ }
4810
+ const agentName = phaseToBuiltinAgentMap[phaseId];
4592
4811
  if (agentName) {
4593
4812
  const agent = getAgentByName(agentName);
4594
4813
  if (agent) {
4595
4814
  this.currentAgent = agent;
4596
- this.emit(AGENT_EVENT.AGENT_SWITCH, { name: agent.name, description: agent.description });
4597
- this.think(THOUGHT_TYPE.OBSERVATION, `Switched to ${agent.name} agent for ${phaseId} phase`);
4815
+ this.currentSpec = null;
4816
+ this.emit(AGENT_EVENT.AGENT_SWITCH, { name: agent.name, description: agent.description, type: "builtin" });
4817
+ this.think(THOUGHT_TYPE.OBSERVATION, `Switched to ${agent.name} agent (builtin) for ${phaseId} phase`);
4598
4818
  }
4599
4819
  }
4600
4820
  }
@@ -4813,7 +5033,15 @@ Goal: Deep penetration to obtain root/system privileges, extract internal data,
4813
5033
  { role: "user", content: contextPrompt }
4814
5034
  ];
4815
5035
  let systemPrompt = AUTONOMOUS_HACKING_PROMPT;
4816
- if (this.currentAgent) {
5036
+ if (this.currentSpec && this.currentSpec.systemPrompt) {
5037
+ systemPrompt = `${AUTONOMOUS_HACKING_PROMPT}
5038
+
5039
+ ---
5040
+
5041
+ ## Current Agent: ${this.currentSpec.name}
5042
+
5043
+ ${this.currentSpec.systemPrompt}`;
5044
+ } else if (this.currentAgent) {
4817
5045
  systemPrompt = buildAgentSystemPrompt(systemPrompt, this.currentAgent);
4818
5046
  }
4819
5047
  this.emit(AGENT_EVENT.LLM_START, { model: LLM_MODEL });
@@ -4912,15 +5140,15 @@ Use report_finding tool for important discoveries.
4912
5140
  toolInput,
4913
5141
  riskLevel: risk
4914
5142
  });
4915
- const decision = await new Promise((resolve) => {
5143
+ const decision = await new Promise((resolve2) => {
4916
5144
  const handler = (response2) => {
4917
5145
  if (response2.requestId === block.id) {
4918
5146
  this.approvalManager.removeListener("approval_response", handler);
4919
5147
  if (response2.decision === "approve_always") {
4920
5148
  this.approvalManager.autoApprovedTools?.add(toolName);
4921
- resolve("approve");
5149
+ resolve2("approve");
4922
5150
  } else {
4923
- resolve(response2.decision);
5151
+ resolve2(response2.decision);
4924
5152
  }
4925
5153
  }
4926
5154
  };
@@ -5389,8 +5617,8 @@ Respond helpfully to the user's message. If they ask to perform security testing
5389
5617
  };
5390
5618
 
5391
5619
  // src/core/session/session-manager.ts
5392
- import * as fs3 from "fs/promises";
5393
- import * as path3 from "path";
5620
+ import * as fs4 from "fs/promises";
5621
+ import * as path4 from "path";
5394
5622
  import { EventEmitter as EventEmitter5 } from "events";
5395
5623
  var SESSIONS_DIR = ".pentesting/sessions";
5396
5624
  function generateSessionId() {
@@ -5403,14 +5631,14 @@ var SessionManager = class extends EventEmitter5 {
5403
5631
  currentSession = null;
5404
5632
  constructor(baseDir) {
5405
5633
  super();
5406
- this.sessionsDir = path3.join(baseDir || process.cwd(), SESSIONS_DIR);
5634
+ this.sessionsDir = path4.join(baseDir || process.cwd(), SESSIONS_DIR);
5407
5635
  }
5408
5636
  /**
5409
5637
  * Initialize sessions directory
5410
5638
  */
5411
5639
  async initialize() {
5412
5640
  try {
5413
- await fs3.mkdir(this.sessionsDir, { recursive: true });
5641
+ await fs4.mkdir(this.sessionsDir, { recursive: true });
5414
5642
  } catch {
5415
5643
  }
5416
5644
  }
@@ -5430,8 +5658,8 @@ var SessionManager = class extends EventEmitter5 {
5430
5658
  status: "active"
5431
5659
  };
5432
5660
  this.currentSession = metadata;
5433
- const sessionDir = path3.join(this.sessionsDir, metadata.id);
5434
- await fs3.mkdir(sessionDir, { recursive: true });
5661
+ const sessionDir = path4.join(this.sessionsDir, metadata.id);
5662
+ await fs4.mkdir(sessionDir, { recursive: true });
5435
5663
  await this.saveMetadata(metadata);
5436
5664
  this.emit("session_created", metadata);
5437
5665
  return metadata;
@@ -5440,8 +5668,8 @@ var SessionManager = class extends EventEmitter5 {
5440
5668
  * Save session metadata
5441
5669
  */
5442
5670
  async saveMetadata(metadata) {
5443
- const metadataPath = path3.join(this.sessionsDir, metadata.id, "metadata.json");
5444
- await fs3.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
5671
+ const metadataPath = path4.join(this.sessionsDir, metadata.id, "metadata.json");
5672
+ await fs4.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
5445
5673
  }
5446
5674
  /**
5447
5675
  * Save full session snapshot
@@ -5450,21 +5678,21 @@ var SessionManager = class extends EventEmitter5 {
5450
5678
  if (!this.currentSession) {
5451
5679
  throw new Error("No active session");
5452
5680
  }
5453
- const sessionDir = path3.join(this.sessionsDir, this.currentSession.id);
5681
+ const sessionDir = path4.join(this.sessionsDir, this.currentSession.id);
5454
5682
  this.currentSession.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5455
5683
  this.currentSession.currentPhase = snapshot.state.currentPhase;
5456
5684
  await this.saveMetadata(this.currentSession);
5457
- const statePath = path3.join(sessionDir, "state.json");
5458
- await fs3.writeFile(statePath, JSON.stringify(snapshot.state, null, 2));
5459
- const configPath = path3.join(sessionDir, "config.json");
5460
- await fs3.writeFile(configPath, JSON.stringify(snapshot.config, null, 2));
5461
- const historyPath = path3.join(sessionDir, "history.jsonl");
5685
+ const statePath = path4.join(sessionDir, "state.json");
5686
+ await fs4.writeFile(statePath, JSON.stringify(snapshot.state, null, 2));
5687
+ const configPath = path4.join(sessionDir, "config.json");
5688
+ await fs4.writeFile(configPath, JSON.stringify(snapshot.config, null, 2));
5689
+ const historyPath = path4.join(sessionDir, "history.jsonl");
5462
5690
  const historyLine = JSON.stringify({
5463
5691
  timestamp: this.currentSession.updatedAt,
5464
5692
  iteration: snapshot.state.iteration,
5465
5693
  phase: snapshot.state.currentPhase
5466
5694
  }) + "\n";
5467
- await fs3.appendFile(historyPath, historyLine);
5695
+ await fs4.appendFile(historyPath, historyLine);
5468
5696
  this.emit("snapshot_saved", this.currentSession.id);
5469
5697
  }
5470
5698
  /**
@@ -5472,17 +5700,17 @@ var SessionManager = class extends EventEmitter5 {
5472
5700
  */
5473
5701
  async loadSession(sessionId) {
5474
5702
  try {
5475
- const sessionDir = path3.join(this.sessionsDir, sessionId);
5476
- const metadataPath = path3.join(sessionDir, "metadata.json");
5477
- const metadataContent = await fs3.readFile(metadataPath, "utf-8");
5703
+ const sessionDir = path4.join(this.sessionsDir, sessionId);
5704
+ const metadataPath = path4.join(sessionDir, "metadata.json");
5705
+ const metadataContent = await fs4.readFile(metadataPath, "utf-8");
5478
5706
  const metadata = JSON.parse(metadataContent);
5479
- const statePath = path3.join(sessionDir, "state.json");
5480
- const stateContent = await fs3.readFile(statePath, "utf-8");
5707
+ const statePath = path4.join(sessionDir, "state.json");
5708
+ const stateContent = await fs4.readFile(statePath, "utf-8");
5481
5709
  const state = JSON.parse(stateContent);
5482
- const configPath = path3.join(sessionDir, "config.json");
5710
+ const configPath = path4.join(sessionDir, "config.json");
5483
5711
  let config = {};
5484
5712
  try {
5485
- const configContent = await fs3.readFile(configPath, "utf-8");
5713
+ const configContent = await fs4.readFile(configPath, "utf-8");
5486
5714
  config = JSON.parse(configContent);
5487
5715
  } catch {
5488
5716
  }
@@ -5499,13 +5727,13 @@ var SessionManager = class extends EventEmitter5 {
5499
5727
  async listSessions() {
5500
5728
  await this.initialize();
5501
5729
  try {
5502
- const entries = await fs3.readdir(this.sessionsDir, { withFileTypes: true });
5730
+ const entries = await fs4.readdir(this.sessionsDir, { withFileTypes: true });
5503
5731
  const sessions = [];
5504
5732
  for (const entry of entries) {
5505
5733
  if (entry.isDirectory()) {
5506
5734
  try {
5507
- const metadataPath = path3.join(this.sessionsDir, entry.name, "metadata.json");
5508
- const content = await fs3.readFile(metadataPath, "utf-8");
5735
+ const metadataPath = path4.join(this.sessionsDir, entry.name, "metadata.json");
5736
+ const content = await fs4.readFile(metadataPath, "utf-8");
5509
5737
  const metadata = JSON.parse(content);
5510
5738
  sessions.push({
5511
5739
  id: metadata.id,
@@ -5555,14 +5783,14 @@ var SessionManager = class extends EventEmitter5 {
5555
5783
  await this.initialize();
5556
5784
  let deletedCount = 0;
5557
5785
  try {
5558
- const entries = await fs3.readdir(this.sessionsDir, { withFileTypes: true });
5786
+ const entries = await fs4.readdir(this.sessionsDir, { withFileTypes: true });
5559
5787
  for (const entry of entries) {
5560
5788
  if (entry.isDirectory()) {
5561
- const metadataPath = path3.join(this.sessionsDir, entry.name, "metadata.json");
5789
+ const metadataPath = path4.join(this.sessionsDir, entry.name, "metadata.json");
5562
5790
  try {
5563
- await fs3.access(metadataPath);
5791
+ await fs4.access(metadataPath);
5564
5792
  } catch {
5565
- await fs3.rm(path3.join(this.sessionsDir, entry.name), { recursive: true });
5793
+ await fs4.rm(path4.join(this.sessionsDir, entry.name), { recursive: true });
5566
5794
  deletedCount++;
5567
5795
  }
5568
5796
  }
@@ -5584,8 +5812,8 @@ var SessionManager = class extends EventEmitter5 {
5584
5812
  */
5585
5813
  async deleteSession(sessionId) {
5586
5814
  try {
5587
- const sessionDir = path3.join(this.sessionsDir, sessionId);
5588
- await fs3.rm(sessionDir, { recursive: true });
5815
+ const sessionDir = path4.join(this.sessionsDir, sessionId);
5816
+ await fs4.rm(sessionDir, { recursive: true });
5589
5817
  this.emit("session_deleted", sessionId);
5590
5818
  return true;
5591
5819
  } catch {
@@ -5715,8 +5943,8 @@ function getSlashCommandRegistry() {
5715
5943
  }
5716
5944
 
5717
5945
  // src/core/context/context-manager.ts
5718
- import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "fs";
5719
- import { join as join3, dirname as dirname2 } from "path";
5946
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, renameSync } from "fs";
5947
+ import { join as join4, dirname as dirname3 } from "path";
5720
5948
  import { EventEmitter as EventEmitter6 } from "events";
5721
5949
  var CONTEXT_EVENT = {
5722
5950
  MESSAGE_ADDED: "message_added",
@@ -5731,7 +5959,7 @@ var ContextManager2 = class extends EventEmitter6 {
5731
5959
  maxMessages;
5732
5960
  constructor(sessionDir, options) {
5733
5961
  super();
5734
- this.filePath = join3(sessionDir, "context.jsonl");
5962
+ this.filePath = join4(sessionDir, "context.jsonl");
5735
5963
  this.maxMessages = options?.maxMessages || 100;
5736
5964
  this.state = {
5737
5965
  messages: [],
@@ -5739,8 +5967,8 @@ var ContextManager2 = class extends EventEmitter6 {
5739
5967
  tokenCount: 0,
5740
5968
  nextCheckpointId: 0
5741
5969
  };
5742
- const dir = dirname2(this.filePath);
5743
- if (!existsSync(dir)) {
5970
+ const dir = dirname3(this.filePath);
5971
+ if (!existsSync2(dir)) {
5744
5972
  mkdirSync(dir, { recursive: true });
5745
5973
  }
5746
5974
  }
@@ -5748,11 +5976,11 @@ var ContextManager2 = class extends EventEmitter6 {
5748
5976
  * Restore context from file
5749
5977
  */
5750
5978
  async restore() {
5751
- if (!existsSync(this.filePath)) {
5979
+ if (!existsSync2(this.filePath)) {
5752
5980
  return false;
5753
5981
  }
5754
5982
  try {
5755
- const content = readFileSync(this.filePath, "utf-8");
5983
+ const content = readFileSync2(this.filePath, "utf-8");
5756
5984
  const lines = content.trim().split("\n").filter((l) => l);
5757
5985
  for (const line of lines) {
5758
5986
  const data = JSON.parse(line);
@@ -5827,7 +6055,7 @@ var ContextManager2 = class extends EventEmitter6 {
5827
6055
  return false;
5828
6056
  }
5829
6057
  const backupPath = `${this.filePath}.bak`;
5830
- if (existsSync(this.filePath)) {
6058
+ if (existsSync2(this.filePath)) {
5831
6059
  renameSync(this.filePath, backupPath);
5832
6060
  }
5833
6061
  this.state.messages = this.state.messages.slice(0, checkpoint.messageCount);
@@ -5851,7 +6079,7 @@ var ContextManager2 = class extends EventEmitter6 {
5851
6079
  */
5852
6080
  async clear() {
5853
6081
  const backupPath = `${this.filePath}.${Date.now()}.bak`;
5854
- if (existsSync(this.filePath)) {
6082
+ if (existsSync2(this.filePath)) {
5855
6083
  renameSync(this.filePath, backupPath);
5856
6084
  }
5857
6085
  this.state = {
@@ -5899,7 +6127,7 @@ ${summary}`,
5899
6127
  metadata: { compacted: true }
5900
6128
  };
5901
6129
  const backupPath = `${this.filePath}.compact.bak`;
5902
- if (existsSync(this.filePath)) {
6130
+ if (existsSync2(this.filePath)) {
5903
6131
  renameSync(this.filePath, backupPath);
5904
6132
  }
5905
6133
  this.state.messages = [summaryMessage, ...preserved];
@@ -5929,20 +6157,20 @@ ${summary}`,
5929
6157
  };
5930
6158
 
5931
6159
  // src/wire/wire-logger.ts
5932
- import { existsSync as existsSync2, appendFileSync, readFileSync as readFileSync2, mkdirSync as mkdirSync2 } from "fs";
5933
- import { join as join4, dirname as dirname3 } from "path";
6160
+ import { existsSync as existsSync3, appendFileSync, readFileSync as readFileSync3, mkdirSync as mkdirSync2 } from "fs";
6161
+ import { join as join5, dirname as dirname4 } from "path";
5934
6162
  var WireFile = class {
5935
6163
  filePath;
5936
6164
  sequence = 0;
5937
6165
  constructor(sessionDir) {
5938
- this.filePath = join4(sessionDir, "wire.jsonl");
5939
- const dir = dirname3(this.filePath);
5940
- if (!existsSync2(dir)) {
6166
+ this.filePath = join5(sessionDir, "wire.jsonl");
6167
+ const dir = dirname4(this.filePath);
6168
+ if (!existsSync3(dir)) {
5941
6169
  mkdirSync2(dir, { recursive: true });
5942
6170
  }
5943
- if (existsSync2(this.filePath)) {
6171
+ if (existsSync3(this.filePath)) {
5944
6172
  try {
5945
- const content = readFileSync2(this.filePath, "utf-8");
6173
+ const content = readFileSync3(this.filePath, "utf-8");
5946
6174
  this.sequence = content.trim().split("\n").filter((l) => l).length;
5947
6175
  } catch {
5948
6176
  this.sequence = 0;
@@ -5968,10 +6196,10 @@ var WireFile = class {
5968
6196
  * Read all records from the wire log
5969
6197
  */
5970
6198
  async *readRecords() {
5971
- if (!existsSync2(this.filePath)) {
6199
+ if (!existsSync3(this.filePath)) {
5972
6200
  return;
5973
6201
  }
5974
- const content = readFileSync2(this.filePath, "utf-8");
6202
+ const content = readFileSync3(this.filePath, "utf-8");
5975
6203
  const lines = content.trim().split("\n").filter((l) => l);
5976
6204
  for (const line of lines) {
5977
6205
  try {
@@ -5984,11 +6212,11 @@ var WireFile = class {
5984
6212
  * Check if wire file is empty
5985
6213
  */
5986
6214
  isEmpty() {
5987
- if (!existsSync2(this.filePath)) {
6215
+ if (!existsSync3(this.filePath)) {
5988
6216
  return true;
5989
6217
  }
5990
6218
  try {
5991
- const content = readFileSync2(this.filePath, "utf-8");
6219
+ const content = readFileSync3(this.filePath, "utf-8");
5992
6220
  return content.trim().length === 0;
5993
6221
  } catch {
5994
6222
  return true;
@@ -6095,8 +6323,8 @@ function initWireLogger(sessionDir, sessionId) {
6095
6323
  // src/utils/clipboard.ts
6096
6324
  import { execSync } from "child_process";
6097
6325
  import { platform } from "os";
6098
- import { existsSync as existsSync3, readFileSync as readFileSync3, unlinkSync as unlinkSync2 } from "fs";
6099
- import { join as join5 } from "path";
6326
+ import { existsSync as existsSync4, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "fs";
6327
+ import { join as join6 } from "path";
6100
6328
  import { tmpdir } from "os";
6101
6329
  function readClipboardText() {
6102
6330
  const os = platform();
@@ -6120,7 +6348,7 @@ function readClipboardText() {
6120
6348
  }
6121
6349
  function readClipboardImage() {
6122
6350
  const os = platform();
6123
- const tmpPath = join5(tmpdir(), `clipboard-${Date.now()}.png`);
6351
+ const tmpPath = join6(tmpdir(), `clipboard-${Date.now()}.png`);
6124
6352
  try {
6125
6353
  if (os === "darwin") {
6126
6354
  const script = `
@@ -6136,8 +6364,8 @@ function readClipboardImage() {
6136
6364
  end try
6137
6365
  `;
6138
6366
  const result = execSync(`osascript -e '${script}'`, { encoding: "utf-8" }).trim();
6139
- if (result === "success" && existsSync3(tmpPath)) {
6140
- const imageBuffer = readFileSync3(tmpPath);
6367
+ if (result === "success" && existsSync4(tmpPath)) {
6368
+ const imageBuffer = readFileSync4(tmpPath);
6141
6369
  const base64 = imageBuffer.toString("base64");
6142
6370
  return { path: tmpPath, base64, mimeType: "image/png" };
6143
6371
  }
@@ -6147,10 +6375,10 @@ function readClipboardImage() {
6147
6375
  encoding: "utf-8",
6148
6376
  shell: "/bin/bash"
6149
6377
  });
6150
- if (existsSync3(tmpPath)) {
6378
+ if (existsSync4(tmpPath)) {
6151
6379
  const stats = __require("fs").statSync(tmpPath);
6152
6380
  if (stats.size > 0) {
6153
- const imageBuffer = readFileSync3(tmpPath);
6381
+ const imageBuffer = readFileSync4(tmpPath);
6154
6382
  const base64 = imageBuffer.toString("base64");
6155
6383
  return { path: tmpPath, base64, mimeType: "image/png" };
6156
6384
  }
@@ -6169,8 +6397,8 @@ function readClipboardImage() {
6169
6397
  }
6170
6398
  `;
6171
6399
  const result = execSync(`powershell -command "${script}"`, { encoding: "utf-8" }).trim();
6172
- if (result === "success" && existsSync3(tmpPath)) {
6173
- const imageBuffer = readFileSync3(tmpPath);
6400
+ if (result === "success" && existsSync4(tmpPath)) {
6401
+ const imageBuffer = readFileSync4(tmpPath);
6174
6402
  const base64 = imageBuffer.toString("base64");
6175
6403
  return { path: tmpPath, base64, mimeType: "image/png" };
6176
6404
  }
@@ -6178,7 +6406,7 @@ function readClipboardImage() {
6178
6406
  } catch (error) {
6179
6407
  console.error("Failed to read clipboard image:", error);
6180
6408
  }
6181
- if (existsSync3(tmpPath)) {
6409
+ if (existsSync4(tmpPath)) {
6182
6410
  try {
6183
6411
  unlinkSync2(tmpPath);
6184
6412
  } catch {
@@ -6420,7 +6648,7 @@ var FindingDisplay = ({ block }) => {
6420
6648
 
6421
6649
  // src/cli/app.tsx
6422
6650
  import { homedir } from "os";
6423
- import { join as join6 } from "path";
6651
+ import { join as join7 } from "path";
6424
6652
 
6425
6653
  // src/cli/utils/keyboard-listener.ts
6426
6654
  import { EventEmitter as EventEmitter7 } from "events";
@@ -6723,7 +6951,7 @@ var App = ({ autoApprove = false, target }) => {
6723
6951
  const [agent] = useState(() => new AutonomousHackingAgent(void 0, { autoApprove }));
6724
6952
  const sessionManager2 = getSessionManager();
6725
6953
  const approvalManager2 = getApprovalManager({ yoloMode: autoApprove });
6726
- const sessionDirRef = useRef(join6(homedir(), ".pentest", "sessions", `session-${Date.now()}`));
6954
+ const sessionDirRef = useRef(join7(homedir(), ".pentest", "sessions", `session-${Date.now()}`));
6727
6955
  const contextManagerRef = useRef(null);
6728
6956
  const wireLoggerRef = useRef(null);
6729
6957
  const keyboardListenerRef = useRef(getKeyboardListener());
@@ -7740,8 +7968,8 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
7740
7968
  const summary = agent.getSummary();
7741
7969
  console.log(JSON.stringify(summary, null, 2));
7742
7970
  if (options.output) {
7743
- const fs4 = await import("fs/promises");
7744
- await fs4.writeFile(options.output, JSON.stringify(summary, null, 2));
7971
+ const fs5 = await import("fs/promises");
7972
+ await fs5.writeFile(options.output, JSON.stringify(summary, null, 2));
7745
7973
  console.log(chalk.hex(THEME.text.accent)(`
7746
7974
  [+] Report saved to: ${options.output}`));
7747
7975
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pentesting",
3
- "version": "0.7.40",
3
+ "version": "0.7.43",
4
4
  "description": "Autonomous Penetration Testing AI Agent",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -69,7 +69,8 @@
69
69
  "ink-text-input": "^6.0.0",
70
70
  "nanospinner": "^1.2.2",
71
71
  "ora": "^8.1.1",
72
- "react": "^18.3.1"
72
+ "react": "^18.3.1",
73
+ "yaml": "^2.8.2"
73
74
  },
74
75
  "devDependencies": {
75
76
  "@types/node": "^22.13.1",