pentesting 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +845 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/index.tsx
|
|
4
10
|
import { render } from "ink";
|
|
@@ -6,7 +12,7 @@ import { Command } from "commander";
|
|
|
6
12
|
|
|
7
13
|
// src/cli/app.tsx
|
|
8
14
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
9
|
-
import { Box, Text, useInput, useApp, Static } from "ink";
|
|
15
|
+
import { Box as Box2, Text as Text2, useInput, useApp, Static } from "ink";
|
|
10
16
|
import TextInput from "ink-text-input";
|
|
11
17
|
import Spinner from "ink-spinner";
|
|
12
18
|
|
|
@@ -1194,7 +1200,7 @@ async function executeMetasploit(input) {
|
|
|
1194
1200
|
return executeBash(`msfconsole -q -x "${command}; exit"`, { timeout: 3e5 });
|
|
1195
1201
|
}
|
|
1196
1202
|
async function generatePayload(input) {
|
|
1197
|
-
const { payload_type, lhost, lport, platform, format, encoder, output } = input;
|
|
1203
|
+
const { payload_type, lhost, lport, platform: platform2, format, encoder, output } = input;
|
|
1198
1204
|
const payloads = {
|
|
1199
1205
|
windows: {
|
|
1200
1206
|
reverse_tcp: "windows/meterpreter/reverse_tcp",
|
|
@@ -1213,7 +1219,7 @@ async function generatePayload(input) {
|
|
|
1213
1219
|
reverse_tcp: "python/meterpreter/reverse_tcp"
|
|
1214
1220
|
}
|
|
1215
1221
|
};
|
|
1216
|
-
const payloadName = payloads[
|
|
1222
|
+
const payloadName = payloads[platform2]?.[payload_type] || `${platform2}/meterpreter/reverse_tcp`;
|
|
1217
1223
|
let cmd = `msfvenom -p ${payloadName} LHOST=${lhost} LPORT=${lport}`;
|
|
1218
1224
|
if (format) cmd += ` -f ${format}`;
|
|
1219
1225
|
if (encoder) cmd += ` -e ${encoder}`;
|
|
@@ -1376,7 +1382,7 @@ const { chromium } = require('playwright');
|
|
|
1376
1382
|
}
|
|
1377
1383
|
|
|
1378
1384
|
// src/config/constants.ts
|
|
1379
|
-
var APP_VERSION = "0.3.
|
|
1385
|
+
var APP_VERSION = "0.3.2";
|
|
1380
1386
|
var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
|
|
1381
1387
|
var LLM_API_KEY = process.env.PENTEST_API_KEY || process.env.ANTHROPIC_API_KEY || "";
|
|
1382
1388
|
var LLM_BASE_URL = process.env.PENTEST_BASE_URL || void 0;
|
|
@@ -4231,6 +4237,595 @@ function getSessionManager() {
|
|
|
4231
4237
|
return sessionManager;
|
|
4232
4238
|
}
|
|
4233
4239
|
|
|
4240
|
+
// src/core/commands/slash-registry.ts
|
|
4241
|
+
var SlashCommandRegistry = class {
|
|
4242
|
+
commands = /* @__PURE__ */ new Map();
|
|
4243
|
+
aliases = /* @__PURE__ */ new Map();
|
|
4244
|
+
/**
|
|
4245
|
+
* Register a command with optional aliases
|
|
4246
|
+
*/
|
|
4247
|
+
register(name, handler, options) {
|
|
4248
|
+
const cmd = {
|
|
4249
|
+
name,
|
|
4250
|
+
description: options?.description || "",
|
|
4251
|
+
aliases: options?.aliases || [],
|
|
4252
|
+
handler
|
|
4253
|
+
};
|
|
4254
|
+
this.commands.set(name, cmd);
|
|
4255
|
+
this.aliases.set(name, cmd);
|
|
4256
|
+
for (const alias of cmd.aliases) {
|
|
4257
|
+
this.aliases.set(alias, cmd);
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
/**
|
|
4261
|
+
* Find a command by name or alias
|
|
4262
|
+
*/
|
|
4263
|
+
find(nameOrAlias) {
|
|
4264
|
+
return this.aliases.get(nameOrAlias);
|
|
4265
|
+
}
|
|
4266
|
+
/**
|
|
4267
|
+
* Execute a command
|
|
4268
|
+
*/
|
|
4269
|
+
async execute(input) {
|
|
4270
|
+
const parsed = this.parse(input);
|
|
4271
|
+
if (!parsed) {
|
|
4272
|
+
return { handled: false };
|
|
4273
|
+
}
|
|
4274
|
+
const cmd = this.find(parsed.name);
|
|
4275
|
+
if (!cmd) {
|
|
4276
|
+
return { handled: false };
|
|
4277
|
+
}
|
|
4278
|
+
const result = await cmd.handler(parsed.args);
|
|
4279
|
+
return { handled: true, result: result || void 0 };
|
|
4280
|
+
}
|
|
4281
|
+
/**
|
|
4282
|
+
* Parse slash command from input
|
|
4283
|
+
*/
|
|
4284
|
+
parse(input) {
|
|
4285
|
+
const trimmed = input.trim();
|
|
4286
|
+
if (!trimmed.startsWith("/")) {
|
|
4287
|
+
return null;
|
|
4288
|
+
}
|
|
4289
|
+
const match = trimmed.match(/^\/([a-zA-Z0-9_-]+)(?:\s+(.*))?$/);
|
|
4290
|
+
if (!match) {
|
|
4291
|
+
return null;
|
|
4292
|
+
}
|
|
4293
|
+
return {
|
|
4294
|
+
name: match[1].toLowerCase(),
|
|
4295
|
+
args: match[2] || ""
|
|
4296
|
+
};
|
|
4297
|
+
}
|
|
4298
|
+
/**
|
|
4299
|
+
* Get all commands (for help display)
|
|
4300
|
+
*/
|
|
4301
|
+
list() {
|
|
4302
|
+
return Array.from(this.commands.values());
|
|
4303
|
+
}
|
|
4304
|
+
/**
|
|
4305
|
+
* Get formatted help text
|
|
4306
|
+
*/
|
|
4307
|
+
getHelp() {
|
|
4308
|
+
const lines = ["Available commands:", ""];
|
|
4309
|
+
for (const cmd of this.list()) {
|
|
4310
|
+
const aliasStr = cmd.aliases.length > 0 ? ` (${cmd.aliases.join(", ")})` : "";
|
|
4311
|
+
lines.push(` /${cmd.name}${aliasStr}`);
|
|
4312
|
+
if (cmd.description) {
|
|
4313
|
+
lines.push(` ${cmd.description}`);
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
return lines.join("\n");
|
|
4317
|
+
}
|
|
4318
|
+
/**
|
|
4319
|
+
* Get matching commands for autocomplete
|
|
4320
|
+
*/
|
|
4321
|
+
getCompletions(partial) {
|
|
4322
|
+
const search = partial.toLowerCase().replace(/^\//, "");
|
|
4323
|
+
return this.list().filter(
|
|
4324
|
+
(cmd) => cmd.name.startsWith(search) || cmd.aliases.some((a) => a.startsWith(search))
|
|
4325
|
+
);
|
|
4326
|
+
}
|
|
4327
|
+
};
|
|
4328
|
+
var registry = null;
|
|
4329
|
+
function getSlashCommandRegistry() {
|
|
4330
|
+
if (!registry) {
|
|
4331
|
+
registry = new SlashCommandRegistry();
|
|
4332
|
+
}
|
|
4333
|
+
return registry;
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
// src/core/context/context-manager.ts
|
|
4337
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "fs";
|
|
4338
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
4339
|
+
import { EventEmitter as EventEmitter6 } from "events";
|
|
4340
|
+
var CONTEXT_EVENT = {
|
|
4341
|
+
MESSAGE_ADDED: "message_added",
|
|
4342
|
+
CHECKPOINT_CREATED: "checkpoint_created",
|
|
4343
|
+
REVERTED: "reverted",
|
|
4344
|
+
CLEARED: "cleared",
|
|
4345
|
+
COMPACTED: "compacted"
|
|
4346
|
+
};
|
|
4347
|
+
var ContextManager2 = class extends EventEmitter6 {
|
|
4348
|
+
filePath;
|
|
4349
|
+
state;
|
|
4350
|
+
maxMessages;
|
|
4351
|
+
constructor(sessionDir, options) {
|
|
4352
|
+
super();
|
|
4353
|
+
this.filePath = join3(sessionDir, "context.jsonl");
|
|
4354
|
+
this.maxMessages = options?.maxMessages || 100;
|
|
4355
|
+
this.state = {
|
|
4356
|
+
messages: [],
|
|
4357
|
+
checkpoints: [],
|
|
4358
|
+
tokenCount: 0,
|
|
4359
|
+
nextCheckpointId: 0
|
|
4360
|
+
};
|
|
4361
|
+
const dir = dirname2(this.filePath);
|
|
4362
|
+
if (!existsSync(dir)) {
|
|
4363
|
+
mkdirSync(dir, { recursive: true });
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
/**
|
|
4367
|
+
* Restore context from file
|
|
4368
|
+
*/
|
|
4369
|
+
async restore() {
|
|
4370
|
+
if (!existsSync(this.filePath)) {
|
|
4371
|
+
return false;
|
|
4372
|
+
}
|
|
4373
|
+
try {
|
|
4374
|
+
const content = readFileSync(this.filePath, "utf-8");
|
|
4375
|
+
const lines = content.trim().split("\n").filter((l) => l);
|
|
4376
|
+
for (const line of lines) {
|
|
4377
|
+
const data = JSON.parse(line);
|
|
4378
|
+
if (data.role === "_checkpoint") {
|
|
4379
|
+
this.state.checkpoints.push({
|
|
4380
|
+
id: data.id,
|
|
4381
|
+
timestamp: data.timestamp,
|
|
4382
|
+
messageCount: data.messageCount,
|
|
4383
|
+
description: data.description
|
|
4384
|
+
});
|
|
4385
|
+
this.state.nextCheckpointId = Math.max(this.state.nextCheckpointId, data.id + 1);
|
|
4386
|
+
} else if (data.role === "_usage") {
|
|
4387
|
+
this.state.tokenCount = data.tokenCount;
|
|
4388
|
+
} else {
|
|
4389
|
+
this.state.messages.push(data);
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
return this.state.messages.length > 0;
|
|
4393
|
+
} catch (error) {
|
|
4394
|
+
console.error("Failed to restore context:", error);
|
|
4395
|
+
return false;
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
/**
|
|
4399
|
+
* Get conversation history
|
|
4400
|
+
*/
|
|
4401
|
+
getHistory() {
|
|
4402
|
+
return [...this.state.messages];
|
|
4403
|
+
}
|
|
4404
|
+
/**
|
|
4405
|
+
* Get checkpoints
|
|
4406
|
+
*/
|
|
4407
|
+
getCheckpoints() {
|
|
4408
|
+
return [...this.state.checkpoints];
|
|
4409
|
+
}
|
|
4410
|
+
/**
|
|
4411
|
+
* Add a message to context
|
|
4412
|
+
*/
|
|
4413
|
+
async addMessage(message) {
|
|
4414
|
+
const msg = {
|
|
4415
|
+
...message,
|
|
4416
|
+
timestamp: Date.now()
|
|
4417
|
+
};
|
|
4418
|
+
this.state.messages.push(msg);
|
|
4419
|
+
await this.appendToFile(msg);
|
|
4420
|
+
this.emit(CONTEXT_EVENT.MESSAGE_ADDED, msg);
|
|
4421
|
+
}
|
|
4422
|
+
/**
|
|
4423
|
+
* Create a checkpoint
|
|
4424
|
+
*/
|
|
4425
|
+
async checkpoint(description) {
|
|
4426
|
+
const checkpoint = {
|
|
4427
|
+
id: this.state.nextCheckpointId++,
|
|
4428
|
+
timestamp: Date.now(),
|
|
4429
|
+
messageCount: this.state.messages.length,
|
|
4430
|
+
description
|
|
4431
|
+
};
|
|
4432
|
+
this.state.checkpoints.push(checkpoint);
|
|
4433
|
+
await this.appendToFile({
|
|
4434
|
+
role: "_checkpoint",
|
|
4435
|
+
...checkpoint
|
|
4436
|
+
});
|
|
4437
|
+
this.emit(CONTEXT_EVENT.CHECKPOINT_CREATED, checkpoint);
|
|
4438
|
+
return checkpoint;
|
|
4439
|
+
}
|
|
4440
|
+
/**
|
|
4441
|
+
* Revert to a specific checkpoint
|
|
4442
|
+
*/
|
|
4443
|
+
async revertTo(checkpointId) {
|
|
4444
|
+
const checkpoint = this.state.checkpoints.find((c) => c.id === checkpointId);
|
|
4445
|
+
if (!checkpoint) {
|
|
4446
|
+
return false;
|
|
4447
|
+
}
|
|
4448
|
+
const backupPath = `${this.filePath}.bak`;
|
|
4449
|
+
if (existsSync(this.filePath)) {
|
|
4450
|
+
renameSync(this.filePath, backupPath);
|
|
4451
|
+
}
|
|
4452
|
+
this.state.messages = this.state.messages.slice(0, checkpoint.messageCount);
|
|
4453
|
+
this.state.checkpoints = this.state.checkpoints.filter((c) => c.id < checkpointId);
|
|
4454
|
+
await this.rewriteFile();
|
|
4455
|
+
this.emit(CONTEXT_EVENT.REVERTED, checkpointId);
|
|
4456
|
+
return true;
|
|
4457
|
+
}
|
|
4458
|
+
/**
|
|
4459
|
+
* Undo last checkpoint (go back one step)
|
|
4460
|
+
*/
|
|
4461
|
+
async undo() {
|
|
4462
|
+
if (this.state.checkpoints.length === 0) {
|
|
4463
|
+
return false;
|
|
4464
|
+
}
|
|
4465
|
+
const lastCheckpoint = this.state.checkpoints[this.state.checkpoints.length - 1];
|
|
4466
|
+
return this.revertTo(lastCheckpoint.id);
|
|
4467
|
+
}
|
|
4468
|
+
/**
|
|
4469
|
+
* Clear all context
|
|
4470
|
+
*/
|
|
4471
|
+
async clear() {
|
|
4472
|
+
const backupPath = `${this.filePath}.${Date.now()}.bak`;
|
|
4473
|
+
if (existsSync(this.filePath)) {
|
|
4474
|
+
renameSync(this.filePath, backupPath);
|
|
4475
|
+
}
|
|
4476
|
+
this.state = {
|
|
4477
|
+
messages: [],
|
|
4478
|
+
checkpoints: [],
|
|
4479
|
+
tokenCount: 0,
|
|
4480
|
+
nextCheckpointId: 0
|
|
4481
|
+
};
|
|
4482
|
+
this.emit(CONTEXT_EVENT.CLEARED);
|
|
4483
|
+
}
|
|
4484
|
+
/**
|
|
4485
|
+
* Update token count
|
|
4486
|
+
*/
|
|
4487
|
+
async updateTokenCount(count) {
|
|
4488
|
+
this.state.tokenCount = count;
|
|
4489
|
+
await this.appendToFile({ role: "_usage", tokenCount: count });
|
|
4490
|
+
}
|
|
4491
|
+
/**
|
|
4492
|
+
* Get token count
|
|
4493
|
+
*/
|
|
4494
|
+
getTokenCount() {
|
|
4495
|
+
return this.state.tokenCount;
|
|
4496
|
+
}
|
|
4497
|
+
/**
|
|
4498
|
+
* Check if context needs compaction
|
|
4499
|
+
*/
|
|
4500
|
+
needsCompaction(maxTokens, reservedTokens = 5e4) {
|
|
4501
|
+
return this.state.tokenCount + reservedTokens >= maxTokens;
|
|
4502
|
+
}
|
|
4503
|
+
/**
|
|
4504
|
+
* Compact context (keep last N messages, summarize rest)
|
|
4505
|
+
*/
|
|
4506
|
+
async compact(summary, keepLast = 2) {
|
|
4507
|
+
if (this.state.messages.length <= keepLast) {
|
|
4508
|
+
return;
|
|
4509
|
+
}
|
|
4510
|
+
const preserved = this.state.messages.slice(-keepLast);
|
|
4511
|
+
const summaryMessage = {
|
|
4512
|
+
role: "system",
|
|
4513
|
+
content: `[Context Compacted]
|
|
4514
|
+
|
|
4515
|
+
Previous conversation summary:
|
|
4516
|
+
${summary}`,
|
|
4517
|
+
timestamp: Date.now(),
|
|
4518
|
+
metadata: { compacted: true }
|
|
4519
|
+
};
|
|
4520
|
+
const backupPath = `${this.filePath}.compact.bak`;
|
|
4521
|
+
if (existsSync(this.filePath)) {
|
|
4522
|
+
renameSync(this.filePath, backupPath);
|
|
4523
|
+
}
|
|
4524
|
+
this.state.messages = [summaryMessage, ...preserved];
|
|
4525
|
+
this.state.checkpoints = [];
|
|
4526
|
+
this.state.nextCheckpointId = 0;
|
|
4527
|
+
await this.rewriteFile();
|
|
4528
|
+
this.emit(CONTEXT_EVENT.COMPACTED, { summary, preserved: preserved.length });
|
|
4529
|
+
}
|
|
4530
|
+
async appendToFile(data) {
|
|
4531
|
+
const line = JSON.stringify(data) + "\n";
|
|
4532
|
+
const { appendFileSync: appendFileSync2 } = await import("fs");
|
|
4533
|
+
appendFileSync2(this.filePath, line, "utf-8");
|
|
4534
|
+
}
|
|
4535
|
+
async rewriteFile() {
|
|
4536
|
+
let content = "";
|
|
4537
|
+
for (const msg of this.state.messages) {
|
|
4538
|
+
content += JSON.stringify(msg) + "\n";
|
|
4539
|
+
}
|
|
4540
|
+
for (const cp of this.state.checkpoints) {
|
|
4541
|
+
content += JSON.stringify({ role: "_checkpoint", ...cp }) + "\n";
|
|
4542
|
+
}
|
|
4543
|
+
if (this.state.tokenCount > 0) {
|
|
4544
|
+
content += JSON.stringify({ role: "_usage", tokenCount: this.state.tokenCount }) + "\n";
|
|
4545
|
+
}
|
|
4546
|
+
writeFileSync(this.filePath, content, "utf-8");
|
|
4547
|
+
}
|
|
4548
|
+
};
|
|
4549
|
+
|
|
4550
|
+
// src/wire/wire-logger.ts
|
|
4551
|
+
import { existsSync as existsSync2, appendFileSync, readFileSync as readFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
4552
|
+
import { join as join4, dirname as dirname3 } from "path";
|
|
4553
|
+
var WireFile = class {
|
|
4554
|
+
filePath;
|
|
4555
|
+
sequence = 0;
|
|
4556
|
+
constructor(sessionDir) {
|
|
4557
|
+
this.filePath = join4(sessionDir, "wire.jsonl");
|
|
4558
|
+
const dir = dirname3(this.filePath);
|
|
4559
|
+
if (!existsSync2(dir)) {
|
|
4560
|
+
mkdirSync2(dir, { recursive: true });
|
|
4561
|
+
}
|
|
4562
|
+
if (existsSync2(this.filePath)) {
|
|
4563
|
+
try {
|
|
4564
|
+
const content = readFileSync2(this.filePath, "utf-8");
|
|
4565
|
+
this.sequence = content.trim().split("\n").filter((l) => l).length;
|
|
4566
|
+
} catch {
|
|
4567
|
+
this.sequence = 0;
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
/**
|
|
4572
|
+
* Write a message to the wire log
|
|
4573
|
+
*/
|
|
4574
|
+
write(message) {
|
|
4575
|
+
const record = {
|
|
4576
|
+
sequence: this.sequence++,
|
|
4577
|
+
message: {
|
|
4578
|
+
...message,
|
|
4579
|
+
timestamp: Date.now()
|
|
4580
|
+
}
|
|
4581
|
+
};
|
|
4582
|
+
const line = JSON.stringify(record) + "\n";
|
|
4583
|
+
appendFileSync(this.filePath, line, "utf-8");
|
|
4584
|
+
return record;
|
|
4585
|
+
}
|
|
4586
|
+
/**
|
|
4587
|
+
* Read all records from the wire log
|
|
4588
|
+
*/
|
|
4589
|
+
async *readRecords() {
|
|
4590
|
+
if (!existsSync2(this.filePath)) {
|
|
4591
|
+
return;
|
|
4592
|
+
}
|
|
4593
|
+
const content = readFileSync2(this.filePath, "utf-8");
|
|
4594
|
+
const lines = content.trim().split("\n").filter((l) => l);
|
|
4595
|
+
for (const line of lines) {
|
|
4596
|
+
try {
|
|
4597
|
+
yield JSON.parse(line);
|
|
4598
|
+
} catch {
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
/**
|
|
4603
|
+
* Check if wire file is empty
|
|
4604
|
+
*/
|
|
4605
|
+
isEmpty() {
|
|
4606
|
+
if (!existsSync2(this.filePath)) {
|
|
4607
|
+
return true;
|
|
4608
|
+
}
|
|
4609
|
+
try {
|
|
4610
|
+
const content = readFileSync2(this.filePath, "utf-8");
|
|
4611
|
+
return content.trim().length === 0;
|
|
4612
|
+
} catch {
|
|
4613
|
+
return true;
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
/**
|
|
4617
|
+
* Get file path
|
|
4618
|
+
*/
|
|
4619
|
+
getPath() {
|
|
4620
|
+
return this.filePath;
|
|
4621
|
+
}
|
|
4622
|
+
};
|
|
4623
|
+
var WireLogger = class {
|
|
4624
|
+
wire;
|
|
4625
|
+
sessionId;
|
|
4626
|
+
constructor(sessionDir, sessionId) {
|
|
4627
|
+
this.wire = new WireFile(sessionDir);
|
|
4628
|
+
this.sessionId = sessionId;
|
|
4629
|
+
}
|
|
4630
|
+
turnBegin(userInput) {
|
|
4631
|
+
this.wire.write({
|
|
4632
|
+
type: "turn_begin",
|
|
4633
|
+
sessionId: this.sessionId,
|
|
4634
|
+
data: { userInput }
|
|
4635
|
+
});
|
|
4636
|
+
}
|
|
4637
|
+
turnEnd() {
|
|
4638
|
+
this.wire.write({
|
|
4639
|
+
type: "turn_end",
|
|
4640
|
+
sessionId: this.sessionId,
|
|
4641
|
+
data: {}
|
|
4642
|
+
});
|
|
4643
|
+
}
|
|
4644
|
+
stepBegin(stepId) {
|
|
4645
|
+
this.wire.write({
|
|
4646
|
+
type: "step_begin",
|
|
4647
|
+
sessionId: this.sessionId,
|
|
4648
|
+
data: { stepId }
|
|
4649
|
+
});
|
|
4650
|
+
}
|
|
4651
|
+
stepEnd(stepId) {
|
|
4652
|
+
this.wire.write({
|
|
4653
|
+
type: "step_end",
|
|
4654
|
+
sessionId: this.sessionId,
|
|
4655
|
+
data: { stepId }
|
|
4656
|
+
});
|
|
4657
|
+
}
|
|
4658
|
+
contentPart(content, isThinking = false) {
|
|
4659
|
+
this.wire.write({
|
|
4660
|
+
type: "content_part",
|
|
4661
|
+
sessionId: this.sessionId,
|
|
4662
|
+
data: { content, isThinking }
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
toolCall(toolId, toolName, args) {
|
|
4666
|
+
this.wire.write({
|
|
4667
|
+
type: "tool_call",
|
|
4668
|
+
sessionId: this.sessionId,
|
|
4669
|
+
data: { toolId, toolName, args }
|
|
4670
|
+
});
|
|
4671
|
+
}
|
|
4672
|
+
toolResult(toolId, result, isError = false) {
|
|
4673
|
+
this.wire.write({
|
|
4674
|
+
type: "tool_result",
|
|
4675
|
+
sessionId: this.sessionId,
|
|
4676
|
+
data: { toolId, result, isError }
|
|
4677
|
+
});
|
|
4678
|
+
}
|
|
4679
|
+
approvalRequest(id, toolName, riskLevel) {
|
|
4680
|
+
this.wire.write({
|
|
4681
|
+
type: "approval_request",
|
|
4682
|
+
sessionId: this.sessionId,
|
|
4683
|
+
data: { id, toolName, riskLevel }
|
|
4684
|
+
});
|
|
4685
|
+
}
|
|
4686
|
+
approvalResponse(id, decision) {
|
|
4687
|
+
this.wire.write({
|
|
4688
|
+
type: "approval_response",
|
|
4689
|
+
sessionId: this.sessionId,
|
|
4690
|
+
data: { id, decision }
|
|
4691
|
+
});
|
|
4692
|
+
}
|
|
4693
|
+
statusUpdate(data) {
|
|
4694
|
+
this.wire.write({
|
|
4695
|
+
type: "status_update",
|
|
4696
|
+
sessionId: this.sessionId,
|
|
4697
|
+
data
|
|
4698
|
+
});
|
|
4699
|
+
}
|
|
4700
|
+
error(message, stack) {
|
|
4701
|
+
this.wire.write({
|
|
4702
|
+
type: "error",
|
|
4703
|
+
sessionId: this.sessionId,
|
|
4704
|
+
data: { message, stack }
|
|
4705
|
+
});
|
|
4706
|
+
}
|
|
4707
|
+
};
|
|
4708
|
+
var wireLogger = null;
|
|
4709
|
+
function initWireLogger(sessionDir, sessionId) {
|
|
4710
|
+
wireLogger = new WireLogger(sessionDir, sessionId);
|
|
4711
|
+
return wireLogger;
|
|
4712
|
+
}
|
|
4713
|
+
|
|
4714
|
+
// src/utils/clipboard.ts
|
|
4715
|
+
import { execSync } from "child_process";
|
|
4716
|
+
import { platform } from "os";
|
|
4717
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
4718
|
+
import { join as join5 } from "path";
|
|
4719
|
+
import { tmpdir } from "os";
|
|
4720
|
+
function readClipboardText() {
|
|
4721
|
+
const os = platform();
|
|
4722
|
+
try {
|
|
4723
|
+
if (os === "darwin") {
|
|
4724
|
+
return execSync("pbpaste", { encoding: "utf-8" });
|
|
4725
|
+
} else if (os === "linux") {
|
|
4726
|
+
try {
|
|
4727
|
+
return execSync("xclip -selection clipboard -o", { encoding: "utf-8" });
|
|
4728
|
+
} catch {
|
|
4729
|
+
return execSync("xsel --clipboard --output", { encoding: "utf-8" });
|
|
4730
|
+
}
|
|
4731
|
+
} else if (os === "win32") {
|
|
4732
|
+
return execSync('powershell -command "Get-Clipboard"', { encoding: "utf-8" });
|
|
4733
|
+
}
|
|
4734
|
+
} catch (error) {
|
|
4735
|
+
console.error("Failed to read clipboard:", error);
|
|
4736
|
+
return null;
|
|
4737
|
+
}
|
|
4738
|
+
return null;
|
|
4739
|
+
}
|
|
4740
|
+
function readClipboardImage() {
|
|
4741
|
+
const os = platform();
|
|
4742
|
+
const tmpPath = join5(tmpdir(), `clipboard-${Date.now()}.png`);
|
|
4743
|
+
try {
|
|
4744
|
+
if (os === "darwin") {
|
|
4745
|
+
const script = `
|
|
4746
|
+
set saveFile to POSIX file "${tmpPath}"
|
|
4747
|
+
try
|
|
4748
|
+
set imageData to the clipboard as \xABclass PNGf\xBB
|
|
4749
|
+
set fileRef to open for access saveFile with write permission
|
|
4750
|
+
write imageData to fileRef
|
|
4751
|
+
close access fileRef
|
|
4752
|
+
return "success"
|
|
4753
|
+
on error
|
|
4754
|
+
return "no image"
|
|
4755
|
+
end try
|
|
4756
|
+
`;
|
|
4757
|
+
const result = execSync(`osascript -e '${script}'`, { encoding: "utf-8" }).trim();
|
|
4758
|
+
if (result === "success" && existsSync3(tmpPath)) {
|
|
4759
|
+
const imageBuffer = readFileSync3(tmpPath);
|
|
4760
|
+
const base64 = imageBuffer.toString("base64");
|
|
4761
|
+
return { path: tmpPath, base64 };
|
|
4762
|
+
}
|
|
4763
|
+
} else if (os === "linux") {
|
|
4764
|
+
try {
|
|
4765
|
+
execSync(`xclip -selection clipboard -t image/png -o > "${tmpPath}"`, {
|
|
4766
|
+
encoding: "utf-8",
|
|
4767
|
+
shell: "/bin/bash"
|
|
4768
|
+
});
|
|
4769
|
+
if (existsSync3(tmpPath)) {
|
|
4770
|
+
const stats = __require("fs").statSync(tmpPath);
|
|
4771
|
+
if (stats.size > 0) {
|
|
4772
|
+
const imageBuffer = readFileSync3(tmpPath);
|
|
4773
|
+
const base64 = imageBuffer.toString("base64");
|
|
4774
|
+
return { path: tmpPath, base64 };
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
} catch {
|
|
4778
|
+
}
|
|
4779
|
+
} else if (os === "win32") {
|
|
4780
|
+
const script = `
|
|
4781
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
4782
|
+
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
4783
|
+
if ($img -ne $null) {
|
|
4784
|
+
$img.Save("${tmpPath.replace(/\\/g, "\\\\")}")
|
|
4785
|
+
Write-Output "success"
|
|
4786
|
+
} else {
|
|
4787
|
+
Write-Output "no image"
|
|
4788
|
+
}
|
|
4789
|
+
`;
|
|
4790
|
+
const result = execSync(`powershell -command "${script}"`, { encoding: "utf-8" }).trim();
|
|
4791
|
+
if (result === "success" && existsSync3(tmpPath)) {
|
|
4792
|
+
const imageBuffer = readFileSync3(tmpPath);
|
|
4793
|
+
const base64 = imageBuffer.toString("base64");
|
|
4794
|
+
return { path: tmpPath, base64 };
|
|
4795
|
+
}
|
|
4796
|
+
}
|
|
4797
|
+
} catch (error) {
|
|
4798
|
+
console.error("Failed to read clipboard image:", error);
|
|
4799
|
+
}
|
|
4800
|
+
if (existsSync3(tmpPath)) {
|
|
4801
|
+
try {
|
|
4802
|
+
unlinkSync2(tmpPath);
|
|
4803
|
+
} catch {
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
return null;
|
|
4807
|
+
}
|
|
4808
|
+
function hasClipboardImage() {
|
|
4809
|
+
const os = platform();
|
|
4810
|
+
try {
|
|
4811
|
+
if (os === "darwin") {
|
|
4812
|
+
const result = execSync(
|
|
4813
|
+
`osascript -e 'try' -e 'the clipboard as \xABclass PNGf\xBB' -e 'return "yes"' -e 'on error' -e 'return "no"' -e 'end try'`,
|
|
4814
|
+
{ encoding: "utf-8" }
|
|
4815
|
+
).trim();
|
|
4816
|
+
return result === "yes";
|
|
4817
|
+
} else if (os === "linux") {
|
|
4818
|
+
const result = execSync("xclip -selection clipboard -t TARGETS -o", {
|
|
4819
|
+
encoding: "utf-8"
|
|
4820
|
+
});
|
|
4821
|
+
return result.includes("image/png") || result.includes("image/jpeg");
|
|
4822
|
+
}
|
|
4823
|
+
} catch {
|
|
4824
|
+
return false;
|
|
4825
|
+
}
|
|
4826
|
+
return false;
|
|
4827
|
+
}
|
|
4828
|
+
|
|
4234
4829
|
// src/config/theme.ts
|
|
4235
4830
|
var THEME = {
|
|
4236
4831
|
// Primary backgrounds (dark purple tones)
|
|
@@ -4348,8 +4943,68 @@ var ASCII_BANNER = `
|
|
|
4348
4943
|
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
4349
4944
|
`;
|
|
4350
4945
|
|
|
4351
|
-
// src/cli/
|
|
4946
|
+
// src/cli/components/rich-display.tsx
|
|
4947
|
+
import { Box, Text } from "ink";
|
|
4352
4948
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4949
|
+
var SeverityBadge = ({ severity }) => {
|
|
4950
|
+
const colors = {
|
|
4951
|
+
critical: THEME.semantic.critical,
|
|
4952
|
+
high: THEME.semantic.high,
|
|
4953
|
+
medium: THEME.semantic.medium,
|
|
4954
|
+
low: THEME.semantic.low,
|
|
4955
|
+
info: THEME.semantic.info
|
|
4956
|
+
};
|
|
4957
|
+
return /* @__PURE__ */ jsxs(Text, { color: colors[severity], bold: true, children: [
|
|
4958
|
+
"\u25CF ",
|
|
4959
|
+
severity.toUpperCase()
|
|
4960
|
+
] });
|
|
4961
|
+
};
|
|
4962
|
+
var FindingDisplay = ({ block }) => {
|
|
4963
|
+
const severityColors = {
|
|
4964
|
+
critical: THEME.semantic.critical,
|
|
4965
|
+
high: THEME.semantic.high,
|
|
4966
|
+
medium: THEME.semantic.medium,
|
|
4967
|
+
low: THEME.semantic.low,
|
|
4968
|
+
info: THEME.semantic.info
|
|
4969
|
+
};
|
|
4970
|
+
return /* @__PURE__ */ jsxs(
|
|
4971
|
+
Box,
|
|
4972
|
+
{
|
|
4973
|
+
flexDirection: "column",
|
|
4974
|
+
borderStyle: "double",
|
|
4975
|
+
borderColor: severityColors[block.severity],
|
|
4976
|
+
paddingX: 1,
|
|
4977
|
+
children: [
|
|
4978
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
4979
|
+
/* @__PURE__ */ jsx(SeverityBadge, { severity: block.severity }),
|
|
4980
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME.text.primary, bold: true, children: [
|
|
4981
|
+
" ",
|
|
4982
|
+
block.title
|
|
4983
|
+
] }),
|
|
4984
|
+
block.cve && /* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
|
|
4985
|
+
" (",
|
|
4986
|
+
block.cve,
|
|
4987
|
+
")"
|
|
4988
|
+
] })
|
|
4989
|
+
] }),
|
|
4990
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx(Text, { color: THEME.text.secondary, children: block.description }) }),
|
|
4991
|
+
block.evidence && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
|
|
4992
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.accent.cyan, bold: true, children: "Evidence:" }),
|
|
4993
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: block.evidence })
|
|
4994
|
+
] }),
|
|
4995
|
+
block.recommendation && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
|
|
4996
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.accent.green, bold: true, children: "Recommendation:" }),
|
|
4997
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.text.secondary, children: block.recommendation })
|
|
4998
|
+
] })
|
|
4999
|
+
]
|
|
5000
|
+
}
|
|
5001
|
+
);
|
|
5002
|
+
};
|
|
5003
|
+
|
|
5004
|
+
// src/cli/app.tsx
|
|
5005
|
+
import { homedir } from "os";
|
|
5006
|
+
import { join as join6 } from "path";
|
|
5007
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4353
5008
|
var App = ({ autoApprove = false, target }) => {
|
|
4354
5009
|
const { exit } = useApp();
|
|
4355
5010
|
const [messages, setMessages] = useState([]);
|
|
@@ -4362,9 +5017,23 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4362
5017
|
const [tokenUsage, setTokenUsage] = useState({ input: 0, output: 0, total: 0 });
|
|
4363
5018
|
const [showCommandHints, setShowCommandHints] = useState(false);
|
|
4364
5019
|
const [mode, setMode] = useState("agent");
|
|
5020
|
+
const [checkpointCount, setCheckpointCount] = useState(0);
|
|
4365
5021
|
const [agent] = useState(() => new AutonomousHackingAgent(void 0, { autoApprove }));
|
|
4366
5022
|
const sessionManager2 = getSessionManager();
|
|
4367
5023
|
const approvalManager2 = getApprovalManager({ yoloMode: autoApprove });
|
|
5024
|
+
const sessionDirRef = useRef(join6(homedir(), ".pentest", "sessions", `session-${Date.now()}`));
|
|
5025
|
+
const contextManagerRef = useRef(null);
|
|
5026
|
+
const wireLoggerRef = useRef(null);
|
|
5027
|
+
useEffect(() => {
|
|
5028
|
+
const sessionId = `session-${Date.now()}`;
|
|
5029
|
+
contextManagerRef.current = new ContextManager2(sessionDirRef.current);
|
|
5030
|
+
wireLoggerRef.current = initWireLogger(sessionDirRef.current, sessionId);
|
|
5031
|
+
contextManagerRef.current.restore().then((restored) => {
|
|
5032
|
+
if (restored) {
|
|
5033
|
+
setCheckpointCount(contextManagerRef.current?.getCheckpoints().length || 0);
|
|
5034
|
+
}
|
|
5035
|
+
});
|
|
5036
|
+
}, []);
|
|
4368
5037
|
const startTimeRef = useRef(0);
|
|
4369
5038
|
const timerRef = useRef(null);
|
|
4370
5039
|
const addMessage = useCallback((type, content, duration) => {
|
|
@@ -4402,31 +5071,39 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4402
5071
|
}
|
|
4403
5072
|
agent.on(AGENT_EVENT.THOUGHT, (thought) => {
|
|
4404
5073
|
setCurrentStatus(thought.content.slice(0, 60));
|
|
5074
|
+
wireLoggerRef.current?.contentPart(thought.content, thought.type === "thinking");
|
|
4405
5075
|
});
|
|
4406
5076
|
agent.on(AGENT_EVENT.TOOL_CALL, (data) => {
|
|
4407
5077
|
const args = Object.entries(data.input).slice(0, 2).map(([k, v]) => `${k}=${typeof v === "string" ? v.slice(0, 30) : "..."}`).join(" ");
|
|
4408
5078
|
setCurrentStatus(`Running ${data.name}...`);
|
|
4409
5079
|
addMessage(MESSAGE_TYPE.TOOL, `\u25B6 ${data.name} ${args}`);
|
|
5080
|
+
wireLoggerRef.current?.toolCall(data.id, data.name, data.input);
|
|
4410
5081
|
});
|
|
4411
5082
|
agent.on(AGENT_EVENT.TOOL_RESULT, (data) => {
|
|
4412
5083
|
const icon = data.result.success ? "\u2713" : "\u2717";
|
|
4413
5084
|
const preview = data.result.output?.slice(0, 100).replace(/\n/g, " ") || "";
|
|
4414
5085
|
addMessage(MESSAGE_TYPE.RESULT, `${icon} ${preview}`);
|
|
5086
|
+
wireLoggerRef.current?.toolResult(data.id, data.result, !data.result.success);
|
|
4415
5087
|
});
|
|
4416
5088
|
agent.on(AGENT_EVENT.ITERATION, (data) => {
|
|
4417
5089
|
setCurrentStatus(`Phase: ${data.phase} (iteration ${data.current})`);
|
|
5090
|
+
wireLoggerRef.current?.stepBegin(data.current);
|
|
4418
5091
|
});
|
|
4419
5092
|
agent.on(AGENT_EVENT.FINDING, (finding) => {
|
|
4420
5093
|
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F3AF} [${finding.severity.toUpperCase()}] ${finding.title}`);
|
|
5094
|
+
wireLoggerRef.current?.statusUpdate({ event: "finding", ...finding });
|
|
4421
5095
|
});
|
|
4422
5096
|
agent.on(AGENT_EVENT.PHASE_CHANGE, (data) => {
|
|
4423
5097
|
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4CD} Phase: ${data.phaseId}`);
|
|
5098
|
+
wireLoggerRef.current?.statusUpdate({ event: "phase_change", phase: data.phaseId });
|
|
4424
5099
|
});
|
|
4425
5100
|
agent.on(AGENT_EVENT.CONTEXT_COMPACTED, () => {
|
|
4426
5101
|
addMessage(MESSAGE_TYPE.SYSTEM, "\u{1F4BE} Context compacted to save tokens");
|
|
5102
|
+
wireLoggerRef.current?.statusUpdate({ event: "context_compacted" });
|
|
4427
5103
|
});
|
|
4428
5104
|
agent.on(AGENT_EVENT.TOKEN_USAGE, (usage) => {
|
|
4429
5105
|
setTokenUsage(usage);
|
|
5106
|
+
wireLoggerRef.current?.statusUpdate({ event: "token_usage", ...usage });
|
|
4430
5107
|
});
|
|
4431
5108
|
agent.on(AGENT_EVENT.APPROVAL_NEEDED, (data) => {
|
|
4432
5109
|
setPendingApproval({
|
|
@@ -4505,16 +5182,30 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4505
5182
|
case "h":
|
|
4506
5183
|
addMessage(
|
|
4507
5184
|
MESSAGE_TYPE.SYSTEM,
|
|
4508
|
-
|
|
5185
|
+
`\u2500\u2500 Core \u2500\u2500
|
|
5186
|
+
/target <ip> Set target
|
|
4509
5187
|
/start [goal] Start autonomous pentest
|
|
4510
5188
|
/stop Stop operation
|
|
4511
|
-
/
|
|
5189
|
+
/status Show status report
|
|
5190
|
+
|
|
5191
|
+
\u2500\u2500 Session \u2500\u2500
|
|
5192
|
+
/checkpoint [desc] Create checkpoint
|
|
5193
|
+
/checkpoints List all checkpoints
|
|
5194
|
+
/undo Undo to last checkpoint
|
|
5195
|
+
/revert <id> Revert to checkpoint
|
|
5196
|
+
/compact Compact context
|
|
4512
5197
|
/sessions List saved sessions
|
|
4513
5198
|
/resume [id] Resume session
|
|
5199
|
+
|
|
5200
|
+
\u2500\u2500 Findings \u2500\u2500
|
|
5201
|
+
/findings Show findings
|
|
5202
|
+
|
|
5203
|
+
\u2500\u2500 Utility \u2500\u2500
|
|
5204
|
+
/paste Paste from clipboard
|
|
4514
5205
|
/yolo Toggle auto-approve
|
|
4515
5206
|
/clear Clear screen
|
|
4516
5207
|
/exit Exit
|
|
4517
|
-
/
|
|
5208
|
+
/y /n /ya Approve/Deny/Always (approval)`
|
|
4518
5209
|
);
|
|
4519
5210
|
return;
|
|
4520
5211
|
case CLI_COMMAND.TARGET:
|
|
@@ -4628,7 +5319,125 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4628
5319
|
addMessage(MESSAGE_TYPE.ERROR, "No pending approval");
|
|
4629
5320
|
}
|
|
4630
5321
|
return;
|
|
5322
|
+
// kimi-cli inspired commands - REAL IMPLEMENTATIONS
|
|
5323
|
+
case "undo":
|
|
5324
|
+
case "u":
|
|
5325
|
+
if (contextManagerRef.current) {
|
|
5326
|
+
const success = await contextManagerRef.current.undo();
|
|
5327
|
+
if (success) {
|
|
5328
|
+
setCheckpointCount((prev) => Math.max(0, prev - 1));
|
|
5329
|
+
wireLoggerRef.current?.statusUpdate({ action: "undo" });
|
|
5330
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "\u21A9\uFE0F Undone to previous checkpoint");
|
|
5331
|
+
} else {
|
|
5332
|
+
addMessage(MESSAGE_TYPE.ERROR, "No checkpoint to undo");
|
|
5333
|
+
}
|
|
5334
|
+
} else {
|
|
5335
|
+
addMessage(MESSAGE_TYPE.ERROR, "Context manager not initialized");
|
|
5336
|
+
}
|
|
5337
|
+
return;
|
|
5338
|
+
case "checkpoint":
|
|
5339
|
+
case "cp":
|
|
5340
|
+
if (contextManagerRef.current) {
|
|
5341
|
+
const description = args.join(" ") || void 0;
|
|
5342
|
+
const cp = await contextManagerRef.current.checkpoint(description);
|
|
5343
|
+
setCheckpointCount((prev) => prev + 1);
|
|
5344
|
+
wireLoggerRef.current?.statusUpdate({ action: "checkpoint", id: cp.id });
|
|
5345
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4CD} Checkpoint #${cp.id} created${description ? `: ${description}` : ""}`);
|
|
5346
|
+
} else {
|
|
5347
|
+
addMessage(MESSAGE_TYPE.ERROR, "Context manager not initialized");
|
|
5348
|
+
}
|
|
5349
|
+
return;
|
|
5350
|
+
case "compact":
|
|
5351
|
+
if (contextManagerRef.current) {
|
|
5352
|
+
const summary = "Previous conversation summarized for context efficiency.";
|
|
5353
|
+
await contextManagerRef.current.compact(summary, 3);
|
|
5354
|
+
wireLoggerRef.current?.statusUpdate({ action: "compact" });
|
|
5355
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "\u{1F5DC}\uFE0F Context compacted (keeping last 3 messages)");
|
|
5356
|
+
} else {
|
|
5357
|
+
addMessage(MESSAGE_TYPE.ERROR, "Context manager not initialized");
|
|
5358
|
+
}
|
|
5359
|
+
return;
|
|
5360
|
+
case "paste":
|
|
5361
|
+
case "p":
|
|
5362
|
+
try {
|
|
5363
|
+
if (hasClipboardImage()) {
|
|
5364
|
+
const img = readClipboardImage();
|
|
5365
|
+
if (img) {
|
|
5366
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4F7} Image from clipboard: ${img.path}`);
|
|
5367
|
+
addMessage(MESSAGE_TYPE.SYSTEM, ` Size: ${Math.round(img.base64.length / 1024)}KB base64`);
|
|
5368
|
+
}
|
|
5369
|
+
} else {
|
|
5370
|
+
const text = readClipboardText();
|
|
5371
|
+
if (text) {
|
|
5372
|
+
const preview = text.length > 100 ? text.slice(0, 100) + "..." : text;
|
|
5373
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4CB} Clipboard: ${preview}`);
|
|
5374
|
+
setInput(text);
|
|
5375
|
+
} else {
|
|
5376
|
+
addMessage(MESSAGE_TYPE.ERROR, "Clipboard is empty");
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5379
|
+
} catch (e) {
|
|
5380
|
+
addMessage(MESSAGE_TYPE.ERROR, `Clipboard error: ${e instanceof Error ? e.message : String(e)}`);
|
|
5381
|
+
}
|
|
5382
|
+
return;
|
|
5383
|
+
case "revert":
|
|
5384
|
+
if (contextManagerRef.current && args[0]) {
|
|
5385
|
+
const cpId = parseInt(args[0], 10);
|
|
5386
|
+
if (isNaN(cpId)) {
|
|
5387
|
+
addMessage(MESSAGE_TYPE.ERROR, "Usage: /revert <checkpoint_id>");
|
|
5388
|
+
return;
|
|
5389
|
+
}
|
|
5390
|
+
const success = await contextManagerRef.current.revertTo(cpId);
|
|
5391
|
+
if (success) {
|
|
5392
|
+
wireLoggerRef.current?.statusUpdate({ action: "revert", checkpointId: cpId });
|
|
5393
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u21A9\uFE0F Reverted to checkpoint #${cpId}`);
|
|
5394
|
+
} else {
|
|
5395
|
+
addMessage(MESSAGE_TYPE.ERROR, `Checkpoint #${cpId} not found`);
|
|
5396
|
+
}
|
|
5397
|
+
} else {
|
|
5398
|
+
addMessage(MESSAGE_TYPE.ERROR, "Usage: /revert <checkpoint_id>");
|
|
5399
|
+
}
|
|
5400
|
+
return;
|
|
5401
|
+
case "checkpoints":
|
|
5402
|
+
case "cps":
|
|
5403
|
+
if (contextManagerRef.current) {
|
|
5404
|
+
const cps = contextManagerRef.current.getCheckpoints();
|
|
5405
|
+
if (cps.length === 0) {
|
|
5406
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "No checkpoints");
|
|
5407
|
+
} else {
|
|
5408
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4CD} ${cps.length} Checkpoints:`);
|
|
5409
|
+
cps.forEach((cp) => {
|
|
5410
|
+
const time = new Date(cp.timestamp).toLocaleTimeString();
|
|
5411
|
+
addMessage(MESSAGE_TYPE.SYSTEM, ` #${cp.id} @ ${time} (${cp.messageCount} msgs)${cp.description ? ` - ${cp.description}` : ""}`);
|
|
5412
|
+
});
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
return;
|
|
5416
|
+
case "status":
|
|
5417
|
+
const state2 = agent.getState();
|
|
5418
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4CA} Status Report:
|
|
5419
|
+
Phase: ${state2.currentPhase}
|
|
5420
|
+
Iteration: ${state2.iteration}
|
|
5421
|
+
Findings: ${state2.findings.length}
|
|
5422
|
+
Compromised: ${state2.compromisedHosts.length}
|
|
5423
|
+
Tokens: ${tokenUsage.total.toLocaleString()}
|
|
5424
|
+
Checkpoints: ${checkpointCount}`);
|
|
5425
|
+
return;
|
|
5426
|
+
case "think":
|
|
5427
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "\u{1F9E0} Thinking mode: Extended reasoning enabled");
|
|
5428
|
+
return;
|
|
4631
5429
|
default:
|
|
5430
|
+
const slashRegistry = getSlashCommandRegistry();
|
|
5431
|
+
const slashCmd = slashRegistry.find(cmd);
|
|
5432
|
+
if (slashCmd) {
|
|
5433
|
+
try {
|
|
5434
|
+
const result = await slashCmd.handler(args.join(" "));
|
|
5435
|
+
addMessage(MESSAGE_TYPE.SYSTEM, result || `\u2713 /${cmd} executed`);
|
|
5436
|
+
} catch (e) {
|
|
5437
|
+
addMessage(MESSAGE_TYPE.ERROR, e instanceof Error ? e.message : String(e));
|
|
5438
|
+
}
|
|
5439
|
+
return;
|
|
5440
|
+
}
|
|
4632
5441
|
const cmdResult = await agent.processCommand(trimmed);
|
|
4633
5442
|
if (cmdResult) {
|
|
4634
5443
|
addMessage(MESSAGE_TYPE.ASSISTANT, cmdResult);
|
|
@@ -4643,8 +5452,8 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4643
5452
|
startTimer();
|
|
4644
5453
|
setCurrentStatus(`Running: ${trimmed}`);
|
|
4645
5454
|
try {
|
|
4646
|
-
const { execSync } = await import("child_process");
|
|
4647
|
-
const output =
|
|
5455
|
+
const { execSync: execSync2 } = await import("child_process");
|
|
5456
|
+
const output = execSync2(trimmed, {
|
|
4648
5457
|
encoding: "utf-8",
|
|
4649
5458
|
timeout: 3e4,
|
|
4650
5459
|
maxBuffer: 1024 * 1024
|
|
@@ -4728,48 +5537,51 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4728
5537
|
return styles[type] || styles[MESSAGE_TYPE.SYSTEM];
|
|
4729
5538
|
};
|
|
4730
5539
|
const state = agent.getState();
|
|
4731
|
-
return /* @__PURE__ */
|
|
4732
|
-
/* @__PURE__ */
|
|
5540
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
5541
|
+
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx2(Static, { items: messages.slice(-40), children: (msg) => {
|
|
4733
5542
|
const style = getStyle(msg.type);
|
|
4734
|
-
|
|
5543
|
+
if (msg.finding) {
|
|
5544
|
+
return /* @__PURE__ */ jsx2(Box2, { marginY: 0, children: /* @__PURE__ */ jsx2(FindingDisplay, { block: msg.finding }) }, msg.id);
|
|
5545
|
+
}
|
|
5546
|
+
return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: style.color, dimColor: style.dim, children: [
|
|
4735
5547
|
style.prefix,
|
|
4736
5548
|
" ",
|
|
4737
5549
|
msg.content,
|
|
4738
|
-
msg.duration && /* @__PURE__ */
|
|
5550
|
+
msg.duration && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
4739
5551
|
" (",
|
|
4740
5552
|
msg.duration,
|
|
4741
5553
|
"s)"
|
|
4742
5554
|
] })
|
|
4743
5555
|
] }) }, msg.id);
|
|
4744
5556
|
} }) }),
|
|
4745
|
-
pendingApproval && /* @__PURE__ */
|
|
4746
|
-
/* @__PURE__ */
|
|
5557
|
+
pendingApproval && /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginBottom: 1, children: [
|
|
5558
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "yellow", bold: true, children: [
|
|
4747
5559
|
"\u26A0\uFE0F APPROVAL NEEDED: ",
|
|
4748
5560
|
pendingApproval.toolName,
|
|
4749
5561
|
" (",
|
|
4750
5562
|
pendingApproval.riskLevel,
|
|
4751
5563
|
" risk)"
|
|
4752
5564
|
] }),
|
|
4753
|
-
/* @__PURE__ */
|
|
4754
|
-
/* @__PURE__ */
|
|
5565
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: Object.entries(pendingApproval.toolInput).slice(0, 2).map(([k, v]) => `${k}=${typeof v === "string" ? v.slice(0, 50) : JSON.stringify(v).slice(0, 50)}`).join(", ") }),
|
|
5566
|
+
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: approvalOptions.map((opt, idx) => /* @__PURE__ */ jsxs2(Text2, { color: idx === approvalSelectedIndex ? "cyan" : "gray", children: [
|
|
4755
5567
|
idx === approvalSelectedIndex ? "\u2192 " : " ",
|
|
4756
5568
|
opt.label
|
|
4757
5569
|
] }, opt.decision)) }),
|
|
4758
|
-
/* @__PURE__ */
|
|
5570
|
+
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2191\u2193 to select, Enter to confirm, or type /y /n /ya" }) })
|
|
4759
5571
|
] }),
|
|
4760
|
-
isProcessing ? /* @__PURE__ */
|
|
4761
|
-
/* @__PURE__ */
|
|
4762
|
-
/* @__PURE__ */
|
|
5572
|
+
isProcessing ? /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
5573
|
+
/* @__PURE__ */ jsx2(Text2, { color: THEME.status.running, children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
|
|
5574
|
+
/* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
|
|
4763
5575
|
" ",
|
|
4764
5576
|
currentStatus,
|
|
4765
|
-
elapsedTime > 0 && /* @__PURE__ */
|
|
5577
|
+
elapsedTime > 0 && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
4766
5578
|
" (",
|
|
4767
5579
|
elapsedTime,
|
|
4768
5580
|
"s)"
|
|
4769
5581
|
] })
|
|
4770
5582
|
] })
|
|
4771
|
-
] }) : /* @__PURE__ */
|
|
4772
|
-
showCommandHints && input.startsWith("/") && /* @__PURE__ */
|
|
5583
|
+
] }) : /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
5584
|
+
showCommandHints && input.startsWith("/") && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: [
|
|
4773
5585
|
"/target <ip>",
|
|
4774
5586
|
"/start",
|
|
4775
5587
|
"/stop",
|
|
@@ -4780,9 +5592,9 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4780
5592
|
"/exit",
|
|
4781
5593
|
pendingApproval ? "/y /n /ya" : ""
|
|
4782
5594
|
].filter((cmd) => cmd && cmd.toLowerCase().includes(input.toLowerCase().slice(1))).slice(0, 5).join(" \u2502 ") }) }),
|
|
4783
|
-
/* @__PURE__ */
|
|
4784
|
-
/* @__PURE__ */
|
|
4785
|
-
/* @__PURE__ */
|
|
5595
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
5596
|
+
/* @__PURE__ */ jsx2(Text2, { color: mode === "agent" ? THEME.status.success : "yellow", children: mode === "agent" ? "\u2728 " : "$ " }),
|
|
5597
|
+
/* @__PURE__ */ jsx2(
|
|
4786
5598
|
TextInput,
|
|
4787
5599
|
{
|
|
4788
5600
|
value: input,
|
|
@@ -4796,8 +5608,8 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4796
5608
|
)
|
|
4797
5609
|
] })
|
|
4798
5610
|
] }),
|
|
4799
|
-
/* @__PURE__ */
|
|
4800
|
-
/* @__PURE__ */
|
|
5611
|
+
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, justifyContent: "space-between", children: [
|
|
5612
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
4801
5613
|
mode === "agent" ? "\u{1F916}" : "$",
|
|
4802
5614
|
" ",
|
|
4803
5615
|
state.target.primary || "No target",
|
|
@@ -4809,7 +5621,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
4809
5621
|
tokenUsage.total > 0 && `${(tokenUsage.total / 1e3).toFixed(1)}k tokens \u2502`,
|
|
4810
5622
|
state.currentPhase !== AGENT_STATUS.IDLE && ` ${state.currentPhase} \u2502`
|
|
4811
5623
|
] }),
|
|
4812
|
-
/* @__PURE__ */
|
|
5624
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
4813
5625
|
"Ctrl+X mode \u2502 /help \u2502 Ctrl+C ",
|
|
4814
5626
|
isProcessing ? "stop" : "exit"
|
|
4815
5627
|
] })
|
|
@@ -4820,7 +5632,7 @@ var app_default = App;
|
|
|
4820
5632
|
|
|
4821
5633
|
// src/index.tsx
|
|
4822
5634
|
import chalk from "chalk";
|
|
4823
|
-
import { jsx as
|
|
5635
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
4824
5636
|
var program = new Command();
|
|
4825
5637
|
program.name("pentesting").version(APP_VERSION).description(APP_DESCRIPTION).option("--dangerously-skip-permissions", "Skip all permission prompts (dangerous!)").option("-t, --target <target>", "Set initial target");
|
|
4826
5638
|
program.command("interactive", { isDefault: true }).alias("i").description("Start interactive TUI mode").action(() => {
|
|
@@ -4834,7 +5646,7 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
|
|
|
4834
5646
|
}
|
|
4835
5647
|
console.log(chalk.hex(THEME.text.accent)("Starting Pentest interactive mode...\n"));
|
|
4836
5648
|
const { waitUntilExit } = render(
|
|
4837
|
-
/* @__PURE__ */
|
|
5649
|
+
/* @__PURE__ */ jsx3(
|
|
4838
5650
|
app_default,
|
|
4839
5651
|
{
|
|
4840
5652
|
autoApprove: skipPermissions,
|