pentesting 0.16.3 → 0.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +1459 -1479
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -7,10 +7,11 @@ import chalk from "chalk";
|
|
|
7
7
|
import gradient from "gradient-string";
|
|
8
8
|
|
|
9
9
|
// src/platform/tui/app.tsx
|
|
10
|
+
import { useState as useState2, useCallback as useCallback2, useEffect as useEffect2 } from "react";
|
|
11
|
+
import { Box as Box5, useInput, useApp } from "ink";
|
|
12
|
+
|
|
13
|
+
// src/platform/tui/hooks/useAgent.ts
|
|
10
14
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
11
|
-
import { Box as Box2, Text as Text2, useInput, useApp, Static } from "ink";
|
|
12
|
-
import TextInput from "ink-text-input";
|
|
13
|
-
import Spinner from "ink-spinner";
|
|
14
15
|
|
|
15
16
|
// src/shared/constants/agent.ts
|
|
16
17
|
var AGENT_LIMITS = {
|
|
@@ -38,8 +39,22 @@ var DISPLAY_LIMITS = {
|
|
|
38
39
|
var ID_LENGTH = 7;
|
|
39
40
|
var ID_RADIX = 36;
|
|
40
41
|
var APP_NAME = "Pentest AI";
|
|
41
|
-
var APP_VERSION = "0.
|
|
42
|
+
var APP_VERSION = "0.16.3";
|
|
42
43
|
var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
|
|
44
|
+
var UI_ROLES = {
|
|
45
|
+
SYSTEM: "system",
|
|
46
|
+
USER: "user",
|
|
47
|
+
AI: "ai",
|
|
48
|
+
ERROR: "error"
|
|
49
|
+
};
|
|
50
|
+
var LLM_ROLES = {
|
|
51
|
+
SYSTEM: "system",
|
|
52
|
+
USER: "user",
|
|
53
|
+
ASSISTANT: "assistant"
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/shared/utils/id.ts
|
|
57
|
+
var generateId = (radix = 36, length = 8) => Math.random().toString(radix).substring(2, 2 + length);
|
|
43
58
|
|
|
44
59
|
// src/shared/constants/protocol.ts
|
|
45
60
|
var PHASES = {
|
|
@@ -59,7 +74,9 @@ var AGENT_ROLES = {
|
|
|
59
74
|
WEB: "web",
|
|
60
75
|
EXPLOTER: "exploit",
|
|
61
76
|
DATABASE: "database",
|
|
62
|
-
INFRA: "infra"
|
|
77
|
+
INFRA: "infra",
|
|
78
|
+
VULN: "vulnerability_analysis",
|
|
79
|
+
POST_EXPLOIT: "post_exploitation"
|
|
63
80
|
};
|
|
64
81
|
var SERVICES = {
|
|
65
82
|
HTTP: "http",
|
|
@@ -166,11 +183,6 @@ var TODO_STATUSES = {
|
|
|
166
183
|
DONE: "done",
|
|
167
184
|
SKIPPED: "skipped"
|
|
168
185
|
};
|
|
169
|
-
var TODO_ACTIONS = {
|
|
170
|
-
ADD: "add",
|
|
171
|
-
COMPLETE: "complete",
|
|
172
|
-
REMOVE: "remove"
|
|
173
|
-
};
|
|
174
186
|
var CORE_BINARIES = {
|
|
175
187
|
NMAP: "nmap",
|
|
176
188
|
MASSCAN: "masscan",
|
|
@@ -181,6 +193,12 @@ var CORE_BINARIES = {
|
|
|
181
193
|
SQLMAP: "sqlmap",
|
|
182
194
|
METASPLOIT: "msfconsole"
|
|
183
195
|
};
|
|
196
|
+
var APPROVAL_STATUSES = {
|
|
197
|
+
AUTO: "auto",
|
|
198
|
+
USER_CONFIRMED: "user_confirmed",
|
|
199
|
+
USER_REVIEWED: "user_reviewed",
|
|
200
|
+
DENIED: "denied"
|
|
201
|
+
};
|
|
184
202
|
var PRIORITIES = {
|
|
185
203
|
HIGH: "high",
|
|
186
204
|
MEDIUM: "medium",
|
|
@@ -191,6 +209,116 @@ var NOISE_LEVELS = {
|
|
|
191
209
|
MEDIUM: "medium",
|
|
192
210
|
HIGH: "high"
|
|
193
211
|
};
|
|
212
|
+
var EVENT_TYPES = {
|
|
213
|
+
START: "start",
|
|
214
|
+
PHASE_CHANGE: "phase_change",
|
|
215
|
+
STATE_CHANGE: "state_change",
|
|
216
|
+
REASONING_START: "reasoning_start",
|
|
217
|
+
REASONING_DELTA: "reasoning_delta",
|
|
218
|
+
REASONING_END: "reasoning_end",
|
|
219
|
+
TOOL_CALL: "tool_call",
|
|
220
|
+
TOOL_RESULT: "tool_result",
|
|
221
|
+
ERROR: "error",
|
|
222
|
+
COMPLETE: "complete",
|
|
223
|
+
THINK: "think",
|
|
224
|
+
APPROVAL_ASK: "approval_ask",
|
|
225
|
+
APPROVAL_RESULT: "approval_result",
|
|
226
|
+
DELEGATE: "delegate",
|
|
227
|
+
DELEGATE_RESULT: "delegate_result",
|
|
228
|
+
ESCALATE: "escalate",
|
|
229
|
+
AGENT_SWITCH: "agent_switch",
|
|
230
|
+
OBSERVE: "observe",
|
|
231
|
+
RETRY: "retry",
|
|
232
|
+
USAGE_UPDATE: "usage_update",
|
|
233
|
+
INPUT_REQUEST: "input_request",
|
|
234
|
+
LOG: "log"
|
|
235
|
+
};
|
|
236
|
+
var UI_COMMANDS = {
|
|
237
|
+
HELP: "help",
|
|
238
|
+
HELP_SHORT: "h",
|
|
239
|
+
CLEAR: "clear",
|
|
240
|
+
CLEAR_SHORT: "c",
|
|
241
|
+
TARGET: "target",
|
|
242
|
+
TARGET_SHORT: "t",
|
|
243
|
+
START: "start",
|
|
244
|
+
START_SHORT: "s",
|
|
245
|
+
FINDINGS: "findings",
|
|
246
|
+
FINDINGS_SHORT: "f",
|
|
247
|
+
EXIT: "exit",
|
|
248
|
+
QUIT: "quit",
|
|
249
|
+
EXIT_SHORT: "q"
|
|
250
|
+
};
|
|
251
|
+
var PASSIVE_BINARIES = ["whois", "dig", "curl -i"];
|
|
252
|
+
var EXPLOIT_BINARIES = ["impacket"];
|
|
253
|
+
var MASK_PARTS = {
|
|
254
|
+
C24: "24",
|
|
255
|
+
C16: "16",
|
|
256
|
+
C8: "8"
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// src/engine/state-serializer.ts
|
|
260
|
+
var StateSerializer = class {
|
|
261
|
+
/**
|
|
262
|
+
* Convert full state to a compact prompt summary
|
|
263
|
+
*/
|
|
264
|
+
static toPrompt(state) {
|
|
265
|
+
const lines = [];
|
|
266
|
+
const data = state.data;
|
|
267
|
+
if (data.engagement) {
|
|
268
|
+
lines.push(`Engagement: ${data.engagement.name} (${data.engagement.client})`);
|
|
269
|
+
}
|
|
270
|
+
const scope = state.getScope();
|
|
271
|
+
if (scope) {
|
|
272
|
+
lines.push(`Scope: CIDR=[${scope.allowedCidrs.join(", ")}] Domains=[${scope.allowedDomains.join(", ")}]`);
|
|
273
|
+
}
|
|
274
|
+
const targets = state.getAllTargets();
|
|
275
|
+
if (targets.length > 0) {
|
|
276
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
277
|
+
for (const t of targets) {
|
|
278
|
+
const cat = t.primaryCategory || SERVICE_CATEGORIES.NETWORK;
|
|
279
|
+
if (!byCategory.has(cat)) byCategory.set(cat, []);
|
|
280
|
+
byCategory.get(cat).push(t);
|
|
281
|
+
}
|
|
282
|
+
lines.push(`Targets (${targets.length}):`);
|
|
283
|
+
for (const [cat, catTargets] of byCategory) {
|
|
284
|
+
lines.push(` [${cat}] ${catTargets.length} hosts`);
|
|
285
|
+
for (const t of catTargets.slice(0, 3)) {
|
|
286
|
+
const ports = (t.ports || []).map((p) => `${p.port}/${p.service}`).join(", ");
|
|
287
|
+
lines.push(` ${t.ip}${t.hostname ? ` (${t.hostname})` : ""}: ${ports || "unknown"}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const findings = state.getFindings();
|
|
292
|
+
if (findings.length > 0) {
|
|
293
|
+
const counts = findings.reduce((acc, f) => {
|
|
294
|
+
acc[f.severity] = (acc[f.severity] || 0) + 1;
|
|
295
|
+
return acc;
|
|
296
|
+
}, { critical: 0, high: 0, medium: 0, low: 0 });
|
|
297
|
+
lines.push(`Findings: ${findings.length} total (crit:${counts.critical} high:${counts.high} med:${counts.medium})`);
|
|
298
|
+
const important = findings.filter((f) => f.severity === "critical" || f.severity === "high");
|
|
299
|
+
if (important.length > 0) {
|
|
300
|
+
lines.push(` Important Findings:`);
|
|
301
|
+
for (const f of important.slice(0, 5)) {
|
|
302
|
+
lines.push(` [${f.severity.toUpperCase()}] ${f.title} (${f.category || "general"})`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const loot = state.getLoot();
|
|
307
|
+
if (loot.length > 0) {
|
|
308
|
+
lines.push(`Loot: ${loot.length} items`);
|
|
309
|
+
}
|
|
310
|
+
const todo = state.getTodo();
|
|
311
|
+
if (todo.length > 0) {
|
|
312
|
+
lines.push(`TODO (${todo.length}):`);
|
|
313
|
+
for (const t of todo.slice(0, DISPLAY_LIMITS.COMPACT_LIST_ITEMS)) {
|
|
314
|
+
const status = t.status === "done" ? "[x]" : t.status === "in_progress" ? "[->]" : "[ ]";
|
|
315
|
+
lines.push(` ${status} ${t.content} (${t.priority})`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
lines.push(`Phase: ${state.getPhase()}`);
|
|
319
|
+
return lines.join("\n");
|
|
320
|
+
}
|
|
321
|
+
};
|
|
194
322
|
|
|
195
323
|
// src/engine/state.ts
|
|
196
324
|
var SharedState = class {
|
|
@@ -206,14 +334,7 @@ var SharedState = class {
|
|
|
206
334
|
currentPhase: PHASES.RECON
|
|
207
335
|
};
|
|
208
336
|
}
|
|
209
|
-
// Engagement
|
|
210
|
-
setEngagement(engagement) {
|
|
211
|
-
this.data.engagement = engagement;
|
|
212
|
-
}
|
|
213
|
-
getEngagement() {
|
|
214
|
-
return this.data.engagement;
|
|
215
|
-
}
|
|
216
|
-
// Scope
|
|
337
|
+
// --- Engagement & Scope ---
|
|
217
338
|
getScope() {
|
|
218
339
|
return this.data.engagement?.scope || null;
|
|
219
340
|
}
|
|
@@ -222,16 +343,17 @@ var SharedState = class {
|
|
|
222
343
|
this.data.engagement.scope = scope;
|
|
223
344
|
} else {
|
|
224
345
|
this.data.engagement = {
|
|
225
|
-
id:
|
|
226
|
-
name: "auto-
|
|
227
|
-
|
|
346
|
+
id: generateId(ID_RADIX, ID_LENGTH),
|
|
347
|
+
name: "auto-assessment",
|
|
348
|
+
// Could be added to constants if needed
|
|
349
|
+
client: "internal",
|
|
228
350
|
scope,
|
|
229
351
|
phase: this.data.currentPhase,
|
|
230
352
|
startedAt: Date.now()
|
|
231
353
|
};
|
|
232
354
|
}
|
|
233
355
|
}
|
|
234
|
-
// Targets
|
|
356
|
+
// --- Targets ---
|
|
235
357
|
addTarget(target) {
|
|
236
358
|
this.data.targets.set(target.ip, target);
|
|
237
359
|
}
|
|
@@ -244,142 +366,49 @@ var SharedState = class {
|
|
|
244
366
|
getTargets() {
|
|
245
367
|
return this.data.targets;
|
|
246
368
|
}
|
|
247
|
-
/**
|
|
248
|
-
* Update target with service fingerprint
|
|
249
|
-
*/
|
|
250
369
|
addServiceFingerprint(ip, fingerprint) {
|
|
251
370
|
const target = this.getTarget(ip);
|
|
252
|
-
if (target)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Get most common service category for target
|
|
262
|
-
*/
|
|
263
|
-
getMostCommonCategory(services) {
|
|
264
|
-
const counts = /* @__PURE__ */ new Map();
|
|
265
|
-
for (const s of services) {
|
|
266
|
-
counts.set(s.category, (counts.get(s.category) || 0) + 1);
|
|
267
|
-
}
|
|
371
|
+
if (!target) return;
|
|
372
|
+
target.services = target.services || [];
|
|
373
|
+
target.services.push(fingerprint);
|
|
374
|
+
target.primaryCategory = this.calculatePrimaryCategory(target.services);
|
|
375
|
+
}
|
|
376
|
+
calculatePrimaryCategory(services) {
|
|
377
|
+
const counts = {};
|
|
378
|
+
let winner = SERVICE_CATEGORIES.NETWORK;
|
|
268
379
|
let max = 0;
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (
|
|
272
|
-
max =
|
|
273
|
-
|
|
380
|
+
for (const s of services) {
|
|
381
|
+
counts[s.category] = (counts[s.category] || 0) + 1;
|
|
382
|
+
if (counts[s.category] > max) {
|
|
383
|
+
max = counts[s.category];
|
|
384
|
+
winner = s.category;
|
|
274
385
|
}
|
|
275
386
|
}
|
|
276
|
-
return
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Get targets by category
|
|
280
|
-
*/
|
|
281
|
-
getTargetsByCategory(category) {
|
|
282
|
-
return this.getAllTargets().filter(
|
|
283
|
-
(t) => t.primaryCategory === category || t.services?.some((s) => s.category === category)
|
|
284
|
-
);
|
|
387
|
+
return winner;
|
|
285
388
|
}
|
|
286
|
-
|
|
287
|
-
* Get high-risk targets
|
|
288
|
-
*/
|
|
289
|
-
getHighRiskTargets() {
|
|
290
|
-
const highRisk = [SERVICE_CATEGORIES.ICS, SERVICE_CATEGORIES.AD, SERVICE_CATEGORIES.DATABASE, SERVICE_CATEGORIES.CLOUD, SERVICE_CATEGORIES.CONTAINER];
|
|
291
|
-
return this.getAllTargets().filter(
|
|
292
|
-
(t) => highRisk.includes(t.primaryCategory || SERVICE_CATEGORIES.NETWORK)
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
// Findings
|
|
389
|
+
// --- Findings & Loot ---
|
|
296
390
|
addFinding(finding) {
|
|
297
391
|
this.data.findings.push(finding);
|
|
298
392
|
}
|
|
299
393
|
getFindings() {
|
|
300
394
|
return this.data.findings;
|
|
301
395
|
}
|
|
302
|
-
getFindingsBySeverity(severity) {
|
|
303
|
-
return this.data.findings.filter((f) => f.severity === severity);
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Get findings by category
|
|
307
|
-
*/
|
|
308
|
-
getFindingsByCategory(category) {
|
|
309
|
-
return this.data.findings.filter((f) => f.category === category);
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Get findings by attack tactic
|
|
313
|
-
*/
|
|
314
|
-
getFindingsByTactic(tactic) {
|
|
315
|
-
return this.data.findings.filter((f) => f.attackPattern === tactic);
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Get verified findings only
|
|
319
|
-
*/
|
|
320
|
-
getVerifiedFindings() {
|
|
321
|
-
return this.data.findings.filter((f) => f.verified);
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Get findings with available exploits
|
|
325
|
-
*/
|
|
326
|
-
getExploitableFindings() {
|
|
327
|
-
return this.data.findings.filter((f) => f.exploitAvailable === true);
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Get chainable findings (attack chains)
|
|
331
|
-
*/
|
|
332
|
-
getChainableFindings() {
|
|
333
|
-
const chains = /* @__PURE__ */ new Map();
|
|
334
|
-
for (const f of this.data.findings) {
|
|
335
|
-
if (f.chainable && f.chainable.length > 0) {
|
|
336
|
-
chains.set(f.id, [f, ...this.data.findings.filter((o) => f.chainable?.includes(o.id))]);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
return chains;
|
|
340
|
-
}
|
|
341
|
-
// Loot
|
|
342
396
|
addLoot(loot) {
|
|
343
397
|
this.data.loot.push(loot);
|
|
344
398
|
}
|
|
345
399
|
getLoot() {
|
|
346
400
|
return this.data.loot;
|
|
347
401
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Get loot by type
|
|
356
|
-
*/
|
|
357
|
-
getLootByType(type) {
|
|
358
|
-
return this.data.loot.filter((l) => l.type === type);
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Get crackable loot (hashes, tickets)
|
|
362
|
-
*/
|
|
363
|
-
getCrackableLoot() {
|
|
364
|
-
return this.data.loot.filter((l) => l.crackable === true && !l.cracked);
|
|
365
|
-
}
|
|
366
|
-
// TODO List Management
|
|
367
|
-
addTodo(content, priority = "medium") {
|
|
368
|
-
const id = Math.random().toString(ID_RADIX).substring(ID_LENGTH);
|
|
369
|
-
const todo = {
|
|
370
|
-
id,
|
|
371
|
-
content,
|
|
372
|
-
status: TODO_STATUSES.PENDING,
|
|
373
|
-
priority
|
|
374
|
-
};
|
|
375
|
-
this.data.todo.push(todo);
|
|
402
|
+
// --- TODO Management ---
|
|
403
|
+
addTodo(content, priority = PRIORITIES.MEDIUM) {
|
|
404
|
+
const id = generateId(ID_RADIX, ID_LENGTH);
|
|
405
|
+
const item = { id, content, status: TODO_STATUSES.PENDING, priority };
|
|
406
|
+
this.data.todo.push(item);
|
|
376
407
|
return id;
|
|
377
408
|
}
|
|
378
409
|
updateTodo(id, updates) {
|
|
379
|
-
const
|
|
380
|
-
if (
|
|
381
|
-
this.data.todo[index] = { ...this.data.todo[index], ...updates };
|
|
382
|
-
}
|
|
410
|
+
const item = this.data.todo.find((t) => t.id === id);
|
|
411
|
+
if (item) Object.assign(item, updates);
|
|
383
412
|
}
|
|
384
413
|
getTodo() {
|
|
385
414
|
return this.data.todo;
|
|
@@ -387,90 +416,29 @@ var SharedState = class {
|
|
|
387
416
|
completeTodo(id) {
|
|
388
417
|
this.updateTodo(id, { status: TODO_STATUSES.DONE });
|
|
389
418
|
}
|
|
390
|
-
//
|
|
419
|
+
// --- Logs & Phase ---
|
|
391
420
|
logAction(action) {
|
|
392
|
-
|
|
393
|
-
id:
|
|
421
|
+
this.data.actionLog.push({
|
|
422
|
+
id: generateId(ID_RADIX, ID_LENGTH),
|
|
394
423
|
timestamp: Date.now(),
|
|
395
424
|
...action
|
|
396
|
-
};
|
|
397
|
-
this.data.actionLog.push(log);
|
|
425
|
+
});
|
|
398
426
|
}
|
|
399
427
|
getRecentActions(count = DISPLAY_LIMITS.COMPACT_LIST_ITEMS) {
|
|
400
428
|
return this.data.actionLog.slice(-count);
|
|
401
429
|
}
|
|
402
|
-
getActionLog() {
|
|
403
|
-
return this.data.actionLog;
|
|
404
|
-
}
|
|
405
|
-
// Phase
|
|
406
430
|
setPhase(phase) {
|
|
407
431
|
this.data.currentPhase = phase;
|
|
408
432
|
}
|
|
409
433
|
getPhase() {
|
|
410
434
|
return this.data.currentPhase;
|
|
411
435
|
}
|
|
412
|
-
|
|
436
|
+
/**
|
|
437
|
+
* @remarks
|
|
438
|
+
* WHY: Delegating complex string builder to specialized serializer.
|
|
439
|
+
*/
|
|
413
440
|
toPrompt() {
|
|
414
|
-
|
|
415
|
-
if (this.data.engagement) {
|
|
416
|
-
lines.push(`Engagement: ${this.data.engagement.name} (${this.data.engagement.client})`);
|
|
417
|
-
}
|
|
418
|
-
const scope = this.getScope();
|
|
419
|
-
if (scope) {
|
|
420
|
-
lines.push(`Scope: CIDR=[${scope.allowedCidrs.join(", ")}] Domains=[${scope.allowedDomains.join(", ")}]`);
|
|
421
|
-
}
|
|
422
|
-
const targets = this.getAllTargets();
|
|
423
|
-
if (targets.length > 0) {
|
|
424
|
-
const byCategory = /* @__PURE__ */ new Map();
|
|
425
|
-
for (const t of targets) {
|
|
426
|
-
const cat = t.primaryCategory || SERVICE_CATEGORIES.NETWORK;
|
|
427
|
-
if (!byCategory.has(cat)) {
|
|
428
|
-
byCategory.set(cat, []);
|
|
429
|
-
}
|
|
430
|
-
byCategory.get(cat).push(t);
|
|
431
|
-
}
|
|
432
|
-
lines.push(`Targets (${targets.length}):`);
|
|
433
|
-
for (const [cat, catTargets] of byCategory) {
|
|
434
|
-
lines.push(` [${cat}] ${catTargets.length} hosts`);
|
|
435
|
-
for (const t of catTargets.slice(0, 3)) {
|
|
436
|
-
const ports = t.ports.map((p) => `${p.port}/${p.service}`).join(", ");
|
|
437
|
-
lines.push(` ${t.ip}${t.hostname ? ` (${t.hostname})` : ""}: ${ports || "unknown"}`);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
const findings = this.getFindings();
|
|
442
|
-
if (findings.length > 0) {
|
|
443
|
-
const bySeverity = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
444
|
-
for (const f of findings) {
|
|
445
|
-
bySeverity[f.severity]++;
|
|
446
|
-
}
|
|
447
|
-
lines.push(`Findings: ${findings.length} total (crit:${bySeverity.critical} high:${bySeverity.high} medium:${bySeverity.medium})`);
|
|
448
|
-
const criticalHigh = findings.filter((f) => f.severity === "critical" || f.severity === "high");
|
|
449
|
-
if (criticalHigh.length > 0) {
|
|
450
|
-
lines.push(` Critical/High:`);
|
|
451
|
-
for (const f of criticalHigh.slice(0, 5)) {
|
|
452
|
-
lines.push(` [${f.severity.toUpperCase()}] ${f.title} (${f.category || "general"})`);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
const loot = this.getLoot();
|
|
457
|
-
if (loot.length > 0) {
|
|
458
|
-
const byType = /* @__PURE__ */ new Map();
|
|
459
|
-
for (const l of loot) {
|
|
460
|
-
byType.set(l.type, (byType.get(l.type) || 0) + 1);
|
|
461
|
-
}
|
|
462
|
-
lines.push(`Loot: ${loot.length} items (${Array.from(byType.entries()).map(([t, c]) => `${t}:${c}`).join(", ")})`);
|
|
463
|
-
}
|
|
464
|
-
const todo = this.getTodo();
|
|
465
|
-
if (todo.length > 0) {
|
|
466
|
-
lines.push(`TODO (${todo.length}):`);
|
|
467
|
-
for (const t of todo.slice(0, DISPLAY_LIMITS.COMPACT_LIST_ITEMS)) {
|
|
468
|
-
const status = t.status === "done" ? "[x]" : t.status === "in_progress" ? "[->]" : "[ ]";
|
|
469
|
-
lines.push(` ${status} ${t.content} (${t.priority})`);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
lines.push(`Phase: ${this.getPhase()}`);
|
|
473
|
-
return lines.join("\n");
|
|
441
|
+
return StateSerializer.toPrompt(this);
|
|
474
442
|
}
|
|
475
443
|
};
|
|
476
444
|
|
|
@@ -478,7 +446,11 @@ var SharedState = class {
|
|
|
478
446
|
var AgentEventEmitter = class {
|
|
479
447
|
listeners = /* @__PURE__ */ new Map();
|
|
480
448
|
/**
|
|
481
|
-
* Subscribe to
|
|
449
|
+
* Subscribe to a specific event type with full type safety.
|
|
450
|
+
*
|
|
451
|
+
* @remarks
|
|
452
|
+
* WHY: Generic overload narrows the event type so subscribers get
|
|
453
|
+
* autocomplete on `event.data.*` without manual casting.
|
|
482
454
|
*/
|
|
483
455
|
on(eventType, listener) {
|
|
484
456
|
if (!this.listeners.has(eventType)) {
|
|
@@ -612,17 +584,17 @@ var ScopeGuard = class {
|
|
|
612
584
|
if (target === cidr) return true;
|
|
613
585
|
if (cidr.includes("/")) {
|
|
614
586
|
const [network, mask] = cidr.split("/");
|
|
615
|
-
if (mask ===
|
|
587
|
+
if (mask === MASK_PARTS.C24) {
|
|
616
588
|
const networkPrefix = network.split(".").slice(0, 3).join(".");
|
|
617
589
|
const targetPrefix = target.split(".").slice(0, 3).join(".");
|
|
618
590
|
return networkPrefix === targetPrefix;
|
|
619
591
|
}
|
|
620
|
-
if (mask ===
|
|
592
|
+
if (mask === MASK_PARTS.C8) {
|
|
621
593
|
const networkPrefix = network.split(".")[0];
|
|
622
594
|
const targetPrefix = target.split(".")[0];
|
|
623
595
|
return networkPrefix === targetPrefix;
|
|
624
596
|
}
|
|
625
|
-
if (mask ===
|
|
597
|
+
if (mask === MASK_PARTS.C16) {
|
|
626
598
|
const networkPrefix = network.split(".").slice(0, 2).join(".");
|
|
627
599
|
const targetPrefix = target.split(".").slice(0, 2).join(".");
|
|
628
600
|
return networkPrefix === targetPrefix;
|
|
@@ -668,7 +640,7 @@ function getApprovalLevel(toolCall) {
|
|
|
668
640
|
const input = toolCall.input;
|
|
669
641
|
if (tool === TOOL_NAMES.RUN_CMD) {
|
|
670
642
|
const command = String(input.command || "").toLowerCase();
|
|
671
|
-
if (
|
|
643
|
+
if (PASSIVE_BINARIES.some((p) => command.includes(p))) {
|
|
672
644
|
return APPROVAL_LEVELS.AUTO;
|
|
673
645
|
}
|
|
674
646
|
if ([CORE_BINARIES.NMAP, CORE_BINARIES.FFUF, CORE_BINARIES.NUCLEI].some((p) => command.includes(p))) {
|
|
@@ -677,7 +649,7 @@ function getApprovalLevel(toolCall) {
|
|
|
677
649
|
}
|
|
678
650
|
return APPROVAL_LEVELS.CONFIRM;
|
|
679
651
|
}
|
|
680
|
-
if ([CORE_BINARIES.SQLMAP, CORE_BINARIES.METASPLOIT,
|
|
652
|
+
if ([CORE_BINARIES.SQLMAP, CORE_BINARIES.METASPLOIT, ...EXPLOIT_BINARIES].some((p) => command.includes(p))) {
|
|
681
653
|
return APPROVAL_LEVELS.REVIEW;
|
|
682
654
|
}
|
|
683
655
|
}
|
|
@@ -705,32 +677,83 @@ var ApprovalGate = class {
|
|
|
705
677
|
};
|
|
706
678
|
|
|
707
679
|
// src/engine/tools-base.ts
|
|
708
|
-
import {
|
|
680
|
+
import { spawn } from "child_process";
|
|
709
681
|
import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
|
|
710
682
|
import { join } from "path";
|
|
711
|
-
var DEFAULT_COMMAND_TIMEOUT =
|
|
683
|
+
var DEFAULT_COMMAND_TIMEOUT = 3e5;
|
|
684
|
+
var INPUT_PROMPT_PATTERNS = [
|
|
685
|
+
/\[sudo\] password/i,
|
|
686
|
+
/Password:/i,
|
|
687
|
+
/password for/i,
|
|
688
|
+
/Enter passphrase/i,
|
|
689
|
+
/Are you sure.*\(yes\/no\)/i,
|
|
690
|
+
/\(y\/N\)/i,
|
|
691
|
+
/\(Y\/n\)/i
|
|
692
|
+
];
|
|
693
|
+
var globalInputHandler = null;
|
|
694
|
+
function setInputHandler(handler) {
|
|
695
|
+
globalInputHandler = handler;
|
|
696
|
+
}
|
|
712
697
|
async function runCommand(command, args = [], options = {}) {
|
|
713
|
-
|
|
714
|
-
const
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
stdio: "pipe",
|
|
720
|
-
timeout: DEFAULT_COMMAND_TIMEOUT,
|
|
721
|
-
...options
|
|
698
|
+
return new Promise((resolve) => {
|
|
699
|
+
const timeout = options.timeout || DEFAULT_COMMAND_TIMEOUT;
|
|
700
|
+
const child = spawn("sh", ["-c", command], {
|
|
701
|
+
timeout,
|
|
702
|
+
env: { ...process.env, ...options.env },
|
|
703
|
+
cwd: options.cwd
|
|
722
704
|
});
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
705
|
+
let stdout = "";
|
|
706
|
+
let stderr = "";
|
|
707
|
+
let inputHandled = false;
|
|
708
|
+
const checkForInputPrompt = async (data) => {
|
|
709
|
+
if (inputHandled || !globalInputHandler) return;
|
|
710
|
+
for (const pattern of INPUT_PROMPT_PATTERNS) {
|
|
711
|
+
if (pattern.test(data)) {
|
|
712
|
+
inputHandled = true;
|
|
713
|
+
const promptText = data.trim();
|
|
714
|
+
try {
|
|
715
|
+
const userInput = await globalInputHandler(promptText);
|
|
716
|
+
if (userInput !== null && child.stdin.writable) {
|
|
717
|
+
child.stdin.write(userInput + "\n");
|
|
718
|
+
}
|
|
719
|
+
} catch {
|
|
720
|
+
}
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
732
724
|
};
|
|
733
|
-
|
|
725
|
+
child.stdout.on("data", (data) => {
|
|
726
|
+
const text = data.toString();
|
|
727
|
+
stdout += text;
|
|
728
|
+
checkForInputPrompt(text);
|
|
729
|
+
});
|
|
730
|
+
child.stderr.on("data", (data) => {
|
|
731
|
+
const text = data.toString();
|
|
732
|
+
stderr += text;
|
|
733
|
+
checkForInputPrompt(text);
|
|
734
|
+
});
|
|
735
|
+
child.on("close", (code) => {
|
|
736
|
+
if (code === 0) {
|
|
737
|
+
resolve({
|
|
738
|
+
success: true,
|
|
739
|
+
output: stdout.trim() || stderr.trim()
|
|
740
|
+
});
|
|
741
|
+
} else {
|
|
742
|
+
resolve({
|
|
743
|
+
success: false,
|
|
744
|
+
output: stdout.trim(),
|
|
745
|
+
error: stderr.trim() || `Process exited with code ${code}`
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
child.on("error", (err) => {
|
|
750
|
+
resolve({
|
|
751
|
+
success: false,
|
|
752
|
+
output: "",
|
|
753
|
+
error: err.message || String(err)
|
|
754
|
+
});
|
|
755
|
+
});
|
|
756
|
+
});
|
|
734
757
|
}
|
|
735
758
|
async function readFileContent(filePath) {
|
|
736
759
|
try {
|
|
@@ -774,8 +797,40 @@ async function writeFileContent(filePath, content) {
|
|
|
774
797
|
}
|
|
775
798
|
}
|
|
776
799
|
|
|
800
|
+
// src/engine/tools/system.ts
|
|
801
|
+
var systemTools = [
|
|
802
|
+
{
|
|
803
|
+
name: TOOL_NAMES.RUN_CMD,
|
|
804
|
+
description: "Execute shell command safely. Use for reconnaissance, scanning, and exploitation.",
|
|
805
|
+
parameters: {
|
|
806
|
+
command: { type: "string", description: "The shell command to execute" }
|
|
807
|
+
},
|
|
808
|
+
required: ["command"],
|
|
809
|
+
execute: async (params) => runCommand(params.command, [])
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
name: TOOL_NAMES.READ_FILE,
|
|
813
|
+
description: "Read local file content (logs, configs, evidence)",
|
|
814
|
+
parameters: {
|
|
815
|
+
path: { type: "string", description: "Path to the file" }
|
|
816
|
+
},
|
|
817
|
+
required: ["path"],
|
|
818
|
+
execute: async (params) => readFileContent(params.path)
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
name: TOOL_NAMES.WRITE_FILE,
|
|
822
|
+
description: "Write content to file (creates parent directories if needed)",
|
|
823
|
+
parameters: {
|
|
824
|
+
path: { type: "string", description: "Absolute path to the file" },
|
|
825
|
+
content: { type: "string", description: "File content" }
|
|
826
|
+
},
|
|
827
|
+
required: ["path", "content"],
|
|
828
|
+
execute: async (params) => writeFileContent(params.path, params.content)
|
|
829
|
+
}
|
|
830
|
+
];
|
|
831
|
+
|
|
777
832
|
// src/engine/tools-mid.ts
|
|
778
|
-
import { execFileSync
|
|
833
|
+
import { execFileSync } from "child_process";
|
|
779
834
|
async function parseNmap(xmlPath) {
|
|
780
835
|
try {
|
|
781
836
|
const fileResult = await readFileContent(xmlPath);
|
|
@@ -843,7 +898,7 @@ async function searchExploitDB(service, version) {
|
|
|
843
898
|
try {
|
|
844
899
|
const query = version ? `${service} ${version}` : service;
|
|
845
900
|
try {
|
|
846
|
-
const output =
|
|
901
|
+
const output = execFileSync("searchsploit", [query, "--color", "never"], {
|
|
847
902
|
encoding: "utf-8",
|
|
848
903
|
stdio: ["ignore", "pipe", "pipe"],
|
|
849
904
|
timeout: 1e4
|
|
@@ -875,59 +930,93 @@ async function searchExploitDB(service, version) {
|
|
|
875
930
|
};
|
|
876
931
|
}
|
|
877
932
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
933
|
+
|
|
934
|
+
// src/engine/tools/pentest.ts
|
|
935
|
+
var createPentestTools = (state) => [
|
|
936
|
+
{
|
|
937
|
+
name: TOOL_NAMES.PARSE_NMAP,
|
|
938
|
+
description: "Parse nmap XML output to structured JSON",
|
|
939
|
+
parameters: { path: { type: "string", description: "Path to nmap XML" } },
|
|
940
|
+
required: ["path"],
|
|
941
|
+
execute: async (p) => parseNmap(p.path)
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
name: TOOL_NAMES.SEARCH_CVE,
|
|
945
|
+
description: "Search CVE and Exploit-DB for vulnerabilities",
|
|
946
|
+
parameters: {
|
|
947
|
+
service: { type: "string", description: "Service name" },
|
|
948
|
+
version: { type: "string", description: "Version number" }
|
|
949
|
+
},
|
|
950
|
+
required: ["service"],
|
|
951
|
+
execute: async (p) => searchCVE(p.service, p.version)
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
name: TOOL_NAMES.ADD_FINDING,
|
|
955
|
+
description: "Add a security finding",
|
|
956
|
+
parameters: {
|
|
957
|
+
title: { type: "string", description: "Finding title" },
|
|
958
|
+
severity: { type: "string", description: "Severity" },
|
|
959
|
+
affected: { type: "array", items: { type: "string" }, description: "Affected host:port" }
|
|
960
|
+
},
|
|
961
|
+
required: ["title", "severity"],
|
|
962
|
+
execute: async (p) => {
|
|
963
|
+
state.addFinding({
|
|
964
|
+
id: generateId(ID_RADIX, ID_LENGTH),
|
|
965
|
+
title: p.title,
|
|
966
|
+
severity: p.severity,
|
|
967
|
+
affected: p.affected || [],
|
|
968
|
+
description: p.description || "",
|
|
969
|
+
evidence: [],
|
|
970
|
+
verified: false,
|
|
971
|
+
remediation: "",
|
|
972
|
+
foundAt: Date.now()
|
|
973
|
+
});
|
|
974
|
+
return { success: true, output: `Added: ${p.title}` };
|
|
897
975
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
return {
|
|
905
|
-
success: false,
|
|
906
|
-
output: "",
|
|
907
|
-
error: error.message || String(error)
|
|
908
|
-
};
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
name: TOOL_NAMES.GET_STATE,
|
|
979
|
+
description: "Get current engagement state summary",
|
|
980
|
+
parameters: {},
|
|
981
|
+
execute: async () => ({ success: true, output: state.toPrompt() })
|
|
909
982
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
983
|
+
];
|
|
984
|
+
|
|
985
|
+
// src/engine/tools/agents.ts
|
|
986
|
+
var SUB_AGENT_PROMPTS = {
|
|
987
|
+
[AGENT_ROLES.RECON]: `You are a RECONNAISSANCE specialist. Focus on: OSINT, gathering information, and target discovery.`,
|
|
988
|
+
[AGENT_ROLES.WEB]: `You are a WEB APPLICATION specialist. Focus on: Endpoints, discovery, and web vulnerabilities.`,
|
|
989
|
+
[AGENT_ROLES.EXPLOTER]: `You are an EXPLOITATION specialist. Focus on: Exploit research and PoC development.`
|
|
990
|
+
};
|
|
991
|
+
var createAgentTools = (executor) => [
|
|
992
|
+
{
|
|
993
|
+
name: TOOL_NAMES.SPAWN_SUB,
|
|
994
|
+
description: "Spawn a sub-agent with specialized prompt",
|
|
995
|
+
parameters: {
|
|
996
|
+
task: { type: "string", description: "Specific task" },
|
|
997
|
+
agent_type: { type: "string", description: "Agent type (recon, web, exploit)" }
|
|
998
|
+
},
|
|
999
|
+
required: ["task"],
|
|
1000
|
+
execute: async (p) => {
|
|
1001
|
+
if (!executor) return { success: false, output: "SubAgentExecutor not available" };
|
|
1002
|
+
const type = p.agent_type || AGENT_ROLES.RECON;
|
|
1003
|
+
const prompt = SUB_AGENT_PROMPTS[type] || SUB_AGENT_PROMPTS[AGENT_ROLES.RECON];
|
|
1004
|
+
const result = await executor.executeSubTask(p.task, prompt);
|
|
1005
|
+
return { success: result.completed, output: result.output };
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
name: TOOL_NAMES.ASK_USER,
|
|
1010
|
+
description: "Ask the user a question and wait for input.",
|
|
1011
|
+
parameters: { question: { type: "string", description: "Question to ask" } },
|
|
1012
|
+
required: ["question"],
|
|
1013
|
+
execute: async (p) => ({
|
|
917
1014
|
success: true,
|
|
918
|
-
output:
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
}, null, 2)
|
|
922
|
-
};
|
|
923
|
-
} catch (error) {
|
|
924
|
-
return {
|
|
925
|
-
success: false,
|
|
926
|
-
output: "",
|
|
927
|
-
error: error.message || String(error)
|
|
928
|
-
};
|
|
1015
|
+
output: `[ASK_USER] ${p.question}
|
|
1016
|
+
(Waiting for user response via TUI input)`
|
|
1017
|
+
})
|
|
929
1018
|
}
|
|
930
|
-
|
|
1019
|
+
];
|
|
931
1020
|
|
|
932
1021
|
// src/engine/tools.ts
|
|
933
1022
|
var ToolRegistry = class {
|
|
@@ -937,348 +1026,175 @@ var ToolRegistry = class {
|
|
|
937
1026
|
this.approvalGate = approvalGate;
|
|
938
1027
|
this.events = events;
|
|
939
1028
|
this.subAgentExecutor = subAgentExecutor;
|
|
940
|
-
this.
|
|
941
|
-
this.registerMidLevelTools();
|
|
942
|
-
this.registerHighLevelTools();
|
|
1029
|
+
this.initializeRegistry();
|
|
943
1030
|
}
|
|
944
1031
|
tools = /* @__PURE__ */ new Map();
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1032
|
+
initializeRegistry() {
|
|
1033
|
+
const allTools = [
|
|
1034
|
+
...systemTools,
|
|
1035
|
+
...createPentestTools(this.state),
|
|
1036
|
+
...createAgentTools(this.subAgentExecutor)
|
|
1037
|
+
];
|
|
1038
|
+
allTools.forEach((t) => this.tools.set(t.name, t));
|
|
1039
|
+
}
|
|
948
1040
|
getAll() {
|
|
949
1041
|
return Array.from(this.tools.values());
|
|
950
1042
|
}
|
|
951
|
-
/**
|
|
952
|
-
* Get tool by name
|
|
953
|
-
*/
|
|
954
1043
|
getTool(name) {
|
|
955
1044
|
return this.tools.get(name);
|
|
956
1045
|
}
|
|
957
1046
|
/**
|
|
958
|
-
* Execute tool with
|
|
1047
|
+
* Execute tool with integrated safety pipeline (Scope -> Approval -> Execution -> Log)
|
|
1048
|
+
* Implements §8-1 (Safe Execution Pattern).
|
|
959
1049
|
*/
|
|
960
1050
|
async execute(toolCall) {
|
|
961
1051
|
const tool = this.getTool(toolCall.name);
|
|
962
|
-
if (!tool) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
output: "",
|
|
966
|
-
error: `Unknown tool: ${toolCall.name}`
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
const scopeResult = this.scopeGuard.check(toolCall);
|
|
970
|
-
if (!scopeResult.allowed) {
|
|
971
|
-
return {
|
|
972
|
-
success: false,
|
|
973
|
-
output: "",
|
|
974
|
-
error: scopeResult.reason
|
|
975
|
-
};
|
|
976
|
-
}
|
|
1052
|
+
if (!tool) return { success: false, output: "", error: `Unknown tool: ${toolCall.name}` };
|
|
1053
|
+
const scopeCheck = this.scopeGuard.check(toolCall);
|
|
1054
|
+
if (!scopeCheck.allowed) return { success: false, output: "", error: scopeCheck.reason };
|
|
977
1055
|
const approval = await this.approvalGate.request(toolCall);
|
|
978
1056
|
if (!approval.approved) {
|
|
979
|
-
this.
|
|
980
|
-
|
|
981
|
-
command: JSON.stringify(toolCall.input),
|
|
982
|
-
approval: "denied",
|
|
983
|
-
noiseLevel: "none",
|
|
984
|
-
outputSummary: approval.reason || "Denied"
|
|
985
|
-
});
|
|
986
|
-
return {
|
|
987
|
-
success: false,
|
|
988
|
-
output: "",
|
|
989
|
-
error: approval.reason || "Execution denied"
|
|
990
|
-
};
|
|
1057
|
+
this.logDeniedAction(toolCall, approval.reason || "Execution denied");
|
|
1058
|
+
return { success: false, output: "", error: approval.reason || "Denied by policy" };
|
|
991
1059
|
}
|
|
992
1060
|
const result = await tool.execute(toolCall.input);
|
|
1061
|
+
this.logSuccessfulAction(toolCall, approval, result);
|
|
1062
|
+
return result;
|
|
1063
|
+
}
|
|
1064
|
+
logDeniedAction(toolCall, reason) {
|
|
993
1065
|
this.state.logAction({
|
|
994
1066
|
tool: toolCall.name,
|
|
995
1067
|
command: JSON.stringify(toolCall.input),
|
|
996
|
-
approval:
|
|
997
|
-
noiseLevel:
|
|
998
|
-
outputSummary:
|
|
1068
|
+
approval: APPROVAL_STATUSES.DENIED,
|
|
1069
|
+
noiseLevel: NOISE_LEVELS.LOW,
|
|
1070
|
+
outputSummary: reason
|
|
999
1071
|
});
|
|
1000
|
-
return result;
|
|
1001
1072
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
parameters: {
|
|
1010
|
-
command: { type: "string", description: 'The shell command to execute (e.g., "nmap -sV 10.0.0.1")' }
|
|
1011
|
-
},
|
|
1012
|
-
required: ["command"],
|
|
1013
|
-
execute: async (params) => {
|
|
1014
|
-
const command = params.command;
|
|
1015
|
-
return await runCommand(command, []);
|
|
1016
|
-
}
|
|
1017
|
-
});
|
|
1018
|
-
this.tools.set(TOOL_NAMES.READ_FILE, {
|
|
1019
|
-
name: TOOL_NAMES.READ_FILE,
|
|
1020
|
-
description: "Read local file content (logs, configs, evidence)",
|
|
1021
|
-
parameters: {
|
|
1022
|
-
path: { type: "string", description: "Path to the file" }
|
|
1023
|
-
},
|
|
1024
|
-
required: ["path"],
|
|
1025
|
-
execute: async (params) => {
|
|
1026
|
-
const filePath = params.path;
|
|
1027
|
-
return await readFileContent(filePath);
|
|
1028
|
-
}
|
|
1029
|
-
});
|
|
1030
|
-
this.tools.set(TOOL_NAMES.WRITE_FILE, {
|
|
1031
|
-
name: TOOL_NAMES.WRITE_FILE,
|
|
1032
|
-
description: "Write content to file (creates parent directories if needed)",
|
|
1033
|
-
parameters: {
|
|
1034
|
-
path: { type: "string", description: "Absolute path to the file" },
|
|
1035
|
-
content: { type: "string", description: "File content" }
|
|
1036
|
-
},
|
|
1037
|
-
required: ["path", "content"],
|
|
1038
|
-
execute: async (params) => {
|
|
1039
|
-
const filePath = params.path;
|
|
1040
|
-
const content = params.content;
|
|
1041
|
-
return await writeFileContent(filePath, content);
|
|
1042
|
-
}
|
|
1073
|
+
logSuccessfulAction(toolCall, approval, result) {
|
|
1074
|
+
this.state.logAction({
|
|
1075
|
+
tool: toolCall.name,
|
|
1076
|
+
command: JSON.stringify(toolCall.input),
|
|
1077
|
+
approval: approval.approved ? APPROVAL_STATUSES.AUTO : APPROVAL_STATUSES.USER_CONFIRMED,
|
|
1078
|
+
noiseLevel: this.getNoiseLevel(toolCall),
|
|
1079
|
+
outputSummary: result.output.slice(0, 200)
|
|
1043
1080
|
});
|
|
1044
1081
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
path: { type: "string", description: "Path to nmap XML output file" }
|
|
1054
|
-
},
|
|
1055
|
-
required: ["path"],
|
|
1056
|
-
execute: async (params) => {
|
|
1057
|
-
const xmlPath = params.path;
|
|
1058
|
-
return await parseNmap(xmlPath);
|
|
1059
|
-
}
|
|
1060
|
-
});
|
|
1061
|
-
this.tools.set(TOOL_NAMES.SEARCH_CVE, {
|
|
1062
|
-
name: TOOL_NAMES.SEARCH_CVE,
|
|
1063
|
-
description: "Search CVE and Exploit-DB for vulnerabilities",
|
|
1064
|
-
parameters: {
|
|
1065
|
-
service: { type: "string", description: 'Service name (e.g., "apache", "ssh")' },
|
|
1066
|
-
version: { type: "string", description: "Version number (optional)" }
|
|
1067
|
-
},
|
|
1068
|
-
required: ["service"],
|
|
1069
|
-
execute: async (params) => {
|
|
1070
|
-
const service = params.service;
|
|
1071
|
-
const version = params.version;
|
|
1072
|
-
return await searchCVE(service, version);
|
|
1073
|
-
}
|
|
1074
|
-
});
|
|
1075
|
-
this.tools.set(TOOL_NAMES.WEB_SEARCH, {
|
|
1076
|
-
name: TOOL_NAMES.WEB_SEARCH,
|
|
1077
|
-
description: "Search web for reconnaissance info",
|
|
1078
|
-
parameters: {
|
|
1079
|
-
query: { type: "string", description: "Search query" }
|
|
1080
|
-
},
|
|
1081
|
-
required: ["query"],
|
|
1082
|
-
execute: async (params) => {
|
|
1083
|
-
const query = params.query;
|
|
1084
|
-
const engine = params.engine || "duckduckgo";
|
|
1085
|
-
return await webSearch(query, engine);
|
|
1086
|
-
}
|
|
1087
|
-
});
|
|
1088
|
-
this.tools.set(TOOL_NAMES.EXTRACT_URLS, {
|
|
1089
|
-
name: TOOL_NAMES.EXTRACT_URLS,
|
|
1090
|
-
description: "Extract and deduplicate URLs from text",
|
|
1091
|
-
parameters: {
|
|
1092
|
-
text: { type: "string", description: "Input text containing URLs" }
|
|
1093
|
-
},
|
|
1094
|
-
required: ["text"],
|
|
1095
|
-
execute: async (params) => {
|
|
1096
|
-
const text = params.text;
|
|
1097
|
-
return await extractURLs(text);
|
|
1098
|
-
}
|
|
1099
|
-
});
|
|
1082
|
+
getNoiseLevel(toolCall) {
|
|
1083
|
+
if (toolCall.name !== TOOL_NAMES.RUN_CMD) return NOISE_LEVELS.LOW;
|
|
1084
|
+
const cmd = String(toolCall.input.command || "").toLowerCase();
|
|
1085
|
+
const highNoise = [CORE_BINARIES.NMAP, CORE_BINARIES.MASSCAN, CORE_BINARIES.NUCLEI, CORE_BINARIES.NIKTO];
|
|
1086
|
+
if (highNoise.some((b) => cmd.includes(b))) return NOISE_LEVELS.HIGH;
|
|
1087
|
+
const medNoise = [CORE_BINARIES.FFUF, CORE_BINARIES.GOBUSTER];
|
|
1088
|
+
if (medNoise.some((b) => cmd.includes(b))) return NOISE_LEVELS.MEDIUM;
|
|
1089
|
+
return NOISE_LEVELS.LOW;
|
|
1100
1090
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
required: ["allowed"],
|
|
1194
|
-
execute: async (params) => {
|
|
1195
|
-
const allowed = params.allowed || [];
|
|
1196
|
-
const exclusions = params.exclusions || [];
|
|
1197
|
-
this.state.setScope({
|
|
1198
|
-
allowedCidrs: allowed.filter((a) => a.includes("/")),
|
|
1199
|
-
allowedDomains: allowed.filter((a) => !a.includes("/")),
|
|
1200
|
-
exclusions,
|
|
1201
|
-
noDOS: true,
|
|
1202
|
-
noSocial: true
|
|
1203
|
-
});
|
|
1204
|
-
return {
|
|
1205
|
-
success: true,
|
|
1206
|
-
output: `Scope set: ${allowed.length} targets`
|
|
1207
|
-
};
|
|
1208
|
-
}
|
|
1209
|
-
});
|
|
1210
|
-
this.tools.set(TOOL_NAMES.ADD_TARGET, {
|
|
1211
|
-
name: TOOL_NAMES.ADD_TARGET,
|
|
1212
|
-
description: "Add target to engagement",
|
|
1213
|
-
parameters: {
|
|
1214
|
-
ip: { type: "string", description: "IP address" },
|
|
1215
|
-
hostname: { type: "string", description: "Hostname (optional)" }
|
|
1216
|
-
},
|
|
1217
|
-
required: ["ip"],
|
|
1218
|
-
execute: async (params) => {
|
|
1219
|
-
const ip = params.ip;
|
|
1220
|
-
const hostname = params.hostname;
|
|
1221
|
-
this.state.addTarget({
|
|
1222
|
-
ip,
|
|
1223
|
-
hostname,
|
|
1224
|
-
ports: [],
|
|
1225
|
-
tags: [],
|
|
1226
|
-
firstSeen: Date.now()
|
|
1227
|
-
});
|
|
1228
|
-
return {
|
|
1229
|
-
success: true,
|
|
1230
|
-
output: `Target added: ${ip}`
|
|
1231
|
-
};
|
|
1232
|
-
}
|
|
1233
|
-
});
|
|
1234
|
-
this.tools.set(TOOL_NAMES.ASK_USER, {
|
|
1235
|
-
name: TOOL_NAMES.ASK_USER,
|
|
1236
|
-
description: "Ask the user a question and wait for input. Use when you need clarification, authorization, or additional information.",
|
|
1237
|
-
parameters: {
|
|
1238
|
-
question: { type: "string", description: "Question to ask" }
|
|
1239
|
-
},
|
|
1240
|
-
required: ["question"],
|
|
1241
|
-
execute: async (params) => {
|
|
1242
|
-
const question = params.question;
|
|
1243
|
-
return {
|
|
1244
|
-
success: true,
|
|
1245
|
-
output: `[ASK_USER] ${question}
|
|
1246
|
-
(Waiting for user response via TUI input)`
|
|
1247
|
-
};
|
|
1248
|
-
}
|
|
1249
|
-
});
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
// src/shared/constants/services.ts
|
|
1094
|
+
var PORT_CATEGORY_MAP = {
|
|
1095
|
+
80: SERVICE_CATEGORIES.WEB,
|
|
1096
|
+
443: SERVICE_CATEGORIES.WEB,
|
|
1097
|
+
8080: SERVICE_CATEGORIES.WEB,
|
|
1098
|
+
1433: SERVICE_CATEGORIES.DATABASE,
|
|
1099
|
+
3306: SERVICE_CATEGORIES.DATABASE,
|
|
1100
|
+
5432: SERVICE_CATEGORIES.DATABASE,
|
|
1101
|
+
88: SERVICE_CATEGORIES.AD,
|
|
1102
|
+
389: SERVICE_CATEGORIES.AD,
|
|
1103
|
+
445: SERVICE_CATEGORIES.AD,
|
|
1104
|
+
22: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1105
|
+
3389: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1106
|
+
21: SERVICE_CATEGORIES.FILE_SHARING,
|
|
1107
|
+
2049: SERVICE_CATEGORIES.FILE_SHARING,
|
|
1108
|
+
2375: SERVICE_CATEGORIES.CONTAINER,
|
|
1109
|
+
5e3: SERVICE_CATEGORIES.CONTAINER,
|
|
1110
|
+
502: SERVICE_CATEGORIES.ICS,
|
|
1111
|
+
102: SERVICE_CATEGORIES.ICS
|
|
1112
|
+
};
|
|
1113
|
+
var SERVICE_CATEGORY_MAP = {
|
|
1114
|
+
[SERVICES.HTTP]: SERVICE_CATEGORIES.WEB,
|
|
1115
|
+
[SERVICES.HTTPS]: SERVICE_CATEGORIES.WEB,
|
|
1116
|
+
[SERVICES.MYSQL]: SERVICE_CATEGORIES.DATABASE,
|
|
1117
|
+
[SERVICES.MSSQL]: SERVICE_CATEGORIES.DATABASE,
|
|
1118
|
+
[SERVICES.SSH]: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1119
|
+
[SERVICES.FTP]: SERVICE_CATEGORIES.FILE_SHARING,
|
|
1120
|
+
"kerberos": SERVICE_CATEGORIES.AD,
|
|
1121
|
+
"ldap": SERVICE_CATEGORIES.AD,
|
|
1122
|
+
"docker": SERVICE_CATEGORIES.CONTAINER,
|
|
1123
|
+
"modbus": SERVICE_CATEGORIES.ICS
|
|
1124
|
+
};
|
|
1125
|
+
var CATEGORY_APPROVAL2 = {
|
|
1126
|
+
[SERVICE_CATEGORIES.NETWORK]: APPROVAL_LEVELS.CONFIRM,
|
|
1127
|
+
[SERVICE_CATEGORIES.WEB]: APPROVAL_LEVELS.CONFIRM,
|
|
1128
|
+
[SERVICE_CATEGORIES.DATABASE]: APPROVAL_LEVELS.REVIEW,
|
|
1129
|
+
[SERVICE_CATEGORIES.AD]: APPROVAL_LEVELS.REVIEW,
|
|
1130
|
+
[SERVICE_CATEGORIES.EMAIL]: APPROVAL_LEVELS.CONFIRM,
|
|
1131
|
+
[SERVICE_CATEGORIES.REMOTE_ACCESS]: APPROVAL_LEVELS.REVIEW,
|
|
1132
|
+
[SERVICE_CATEGORIES.FILE_SHARING]: APPROVAL_LEVELS.CONFIRM,
|
|
1133
|
+
[SERVICE_CATEGORIES.CLOUD]: APPROVAL_LEVELS.REVIEW,
|
|
1134
|
+
[SERVICE_CATEGORIES.CONTAINER]: APPROVAL_LEVELS.REVIEW,
|
|
1135
|
+
[SERVICE_CATEGORIES.API]: APPROVAL_LEVELS.CONFIRM,
|
|
1136
|
+
[SERVICE_CATEGORIES.WIRELESS]: APPROVAL_LEVELS.REVIEW,
|
|
1137
|
+
[SERVICE_CATEGORIES.ICS]: APPROVAL_LEVELS.BLOCK
|
|
1138
|
+
};
|
|
1139
|
+
var CLOUD_KEYWORDS = [
|
|
1140
|
+
"amazonaws.com",
|
|
1141
|
+
"aws",
|
|
1142
|
+
" azure.com",
|
|
1143
|
+
"googleusercontent.com",
|
|
1144
|
+
"digitalocean.com",
|
|
1145
|
+
"heroku",
|
|
1146
|
+
"vercel"
|
|
1147
|
+
];
|
|
1148
|
+
var PASSIVE_CATEGORIES = [
|
|
1149
|
+
SERVICE_CATEGORIES.NETWORK
|
|
1150
|
+
];
|
|
1151
|
+
var ACTIVE_CATEGORIES = [
|
|
1152
|
+
SERVICE_CATEGORIES.WEB,
|
|
1153
|
+
SERVICE_CATEGORIES.API,
|
|
1154
|
+
SERVICE_CATEGORIES.EMAIL,
|
|
1155
|
+
SERVICE_CATEGORIES.FILE_SHARING
|
|
1156
|
+
];
|
|
1157
|
+
var DANGER_LEVEL_MAP = {
|
|
1158
|
+
[SERVICE_CATEGORIES.NETWORK]: DANGER_LEVELS.PASSIVE,
|
|
1159
|
+
[SERVICE_CATEGORIES.WEB]: DANGER_LEVELS.ACTIVE,
|
|
1160
|
+
[SERVICE_CATEGORIES.API]: DANGER_LEVELS.ACTIVE,
|
|
1161
|
+
[SERVICE_CATEGORIES.EMAIL]: DANGER_LEVELS.ACTIVE,
|
|
1162
|
+
[SERVICE_CATEGORIES.REMOTE_ACCESS]: DANGER_LEVELS.EXPLOIT,
|
|
1163
|
+
[SERVICE_CATEGORIES.FILE_SHARING]: DANGER_LEVELS.ACTIVE,
|
|
1164
|
+
[SERVICE_CATEGORIES.DATABASE]: DANGER_LEVELS.EXPLOIT,
|
|
1165
|
+
[SERVICE_CATEGORIES.AD]: DANGER_LEVELS.EXPLOIT,
|
|
1166
|
+
[SERVICE_CATEGORIES.CLOUD]: DANGER_LEVELS.EXPLOIT,
|
|
1167
|
+
[SERVICE_CATEGORIES.CONTAINER]: DANGER_LEVELS.EXPLOIT,
|
|
1168
|
+
[SERVICE_CATEGORIES.WIRELESS]: DANGER_LEVELS.EXPLOIT,
|
|
1169
|
+
[SERVICE_CATEGORIES.ICS]: DANGER_LEVELS.EXPLOIT
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
// src/engine/utils/service-parser.ts
|
|
1173
|
+
var ServiceParser = class {
|
|
1174
|
+
/** Detect category from port and service name */
|
|
1175
|
+
static detectCategory(port, serviceName) {
|
|
1176
|
+
if (PORT_CATEGORY_MAP[port]) return PORT_CATEGORY_MAP[port];
|
|
1177
|
+
if (serviceName && SERVICE_CATEGORY_MAP[serviceName.toLowerCase()]) {
|
|
1178
|
+
return SERVICE_CATEGORY_MAP[serviceName.toLowerCase()];
|
|
1179
|
+
}
|
|
1180
|
+
if (port >= 8e3 && port <= 9e3) return SERVICE_CATEGORIES.WEB;
|
|
1181
|
+
if (port >= 3e3 && port <= 3500) return SERVICE_CATEGORIES.API;
|
|
1182
|
+
return null;
|
|
1250
1183
|
}
|
|
1251
|
-
/**
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
Your task: ${task}
|
|
1259
|
-
Focus on: Information gathering, OSINT, passive reconnaissance, target discovery
|
|
1260
|
-
Return: Structured target and port information for the main agent.`,
|
|
1261
|
-
[AGENT_ROLES.WEB]: `You are a WEB APPLICATION specialist.
|
|
1262
|
-
Your task: ${task}
|
|
1263
|
-
Focus on: Web app discovery, enumeration, vulnerability scanning
|
|
1264
|
-
Return: Web application structure, endpoints, vulnerabilities found.`,
|
|
1265
|
-
[AGENT_ROLES.EXPLOTER]: `You are an EXPLOITATION specialist.
|
|
1266
|
-
Your task: ${task}
|
|
1267
|
-
Focus on: Exploit research, proof-of-concept testing
|
|
1268
|
-
Return: Exploit results, shell access, credentials obtained.`
|
|
1269
|
-
};
|
|
1270
|
-
return prompts[agentType] || prompts[AGENT_ROLES.RECON];
|
|
1184
|
+
/** Parse category from banner string */
|
|
1185
|
+
static fromBanner(banner) {
|
|
1186
|
+
const b = banner.toLowerCase();
|
|
1187
|
+
if (CLOUD_KEYWORDS.some((k) => b.includes(k))) return SERVICE_CATEGORIES.CLOUD;
|
|
1188
|
+
if (["nginx", "apache", "iis"].some((k) => b.includes(k))) return SERVICE_CATEGORIES.WEB;
|
|
1189
|
+
if (["docker", "kubernetes", "kube"].some((k) => b.includes(k))) return SERVICE_CATEGORIES.CONTAINER;
|
|
1190
|
+
return null;
|
|
1271
1191
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
if (command.includes(CORE_BINARIES.NMAP) || command.includes(CORE_BINARIES.MASSCAN)) return NOISE_LEVELS.HIGH;
|
|
1277
|
-
if (command.includes(CORE_BINARIES.NUCLEI) || command.includes(CORE_BINARIES.NIKTO)) return NOISE_LEVELS.HIGH;
|
|
1278
|
-
if (command.includes(CORE_BINARIES.FFUF) || command.includes(CORE_BINARIES.GOBUSTER)) return NOISE_LEVELS.MEDIUM;
|
|
1192
|
+
/** Check if hostname implies cloud infrastructure */
|
|
1193
|
+
static isCloudHostname(hostname) {
|
|
1194
|
+
const h = hostname.toLowerCase();
|
|
1195
|
+
return CLOUD_KEYWORDS.some((k) => h.includes(k));
|
|
1279
1196
|
}
|
|
1280
|
-
|
|
1281
|
-
}
|
|
1197
|
+
};
|
|
1282
1198
|
|
|
1283
1199
|
// src/domains/registry.ts
|
|
1284
1200
|
import { join as join2, dirname } from "path";
|
|
@@ -1360,181 +1276,12 @@ var DOMAINS = {
|
|
|
1360
1276
|
};
|
|
1361
1277
|
|
|
1362
1278
|
// src/engine/tools-registry.ts
|
|
1363
|
-
var PORT_CATEGORY_MAP = {
|
|
1364
|
-
// Web
|
|
1365
|
-
80: SERVICE_CATEGORIES.WEB,
|
|
1366
|
-
443: SERVICE_CATEGORIES.WEB,
|
|
1367
|
-
8080: SERVICE_CATEGORIES.WEB,
|
|
1368
|
-
8443: SERVICE_CATEGORIES.WEB,
|
|
1369
|
-
8e3: SERVICE_CATEGORIES.WEB,
|
|
1370
|
-
8888: SERVICE_CATEGORIES.WEB,
|
|
1371
|
-
// Database
|
|
1372
|
-
1433: SERVICE_CATEGORIES.DATABASE,
|
|
1373
|
-
// MSSQL
|
|
1374
|
-
3306: SERVICE_CATEGORIES.DATABASE,
|
|
1375
|
-
// MySQL
|
|
1376
|
-
5432: SERVICE_CATEGORIES.DATABASE,
|
|
1377
|
-
// PostgreSQL
|
|
1378
|
-
27017: SERVICE_CATEGORIES.DATABASE,
|
|
1379
|
-
// MongoDB
|
|
1380
|
-
6379: SERVICE_CATEGORIES.DATABASE,
|
|
1381
|
-
// Redis
|
|
1382
|
-
9042: SERVICE_CATEGORIES.DATABASE,
|
|
1383
|
-
// Cassandra
|
|
1384
|
-
9200: SERVICE_CATEGORIES.DATABASE,
|
|
1385
|
-
// Elasticsearch
|
|
1386
|
-
// Active Directory
|
|
1387
|
-
88: SERVICE_CATEGORIES.AD,
|
|
1388
|
-
// Kerberos
|
|
1389
|
-
389: SERVICE_CATEGORIES.AD,
|
|
1390
|
-
// LDAP
|
|
1391
|
-
636: SERVICE_CATEGORIES.AD,
|
|
1392
|
-
// LDAPS
|
|
1393
|
-
3268: SERVICE_CATEGORIES.AD,
|
|
1394
|
-
// LDAP GC
|
|
1395
|
-
3269: SERVICE_CATEGORIES.AD,
|
|
1396
|
-
// LDAP GC SSL
|
|
1397
|
-
445: SERVICE_CATEGORIES.AD,
|
|
1398
|
-
// SMB (also file_sharing)
|
|
1399
|
-
// Email
|
|
1400
|
-
25: SERVICE_CATEGORIES.EMAIL,
|
|
1401
|
-
// SMTP
|
|
1402
|
-
587: SERVICE_CATEGORIES.EMAIL,
|
|
1403
|
-
// SMTP Submission
|
|
1404
|
-
465: SERVICE_CATEGORIES.EMAIL,
|
|
1405
|
-
// SMTPS
|
|
1406
|
-
110: SERVICE_CATEGORIES.EMAIL,
|
|
1407
|
-
// POP3
|
|
1408
|
-
143: SERVICE_CATEGORIES.EMAIL,
|
|
1409
|
-
// IMAP
|
|
1410
|
-
993: SERVICE_CATEGORIES.EMAIL,
|
|
1411
|
-
// IMAPS
|
|
1412
|
-
995: SERVICE_CATEGORIES.EMAIL,
|
|
1413
|
-
// POP3S
|
|
1414
|
-
// Remote Access
|
|
1415
|
-
22: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1416
|
-
// SSH
|
|
1417
|
-
3389: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1418
|
-
// RDP
|
|
1419
|
-
5900: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1420
|
-
// VNC
|
|
1421
|
-
5901: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1422
|
-
// VNC
|
|
1423
|
-
// File Sharing
|
|
1424
|
-
21: SERVICE_CATEGORIES.FILE_SHARING,
|
|
1425
|
-
// FTP
|
|
1426
|
-
139: SERVICE_CATEGORIES.FILE_SHARING,
|
|
1427
|
-
// NetBIOS
|
|
1428
|
-
2049: SERVICE_CATEGORIES.FILE_SHARING,
|
|
1429
|
-
// NFS
|
|
1430
|
-
111: SERVICE_CATEGORIES.FILE_SHARING,
|
|
1431
|
-
// RPC
|
|
1432
|
-
// Container
|
|
1433
|
-
2375: SERVICE_CATEGORIES.CONTAINER,
|
|
1434
|
-
// Docker API
|
|
1435
|
-
2376: SERVICE_CATEGORIES.CONTAINER,
|
|
1436
|
-
// Docker API TLS
|
|
1437
|
-
5e3: SERVICE_CATEGORIES.CONTAINER,
|
|
1438
|
-
// Docker Registry
|
|
1439
|
-
// Cloud (detected via banner, common on 443)
|
|
1440
|
-
// API
|
|
1441
|
-
3e3: SERVICE_CATEGORIES.API,
|
|
1442
|
-
5001: SERVICE_CATEGORIES.API,
|
|
1443
|
-
8001: SERVICE_CATEGORIES.API,
|
|
1444
|
-
// Wireless
|
|
1445
|
-
// ICS
|
|
1446
|
-
502: SERVICE_CATEGORIES.ICS,
|
|
1447
|
-
// Modbus
|
|
1448
|
-
102: SERVICE_CATEGORIES.ICS,
|
|
1449
|
-
// ISO-TSAP
|
|
1450
|
-
2e4: SERVICE_CATEGORIES.ICS,
|
|
1451
|
-
// DNP3
|
|
1452
|
-
44818: SERVICE_CATEGORIES.ICS
|
|
1453
|
-
// Ethernet/IP
|
|
1454
|
-
};
|
|
1455
|
-
var SERVICE_CATEGORY_MAP = {
|
|
1456
|
-
// Web
|
|
1457
|
-
[SERVICES.HTTP]: SERVICE_CATEGORIES.WEB,
|
|
1458
|
-
[SERVICES.HTTPS]: SERVICE_CATEGORIES.WEB,
|
|
1459
|
-
"http-proxy": SERVICE_CATEGORIES.WEB,
|
|
1460
|
-
"ssl/http": SERVICE_CATEGORIES.WEB,
|
|
1461
|
-
"nginx": SERVICE_CATEGORIES.WEB,
|
|
1462
|
-
"apache": SERVICE_CATEGORIES.WEB,
|
|
1463
|
-
"iis": SERVICE_CATEGORIES.WEB,
|
|
1464
|
-
// Database
|
|
1465
|
-
[SERVICES.MYSQL]: SERVICE_CATEGORIES.DATABASE,
|
|
1466
|
-
[SERVICES.MSSQL]: SERVICE_CATEGORIES.DATABASE,
|
|
1467
|
-
[SERVICES.POSTGRES]: SERVICE_CATEGORIES.DATABASE,
|
|
1468
|
-
[SERVICES.MONGODB]: SERVICE_CATEGORIES.DATABASE,
|
|
1469
|
-
[SERVICES.REDIS]: SERVICE_CATEGORIES.DATABASE,
|
|
1470
|
-
[SERVICES.ELASTIC]: SERVICE_CATEGORIES.DATABASE,
|
|
1471
|
-
"cassandra": SERVICE_CATEGORIES.DATABASE,
|
|
1472
|
-
// Active Directory
|
|
1473
|
-
"kerberos": SERVICE_CATEGORIES.AD,
|
|
1474
|
-
"ldap": SERVICE_CATEGORIES.AD,
|
|
1475
|
-
"ldaps": SERVICE_CATEGORIES.AD,
|
|
1476
|
-
"microsoft-ds": SERVICE_CATEGORIES.AD,
|
|
1477
|
-
"netbios-ssn": SERVICE_CATEGORIES.AD,
|
|
1478
|
-
// Email
|
|
1479
|
-
"smtp": SERVICE_CATEGORIES.EMAIL,
|
|
1480
|
-
"pop3": SERVICE_CATEGORIES.EMAIL,
|
|
1481
|
-
"imap": SERVICE_CATEGORIES.EMAIL,
|
|
1482
|
-
// Remote Access
|
|
1483
|
-
[SERVICES.SSH]: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1484
|
-
"ms-wbt-server": SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1485
|
-
// RDP
|
|
1486
|
-
"vnc": SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1487
|
-
// File Sharing
|
|
1488
|
-
[SERVICES.FTP]: SERVICE_CATEGORIES.FILE_SHARING,
|
|
1489
|
-
"ftp-data": SERVICE_CATEGORIES.FILE_SHARING,
|
|
1490
|
-
"nfs": SERVICE_CATEGORIES.FILE_SHARING,
|
|
1491
|
-
"rpcbind": SERVICE_CATEGORIES.FILE_SHARING,
|
|
1492
|
-
// Container
|
|
1493
|
-
"docker": SERVICE_CATEGORIES.CONTAINER,
|
|
1494
|
-
"docker-swap": SERVICE_CATEGORIES.CONTAINER,
|
|
1495
|
-
// Cloud APIs
|
|
1496
|
-
"aws-s3": SERVICE_CATEGORIES.CLOUD,
|
|
1497
|
-
"azure-storage": SERVICE_CATEGORIES.CLOUD,
|
|
1498
|
-
// Wireless
|
|
1499
|
-
// ICS
|
|
1500
|
-
"modbus": SERVICE_CATEGORIES.ICS,
|
|
1501
|
-
"iso-tsap": SERVICE_CATEGORIES.ICS,
|
|
1502
|
-
"dnp3": SERVICE_CATEGORIES.ICS,
|
|
1503
|
-
"enip": SERVICE_CATEGORIES.ICS
|
|
1504
|
-
};
|
|
1505
|
-
var CATEGORY_APPROVAL2 = {
|
|
1506
|
-
[SERVICE_CATEGORIES.NETWORK]: APPROVAL_LEVELS.CONFIRM,
|
|
1507
|
-
[SERVICE_CATEGORIES.WEB]: APPROVAL_LEVELS.CONFIRM,
|
|
1508
|
-
[SERVICE_CATEGORIES.DATABASE]: APPROVAL_LEVELS.REVIEW,
|
|
1509
|
-
[SERVICE_CATEGORIES.AD]: APPROVAL_LEVELS.REVIEW,
|
|
1510
|
-
[SERVICE_CATEGORIES.EMAIL]: APPROVAL_LEVELS.CONFIRM,
|
|
1511
|
-
[SERVICE_CATEGORIES.REMOTE_ACCESS]: APPROVAL_LEVELS.REVIEW,
|
|
1512
|
-
[SERVICE_CATEGORIES.FILE_SHARING]: APPROVAL_LEVELS.CONFIRM,
|
|
1513
|
-
[SERVICE_CATEGORIES.CLOUD]: APPROVAL_LEVELS.REVIEW,
|
|
1514
|
-
[SERVICE_CATEGORIES.CONTAINER]: APPROVAL_LEVELS.REVIEW,
|
|
1515
|
-
[SERVICE_CATEGORIES.API]: APPROVAL_LEVELS.CONFIRM,
|
|
1516
|
-
[SERVICE_CATEGORIES.WIRELESS]: APPROVAL_LEVELS.REVIEW,
|
|
1517
|
-
[SERVICE_CATEGORIES.ICS]: APPROVAL_LEVELS.BLOCK
|
|
1518
|
-
// Requires explicit written authorization
|
|
1519
|
-
};
|
|
1520
|
-
var CATEGORY_DESCRIPTIONS = Object.entries(DOMAINS).reduce((acc, [id, info]) => {
|
|
1521
|
-
acc[id] = info.description;
|
|
1522
|
-
return acc;
|
|
1523
|
-
}, {});
|
|
1524
1279
|
var CategorizedToolRegistry = class extends ToolRegistry {
|
|
1525
|
-
categories;
|
|
1526
|
-
constructor(state, scopeGuard, approvalGate, events,
|
|
1527
|
-
super(state, scopeGuard, approvalGate, events,
|
|
1528
|
-
this.categories = /* @__PURE__ */ new Map();
|
|
1280
|
+
categories = /* @__PURE__ */ new Map();
|
|
1281
|
+
constructor(state, scopeGuard, approvalGate, events, executor) {
|
|
1282
|
+
super(state, scopeGuard, approvalGate, events, executor);
|
|
1529
1283
|
this.initializeCategories();
|
|
1530
1284
|
}
|
|
1531
|
-
/**
|
|
1532
|
-
* Initialize all tool categories with core tools from parent ToolRegistry
|
|
1533
|
-
*
|
|
1534
|
-
* Every service category gets the base tools (run_cmd, read_file, write_file, etc.)
|
|
1535
|
-
* Sub-agents need at least these core tools to function.
|
|
1536
|
-
* spawn_sub is explicitly excluded to prevent recursion (1-level only).
|
|
1537
|
-
*/
|
|
1538
1285
|
initializeCategories() {
|
|
1539
1286
|
const coreToolNames = [
|
|
1540
1287
|
TOOL_NAMES.RUN_CMD,
|
|
@@ -1543,244 +1290,81 @@ var CategorizedToolRegistry = class extends ToolRegistry {
|
|
|
1543
1290
|
TOOL_NAMES.PARSE_NMAP,
|
|
1544
1291
|
TOOL_NAMES.SEARCH_CVE,
|
|
1545
1292
|
TOOL_NAMES.WEB_SEARCH,
|
|
1546
|
-
TOOL_NAMES.EXTRACT_URLS,
|
|
1547
1293
|
TOOL_NAMES.ADD_FINDING,
|
|
1548
1294
|
TOOL_NAMES.UPDATE_TODO,
|
|
1549
1295
|
TOOL_NAMES.GET_STATE,
|
|
1550
1296
|
TOOL_NAMES.ADD_TARGET,
|
|
1551
1297
|
TOOL_NAMES.ASK_USER
|
|
1552
1298
|
];
|
|
1553
|
-
const coreTools =
|
|
1554
|
-
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
}
|
|
1560
|
-
for (const category of Object.keys(CATEGORY_DESCRIPTIONS)) {
|
|
1561
|
-
this.categories.set(category, {
|
|
1562
|
-
name: category,
|
|
1563
|
-
description: CATEGORY_DESCRIPTIONS[category],
|
|
1299
|
+
const coreTools = coreToolNames.map((name) => this.getTool(name)).filter((t) => !!t);
|
|
1300
|
+
Object.keys(DOMAINS).forEach((id) => {
|
|
1301
|
+
const cat = id;
|
|
1302
|
+
this.categories.set(cat, {
|
|
1303
|
+
name: cat,
|
|
1304
|
+
description: DOMAINS[cat]?.description || "",
|
|
1564
1305
|
tools: [...coreTools],
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
// Loaded from Domain Registry or prompt files at runtime
|
|
1568
|
-
dangerLevel: this.getDangerLevel(category),
|
|
1569
|
-
defaultApproval: CATEGORY_APPROVAL2[category],
|
|
1570
|
-
commonPorts: this.getCommonPorts(category),
|
|
1571
|
-
commonServices: this.getCommonServices(category)
|
|
1306
|
+
dangerLevel: this.calculateDanger(cat),
|
|
1307
|
+
defaultApproval: CATEGORY_APPROVAL2[cat] || "confirm"
|
|
1572
1308
|
});
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
/**
|
|
1576
|
-
* Get danger level for category
|
|
1577
|
-
*/
|
|
1578
|
-
getDangerLevel(category) {
|
|
1579
|
-
const passive = [SERVICE_CATEGORIES.NETWORK];
|
|
1580
|
-
const active = [SERVICE_CATEGORIES.WEB, SERVICE_CATEGORIES.API, SERVICE_CATEGORIES.EMAIL, SERVICE_CATEGORIES.FILE_SHARING];
|
|
1581
|
-
if (passive.includes(category)) return DANGER_LEVELS.PASSIVE;
|
|
1582
|
-
if (active.includes(category)) return DANGER_LEVELS.ACTIVE;
|
|
1583
|
-
return DANGER_LEVELS.EXPLOIT;
|
|
1584
|
-
}
|
|
1585
|
-
/**
|
|
1586
|
-
* Get common ports for category
|
|
1587
|
-
*/
|
|
1588
|
-
getCommonPorts(category) {
|
|
1589
|
-
const ports = {
|
|
1590
|
-
[SERVICE_CATEGORIES.NETWORK]: [1, 7, 9, 21, 22, 23, 25, 53, 79, 80, 110, 111, 135, 139, 143, 443, 445, 993, 995, 1723, 3306, 3389, 5432, 5900, 8080],
|
|
1591
|
-
[SERVICE_CATEGORIES.WEB]: [80, 443, 8e3, 8080, 8443, 8888],
|
|
1592
|
-
[SERVICE_CATEGORIES.DATABASE]: [1433, 3306, 5432, 6379, 9042, 9200, 27017, 1521],
|
|
1593
|
-
[SERVICE_CATEGORIES.AD]: [88, 135, 139, 389, 445, 636, 3268, 3269, 5722],
|
|
1594
|
-
[SERVICE_CATEGORIES.EMAIL]: [25, 110, 143, 465, 587, 993, 995],
|
|
1595
|
-
[SERVICE_CATEGORIES.REMOTE_ACCESS]: [22, 23, 3389, 5900, 5901],
|
|
1596
|
-
[SERVICE_CATEGORIES.FILE_SHARING]: [21, 139, 445, 2049, 111],
|
|
1597
|
-
[SERVICE_CATEGORIES.CLOUD]: [443],
|
|
1598
|
-
// Detected via banner
|
|
1599
|
-
[SERVICE_CATEGORIES.CONTAINER]: [2375, 2376, 5e3],
|
|
1600
|
-
[SERVICE_CATEGORIES.API]: [3e3, 5e3, 5001, 8e3, 8001, 8080],
|
|
1601
|
-
[SERVICE_CATEGORIES.WIRELESS]: [],
|
|
1602
|
-
[SERVICE_CATEGORIES.ICS]: [102, 502, 44818, 2e4]
|
|
1603
|
-
};
|
|
1604
|
-
return ports[category] || [];
|
|
1605
|
-
}
|
|
1606
|
-
/**
|
|
1607
|
-
* Get common services for category
|
|
1608
|
-
*/
|
|
1609
|
-
getCommonServices(category) {
|
|
1610
|
-
const services = {
|
|
1611
|
-
[SERVICE_CATEGORIES.NETWORK]: [SERVICES.FTP, SERVICES.SSH, "telnet", "smtp", SERVICES.HTTP, "pop3", "imap", SERVICES.HTTPS],
|
|
1612
|
-
[SERVICE_CATEGORIES.WEB]: [SERVICES.HTTP, SERVICES.HTTPS, "nginx", "apache", "iis"],
|
|
1613
|
-
[SERVICE_CATEGORIES.DATABASE]: [SERVICES.MYSQL, SERVICES.MSSQL, SERVICES.POSTGRES, SERVICES.MONGODB, SERVICES.REDIS, SERVICES.ELASTIC],
|
|
1614
|
-
[SERVICE_CATEGORIES.AD]: ["kerberos", "ldap", "microsoft-ds", "netbios-ssn"],
|
|
1615
|
-
[SERVICE_CATEGORIES.EMAIL]: ["smtp", "pop3", "imap"],
|
|
1616
|
-
[SERVICE_CATEGORIES.REMOTE_ACCESS]: [SERVICES.SSH, "ms-wbt-server", "vnc"],
|
|
1617
|
-
[SERVICE_CATEGORIES.FILE_SHARING]: [SERVICES.FTP, "microsoft-ds", "nfs"],
|
|
1618
|
-
[SERVICE_CATEGORIES.CLOUD]: [SERVICES.HTTP, SERVICES.HTTPS],
|
|
1619
|
-
// Detected via banner analysis
|
|
1620
|
-
[SERVICE_CATEGORIES.CONTAINER]: ["docker"],
|
|
1621
|
-
[SERVICE_CATEGORIES.API]: [SERVICES.HTTP, SERVICES.HTTPS],
|
|
1622
|
-
[SERVICE_CATEGORIES.WIRELESS]: [],
|
|
1623
|
-
[SERVICE_CATEGORIES.ICS]: ["modbus", "iso-tsap", "dnp3", "enip"]
|
|
1624
|
-
};
|
|
1625
|
-
return services[category] || [];
|
|
1626
|
-
}
|
|
1627
|
-
/**
|
|
1628
|
-
* Get all tools in a category
|
|
1629
|
-
*/
|
|
1630
|
-
getByCategory(category) {
|
|
1631
|
-
return this.categories.get(category)?.tools || [];
|
|
1632
|
-
}
|
|
1633
|
-
/**
|
|
1634
|
-
* Get category definition
|
|
1635
|
-
*/
|
|
1636
|
-
getCategory(category) {
|
|
1637
|
-
return this.categories.get(category);
|
|
1309
|
+
});
|
|
1638
1310
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
*/
|
|
1642
|
-
getAllCategories() {
|
|
1643
|
-
return Array.from(this.categories.values());
|
|
1311
|
+
calculateDanger(cat) {
|
|
1312
|
+
return DANGER_LEVEL_MAP[cat] || DANGER_LEVELS.EXPLOIT;
|
|
1644
1313
|
}
|
|
1645
1314
|
/**
|
|
1646
|
-
* Suggest tools based on
|
|
1315
|
+
* Suggest tools based on target services
|
|
1647
1316
|
*/
|
|
1648
1317
|
suggestTools(target) {
|
|
1649
|
-
const
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
tools: catTools.map((t) => t.name)
|
|
1657
|
-
});
|
|
1658
|
-
}
|
|
1659
|
-
}
|
|
1660
|
-
if (target.hostname) {
|
|
1661
|
-
const cloudCat = this.detectCloudProvider(target.hostname);
|
|
1662
|
-
if (cloudCat && !suggestions.find((s) => s.category === cloudCat)) {
|
|
1663
|
-
const catTools = this.getByCategory(cloudCat);
|
|
1664
|
-
suggestions.push({
|
|
1665
|
-
category: cloudCat,
|
|
1666
|
-
tools: catTools.map((t) => t.name)
|
|
1667
|
-
});
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
return suggestions;
|
|
1671
|
-
}
|
|
1672
|
-
/**
|
|
1673
|
-
* Detect category from port number and service name
|
|
1674
|
-
*/
|
|
1675
|
-
detectCategoryFromPort(port, serviceName) {
|
|
1676
|
-
if (PORT_CATEGORY_MAP[port]) {
|
|
1677
|
-
return PORT_CATEGORY_MAP[port];
|
|
1678
|
-
}
|
|
1679
|
-
if (serviceName && SERVICE_CATEGORY_MAP[serviceName.toLowerCase()]) {
|
|
1680
|
-
return SERVICE_CATEGORY_MAP[serviceName.toLowerCase()];
|
|
1681
|
-
}
|
|
1682
|
-
if (port >= 8e3 && port <= 9e3) return SERVICE_CATEGORIES.WEB;
|
|
1683
|
-
if (port >= 3e3 && port <= 3500) return SERVICE_CATEGORIES.API;
|
|
1684
|
-
return null;
|
|
1685
|
-
}
|
|
1686
|
-
/**
|
|
1687
|
-
* Detect cloud provider from hostname
|
|
1688
|
-
*/
|
|
1689
|
-
detectCloudProvider(hostname) {
|
|
1690
|
-
const cloudProviders = [
|
|
1691
|
-
"amazonaws.com",
|
|
1692
|
-
"aws",
|
|
1693
|
-
"s3.amazonaws.com",
|
|
1694
|
-
"azure.com",
|
|
1695
|
-
"azurewebsites.net",
|
|
1696
|
-
"windows.net",
|
|
1697
|
-
"gcp",
|
|
1698
|
-
"googleusercontent.com",
|
|
1699
|
-
"appspot.com",
|
|
1700
|
-
"digitalocean.com",
|
|
1701
|
-
"do.co",
|
|
1702
|
-
"herokuapp.com",
|
|
1703
|
-
"vercel.app",
|
|
1704
|
-
"netlify.app"
|
|
1705
|
-
];
|
|
1706
|
-
const lowerHostname = hostname.toLowerCase();
|
|
1707
|
-
for (const provider of cloudProviders) {
|
|
1708
|
-
if (lowerHostname.includes(provider)) {
|
|
1709
|
-
return SERVICE_CATEGORIES.CLOUD;
|
|
1318
|
+
const results = [];
|
|
1319
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1320
|
+
target.ports.forEach((p) => {
|
|
1321
|
+
const cat = ServiceParser.detectCategory(p.port, p.service);
|
|
1322
|
+
if (cat && !seen.has(cat)) {
|
|
1323
|
+
seen.add(cat);
|
|
1324
|
+
results.push({ category: cat, tools: this.getByCategory(cat).map((t) => t.name) });
|
|
1710
1325
|
}
|
|
1326
|
+
});
|
|
1327
|
+
if (target.hostname && ServiceParser.isCloudHostname(target.hostname) && !seen.has(SERVICE_CATEGORIES.CLOUD)) {
|
|
1328
|
+
results.push({ category: SERVICE_CATEGORIES.CLOUD, tools: this.getByCategory(SERVICE_CATEGORIES.CLOUD).map((t) => t.name) });
|
|
1711
1329
|
}
|
|
1712
|
-
return
|
|
1330
|
+
return results;
|
|
1713
1331
|
}
|
|
1714
|
-
/**
|
|
1715
|
-
* Suggest sub-agent type for target
|
|
1716
|
-
*/
|
|
1717
1332
|
suggestSubAgent(target) {
|
|
1718
1333
|
const suggestions = this.suggestTools(target);
|
|
1719
1334
|
const priority = [
|
|
1720
1335
|
SERVICE_CATEGORIES.ICS,
|
|
1721
|
-
// Critical - block
|
|
1722
1336
|
SERVICE_CATEGORIES.AD,
|
|
1723
|
-
// High value - review
|
|
1724
1337
|
SERVICE_CATEGORIES.DATABASE,
|
|
1725
|
-
// High value - review
|
|
1726
1338
|
SERVICE_CATEGORIES.CLOUD,
|
|
1727
|
-
// High value - review
|
|
1728
1339
|
SERVICE_CATEGORIES.CONTAINER,
|
|
1729
|
-
// Escalation - review
|
|
1730
|
-
SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
1731
|
-
// Access - review
|
|
1732
1340
|
SERVICE_CATEGORIES.WEB,
|
|
1733
|
-
|
|
1734
|
-
SERVICE_CATEGORIES.API,
|
|
1735
|
-
// Common - confirm
|
|
1736
|
-
SERVICE_CATEGORIES.EMAIL,
|
|
1737
|
-
// Info - confirm
|
|
1738
|
-
SERVICE_CATEGORIES.FILE_SHARING,
|
|
1739
|
-
// Access - confirm
|
|
1740
|
-
SERVICE_CATEGORIES.NETWORK,
|
|
1741
|
-
// Basic - confirm
|
|
1742
|
-
SERVICE_CATEGORIES.WIRELESS
|
|
1743
|
-
// Special - review
|
|
1341
|
+
SERVICE_CATEGORIES.NETWORK
|
|
1744
1342
|
];
|
|
1745
1343
|
for (const cat of priority) {
|
|
1746
|
-
if (suggestions.
|
|
1747
|
-
return cat;
|
|
1748
|
-
}
|
|
1344
|
+
if (suggestions.some((s) => s.category === cat)) return cat;
|
|
1749
1345
|
}
|
|
1750
|
-
return
|
|
1346
|
+
return AGENT_ROLES.RECON;
|
|
1751
1347
|
}
|
|
1752
|
-
/**
|
|
1753
|
-
* Get approval level for category
|
|
1754
|
-
*/
|
|
1755
|
-
getApprovalForCategory(category) {
|
|
1756
|
-
return CATEGORY_APPROVAL2[category];
|
|
1757
|
-
}
|
|
1758
|
-
/**
|
|
1759
|
-
* Check if category requires special handling
|
|
1760
|
-
*/
|
|
1761
|
-
isHighRiskCategory(category) {
|
|
1762
|
-
return [SERVICE_CATEGORIES.ICS, SERVICE_CATEGORIES.AD, SERVICE_CATEGORIES.DATABASE, SERVICE_CATEGORIES.CLOUD, SERVICE_CATEGORIES.CONTAINER].includes(category);
|
|
1763
|
-
}
|
|
1764
|
-
/**
|
|
1765
|
-
* Get service fingerprint from port data
|
|
1766
|
-
*/
|
|
1767
1348
|
fingerprintService(port) {
|
|
1768
|
-
const category =
|
|
1349
|
+
const category = ServiceParser.detectCategory(port.port, port.service);
|
|
1769
1350
|
if (!category) return null;
|
|
1770
1351
|
return {
|
|
1771
1352
|
port: port.port,
|
|
1772
1353
|
service: port.service,
|
|
1773
1354
|
version: port.version,
|
|
1774
|
-
banner: port.notes[0]
|
|
1355
|
+
banner: port.notes[0],
|
|
1775
1356
|
category,
|
|
1776
1357
|
confidence: port.version ? 0.9 : 0.7
|
|
1777
1358
|
};
|
|
1778
1359
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
return
|
|
1360
|
+
getByCategory(cat) {
|
|
1361
|
+
return this.categories.get(cat)?.tools || [];
|
|
1362
|
+
}
|
|
1363
|
+
getAllCategories() {
|
|
1364
|
+
return Array.from(this.categories.values());
|
|
1365
|
+
}
|
|
1366
|
+
getApprovalForCategory(cat) {
|
|
1367
|
+
return CATEGORY_APPROVAL2[cat] || "confirm";
|
|
1784
1368
|
}
|
|
1785
1369
|
};
|
|
1786
1370
|
|
|
@@ -1946,6 +1530,21 @@ function getModel() {
|
|
|
1946
1530
|
|
|
1947
1531
|
// src/engine/llm.ts
|
|
1948
1532
|
import Anthropic from "@anthropic-ai/sdk";
|
|
1533
|
+
|
|
1534
|
+
// src/shared/constants/llm.ts
|
|
1535
|
+
var RETRY_CONFIG = {
|
|
1536
|
+
baseDelayMs: 2e3,
|
|
1537
|
+
maxDelayMs: 6e4,
|
|
1538
|
+
jitterMs: 1e3
|
|
1539
|
+
};
|
|
1540
|
+
var LLM_LIMITS = {
|
|
1541
|
+
nonStreamMaxTokens: 4096,
|
|
1542
|
+
streamMaxTokens: 8192,
|
|
1543
|
+
/** WHY: ~3.5 chars/token is a reasonable average for mixed English/CJK content */
|
|
1544
|
+
charsPerTokenEstimate: 3.5
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1547
|
+
// src/engine/llm.ts
|
|
1949
1548
|
var LLMClient = class {
|
|
1950
1549
|
client;
|
|
1951
1550
|
model;
|
|
@@ -1957,15 +1556,46 @@ var LLMClient = class {
|
|
|
1957
1556
|
});
|
|
1958
1557
|
this.model = getModel();
|
|
1959
1558
|
}
|
|
1559
|
+
/** Non-streaming response with automatic retry */
|
|
1560
|
+
async generateResponse(messages, tools, systemPrompt, callbacks) {
|
|
1561
|
+
return this.withRetry(callbacks, () => this.executeNonStream(messages, tools, systemPrompt));
|
|
1562
|
+
}
|
|
1563
|
+
/** Streaming response with reasoning callbacks and automatic retry */
|
|
1564
|
+
async generateResponseStream(messages, tools, systemPrompt, callbacks) {
|
|
1565
|
+
return this.withRetry(callbacks, () => this.executeStream(messages, tools, systemPrompt, callbacks));
|
|
1566
|
+
}
|
|
1567
|
+
getModelName() {
|
|
1568
|
+
return this.model;
|
|
1569
|
+
}
|
|
1570
|
+
// ─── Retry Logic ─────────────────────────────────────────────
|
|
1960
1571
|
/**
|
|
1961
|
-
*
|
|
1572
|
+
* Unified retry wrapper — retries unconditionally on all errors,
|
|
1573
|
+
* respects AbortSignal for instant cancellation.
|
|
1962
1574
|
*/
|
|
1963
|
-
async
|
|
1575
|
+
async withRetry(callbacks, operation) {
|
|
1576
|
+
let attempt = 0;
|
|
1577
|
+
const signal = callbacks?.abortSignal;
|
|
1578
|
+
while (true) {
|
|
1579
|
+
if (signal?.aborted) this.throwAbort();
|
|
1580
|
+
try {
|
|
1581
|
+
return await operation();
|
|
1582
|
+
} catch (error) {
|
|
1583
|
+
if (this.isAbortError(error, signal)) this.throwAbort();
|
|
1584
|
+
const delayMs = this.calculateRetryDelay(attempt);
|
|
1585
|
+
const errorMsg = this.extractErrorMessage(error);
|
|
1586
|
+
callbacks?.onRetry?.(attempt + 1, 0, delayMs, errorMsg);
|
|
1587
|
+
await this.sleep(delayMs, signal);
|
|
1588
|
+
attempt++;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
// ─── Non-Streaming Execution ─────────────────────────────────
|
|
1593
|
+
async executeNonStream(messages, tools, systemPrompt) {
|
|
1964
1594
|
const response = await this.client.messages.create({
|
|
1965
1595
|
model: this.model,
|
|
1966
|
-
max_tokens:
|
|
1596
|
+
max_tokens: LLM_LIMITS.nonStreamMaxTokens,
|
|
1967
1597
|
system: systemPrompt,
|
|
1968
|
-
messages:
|
|
1598
|
+
messages: this.filterSystemMessages(messages),
|
|
1969
1599
|
tools
|
|
1970
1600
|
});
|
|
1971
1601
|
const textBlock = response.content.find((b) => b.type === "text");
|
|
@@ -1977,65 +1607,138 @@ var LLMClient = class {
|
|
|
1977
1607
|
name: b.name,
|
|
1978
1608
|
input: b.input
|
|
1979
1609
|
})),
|
|
1980
|
-
rawResponse: response
|
|
1610
|
+
rawResponse: response,
|
|
1611
|
+
usage: response.usage ? { input_tokens: response.usage.input_tokens, output_tokens: response.usage.output_tokens } : void 0
|
|
1981
1612
|
};
|
|
1982
1613
|
}
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
* This is like opencode's reasoning stream handling
|
|
1986
|
-
*/
|
|
1987
|
-
async generateResponseStream(messages, tools, systemPrompt, callbacks) {
|
|
1614
|
+
// ─── Streaming Execution ─────────────────────────────────────
|
|
1615
|
+
async executeStream(messages, tools, systemPrompt, callbacks) {
|
|
1988
1616
|
const stream = await this.client.messages.create({
|
|
1989
1617
|
model: this.model,
|
|
1990
|
-
max_tokens:
|
|
1618
|
+
max_tokens: LLM_LIMITS.streamMaxTokens,
|
|
1991
1619
|
system: systemPrompt,
|
|
1992
|
-
messages:
|
|
1620
|
+
messages: this.filterSystemMessages(messages),
|
|
1993
1621
|
tools,
|
|
1994
1622
|
stream: true
|
|
1995
1623
|
});
|
|
1996
1624
|
let fullContent = "";
|
|
1625
|
+
let fullReasoning = "";
|
|
1997
1626
|
const toolCallsMap = /* @__PURE__ */ new Map();
|
|
1998
|
-
|
|
1627
|
+
let isReasoningBlock = false;
|
|
1628
|
+
let isTextBlock = false;
|
|
1629
|
+
let usage = { input_tokens: 0, output_tokens: 0 };
|
|
1630
|
+
let totalChars = 0;
|
|
1999
1631
|
for await (const event of stream) {
|
|
2000
1632
|
switch (event.type) {
|
|
2001
1633
|
case "content_block_start":
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
1634
|
+
this.handleBlockStart(
|
|
1635
|
+
event,
|
|
1636
|
+
toolCallsMap,
|
|
1637
|
+
callbacks,
|
|
1638
|
+
() => {
|
|
1639
|
+
isTextBlock = true;
|
|
1640
|
+
},
|
|
1641
|
+
() => {
|
|
1642
|
+
isReasoningBlock = true;
|
|
1643
|
+
}
|
|
1644
|
+
);
|
|
2007
1645
|
break;
|
|
2008
|
-
case "content_block_delta":
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
fullContent += text;
|
|
2012
|
-
|
|
2013
|
-
|
|
1646
|
+
case "content_block_delta": {
|
|
1647
|
+
const delta = event.delta;
|
|
1648
|
+
if (delta.type === "text_delta" && isTextBlock) {
|
|
1649
|
+
fullContent += delta.text;
|
|
1650
|
+
totalChars += delta.text.length;
|
|
1651
|
+
callbacks?.onOutputDelta?.(delta.text);
|
|
1652
|
+
} else if (delta.type === "thinking_delta" && isReasoningBlock) {
|
|
1653
|
+
const text = delta.thinking || "";
|
|
1654
|
+
fullReasoning += text;
|
|
1655
|
+
totalChars += text.length;
|
|
1656
|
+
callbacks?.onReasoningDelta?.(text);
|
|
1657
|
+
} else if (delta.type === "input_json_delta") {
|
|
1658
|
+
totalChars += (delta.partial_json || "").length;
|
|
2014
1659
|
}
|
|
1660
|
+
const estimatedOutput = Math.ceil(totalChars / LLM_LIMITS.charsPerTokenEstimate);
|
|
1661
|
+
callbacks?.onUsageUpdate?.({ input_tokens: usage.input_tokens, output_tokens: estimatedOutput });
|
|
2015
1662
|
break;
|
|
1663
|
+
}
|
|
2016
1664
|
case "content_block_stop":
|
|
1665
|
+
if (isReasoningBlock) callbacks?.onReasoningEnd?.();
|
|
1666
|
+
if (isTextBlock) callbacks?.onOutputEnd?.();
|
|
1667
|
+
isReasoningBlock = false;
|
|
1668
|
+
isTextBlock = false;
|
|
2017
1669
|
break;
|
|
2018
|
-
case "message_start":
|
|
2019
|
-
|
|
2020
|
-
|
|
1670
|
+
case "message_start": {
|
|
1671
|
+
const msg = event.message;
|
|
1672
|
+
if (msg?.usage) {
|
|
1673
|
+
usage = { ...msg.usage };
|
|
1674
|
+
callbacks?.onUsageUpdate?.({ ...usage });
|
|
1675
|
+
}
|
|
2021
1676
|
break;
|
|
2022
|
-
|
|
1677
|
+
}
|
|
1678
|
+
case "message_delta": {
|
|
1679
|
+
const deltaUsage = event.usage;
|
|
1680
|
+
if (deltaUsage) {
|
|
1681
|
+
usage.output_tokens = deltaUsage.output_tokens || 0;
|
|
1682
|
+
callbacks?.onUsageUpdate?.({ ...usage });
|
|
1683
|
+
}
|
|
2023
1684
|
break;
|
|
1685
|
+
}
|
|
2024
1686
|
}
|
|
2025
1687
|
}
|
|
2026
|
-
callbacks?.onThinkingEnd?.();
|
|
2027
1688
|
const toolCalls = Array.from(toolCallsMap.values());
|
|
2028
1689
|
return {
|
|
2029
1690
|
content: fullContent,
|
|
2030
1691
|
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
2031
|
-
rawResponse: null
|
|
1692
|
+
rawResponse: null,
|
|
1693
|
+
reasoning: fullReasoning || void 0,
|
|
1694
|
+
usage: usage.input_tokens > 0 || usage.output_tokens > 0 ? usage : void 0
|
|
2032
1695
|
};
|
|
2033
1696
|
}
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
1697
|
+
// ─── Helpers ─────────────────────────────────────────────────
|
|
1698
|
+
handleBlockStart(event, toolCallsMap, callbacks, onText, onReasoning) {
|
|
1699
|
+
const blockType = event.content_block?.type;
|
|
1700
|
+
if (blockType === "text") {
|
|
1701
|
+
onText();
|
|
1702
|
+
callbacks?.onOutputStart?.();
|
|
1703
|
+
} else if (blockType === "thinking" || blockType === "reasoning") {
|
|
1704
|
+
onReasoning();
|
|
1705
|
+
callbacks?.onReasoningStart?.();
|
|
1706
|
+
} else if (blockType === "tool_use") {
|
|
1707
|
+
const block = event.content_block;
|
|
1708
|
+
toolCallsMap.set(block.id, { id: block.id, name: block.name || "", input: block.input || {} });
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
filterSystemMessages(messages) {
|
|
1712
|
+
return messages.filter((m) => m.role !== "system");
|
|
1713
|
+
}
|
|
1714
|
+
calculateRetryDelay(attempt) {
|
|
1715
|
+
const exponentialDelay = RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt);
|
|
1716
|
+
const jitter = Math.random() * RETRY_CONFIG.jitterMs;
|
|
1717
|
+
return Math.min(exponentialDelay + jitter, RETRY_CONFIG.maxDelayMs);
|
|
1718
|
+
}
|
|
1719
|
+
extractErrorMessage(error) {
|
|
1720
|
+
const e = error;
|
|
1721
|
+
return e?.error?.error?.message || e?.error?.message || e?.message || String(error);
|
|
1722
|
+
}
|
|
1723
|
+
/** Abortable sleep — rejects immediately if signal fires */
|
|
1724
|
+
sleep(ms, signal) {
|
|
1725
|
+
return new Promise((resolve, reject) => {
|
|
1726
|
+
if (signal?.aborted) {
|
|
1727
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
const timer = setTimeout(resolve, ms);
|
|
1731
|
+
signal?.addEventListener("abort", () => {
|
|
1732
|
+
clearTimeout(timer);
|
|
1733
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1734
|
+
}, { once: true });
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
isAbortError(error, signal) {
|
|
1738
|
+
return error?.name === "AbortError" || !!signal?.aborted;
|
|
1739
|
+
}
|
|
1740
|
+
throwAbort() {
|
|
1741
|
+
throw new DOMException("Aborted", "AbortError");
|
|
2039
1742
|
}
|
|
2040
1743
|
};
|
|
2041
1744
|
var llmInstance = null;
|
|
@@ -2054,6 +1757,7 @@ var CoreAgent = class {
|
|
|
2054
1757
|
toolRegistry;
|
|
2055
1758
|
agentType;
|
|
2056
1759
|
maxIterations;
|
|
1760
|
+
abortController = null;
|
|
2057
1761
|
constructor(agentType, state, events, toolRegistry, maxIterations) {
|
|
2058
1762
|
this.agentType = agentType;
|
|
2059
1763
|
this.state = state;
|
|
@@ -2062,22 +1766,35 @@ var CoreAgent = class {
|
|
|
2062
1766
|
this.llm = getLLMClient();
|
|
2063
1767
|
this.maxIterations = maxIterations || AGENT_LIMITS.MAX_ITERATIONS;
|
|
2064
1768
|
}
|
|
2065
|
-
/**
|
|
2066
|
-
|
|
2067
|
-
|
|
1769
|
+
/** Abort the current execution — immediately cancels LLM calls and retries */
|
|
1770
|
+
abort() {
|
|
1771
|
+
this.abortController?.abort();
|
|
1772
|
+
}
|
|
1773
|
+
/** The core loop: Think → Act → Observe */
|
|
2068
1774
|
async run(task, systemPrompt) {
|
|
2069
|
-
|
|
1775
|
+
this.abortController = new AbortController();
|
|
1776
|
+
const messages = [{ role: LLM_ROLES.USER, content: task }];
|
|
2070
1777
|
let toolsExecutedCount = 0;
|
|
2071
1778
|
for (let iteration = 0; iteration < this.maxIterations; iteration++) {
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
1779
|
+
if (this.abortController.signal.aborted) {
|
|
1780
|
+
return this.buildCancelledResult(iteration, toolsExecutedCount);
|
|
1781
|
+
}
|
|
1782
|
+
try {
|
|
1783
|
+
const result = await this.step(iteration, messages, systemPrompt);
|
|
1784
|
+
toolsExecutedCount += result.toolsExecuted;
|
|
1785
|
+
if (result.completed) {
|
|
1786
|
+
return {
|
|
1787
|
+
output: result.output,
|
|
1788
|
+
iterations: iteration + 1,
|
|
1789
|
+
toolsExecuted: toolsExecutedCount,
|
|
1790
|
+
completed: true
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
} catch (error) {
|
|
1794
|
+
if (this.isAbortError(error)) {
|
|
1795
|
+
return this.buildCancelledResult(iteration, toolsExecutedCount);
|
|
1796
|
+
}
|
|
1797
|
+
throw error;
|
|
2081
1798
|
}
|
|
2082
1799
|
}
|
|
2083
1800
|
return {
|
|
@@ -2087,40 +1804,59 @@ var CoreAgent = class {
|
|
|
2087
1804
|
completed: false
|
|
2088
1805
|
};
|
|
2089
1806
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
*/
|
|
1807
|
+
// ─── Step Execution ──────────────────────────────────────────
|
|
1808
|
+
/** Execute a single Think → Act → Observe iteration */
|
|
2093
1809
|
async step(iteration, messages, systemPrompt) {
|
|
2094
1810
|
const phase = this.state.getPhase();
|
|
2095
|
-
|
|
1811
|
+
const stepStartTime = Date.now();
|
|
2096
1812
|
this.emitThink(iteration);
|
|
1813
|
+
const callbacks = this.buildStreamCallbacks(phase);
|
|
2097
1814
|
const response = await this.llm.generateResponseStream(
|
|
2098
1815
|
messages,
|
|
2099
1816
|
this.getToolSchemas(),
|
|
2100
1817
|
systemPrompt,
|
|
2101
|
-
|
|
2102
|
-
onThinkingStart: (phase2, iter) => {
|
|
2103
|
-
},
|
|
2104
|
-
onThinkingDelta: (content) => {
|
|
2105
|
-
this.emitThinkingDelta(content, phase);
|
|
2106
|
-
},
|
|
2107
|
-
onThinkingEnd: () => {
|
|
2108
|
-
this.emitThinkingEnd(phase);
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
1818
|
+
callbacks
|
|
2111
1819
|
);
|
|
2112
|
-
messages.push({ role:
|
|
2113
|
-
|
|
2114
|
-
|
|
1820
|
+
messages.push({ role: LLM_ROLES.ASSISTANT, content: response.content });
|
|
1821
|
+
const stepDuration = Date.now() - stepStartTime;
|
|
1822
|
+
const tokens = response.usage ? { input: response.usage.input_tokens, output: response.usage.output_tokens } : void 0;
|
|
1823
|
+
if (!response.toolCalls?.length) {
|
|
1824
|
+
this.emitComplete(response.content, iteration, 0, stepDuration, tokens);
|
|
2115
1825
|
return { output: response.content, toolsExecuted: 0, completed: true };
|
|
2116
1826
|
}
|
|
2117
1827
|
const results = await this.processToolCalls(response.toolCalls);
|
|
2118
1828
|
this.addToolResultsToMessages(messages, results);
|
|
2119
1829
|
return { output: "", toolsExecuted: results.length, completed: false };
|
|
2120
1830
|
}
|
|
1831
|
+
// ─── Callback Builder ────────────────────────────────────────
|
|
1832
|
+
buildStreamCallbacks(phase) {
|
|
1833
|
+
return {
|
|
1834
|
+
onReasoningStart: () => this.emitReasoningStart(phase),
|
|
1835
|
+
onReasoningDelta: (content) => this.emitReasoningDelta(content, phase),
|
|
1836
|
+
onReasoningEnd: () => this.emitReasoningEnd(phase),
|
|
1837
|
+
onOutputDelta: () => {
|
|
1838
|
+
},
|
|
1839
|
+
onRetry: (attempt, maxRetries, delayMs, error) => {
|
|
1840
|
+
this.events.emit({
|
|
1841
|
+
type: EVENT_TYPES.RETRY,
|
|
1842
|
+
timestamp: Date.now(),
|
|
1843
|
+
data: { attempt, maxRetries, delayMs, error, phase }
|
|
1844
|
+
});
|
|
1845
|
+
},
|
|
1846
|
+
onUsageUpdate: (usage) => {
|
|
1847
|
+
this.events.emit({
|
|
1848
|
+
type: EVENT_TYPES.USAGE_UPDATE,
|
|
1849
|
+
timestamp: Date.now(),
|
|
1850
|
+
data: { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens }
|
|
1851
|
+
});
|
|
1852
|
+
},
|
|
1853
|
+
abortSignal: this.abortController?.signal
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
// ─── Event Emitters ──────────────────────────────────────────
|
|
2121
1857
|
emitThink(iteration) {
|
|
2122
1858
|
this.events.emit({
|
|
2123
|
-
type:
|
|
1859
|
+
type: EVENT_TYPES.THINK,
|
|
2124
1860
|
timestamp: Date.now(),
|
|
2125
1861
|
data: {
|
|
2126
1862
|
thought: `${this.agentType} agent iteration ${iteration + 1}: Decision making`,
|
|
@@ -2128,60 +1864,34 @@ var CoreAgent = class {
|
|
|
2128
1864
|
}
|
|
2129
1865
|
});
|
|
2130
1866
|
}
|
|
2131
|
-
/**
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
emitThinkingStart(phase, iteration) {
|
|
2135
|
-
this.events.emit({
|
|
2136
|
-
type: "thinking_start",
|
|
2137
|
-
timestamp: Date.now(),
|
|
2138
|
-
data: {
|
|
2139
|
-
phase,
|
|
2140
|
-
iteration
|
|
2141
|
-
}
|
|
2142
|
-
});
|
|
1867
|
+
/** Emit reasoning lifecycle events for extended thinking */
|
|
1868
|
+
emitReasoningStart(phase) {
|
|
1869
|
+
this.events.emit({ type: EVENT_TYPES.REASONING_START, timestamp: Date.now(), data: { phase } });
|
|
2143
1870
|
}
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
* This provides real-time transparency of the model's thinking process
|
|
2147
|
-
*/
|
|
2148
|
-
emitThinkingDelta(content, phase) {
|
|
2149
|
-
this.events.emit({
|
|
2150
|
-
type: "thinking_delta",
|
|
2151
|
-
timestamp: Date.now(),
|
|
2152
|
-
data: {
|
|
2153
|
-
content,
|
|
2154
|
-
phase
|
|
2155
|
-
}
|
|
2156
|
-
});
|
|
1871
|
+
emitReasoningDelta(content, phase) {
|
|
1872
|
+
this.events.emit({ type: EVENT_TYPES.REASONING_DELTA, timestamp: Date.now(), data: { content, phase } });
|
|
2157
1873
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
*/
|
|
2161
|
-
emitThinkingEnd(phase) {
|
|
2162
|
-
this.events.emit({
|
|
2163
|
-
type: "thinking_end",
|
|
2164
|
-
timestamp: Date.now(),
|
|
2165
|
-
data: {
|
|
2166
|
-
phase
|
|
2167
|
-
}
|
|
2168
|
-
});
|
|
1874
|
+
emitReasoningEnd(phase) {
|
|
1875
|
+
this.events.emit({ type: EVENT_TYPES.REASONING_END, timestamp: Date.now(), data: { phase } });
|
|
2169
1876
|
}
|
|
2170
|
-
emitComplete(output, iteration, toolsExecuted) {
|
|
1877
|
+
emitComplete(output, iteration, toolsExecuted, durationMs, tokens) {
|
|
2171
1878
|
this.events.emit({
|
|
2172
|
-
type:
|
|
1879
|
+
type: EVENT_TYPES.COMPLETE,
|
|
2173
1880
|
timestamp: Date.now(),
|
|
2174
1881
|
data: {
|
|
2175
1882
|
finalOutput: output,
|
|
2176
1883
|
iterations: iteration + 1,
|
|
2177
|
-
toolsExecuted
|
|
1884
|
+
toolsExecuted,
|
|
1885
|
+
durationMs,
|
|
1886
|
+
tokens
|
|
2178
1887
|
}
|
|
2179
1888
|
});
|
|
2180
1889
|
}
|
|
1890
|
+
// ─── Tool Processing ─────────────────────────────────────────
|
|
2181
1891
|
addToolResultsToMessages(messages, results) {
|
|
2182
1892
|
for (const res of results) {
|
|
2183
1893
|
messages.push({
|
|
2184
|
-
role:
|
|
1894
|
+
role: LLM_ROLES.USER,
|
|
2185
1895
|
content: [
|
|
2186
1896
|
{
|
|
2187
1897
|
type: "tool_result",
|
|
@@ -2196,10 +1906,11 @@ var CoreAgent = class {
|
|
|
2196
1906
|
async processToolCalls(toolCalls) {
|
|
2197
1907
|
const results = [];
|
|
2198
1908
|
for (const call of toolCalls) {
|
|
2199
|
-
const toolName = call.name;
|
|
2200
|
-
const toolInput = call.input;
|
|
2201
1909
|
try {
|
|
2202
|
-
const result = await this.toolRegistry.execute({
|
|
1910
|
+
const result = await this.toolRegistry.execute({
|
|
1911
|
+
name: call.name,
|
|
1912
|
+
input: call.input
|
|
1913
|
+
});
|
|
2203
1914
|
results.push({ toolCallId: call.id, output: result.output, error: result.error });
|
|
2204
1915
|
} catch (error) {
|
|
2205
1916
|
results.push({ toolCallId: call.id, output: String(error), error: String(error) });
|
|
@@ -2218,103 +1929,137 @@ var CoreAgent = class {
|
|
|
2218
1929
|
}
|
|
2219
1930
|
}));
|
|
2220
1931
|
}
|
|
1932
|
+
// ─── Abort Helpers ───────────────────────────────────────────
|
|
1933
|
+
isAbortError(error) {
|
|
1934
|
+
return error?.name === "AbortError" || !!this.abortController?.signal.aborted;
|
|
1935
|
+
}
|
|
1936
|
+
buildCancelledResult(iteration, toolsExecuted) {
|
|
1937
|
+
return {
|
|
1938
|
+
output: "Operation cancelled by user",
|
|
1939
|
+
iterations: iteration,
|
|
1940
|
+
toolsExecuted,
|
|
1941
|
+
completed: false
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
2221
1944
|
};
|
|
2222
1945
|
|
|
2223
1946
|
// src/agents/prompt-builder.ts
|
|
2224
1947
|
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
2225
1948
|
import { join as join3, dirname as dirname2 } from "path";
|
|
2226
1949
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1950
|
+
|
|
1951
|
+
// src/shared/constants/prompts.ts
|
|
1952
|
+
var PROMPT_XML = {
|
|
1953
|
+
PHASE: (phase, content) => `<phase-instructions phase="${phase}">
|
|
1954
|
+
${content}
|
|
1955
|
+
</phase-instructions>`,
|
|
1956
|
+
SCOPE: (allowed, domains, exclude, flags) => `<scope type="ABSOLUTE_CONSTRAINT">
|
|
1957
|
+
Authorized CIDR: ${allowed}
|
|
1958
|
+
Authorized Domains: ${domains}
|
|
1959
|
+
Exclusions: ${exclude}
|
|
1960
|
+
Constraints: ${flags}
|
|
1961
|
+
</scope>`,
|
|
1962
|
+
STATE: (content) => `<current-state>
|
|
1963
|
+
${content}
|
|
1964
|
+
</current-state>`,
|
|
1965
|
+
TODO: (content) => `<todo>
|
|
1966
|
+
${content}
|
|
1967
|
+
</todo>`
|
|
1968
|
+
};
|
|
1969
|
+
var PROMPT_DEFAULTS = {
|
|
1970
|
+
NO_SCOPE: "<scope>NO SCOPE DEFINED. STOP.</scope>",
|
|
1971
|
+
EMPTY_TODO: "Create initial plan",
|
|
1972
|
+
USER_CONTEXT: (context) => `User Context: ${context}`
|
|
1973
|
+
};
|
|
1974
|
+
var PROMPT_PATHS = {
|
|
1975
|
+
ROOT: "../shared/prompts",
|
|
1976
|
+
PHASES: "phases",
|
|
1977
|
+
BASE: "base.md",
|
|
1978
|
+
FILE_EXT: ".md",
|
|
1979
|
+
AGENT_FILES: {
|
|
1980
|
+
ORCHESTRATOR: "orchestrator.md",
|
|
1981
|
+
RECON: "recon.md",
|
|
1982
|
+
VULN: "vuln.md",
|
|
1983
|
+
EXPLOIT: "exploit.md",
|
|
1984
|
+
POST: "post.md",
|
|
1985
|
+
REPORT: "report.md",
|
|
1986
|
+
INFRA: "infra.md"
|
|
1987
|
+
}
|
|
1988
|
+
};
|
|
1989
|
+
var PROMPT_CONFIG = {
|
|
1990
|
+
ENCODING: "utf-8"
|
|
1991
|
+
};
|
|
1992
|
+
var INITIAL_TASKS = {
|
|
1993
|
+
RECON: "Initial reconnaissance and target discovery"
|
|
1994
|
+
};
|
|
1995
|
+
|
|
1996
|
+
// src/agents/prompt-builder.ts
|
|
2227
1997
|
var __dirname3 = dirname2(fileURLToPath3(import.meta.url));
|
|
2228
1998
|
var PromptBuilder = class {
|
|
2229
1999
|
state;
|
|
2230
2000
|
promptDir;
|
|
2231
2001
|
constructor(state) {
|
|
2232
2002
|
this.state = state;
|
|
2233
|
-
this.promptDir = join3(__dirname3,
|
|
2003
|
+
this.promptDir = join3(__dirname3, PROMPT_PATHS.ROOT);
|
|
2234
2004
|
}
|
|
2235
2005
|
/**
|
|
2236
2006
|
* Build complete prompt for LLM iteration
|
|
2007
|
+
* Implements §7-2 (Abstraction Level Consistency)
|
|
2237
2008
|
*/
|
|
2238
2009
|
build(userInput, phase) {
|
|
2239
|
-
|
|
2240
|
-
this.loadFragment(
|
|
2010
|
+
const fragments = [
|
|
2011
|
+
this.loadFragment(PROMPT_PATHS.BASE),
|
|
2241
2012
|
this.loadPhasePrompt(phase),
|
|
2242
2013
|
this.getScopeFragment(),
|
|
2243
2014
|
this.getStateFragment(),
|
|
2244
2015
|
this.getTodoFragment(),
|
|
2245
|
-
|
|
2246
|
-
]
|
|
2016
|
+
PROMPT_DEFAULTS.USER_CONTEXT(userInput)
|
|
2017
|
+
];
|
|
2018
|
+
return fragments.filter((f) => !!f).join("\n\n");
|
|
2247
2019
|
}
|
|
2248
2020
|
loadFragment(filename) {
|
|
2249
2021
|
const path2 = join3(this.promptDir, filename);
|
|
2250
|
-
|
|
2251
|
-
return readFileSync2(path2, "utf-8");
|
|
2022
|
+
return existsSync2(path2) ? readFileSync2(path2, PROMPT_CONFIG.ENCODING) : "";
|
|
2252
2023
|
}
|
|
2253
2024
|
loadPhasePrompt(phase) {
|
|
2254
|
-
const path2 = join3(this.promptDir,
|
|
2255
|
-
return existsSync2(path2) ?
|
|
2256
|
-
${readFileSync2(path2, "utf-8")}
|
|
2257
|
-
</phase-instructions>` : "";
|
|
2025
|
+
const path2 = join3(this.promptDir, PROMPT_PATHS.PHASES, `${phase}${PROMPT_PATHS.FILE_EXT}`);
|
|
2026
|
+
return existsSync2(path2) ? PROMPT_XML.PHASE(phase, readFileSync2(path2, PROMPT_CONFIG.ENCODING)) : "";
|
|
2258
2027
|
}
|
|
2259
2028
|
getScopeFragment() {
|
|
2260
2029
|
const scope = this.state.getScope();
|
|
2261
|
-
if (!scope) return
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2030
|
+
if (!scope) return PROMPT_DEFAULTS.NO_SCOPE;
|
|
2031
|
+
const flags = `NoDoS: ${scope.noDOS} | NoSocial: ${scope.noSocial}`;
|
|
2032
|
+
return PROMPT_XML.SCOPE(
|
|
2033
|
+
scope.allowedCidrs.join(", ") || "none",
|
|
2034
|
+
scope.allowedDomains.join(", ") || "none",
|
|
2035
|
+
scope.exclusions.join(", ") || "none",
|
|
2036
|
+
flags
|
|
2037
|
+
);
|
|
2268
2038
|
}
|
|
2269
2039
|
getStateFragment() {
|
|
2270
|
-
return
|
|
2271
|
-
${this.state.toPrompt()}
|
|
2272
|
-
</current-state>`;
|
|
2040
|
+
return PROMPT_XML.STATE(this.state.toPrompt());
|
|
2273
2041
|
}
|
|
2274
2042
|
getTodoFragment() {
|
|
2275
2043
|
const todo = this.state.getTodo();
|
|
2276
|
-
const list = todo.map((t) => `[${t.status ===
|
|
2277
|
-
return
|
|
2278
|
-
${list || "Create initial plan"}
|
|
2279
|
-
</todo>`;
|
|
2280
|
-
}
|
|
2281
|
-
};
|
|
2282
|
-
|
|
2283
|
-
// src/agents/sub-agent-executor.ts
|
|
2284
|
-
var SubAgentExecutor = class extends CoreAgent {
|
|
2285
|
-
constructor(state, events, toolRegistry) {
|
|
2286
|
-
super(AGENT_ROLES.RECON, state, events, toolRegistry);
|
|
2287
|
-
}
|
|
2288
|
-
/**
|
|
2289
|
-
* Entry point for executing a sub-task
|
|
2290
|
-
*/
|
|
2291
|
-
async executeSubTask(task, systemPrompt) {
|
|
2292
|
-
console.log(`[SubAgent] Executing specialized task: ${task.slice(0, 50)}...`);
|
|
2293
|
-
return await this.run(task, systemPrompt);
|
|
2044
|
+
const list = todo.map((t) => `[${t.status === TODO_STATUSES.DONE ? "x" : " "}] ${t.content} (${t.priority})`).join("\n");
|
|
2045
|
+
return PROMPT_XML.TODO(list || PROMPT_DEFAULTS.EMPTY_TODO);
|
|
2294
2046
|
}
|
|
2295
2047
|
};
|
|
2296
2048
|
|
|
2297
2049
|
// src/agents/main-agent.ts
|
|
2298
2050
|
var MainAgent = class extends CoreAgent {
|
|
2299
|
-
promptBuilder;
|
|
2300
|
-
approvalGate;
|
|
2301
|
-
scopeGuard;
|
|
2302
|
-
userInput = "";
|
|
2303
|
-
constructor() {
|
|
2304
|
-
const state = new SharedState();
|
|
2305
|
-
const events = new AgentEventEmitter();
|
|
2306
|
-
const approvalGate = new ApprovalGate(false);
|
|
2307
|
-
const scopeGuard = new ScopeGuard(state);
|
|
2308
|
-
const subAgentExecutor = new SubAgentExecutor(state, events, null);
|
|
2309
|
-
const toolRegistry = new CategorizedToolRegistry(state, scopeGuard, approvalGate, events, subAgentExecutor);
|
|
2310
|
-
subAgentExecutor.toolRegistry = toolRegistry;
|
|
2051
|
+
promptBuilder;
|
|
2052
|
+
approvalGate;
|
|
2053
|
+
scopeGuard;
|
|
2054
|
+
userInput = "";
|
|
2055
|
+
constructor(state, events, toolRegistry, approvalGate, scopeGuard) {
|
|
2311
2056
|
super(AGENT_ROLES.ORCHESTRATOR, state, events, toolRegistry);
|
|
2312
2057
|
this.approvalGate = approvalGate;
|
|
2313
2058
|
this.scopeGuard = scopeGuard;
|
|
2314
2059
|
this.promptBuilder = new PromptBuilder(state);
|
|
2315
2060
|
}
|
|
2316
2061
|
/**
|
|
2317
|
-
* Public entry point for running the agent
|
|
2062
|
+
* Public entry point for running the agent
|
|
2318
2063
|
*/
|
|
2319
2064
|
async execute(userInput) {
|
|
2320
2065
|
this.userInput = userInput;
|
|
@@ -2323,26 +2068,19 @@ var MainAgent = class extends CoreAgent {
|
|
|
2323
2068
|
const result = await this.run(userInput, this.getCurrentPrompt());
|
|
2324
2069
|
return result.output;
|
|
2325
2070
|
}
|
|
2326
|
-
/**
|
|
2327
|
-
* Override CoreAgent.run to enforce dynamic prompt injection per iteration
|
|
2328
|
-
* But keep the signature compatible with CoreAgent
|
|
2329
|
-
*/
|
|
2330
2071
|
async run(task, _systemPrompt) {
|
|
2331
2072
|
return super.run(task, _systemPrompt);
|
|
2332
2073
|
}
|
|
2333
|
-
/**
|
|
2334
|
-
* Override step to ensure prompt is always fresh with latest state
|
|
2335
|
-
*/
|
|
2336
2074
|
async step(iteration, messages, _unusedPrompt) {
|
|
2337
2075
|
const dynamicPrompt = this.getCurrentPrompt();
|
|
2338
2076
|
const result = await super.step(iteration, messages, dynamicPrompt);
|
|
2339
2077
|
this.emitStateChange();
|
|
2340
2078
|
return result;
|
|
2341
2079
|
}
|
|
2342
|
-
// --- Internal Helpers
|
|
2080
|
+
// --- Internal Helpers ---
|
|
2343
2081
|
initializeTask() {
|
|
2344
2082
|
if (this.state.getTodo().length === 0) {
|
|
2345
|
-
this.state.addTodo(
|
|
2083
|
+
this.state.addTodo(INITIAL_TASKS.RECON, PRIORITIES.HIGH);
|
|
2346
2084
|
}
|
|
2347
2085
|
}
|
|
2348
2086
|
getCurrentPrompt() {
|
|
@@ -2351,14 +2089,14 @@ var MainAgent = class extends CoreAgent {
|
|
|
2351
2089
|
}
|
|
2352
2090
|
emitStart(userInput) {
|
|
2353
2091
|
this.events.emit({
|
|
2354
|
-
type:
|
|
2092
|
+
type: EVENT_TYPES.START,
|
|
2355
2093
|
timestamp: Date.now(),
|
|
2356
2094
|
data: { userInput, phase: this.state.getPhase() }
|
|
2357
2095
|
});
|
|
2358
2096
|
}
|
|
2359
2097
|
emitStateChange() {
|
|
2360
2098
|
this.events.emit({
|
|
2361
|
-
type:
|
|
2099
|
+
type: EVENT_TYPES.STATE_CHANGE,
|
|
2362
2100
|
timestamp: Date.now(),
|
|
2363
2101
|
data: {
|
|
2364
2102
|
phase: this.state.getPhase(),
|
|
@@ -2403,6 +2141,43 @@ var MainAgent = class extends CoreAgent {
|
|
|
2403
2141
|
}
|
|
2404
2142
|
};
|
|
2405
2143
|
|
|
2144
|
+
// src/agents/sub-agent-executor.ts
|
|
2145
|
+
var SubAgentExecutor = class extends CoreAgent {
|
|
2146
|
+
constructor(state, events, toolRegistry) {
|
|
2147
|
+
super(AGENT_ROLES.RECON, state, events, toolRegistry);
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* Entry point for executing a sub-task
|
|
2151
|
+
*/
|
|
2152
|
+
async executeSubTask(task, systemPrompt) {
|
|
2153
|
+
console.log(`[SubAgent] Executing specialized task: ${task.slice(0, 50)}...`);
|
|
2154
|
+
return await this.run(task, systemPrompt);
|
|
2155
|
+
}
|
|
2156
|
+
};
|
|
2157
|
+
|
|
2158
|
+
// src/agents/factory.ts
|
|
2159
|
+
var AgentFactory = class {
|
|
2160
|
+
/**
|
|
2161
|
+
* Create a fully initialized MainAgent system
|
|
2162
|
+
*/
|
|
2163
|
+
static createMainAgent(autoApprove = false) {
|
|
2164
|
+
const state = new SharedState();
|
|
2165
|
+
const events = new AgentEventEmitter();
|
|
2166
|
+
const approvalGate = new ApprovalGate(autoApprove);
|
|
2167
|
+
const scopeGuard = new ScopeGuard(state);
|
|
2168
|
+
const subAgentExecutor = new SubAgentExecutor(state, events, null);
|
|
2169
|
+
const toolRegistry = new CategorizedToolRegistry(
|
|
2170
|
+
state,
|
|
2171
|
+
scopeGuard,
|
|
2172
|
+
approvalGate,
|
|
2173
|
+
events,
|
|
2174
|
+
subAgentExecutor
|
|
2175
|
+
);
|
|
2176
|
+
subAgentExecutor.toolRegistry = toolRegistry;
|
|
2177
|
+
return new MainAgent(state, events, toolRegistry, approvalGate, scopeGuard);
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
|
|
2406
2181
|
// src/shared/constants/thought.ts
|
|
2407
2182
|
var THOUGHT_TYPE = {
|
|
2408
2183
|
THINKING: "thinking",
|
|
@@ -2555,378 +2330,586 @@ var THOUGHT_LABELS = {
|
|
|
2555
2330
|
[THOUGHT_TYPE.BREAKTHROUGH]: "[!]"
|
|
2556
2331
|
};
|
|
2557
2332
|
|
|
2558
|
-
// src/platform/tui/
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
/* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: phase })
|
|
2573
|
-
] }),
|
|
2574
|
-
/* @__PURE__ */ jsxs(Text, { color: THEME.text.secondary, children: [
|
|
2575
|
-
"Targets: ",
|
|
2576
|
-
/* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: targets })
|
|
2577
|
-
] }),
|
|
2578
|
-
/* @__PURE__ */ jsxs(Text, { color: THEME.status.warning, children: [
|
|
2579
|
-
"Findings: ",
|
|
2580
|
-
/* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: findings })
|
|
2581
|
-
] }),
|
|
2582
|
-
/* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
|
|
2583
|
-
"Tasks: ",
|
|
2584
|
-
/* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: todo })
|
|
2585
|
-
] })
|
|
2586
|
-
] }),
|
|
2587
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
2588
|
-
/* @__PURE__ */ jsx(Text, { color: isProcessing ? THEME.status.running : THEME.text.muted, children: isProcessing ? "\u25CF Running " : "\u25CB Idle " }),
|
|
2589
|
-
/* @__PURE__ */ jsxs(Text, { color: THEME.text.secondary, children: [
|
|
2590
|
-
Math.floor(elapsedTime / 60),
|
|
2591
|
-
"m ",
|
|
2592
|
-
Math.floor(elapsedTime % 60),
|
|
2593
|
-
"s"
|
|
2594
|
-
] })
|
|
2595
|
-
] })
|
|
2596
|
-
]
|
|
2597
|
-
}
|
|
2598
|
-
);
|
|
2333
|
+
// src/platform/tui/constants/display.ts
|
|
2334
|
+
var DISPLAY_LIMITS2 = {
|
|
2335
|
+
reasoningBuffer: 1e3,
|
|
2336
|
+
reasoningPreview: 300,
|
|
2337
|
+
reasoningHistorySlice: 500,
|
|
2338
|
+
toolInputPreview: 50,
|
|
2339
|
+
toolInputTruncated: 47,
|
|
2340
|
+
toolOutputPreview: 300,
|
|
2341
|
+
statusThoughtPreview: 100,
|
|
2342
|
+
statusThoughtTruncated: 97,
|
|
2343
|
+
retryErrorPreview: 40,
|
|
2344
|
+
retryErrorTruncated: 37,
|
|
2345
|
+
timerInterval: 1e3,
|
|
2346
|
+
exitDelay: 100
|
|
2599
2347
|
};
|
|
2600
|
-
var
|
|
2348
|
+
var MESSAGE_STYLES = {
|
|
2349
|
+
colors: {
|
|
2350
|
+
user: THEME.text.accent,
|
|
2351
|
+
assistant: THEME.text.primary,
|
|
2352
|
+
system: THEME.text.muted,
|
|
2353
|
+
error: THEME.status.error,
|
|
2354
|
+
tool: THEME.status.running,
|
|
2355
|
+
result: THEME.text.muted,
|
|
2356
|
+
reasoning: THEME.status.warning
|
|
2357
|
+
},
|
|
2358
|
+
prefixes: {
|
|
2359
|
+
user: "\u276F",
|
|
2360
|
+
assistant: ">",
|
|
2361
|
+
system: "\u2022",
|
|
2362
|
+
error: "\u2717",
|
|
2363
|
+
tool: " \u23BF",
|
|
2364
|
+
result: " \u2713",
|
|
2365
|
+
reasoning: "REASONING"
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
var HELP_TEXT = `
|
|
2369
|
+
\u2500\u2500 Commands \u2500\u2500
|
|
2370
|
+
/target <ip> Set target
|
|
2371
|
+
/start [goal] Start autonomous pentest
|
|
2372
|
+
/stop Stop operation
|
|
2373
|
+
/findings Show findings
|
|
2374
|
+
/status Show status
|
|
2375
|
+
/clear Clear screen
|
|
2376
|
+
/exit Exit
|
|
2601
2377
|
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2378
|
+
\u2500\u2500 Examples \u2500\u2500
|
|
2379
|
+
/target 192.168.1.1
|
|
2380
|
+
/start "Recon the target"
|
|
2381
|
+
/findings
|
|
2382
|
+
`;
|
|
2383
|
+
|
|
2384
|
+
// src/platform/tui/utils/format.ts
|
|
2385
|
+
var formatDuration = (ms) => {
|
|
2386
|
+
const totalSec = ms / 1e3;
|
|
2387
|
+
if (totalSec < 60) return `${totalSec.toFixed(1)}s`;
|
|
2388
|
+
const minutes = Math.floor(totalSec / 60);
|
|
2389
|
+
const seconds = Math.floor(totalSec % 60);
|
|
2390
|
+
return `${minutes}m ${seconds}s`;
|
|
2391
|
+
};
|
|
2392
|
+
var formatTokens = (count) => {
|
|
2393
|
+
if (count >= 1e6) return (count / 1e6).toFixed(1) + "M";
|
|
2394
|
+
if (count >= 1e3) return (count / 1e3).toFixed(1) + "K";
|
|
2395
|
+
return String(count);
|
|
2396
|
+
};
|
|
2397
|
+
var formatMeta = (ms, tokens) => {
|
|
2398
|
+
const parts = [];
|
|
2399
|
+
if (ms > 0) parts.push(formatDuration(ms));
|
|
2400
|
+
if (tokens > 0) parts.push(`\u2191 ${formatTokens(tokens)} tokens`);
|
|
2401
|
+
return parts.length > 0 ? `(${parts.join(" \xB7 ")})` : "";
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
// src/platform/tui/hooks/useAgent.ts
|
|
2405
|
+
var useAgent = (autoApprove, target) => {
|
|
2606
2406
|
const [messages, setMessages] = useState([]);
|
|
2607
|
-
const [input, setInput] = useState("");
|
|
2608
2407
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
2609
2408
|
const [currentStatus, setCurrentStatus] = useState("");
|
|
2610
2409
|
const [elapsedTime, setElapsedTime] = useState(0);
|
|
2611
|
-
const [
|
|
2612
|
-
const [
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2410
|
+
const [reasoning, setReasoning] = useState({ isActive: false, content: "", phase: "" });
|
|
2411
|
+
const [retryState, setRetryState] = useState({
|
|
2412
|
+
isRetrying: false,
|
|
2413
|
+
attempt: 0,
|
|
2414
|
+
maxRetries: 0,
|
|
2415
|
+
delayMs: 0,
|
|
2416
|
+
error: "",
|
|
2417
|
+
countdown: 0
|
|
2617
2418
|
});
|
|
2618
|
-
const [
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
ag.addTarget(target);
|
|
2625
|
-
ag.setScope([target]);
|
|
2626
|
-
}
|
|
2627
|
-
return ag;
|
|
2419
|
+
const [currentTokens, setCurrentTokens] = useState(0);
|
|
2420
|
+
const [inputRequest, setInputRequest] = useState({
|
|
2421
|
+
active: false,
|
|
2422
|
+
prompt: "",
|
|
2423
|
+
isPassword: false,
|
|
2424
|
+
resolve: null
|
|
2628
2425
|
});
|
|
2426
|
+
const [stats, setStats] = useState({ phase: "init", targets: 0, findings: 0, todo: 0 });
|
|
2427
|
+
const lastResponseMetaRef = useRef(null);
|
|
2629
2428
|
const startTimeRef = useRef(0);
|
|
2630
2429
|
const timerRef = useRef(null);
|
|
2631
|
-
const
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
timerRef.current = null;
|
|
2430
|
+
const retryCountdownRef = useRef(null);
|
|
2431
|
+
const retryCountRef = useRef(0);
|
|
2432
|
+
const tokenAccumRef = useRef(0);
|
|
2433
|
+
const lastStepTokensRef = useRef(0);
|
|
2434
|
+
const [agent] = useState(() => AgentFactory.createMainAgent(autoApprove));
|
|
2435
|
+
useEffect(() => {
|
|
2436
|
+
if (target) {
|
|
2437
|
+
agent.addTarget(target);
|
|
2438
|
+
agent.setScope([target]);
|
|
2641
2439
|
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
return duration;
|
|
2645
|
-
}, []);
|
|
2440
|
+
}, [agent, target]);
|
|
2441
|
+
const eventsRef = useRef(agent.getEventEmitter());
|
|
2646
2442
|
const addMessage = useCallback((type, content) => {
|
|
2647
2443
|
const id = Math.random().toString(36).substring(7);
|
|
2648
2444
|
setMessages((prev) => [...prev, { id, type, content, timestamp: /* @__PURE__ */ new Date() }]);
|
|
2649
2445
|
}, []);
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2446
|
+
const resetCumulativeCounters = useCallback(() => {
|
|
2447
|
+
setCurrentTokens(0);
|
|
2448
|
+
retryCountRef.current = 0;
|
|
2449
|
+
tokenAccumRef.current = 0;
|
|
2450
|
+
lastStepTokensRef.current = 0;
|
|
2451
|
+
lastResponseMetaRef.current = null;
|
|
2452
|
+
}, []);
|
|
2453
|
+
const manageTimer = useCallback((action) => {
|
|
2454
|
+
if (action === "start") {
|
|
2455
|
+
startTimeRef.current = Date.now();
|
|
2456
|
+
timerRef.current = setInterval(() => {
|
|
2457
|
+
setElapsedTime(Math.floor((Date.now() - startTimeRef.current) / 1e3));
|
|
2458
|
+
}, DISPLAY_LIMITS2.timerInterval);
|
|
2459
|
+
} else {
|
|
2460
|
+
if (timerRef.current) clearInterval(timerRef.current);
|
|
2461
|
+
timerRef.current = null;
|
|
2462
|
+
setElapsedTime(0);
|
|
2653
2463
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2464
|
+
}, []);
|
|
2465
|
+
const executeTask = useCallback(async (task) => {
|
|
2466
|
+
setIsProcessing(true);
|
|
2467
|
+
manageTimer("start");
|
|
2468
|
+
setCurrentStatus("Thinking");
|
|
2469
|
+
resetCumulativeCounters();
|
|
2470
|
+
try {
|
|
2471
|
+
const response = await agent.execute(task);
|
|
2472
|
+
const meta = lastResponseMetaRef.current;
|
|
2473
|
+
const suffix = meta ? ` ${formatMeta(meta.durationMs || 0, (meta.tokens?.input || 0) + (meta.tokens?.output || 0))}` : "";
|
|
2474
|
+
addMessage(UI_ROLES.AI, response + suffix);
|
|
2475
|
+
} catch (e) {
|
|
2476
|
+
addMessage(UI_ROLES.ERROR, e instanceof Error ? e.message : String(e));
|
|
2477
|
+
} finally {
|
|
2478
|
+
manageTimer("stop");
|
|
2479
|
+
setIsProcessing(false);
|
|
2480
|
+
setCurrentStatus("");
|
|
2656
2481
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
}
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
setCurrentStatus(`Executing: ${data.toolName}`);
|
|
2482
|
+
}, [agent, addMessage, manageTimer, resetCumulativeCounters]);
|
|
2483
|
+
const abort = useCallback(() => {
|
|
2484
|
+
agent.abort();
|
|
2485
|
+
setIsProcessing(false);
|
|
2486
|
+
manageTimer("stop");
|
|
2487
|
+
setCurrentStatus("");
|
|
2488
|
+
addMessage(UI_ROLES.SYSTEM, "Interrupted");
|
|
2489
|
+
}, [agent, addMessage, manageTimer]);
|
|
2490
|
+
const cancelInputRequest = useCallback(() => {
|
|
2491
|
+
if (inputRequest.active && inputRequest.resolve) {
|
|
2492
|
+
inputRequest.resolve(null);
|
|
2493
|
+
setInputRequest({ active: false, prompt: "", isPassword: false, resolve: null });
|
|
2494
|
+
addMessage(UI_ROLES.SYSTEM, "Input cancelled");
|
|
2495
|
+
}
|
|
2496
|
+
}, [inputRequest, addMessage]);
|
|
2497
|
+
useEffect(() => {
|
|
2498
|
+
const events = eventsRef.current;
|
|
2499
|
+
const onToolCall = (e) => {
|
|
2500
|
+
setCurrentStatus(`Executing: ${e.data.toolName}`);
|
|
2677
2501
|
let inputStr = "";
|
|
2678
2502
|
try {
|
|
2679
|
-
if (data.input) {
|
|
2680
|
-
const str = JSON.stringify(data.input);
|
|
2681
|
-
inputStr = str.length >
|
|
2503
|
+
if (e.data.input) {
|
|
2504
|
+
const str = JSON.stringify(e.data.input);
|
|
2505
|
+
inputStr = str.length > DISPLAY_LIMITS2.toolInputPreview ? str.substring(0, DISPLAY_LIMITS2.toolInputTruncated) + "..." : str;
|
|
2682
2506
|
}
|
|
2683
|
-
} catch
|
|
2507
|
+
} catch {
|
|
2684
2508
|
}
|
|
2685
|
-
addMessage("tool", ` \u23BF ${data.toolName} ${inputStr}`);
|
|
2686
|
-
}
|
|
2687
|
-
|
|
2688
|
-
const
|
|
2689
|
-
const
|
|
2690
|
-
const
|
|
2691
|
-
const preview = output.slice(0, 300).replace(/\n/g, " ");
|
|
2692
|
-
const more = output.length > 300 ? "..." : "";
|
|
2509
|
+
addMessage("tool", ` \u23BF ${e.data.toolName} ${inputStr}`);
|
|
2510
|
+
};
|
|
2511
|
+
const onToolResult = (e) => {
|
|
2512
|
+
const icon = e.data.success ? "\u2713" : "\u2717";
|
|
2513
|
+
const preview = (e.data.output || "").slice(0, DISPLAY_LIMITS2.toolOutputPreview).replace(/\n/g, " ");
|
|
2514
|
+
const more = (e.data.output || "").length > DISPLAY_LIMITS2.toolOutputPreview ? "..." : "";
|
|
2693
2515
|
addMessage("result", ` ${icon} ${preview}${more}`);
|
|
2516
|
+
};
|
|
2517
|
+
const onReasoningStart = (e) => setReasoning({ isActive: true, content: "", phase: e.data.phase });
|
|
2518
|
+
const onReasoningDelta = (e) => setReasoning((prev) => ({
|
|
2519
|
+
...prev,
|
|
2520
|
+
content: (prev.content + e.data.content).slice(-DISPLAY_LIMITS2.reasoningBuffer)
|
|
2521
|
+
}));
|
|
2522
|
+
const onReasoningEnd = () => setReasoning((prev) => {
|
|
2523
|
+
if (prev.content) addMessage("reasoning", `REASONING ${prev.content.slice(-DISPLAY_LIMITS2.reasoningHistorySlice)}`);
|
|
2524
|
+
return { ...prev, isActive: false };
|
|
2694
2525
|
});
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2526
|
+
const onComplete = (e) => {
|
|
2527
|
+
addMessage("system", `\u2713 Complete ${formatMeta(e.data.durationMs || 0, (e.data.tokens?.input || 0) + (e.data.tokens?.output || 0))}`);
|
|
2528
|
+
lastResponseMetaRef.current = { durationMs: e.data.durationMs, tokens: e.data.tokens };
|
|
2529
|
+
};
|
|
2530
|
+
const onRetry = (e) => {
|
|
2531
|
+
const delaySec = Math.ceil(e.data.delayMs / 1e3);
|
|
2532
|
+
retryCountRef.current += 1;
|
|
2533
|
+
const retryNum = retryCountRef.current;
|
|
2534
|
+
addMessage("system", `\u27F3 Retry #${retryNum} \xB7 ${e.data.error} \xB7 waiting ${delaySec}s...`);
|
|
2535
|
+
setRetryState({ isRetrying: true, attempt: retryNum, maxRetries: e.data.maxRetries, delayMs: e.data.delayMs, error: e.data.error, countdown: delaySec });
|
|
2536
|
+
if (retryCountdownRef.current) clearInterval(retryCountdownRef.current);
|
|
2537
|
+
let remaining = delaySec;
|
|
2538
|
+
retryCountdownRef.current = setInterval(() => {
|
|
2539
|
+
remaining -= 1;
|
|
2540
|
+
if (remaining <= 0) {
|
|
2541
|
+
setRetryState((prev) => ({ ...prev, isRetrying: false, countdown: 0 }));
|
|
2542
|
+
if (retryCountdownRef.current) clearInterval(retryCountdownRef.current);
|
|
2543
|
+
} else {
|
|
2544
|
+
setRetryState((prev) => ({ ...prev, countdown: remaining }));
|
|
2545
|
+
}
|
|
2546
|
+
}, 1e3);
|
|
2547
|
+
};
|
|
2548
|
+
const onUsageUpdate = (e) => {
|
|
2549
|
+
const stepTokens = e.data.inputTokens + e.data.outputTokens;
|
|
2550
|
+
if (stepTokens < lastStepTokensRef.current) tokenAccumRef.current += lastStepTokensRef.current;
|
|
2551
|
+
lastStepTokensRef.current = stepTokens;
|
|
2552
|
+
setCurrentTokens(tokenAccumRef.current + stepTokens);
|
|
2553
|
+
};
|
|
2554
|
+
setInputHandler((p) => {
|
|
2555
|
+
return new Promise((resolve) => setInputRequest({ active: true, prompt: p.trim(), isPassword: /password|passphrase/i.test(p), resolve }));
|
|
2707
2556
|
});
|
|
2708
2557
|
const updateStats = () => {
|
|
2709
|
-
const
|
|
2710
|
-
setStats({
|
|
2711
|
-
phase: agent.getPhase(),
|
|
2712
|
-
targets: state.getTargets().size,
|
|
2713
|
-
findings: state.getFindings().length,
|
|
2714
|
-
todo: state.getTodo().length
|
|
2715
|
-
});
|
|
2558
|
+
const s = agent.getState();
|
|
2559
|
+
setStats({ phase: agent.getPhase(), targets: s.getTargets().size, findings: s.getFindings().length, todo: s.getTodo().length });
|
|
2716
2560
|
};
|
|
2561
|
+
events.on("tool_call", onToolCall);
|
|
2562
|
+
events.on("tool_result", onToolResult);
|
|
2563
|
+
events.on("reasoning_start", onReasoningStart);
|
|
2564
|
+
events.on("reasoning_delta", onReasoningDelta);
|
|
2565
|
+
events.on("reasoning_end", onReasoningEnd);
|
|
2566
|
+
events.on("think", (e) => {
|
|
2567
|
+
const t = e.data.thought;
|
|
2568
|
+
setCurrentStatus(t.length > DISPLAY_LIMITS2.statusThoughtPreview ? t.substring(0, DISPLAY_LIMITS2.statusThoughtTruncated) + "..." : t);
|
|
2569
|
+
});
|
|
2570
|
+
events.on("complete", onComplete);
|
|
2571
|
+
events.on("error", (e) => addMessage("error", e.data.message || "An error occurred"));
|
|
2572
|
+
events.on("retry", onRetry);
|
|
2573
|
+
events.on("usage_update", onUsageUpdate);
|
|
2717
2574
|
events.on("state_change", updateStats);
|
|
2718
2575
|
events.on("start", updateStats);
|
|
2719
|
-
events.on("complete", updateStats);
|
|
2720
2576
|
updateStats();
|
|
2721
2577
|
return () => {
|
|
2578
|
+
events.removeAllListeners();
|
|
2722
2579
|
if (timerRef.current) clearInterval(timerRef.current);
|
|
2723
|
-
|
|
2724
|
-
events.off("start", updateStats);
|
|
2725
|
-
events.off("complete", updateStats);
|
|
2580
|
+
if (retryCountdownRef.current) clearInterval(retryCountdownRef.current);
|
|
2726
2581
|
};
|
|
2727
|
-
}, [agent,
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2582
|
+
}, [agent, addMessage]);
|
|
2583
|
+
return {
|
|
2584
|
+
agent,
|
|
2585
|
+
messages,
|
|
2586
|
+
setMessages,
|
|
2587
|
+
isProcessing,
|
|
2588
|
+
currentStatus,
|
|
2589
|
+
elapsedTime,
|
|
2590
|
+
reasoning,
|
|
2591
|
+
retryState,
|
|
2592
|
+
currentTokens,
|
|
2593
|
+
inputRequest,
|
|
2594
|
+
setInputRequest,
|
|
2595
|
+
stats,
|
|
2596
|
+
executeTask,
|
|
2597
|
+
abort,
|
|
2598
|
+
cancelInputRequest,
|
|
2599
|
+
addMessage
|
|
2600
|
+
};
|
|
2601
|
+
};
|
|
2602
|
+
|
|
2603
|
+
// src/platform/tui/components/MessageList.tsx
|
|
2604
|
+
import { Box, Text, Static } from "ink";
|
|
2605
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2606
|
+
var MessageList = ({ messages }) => {
|
|
2607
|
+
return /* @__PURE__ */ jsx(Static, { items: messages, children: (msg) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: MESSAGE_STYLES.colors[msg.type], dimColor: msg.type === "result", children: [
|
|
2608
|
+
MESSAGE_STYLES.prefixes[msg.type],
|
|
2609
|
+
" ",
|
|
2610
|
+
msg.content
|
|
2611
|
+
] }) }, msg.id) });
|
|
2612
|
+
};
|
|
2613
|
+
|
|
2614
|
+
// src/platform/tui/components/StatusDisplay.tsx
|
|
2615
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
2616
|
+
import Spinner from "ink-spinner";
|
|
2617
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2618
|
+
var StatusDisplay = ({
|
|
2619
|
+
reasoning,
|
|
2620
|
+
retryState,
|
|
2621
|
+
isProcessing,
|
|
2622
|
+
currentStatus,
|
|
2623
|
+
elapsedTime,
|
|
2624
|
+
currentTokens
|
|
2625
|
+
}) => {
|
|
2626
|
+
const truncateError = (err) => {
|
|
2627
|
+
return err.length > DISPLAY_LIMITS2.retryErrorPreview ? err.substring(0, DISPLAY_LIMITS2.retryErrorTruncated) + "..." : err;
|
|
2628
|
+
};
|
|
2629
|
+
const meta = formatMeta(elapsedTime * 1e3, currentTokens);
|
|
2630
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 0, children: [
|
|
2631
|
+
reasoning.isActive && reasoning.content && /* @__PURE__ */ jsxs2(Box2, { marginBottom: 1, flexDirection: "column", children: [
|
|
2632
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
2633
|
+
/* @__PURE__ */ jsx2(Text2, { color: THEME.status.warning, children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
|
|
2634
|
+
/* @__PURE__ */ jsx2(Text2, { color: THEME.status.warning, bold: true, children: " REASONING" }),
|
|
2635
|
+
/* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
|
|
2636
|
+
" ",
|
|
2637
|
+
meta
|
|
2638
|
+
] })
|
|
2639
|
+
] }),
|
|
2640
|
+
/* @__PURE__ */ jsx2(Text2, { color: THEME.text.secondary, dimColor: true, children: reasoning.content.slice(-DISPLAY_LIMITS2.reasoningPreview) })
|
|
2641
|
+
] }),
|
|
2642
|
+
retryState.isRetrying && /* @__PURE__ */ jsxs2(Box2, { marginBottom: 1, children: [
|
|
2643
|
+
/* @__PURE__ */ jsx2(Text2, { color: THEME.status.warning, children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
|
|
2644
|
+
/* @__PURE__ */ jsxs2(Text2, { color: THEME.status.warning, children: [
|
|
2645
|
+
" \u27F3 Retry #",
|
|
2646
|
+
retryState.attempt
|
|
2647
|
+
] }),
|
|
2648
|
+
/* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
|
|
2649
|
+
" \xB7 ",
|
|
2650
|
+
truncateError(retryState.error)
|
|
2651
|
+
] }),
|
|
2652
|
+
/* @__PURE__ */ jsxs2(Text2, { color: THEME.accent.cyan, bold: true, children: [
|
|
2653
|
+
" \xB7 ",
|
|
2654
|
+
retryState.countdown,
|
|
2655
|
+
"s"
|
|
2656
|
+
] })
|
|
2657
|
+
] }),
|
|
2658
|
+
isProcessing && !reasoning.isActive && !retryState.isRetrying && /* @__PURE__ */ jsxs2(Box2, { marginBottom: 1, children: [
|
|
2659
|
+
/* @__PURE__ */ jsx2(Text2, { color: THEME.spinner, children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
|
|
2660
|
+
/* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
|
|
2661
|
+
" ",
|
|
2662
|
+
currentStatus || "Processing"
|
|
2663
|
+
] }),
|
|
2664
|
+
/* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
|
|
2665
|
+
" ",
|
|
2666
|
+
meta
|
|
2667
|
+
] })
|
|
2668
|
+
] })
|
|
2669
|
+
] });
|
|
2670
|
+
};
|
|
2671
|
+
|
|
2672
|
+
// src/platform/tui/components/ChatInput.tsx
|
|
2673
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
2674
|
+
import TextInput from "ink-text-input";
|
|
2675
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2676
|
+
var ChatInput = ({
|
|
2677
|
+
value,
|
|
2678
|
+
onChange,
|
|
2679
|
+
onSubmit,
|
|
2680
|
+
placeholder,
|
|
2681
|
+
inputRequest,
|
|
2682
|
+
secretInput,
|
|
2683
|
+
setSecretInput,
|
|
2684
|
+
onSecretSubmit
|
|
2685
|
+
}) => {
|
|
2686
|
+
return /* @__PURE__ */ jsx3(
|
|
2687
|
+
Box3,
|
|
2688
|
+
{
|
|
2689
|
+
borderStyle: "single",
|
|
2690
|
+
borderColor: inputRequest.active ? THEME.status.warning : THEME.border.default,
|
|
2691
|
+
paddingX: 1,
|
|
2692
|
+
children: inputRequest.active ? /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
2693
|
+
/* @__PURE__ */ jsx3(Text3, { color: THEME.status.warning, children: "\u{1F512}" }),
|
|
2694
|
+
/* @__PURE__ */ jsxs3(Text3, { color: THEME.text.muted, children: [
|
|
2695
|
+
" ",
|
|
2696
|
+
inputRequest.prompt,
|
|
2697
|
+
" "
|
|
2698
|
+
] }),
|
|
2699
|
+
/* @__PURE__ */ jsx3(
|
|
2700
|
+
TextInput,
|
|
2701
|
+
{
|
|
2702
|
+
value: secretInput,
|
|
2703
|
+
onChange: setSecretInput,
|
|
2704
|
+
onSubmit: onSecretSubmit,
|
|
2705
|
+
placeholder: "Enter input...",
|
|
2706
|
+
mask: inputRequest.isPassword ? "\u2022" : void 0
|
|
2707
|
+
}
|
|
2708
|
+
)
|
|
2709
|
+
] }) : /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
2710
|
+
/* @__PURE__ */ jsx3(Text3, { color: THEME.text.secondary, children: "\u25B8" }),
|
|
2711
|
+
/* @__PURE__ */ jsx3(Text3, { children: " " }),
|
|
2712
|
+
/* @__PURE__ */ jsx3(
|
|
2713
|
+
TextInput,
|
|
2714
|
+
{
|
|
2715
|
+
value,
|
|
2716
|
+
onChange,
|
|
2717
|
+
onSubmit,
|
|
2718
|
+
placeholder
|
|
2719
|
+
}
|
|
2720
|
+
)
|
|
2721
|
+
] })
|
|
2722
|
+
}
|
|
2723
|
+
);
|
|
2724
|
+
};
|
|
2725
|
+
|
|
2726
|
+
// src/platform/tui/components/footer.tsx
|
|
2727
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
2728
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2729
|
+
var formatElapsed = (totalSeconds) => {
|
|
2730
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
2731
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
2732
|
+
const seconds = Math.floor(totalSeconds % 60);
|
|
2733
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
2734
|
+
if (hours > 0) {
|
|
2735
|
+
return `${hours}:${pad(minutes)}:${pad(seconds)}`;
|
|
2736
|
+
}
|
|
2737
|
+
return `${minutes}:${pad(seconds)}`;
|
|
2738
|
+
};
|
|
2739
|
+
var Footer = ({ phase, targets, findings, todo, elapsedTime, isProcessing }) => {
|
|
2740
|
+
return /* @__PURE__ */ jsxs4(
|
|
2741
|
+
Box4,
|
|
2742
|
+
{
|
|
2743
|
+
paddingX: 1,
|
|
2744
|
+
marginTop: 0,
|
|
2745
|
+
justifyContent: "space-between",
|
|
2746
|
+
children: [
|
|
2747
|
+
/* @__PURE__ */ jsxs4(Box4, { gap: 2, children: [
|
|
2748
|
+
/* @__PURE__ */ jsxs4(Text4, { color: THEME.text.secondary, children: [
|
|
2749
|
+
"Phase: ",
|
|
2750
|
+
/* @__PURE__ */ jsx4(Text4, { color: THEME.text.primary, children: phase })
|
|
2751
|
+
] }),
|
|
2752
|
+
/* @__PURE__ */ jsxs4(Text4, { color: THEME.text.secondary, children: [
|
|
2753
|
+
"Targets: ",
|
|
2754
|
+
/* @__PURE__ */ jsx4(Text4, { color: THEME.text.primary, children: targets })
|
|
2755
|
+
] }),
|
|
2756
|
+
/* @__PURE__ */ jsxs4(Text4, { color: THEME.text.secondary, children: [
|
|
2757
|
+
"Findings: ",
|
|
2758
|
+
/* @__PURE__ */ jsx4(Text4, { color: THEME.text.primary, children: findings })
|
|
2759
|
+
] }),
|
|
2760
|
+
/* @__PURE__ */ jsxs4(Text4, { color: THEME.text.secondary, children: [
|
|
2761
|
+
"Tasks: ",
|
|
2762
|
+
/* @__PURE__ */ jsx4(Text4, { color: THEME.text.primary, children: todo })
|
|
2763
|
+
] })
|
|
2764
|
+
] }),
|
|
2765
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2766
|
+
/* @__PURE__ */ jsx4(Text4, { color: THEME.text.muted, children: isProcessing ? "Running " : "Idle " }),
|
|
2767
|
+
/* @__PURE__ */ jsx4(Text4, { color: THEME.text.secondary, children: formatElapsed(elapsedTime) })
|
|
2768
|
+
] })
|
|
2769
|
+
]
|
|
2732
2770
|
}
|
|
2771
|
+
);
|
|
2772
|
+
};
|
|
2773
|
+
var footer_default = Footer;
|
|
2774
|
+
|
|
2775
|
+
// src/platform/tui/app.tsx
|
|
2776
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2777
|
+
var App = ({ autoApprove = false, target }) => {
|
|
2778
|
+
const { exit } = useApp();
|
|
2779
|
+
const [input, setInput] = useState2("");
|
|
2780
|
+
const [secretInput, setSecretInput] = useState2("");
|
|
2781
|
+
const {
|
|
2782
|
+
agent,
|
|
2783
|
+
messages,
|
|
2784
|
+
setMessages,
|
|
2785
|
+
isProcessing,
|
|
2786
|
+
currentStatus,
|
|
2787
|
+
elapsedTime,
|
|
2788
|
+
reasoning,
|
|
2789
|
+
retryState,
|
|
2790
|
+
currentTokens,
|
|
2791
|
+
inputRequest,
|
|
2792
|
+
stats,
|
|
2793
|
+
executeTask,
|
|
2794
|
+
abort,
|
|
2795
|
+
cancelInputRequest,
|
|
2796
|
+
addMessage
|
|
2797
|
+
} = useAgent(autoApprove, target);
|
|
2798
|
+
const handleExit = useCallback2(() => {
|
|
2799
|
+
if (inputRequest.active && inputRequest.resolve) inputRequest.resolve(null);
|
|
2733
2800
|
exit();
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2801
|
+
setTimeout(() => process.exit(0), DISPLAY_LIMITS2.exitDelay);
|
|
2802
|
+
}, [exit, inputRequest, cancelInputRequest]);
|
|
2803
|
+
const handleCommand = useCallback2(async (cmd, args) => {
|
|
2804
|
+
switch (cmd) {
|
|
2805
|
+
case UI_COMMANDS.HELP:
|
|
2806
|
+
case UI_COMMANDS.HELP_SHORT:
|
|
2807
|
+
addMessage(UI_ROLES.SYSTEM, HELP_TEXT);
|
|
2808
|
+
break;
|
|
2809
|
+
case UI_COMMANDS.CLEAR:
|
|
2810
|
+
case UI_COMMANDS.CLEAR_SHORT:
|
|
2811
|
+
setMessages([]);
|
|
2812
|
+
break;
|
|
2813
|
+
case UI_COMMANDS.TARGET:
|
|
2814
|
+
case UI_COMMANDS.TARGET_SHORT:
|
|
2815
|
+
if (!args[0]) {
|
|
2816
|
+
addMessage(UI_ROLES.ERROR, "Usage: /target <ip>");
|
|
2817
|
+
break;
|
|
2818
|
+
}
|
|
2819
|
+
agent.addTarget(args[0]);
|
|
2820
|
+
agent.setScope([args[0]]);
|
|
2821
|
+
addMessage(UI_ROLES.SYSTEM, `Target \u2192 ${args[0]}`);
|
|
2822
|
+
break;
|
|
2823
|
+
case UI_COMMANDS.START:
|
|
2824
|
+
case UI_COMMANDS.START_SHORT:
|
|
2825
|
+
if (!agent.getState().getTargets().size) {
|
|
2826
|
+
addMessage(UI_ROLES.ERROR, "Set target first: /target <ip>");
|
|
2827
|
+
break;
|
|
2828
|
+
}
|
|
2829
|
+
executeTask(args.join(" ") || "Perform comprehensive penetration testing");
|
|
2830
|
+
break;
|
|
2831
|
+
case UI_COMMANDS.FINDINGS:
|
|
2832
|
+
case UI_COMMANDS.FINDINGS_SHORT:
|
|
2833
|
+
const findings = agent.getState().getFindings();
|
|
2834
|
+
if (!findings.length) {
|
|
2835
|
+
addMessage(UI_ROLES.SYSTEM, "No findings.");
|
|
2836
|
+
break;
|
|
2837
|
+
}
|
|
2838
|
+
addMessage(UI_ROLES.SYSTEM, `--- ${findings.length} Findings ---`);
|
|
2839
|
+
findings.forEach((f) => addMessage(UI_ROLES.SYSTEM, `[${f.severity}] ${f.title}`));
|
|
2840
|
+
break;
|
|
2841
|
+
case UI_COMMANDS.EXIT:
|
|
2842
|
+
case UI_COMMANDS.QUIT:
|
|
2843
|
+
case UI_COMMANDS.EXIT_SHORT:
|
|
2844
|
+
handleExit();
|
|
2845
|
+
break;
|
|
2846
|
+
default:
|
|
2847
|
+
addMessage(UI_ROLES.ERROR, `Unknown command: /${cmd}`);
|
|
2848
|
+
}
|
|
2849
|
+
}, [agent, addMessage, executeTask, setMessages, handleExit]);
|
|
2850
|
+
const handleSubmit = useCallback2(async (value) => {
|
|
2747
2851
|
const trimmed = value.trim();
|
|
2748
2852
|
if (!trimmed) return;
|
|
2749
2853
|
setInput("");
|
|
2750
|
-
addMessage(
|
|
2854
|
+
addMessage(UI_ROLES.USER, trimmed);
|
|
2751
2855
|
if (trimmed.startsWith("/")) {
|
|
2752
2856
|
const [cmd, ...args] = trimmed.slice(1).split(" ");
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
return;
|
|
2772
|
-
case "target":
|
|
2773
|
-
case "t":
|
|
2774
|
-
if (args[0]) {
|
|
2775
|
-
agent.addTarget(args[0]);
|
|
2776
|
-
agent.setScope([args[0]]);
|
|
2777
|
-
addMessage("system", `Target \u2192 ${args[0]}`);
|
|
2778
|
-
} else {
|
|
2779
|
-
addMessage("system", "Usage: /target <ip>");
|
|
2780
|
-
}
|
|
2781
|
-
return;
|
|
2782
|
-
case "start":
|
|
2783
|
-
case "s":
|
|
2784
|
-
if (!agent.getState().getTargets().size) {
|
|
2785
|
-
addMessage("error", "Set target first: /target <ip>");
|
|
2786
|
-
return;
|
|
2787
|
-
}
|
|
2788
|
-
setIsProcessing(true);
|
|
2789
|
-
startTimer();
|
|
2790
|
-
setCurrentStatus("Initializing");
|
|
2791
|
-
addMessage("system", `Starting: ${args.join(" ") || "Perform comprehensive penetration testing"}`);
|
|
2792
|
-
try {
|
|
2793
|
-
await agent.execute(args.join(" ") || "Perform comprehensive penetration testing");
|
|
2794
|
-
} catch (e) {
|
|
2795
|
-
addMessage("error", e instanceof Error ? e.message : String(e));
|
|
2796
|
-
}
|
|
2797
|
-
stopTimer();
|
|
2798
|
-
setIsProcessing(false);
|
|
2799
|
-
setCurrentStatus("");
|
|
2800
|
-
return;
|
|
2801
|
-
case "stop":
|
|
2802
|
-
addMessage("system", "Stopping...");
|
|
2803
|
-
setIsProcessing(false);
|
|
2804
|
-
setCurrentStatus("");
|
|
2805
|
-
return;
|
|
2806
|
-
case "findings":
|
|
2807
|
-
case "f":
|
|
2808
|
-
const findings = agent.getState().getFindings();
|
|
2809
|
-
if (findings.length === 0) {
|
|
2810
|
-
addMessage("system", "No findings.");
|
|
2811
|
-
} else {
|
|
2812
|
-
addMessage("system", `--- ${findings.length} Findings ---`);
|
|
2813
|
-
findings.forEach((f) => addMessage("system", `[${f.severity}] ${f.title}`));
|
|
2814
|
-
}
|
|
2815
|
-
return;
|
|
2816
|
-
case "status":
|
|
2817
|
-
const state = agent.getState();
|
|
2818
|
-
addMessage("system", `Status:
|
|
2819
|
-
Phase: ${agent.getPhase()}
|
|
2820
|
-
Targets: ${state.getTargets().size}
|
|
2821
|
-
Findings: ${state.getFindings().length}
|
|
2822
|
-
TODO: ${state.getTodo().length}
|
|
2823
|
-
`);
|
|
2824
|
-
return;
|
|
2825
|
-
case "clear":
|
|
2826
|
-
case "c":
|
|
2827
|
-
setMessages([]);
|
|
2828
|
-
return;
|
|
2829
|
-
case "exit":
|
|
2830
|
-
case "quit":
|
|
2831
|
-
case "q":
|
|
2832
|
-
await handleExit();
|
|
2833
|
-
return;
|
|
2834
|
-
default:
|
|
2835
|
-
addMessage("error", `Unknown command: /${cmd}`);
|
|
2836
|
-
return;
|
|
2837
|
-
}
|
|
2838
|
-
}
|
|
2839
|
-
setIsProcessing(true);
|
|
2840
|
-
startTimer();
|
|
2841
|
-
setCurrentStatus("Thinking");
|
|
2842
|
-
try {
|
|
2843
|
-
const response = await agent.execute(trimmed);
|
|
2844
|
-
addMessage("assistant", response);
|
|
2845
|
-
} catch (e) {
|
|
2846
|
-
addMessage("error", e instanceof Error ? e.message : String(e));
|
|
2847
|
-
} finally {
|
|
2848
|
-
stopTimer();
|
|
2849
|
-
setIsProcessing(false);
|
|
2850
|
-
setCurrentStatus("");
|
|
2851
|
-
}
|
|
2852
|
-
}, [agent, addMessage, startTimer, stopTimer, handleExit]);
|
|
2853
|
-
useInput((input2, key) => {
|
|
2854
|
-
if (key.ctrl && input2 === "c") {
|
|
2855
|
-
if (isProcessing) {
|
|
2856
|
-
setIsProcessing(false);
|
|
2857
|
-
stopTimer();
|
|
2858
|
-
setCurrentStatus("");
|
|
2859
|
-
addMessage("system", "Interrupted");
|
|
2860
|
-
} else {
|
|
2861
|
-
exit();
|
|
2862
|
-
}
|
|
2863
|
-
return;
|
|
2864
|
-
}
|
|
2857
|
+
await handleCommand(cmd, args);
|
|
2858
|
+
} else {
|
|
2859
|
+
await executeTask(trimmed);
|
|
2860
|
+
}
|
|
2861
|
+
}, [addMessage, executeTask, handleCommand]);
|
|
2862
|
+
const handleSecretSubmit = useCallback2((value) => {
|
|
2863
|
+
if (!inputRequest.active || !inputRequest.resolve) return;
|
|
2864
|
+
const displayText = inputRequest.isPassword ? "\u2022".repeat(value.length) : value;
|
|
2865
|
+
addMessage(UI_ROLES.SYSTEM, `\u21B3 ${inputRequest.prompt} ${displayText}`);
|
|
2866
|
+
inputRequest.resolve(value);
|
|
2867
|
+
setSecretInput("");
|
|
2868
|
+
}, [inputRequest, addMessage]);
|
|
2869
|
+
useInput((ch, key) => {
|
|
2870
|
+
if (key.escape) {
|
|
2871
|
+
if (inputRequest.active) cancelInputRequest();
|
|
2872
|
+
else if (isProcessing) abort();
|
|
2873
|
+
}
|
|
2874
|
+
if (key.ctrl && ch === "c") handleExit();
|
|
2865
2875
|
});
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
system: "\u2022",
|
|
2881
|
-
error: "\u2717",
|
|
2882
|
-
tool: " \u23BF",
|
|
2883
|
-
result: " \u2713",
|
|
2884
|
-
thinking: "\u{1F9E0}"
|
|
2885
|
-
};
|
|
2886
|
-
return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: colors[msg.type], dimColor: msg.type === "result", children: [
|
|
2887
|
-
prefixes[msg.type],
|
|
2888
|
-
" ",
|
|
2889
|
-
msg.content
|
|
2890
|
-
] }) }, msg.id);
|
|
2891
|
-
} }) }),
|
|
2892
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 0, children: [
|
|
2893
|
-
thinking.isActive && thinking.content && /* @__PURE__ */ jsxs2(Box2, { marginBottom: 1, children: [
|
|
2894
|
-
/* @__PURE__ */ jsx2(Text2, { color: THEME.status.running, children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
|
|
2895
|
-
/* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
|
|
2896
|
-
" ",
|
|
2897
|
-
"Thinking: "
|
|
2898
|
-
] }),
|
|
2899
|
-
/* @__PURE__ */ jsx2(Text2, { color: THEME.text.secondary, dimColor: true, children: thinking.content.slice(-200) })
|
|
2900
|
-
] }),
|
|
2901
|
-
isProcessing && !thinking.isActive && /* @__PURE__ */ jsxs2(Box2, { marginBottom: 1, children: [
|
|
2902
|
-
/* @__PURE__ */ jsx2(Text2, { color: THEME.spinner, children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
|
|
2903
|
-
/* @__PURE__ */ jsxs2(Text2, { color: THEME.text.muted, children: [
|
|
2904
|
-
" ",
|
|
2905
|
-
currentStatus || "Processing"
|
|
2906
|
-
] })
|
|
2907
|
-
] }),
|
|
2908
|
-
/* @__PURE__ */ jsx2(
|
|
2909
|
-
Box2,
|
|
2876
|
+
useEffect2(() => {
|
|
2877
|
+
const onSignal = () => handleExit();
|
|
2878
|
+
process.on("SIGINT", onSignal);
|
|
2879
|
+
process.on("SIGTERM", onSignal);
|
|
2880
|
+
return () => {
|
|
2881
|
+
process.off("SIGINT", onSignal);
|
|
2882
|
+
process.off("SIGTERM", onSignal);
|
|
2883
|
+
};
|
|
2884
|
+
}, [handleExit]);
|
|
2885
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", paddingX: 1, children: [
|
|
2886
|
+
/* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginBottom: 1, flexGrow: 1, children: /* @__PURE__ */ jsx5(MessageList, { messages }) }),
|
|
2887
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
2888
|
+
/* @__PURE__ */ jsx5(
|
|
2889
|
+
StatusDisplay,
|
|
2910
2890
|
{
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2891
|
+
reasoning,
|
|
2892
|
+
retryState,
|
|
2893
|
+
isProcessing,
|
|
2894
|
+
currentStatus,
|
|
2895
|
+
elapsedTime,
|
|
2896
|
+
currentTokens
|
|
2897
|
+
}
|
|
2898
|
+
),
|
|
2899
|
+
/* @__PURE__ */ jsx5(
|
|
2900
|
+
ChatInput,
|
|
2901
|
+
{
|
|
2902
|
+
value: input,
|
|
2903
|
+
onChange: setInput,
|
|
2904
|
+
onSubmit: handleSubmit,
|
|
2905
|
+
placeholder: "Message or /help...",
|
|
2906
|
+
inputRequest,
|
|
2907
|
+
secretInput,
|
|
2908
|
+
setSecretInput,
|
|
2909
|
+
onSecretSubmit: handleSecretSubmit
|
|
2927
2910
|
}
|
|
2928
2911
|
),
|
|
2929
|
-
/* @__PURE__ */
|
|
2912
|
+
/* @__PURE__ */ jsx5(
|
|
2930
2913
|
footer_default,
|
|
2931
2914
|
{
|
|
2932
2915
|
phase: stats.phase,
|
|
@@ -2956,7 +2939,7 @@ var EXIT_CODE = {
|
|
|
2956
2939
|
};
|
|
2957
2940
|
|
|
2958
2941
|
// src/platform/tui/main.tsx
|
|
2959
|
-
import { jsx as
|
|
2942
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
2960
2943
|
var program = new Command();
|
|
2961
2944
|
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");
|
|
2962
2945
|
program.command("interactive", { isDefault: true }).alias("i").description("Start interactive TUI mode").action(async () => {
|
|
@@ -2972,7 +2955,7 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
|
|
|
2972
2955
|
console.log(chalk.hex(THEME.status.error)("[!] All tool executions will be auto-approved!\n"));
|
|
2973
2956
|
}
|
|
2974
2957
|
const { waitUntilExit } = render(
|
|
2975
|
-
/* @__PURE__ */
|
|
2958
|
+
/* @__PURE__ */ jsx6(
|
|
2976
2959
|
app_default,
|
|
2977
2960
|
{
|
|
2978
2961
|
autoApprove: skipPermissions,
|
|
@@ -2991,7 +2974,7 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
|
|
|
2991
2974
|
}
|
|
2992
2975
|
console.log(chalk.hex(THEME.text.accent)(`[target] Objective: ${objective}
|
|
2993
2976
|
`));
|
|
2994
|
-
const agent =
|
|
2977
|
+
const agent = AgentFactory.createMainAgent(skipPermissions);
|
|
2995
2978
|
if (skipPermissions) {
|
|
2996
2979
|
agent.setAutoApprove(true);
|
|
2997
2980
|
}
|
|
@@ -3029,10 +3012,7 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
|
|
|
3029
3012
|
console.log(chalk.hex(THEME.text.accent)(`
|
|
3030
3013
|
[scan] Target: ${target} (${options.scanType})
|
|
3031
3014
|
`));
|
|
3032
|
-
const agent =
|
|
3033
|
-
if (skipPermissions) {
|
|
3034
|
-
agent.setAutoApprove(true);
|
|
3035
|
-
}
|
|
3015
|
+
const agent = AgentFactory.createMainAgent(skipPermissions);
|
|
3036
3016
|
agent.addTarget(target);
|
|
3037
3017
|
agent.setScope([target]);
|
|
3038
3018
|
const shutdown = async (exitCode = 0) => {
|