pentesting 0.70.5 → 0.70.7
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 +512 -545
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -82,12 +82,9 @@ var THEME = {
|
|
|
82
82
|
spinner: COLORS.primary
|
|
83
83
|
};
|
|
84
84
|
var ASCII_BANNER = `
|
|
85
|
-
\
|
|
86
|
-
\
|
|
87
|
-
\
|
|
88
|
-
\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
89
|
-
\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
90
|
-
\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
85
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
86
|
+
\u2502 P E N T E S T I N G A G E N T \u2502
|
|
87
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
91
88
|
`;
|
|
92
89
|
var ICONS = {
|
|
93
90
|
// Status
|
|
@@ -762,7 +759,7 @@ var INPUT_PROMPT_PATTERNS = [
|
|
|
762
759
|
|
|
763
760
|
// src/shared/constants/agent.ts
|
|
764
761
|
var APP_NAME = "Pentest AI";
|
|
765
|
-
var APP_VERSION = "0.70.
|
|
762
|
+
var APP_VERSION = "0.70.7";
|
|
766
763
|
var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
|
|
767
764
|
var LLM_ROLES = {
|
|
768
765
|
SYSTEM: "system",
|
|
@@ -817,7 +814,7 @@ import { render } from "ink";
|
|
|
817
814
|
import chalk from "chalk";
|
|
818
815
|
|
|
819
816
|
// src/platform/tui/app.tsx
|
|
820
|
-
import { useState as
|
|
817
|
+
import { useState as useState6, useCallback as useCallback9, useRef as useRef8 } from "react";
|
|
821
818
|
import { Box as Box19, useApp, useStdout as useStdout4 } from "ink";
|
|
822
819
|
|
|
823
820
|
// src/platform/tui/hooks/useAgent.ts
|
|
@@ -1115,7 +1112,8 @@ var TOOL_NAMES = {
|
|
|
1115
1112
|
MITM_PROXY: "mitm_proxy",
|
|
1116
1113
|
PACKET_SNIFF: "packet_sniff",
|
|
1117
1114
|
DNS_SPOOF: "dns_spoof",
|
|
1118
|
-
TRAFFIC_INTERCEPT: "traffic_intercept"
|
|
1115
|
+
TRAFFIC_INTERCEPT: "traffic_intercept",
|
|
1116
|
+
ANALYZE_PCAP: "analyze_pcap"
|
|
1119
1117
|
};
|
|
1120
1118
|
|
|
1121
1119
|
// src/shared/constants/attack.ts
|
|
@@ -1174,7 +1172,8 @@ var EVENT_TYPES = {
|
|
|
1174
1172
|
RETRY: "retry",
|
|
1175
1173
|
USAGE_UPDATE: "usage_update",
|
|
1176
1174
|
INPUT_REQUEST: "input_request",
|
|
1177
|
-
FLAG_FOUND: "flag_found"
|
|
1175
|
+
FLAG_FOUND: "flag_found",
|
|
1176
|
+
NOTIFICATION: "notification"
|
|
1178
1177
|
};
|
|
1179
1178
|
var COMMAND_EVENT_TYPES = {
|
|
1180
1179
|
TOOL_MISSING: "tool_missing",
|
|
@@ -1186,7 +1185,9 @@ var COMMAND_EVENT_TYPES = {
|
|
|
1186
1185
|
COMMAND_SUCCESS: "command_success",
|
|
1187
1186
|
COMMAND_FAILED: "command_failed",
|
|
1188
1187
|
COMMAND_ERROR: "command_error",
|
|
1189
|
-
INPUT_REQUIRED: "input_required"
|
|
1188
|
+
INPUT_REQUIRED: "input_required",
|
|
1189
|
+
COMMAND_STDOUT: "command_stdout",
|
|
1190
|
+
PROCESS_NOTIFICATION: "process_notification"
|
|
1190
1191
|
};
|
|
1191
1192
|
|
|
1192
1193
|
// src/shared/constants/ui-commands.ts
|
|
@@ -1268,13 +1269,6 @@ var EDGE_STATUS = {
|
|
|
1268
1269
|
SUCCEEDED: "succeeded",
|
|
1269
1270
|
FAILED: "failed"
|
|
1270
1271
|
};
|
|
1271
|
-
var SEVERITY = {
|
|
1272
|
-
CRITICAL: "critical",
|
|
1273
|
-
HIGH: "high",
|
|
1274
|
-
MEDIUM: "medium",
|
|
1275
|
-
LOW: "low",
|
|
1276
|
-
INFO: "info"
|
|
1277
|
-
};
|
|
1278
1272
|
var IMPACT_WEIGHT = {
|
|
1279
1273
|
critical: 4,
|
|
1280
1274
|
high: 3,
|
|
@@ -1523,21 +1517,21 @@ function dfsChains(nodeId, path2, pathEdges, visited, ctx) {
|
|
|
1523
1517
|
visited.delete(nodeId);
|
|
1524
1518
|
}
|
|
1525
1519
|
function estimateImpact(node) {
|
|
1526
|
-
if (node.type === NODE_TYPE.LOOT) return
|
|
1520
|
+
if (node.type === NODE_TYPE.LOOT) return SEVERITIES.CRITICAL;
|
|
1527
1521
|
if (node.type === NODE_TYPE.ACCESS) {
|
|
1528
1522
|
const level = String(node.data.level || "");
|
|
1529
|
-
if (["root", "SYSTEM", "Administrator", "admin"].includes(level)) return
|
|
1530
|
-
return
|
|
1523
|
+
if (["root", "SYSTEM", "Administrator", "admin"].includes(level)) return SEVERITIES.CRITICAL;
|
|
1524
|
+
return SEVERITIES.HIGH;
|
|
1531
1525
|
}
|
|
1532
1526
|
if (node.type === NODE_TYPE.VULNERABILITY) {
|
|
1533
1527
|
const sev = String(node.data.severity || "");
|
|
1534
|
-
if (sev ===
|
|
1535
|
-
if (sev ===
|
|
1536
|
-
if (sev ===
|
|
1528
|
+
if (sev === SEVERITIES.CRITICAL) return SEVERITIES.CRITICAL;
|
|
1529
|
+
if (sev === SEVERITIES.HIGH) return SEVERITIES.HIGH;
|
|
1530
|
+
if (sev === SEVERITIES.MEDIUM) return SEVERITIES.MEDIUM;
|
|
1537
1531
|
}
|
|
1538
|
-
if (node.type === NODE_TYPE.DOMAIN) return
|
|
1539
|
-
if (node.type === NODE_TYPE.CERTIFICATE_TEMPLATE) return
|
|
1540
|
-
return
|
|
1532
|
+
if (node.type === NODE_TYPE.DOMAIN) return SEVERITIES.CRITICAL;
|
|
1533
|
+
if (node.type === NODE_TYPE.CERTIFICATE_TEMPLATE) return SEVERITIES.HIGH;
|
|
1534
|
+
return SEVERITIES.LOW;
|
|
1541
1535
|
}
|
|
1542
1536
|
function extractSuccessfulChain(nodes, edges) {
|
|
1543
1537
|
const MIN_CHAIN_LENGTH = 2;
|
|
@@ -1753,10 +1747,10 @@ function formatPathsList(nodes, edges, failedPaths) {
|
|
|
1753
1747
|
const c = chains[i];
|
|
1754
1748
|
const prob = (c.probability * 100).toFixed(0);
|
|
1755
1749
|
const impactColors = {
|
|
1756
|
-
[
|
|
1757
|
-
[
|
|
1758
|
-
[
|
|
1759
|
-
[
|
|
1750
|
+
[SEVERITIES.CRITICAL]: "\u{1F534}",
|
|
1751
|
+
[SEVERITIES.HIGH]: "\u{1F7E0}",
|
|
1752
|
+
[SEVERITIES.MEDIUM]: "\u{1F7E1}",
|
|
1753
|
+
[SEVERITIES.LOW]: "\u{1F7E2}"
|
|
1760
1754
|
};
|
|
1761
1755
|
const impactIcon = impactColors[c.impact] || "\u26AA";
|
|
1762
1756
|
lines.push(`${impactIcon} PATH ${i + 1} [${c.impact.toUpperCase()} | prob: ${prob}% | steps: ${c.length}]`);
|
|
@@ -4397,133 +4391,133 @@ function injectCurlMaxTime(command, timeoutSec) {
|
|
|
4397
4391
|
if (/--max-time\b|-m\s+\d/.test(command)) return command;
|
|
4398
4392
|
return command.replace(/\bcurl\b/, `curl --max-time ${timeoutSec}`);
|
|
4399
4393
|
}
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
resolve({
|
|
4409
|
-
success: false,
|
|
4410
|
-
output: "",
|
|
4411
|
-
error: `\u{1F6D1} TOR IP LEAK BLOCKED
|
|
4394
|
+
function prepareCommand(command) {
|
|
4395
|
+
const safeCommand = injectCurlMaxTime(command, CURL_MAX_TIME_SEC);
|
|
4396
|
+
const torLeak = checkTorLeakRisk(safeCommand);
|
|
4397
|
+
if (!torLeak.safe) {
|
|
4398
|
+
return {
|
|
4399
|
+
safeCommand,
|
|
4400
|
+
execCommand: "",
|
|
4401
|
+
error: `\u{1F6D1} TOR IP LEAK BLOCKED
|
|
4412
4402
|
Reason: ${torLeak.reason}
|
|
4413
4403
|
Suggestion: ${torLeak.suggestion}`
|
|
4404
|
+
};
|
|
4405
|
+
}
|
|
4406
|
+
return { safeCommand, execCommand: wrapCommandForTor(safeCommand) };
|
|
4407
|
+
}
|
|
4408
|
+
function setupTimeout(child, timeoutMs, onTimeout) {
|
|
4409
|
+
return setTimeout(() => {
|
|
4410
|
+
onTimeout();
|
|
4411
|
+
try {
|
|
4412
|
+
process.kill(-child.pid, "SIGKILL");
|
|
4413
|
+
} catch {
|
|
4414
|
+
try {
|
|
4415
|
+
child.kill("SIGKILL");
|
|
4416
|
+
} catch {
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
}, timeoutMs);
|
|
4420
|
+
}
|
|
4421
|
+
var ProcessIOHandler = class {
|
|
4422
|
+
constructor(child, processState, eventEmitter, inputHandler) {
|
|
4423
|
+
this.child = child;
|
|
4424
|
+
this.processState = processState;
|
|
4425
|
+
this.eventEmitter = eventEmitter;
|
|
4426
|
+
this.inputHandler = inputHandler;
|
|
4427
|
+
}
|
|
4428
|
+
stdout = "";
|
|
4429
|
+
stderr = "";
|
|
4430
|
+
inputHandled = false;
|
|
4431
|
+
async checkForInputPrompt(data) {
|
|
4432
|
+
if (this.inputHandled || !this.inputHandler || this.processState.terminated) return;
|
|
4433
|
+
for (const pattern of INPUT_PROMPT_PATTERNS) {
|
|
4434
|
+
if (pattern.test(data)) {
|
|
4435
|
+
this.inputHandled = true;
|
|
4436
|
+
this.eventEmitter?.({
|
|
4437
|
+
type: COMMAND_EVENT_TYPES.INPUT_REQUIRED,
|
|
4438
|
+
message: `Input required: ${data.trim().slice(0, SYSTEM_LIMITS.MAX_PROMPT_PREVIEW)}`,
|
|
4439
|
+
detail: "Waiting for user input..."
|
|
4440
|
+
});
|
|
4441
|
+
try {
|
|
4442
|
+
const userInput = await this.inputHandler(data.trim());
|
|
4443
|
+
if (userInput !== null && !this.processState.terminated && this.child.stdin?.writable) {
|
|
4444
|
+
this.child.stdin.write(userInput + "\n");
|
|
4445
|
+
}
|
|
4446
|
+
} catch {
|
|
4447
|
+
}
|
|
4448
|
+
return;
|
|
4449
|
+
}
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
handleStdout(data) {
|
|
4453
|
+
const text = data.toString();
|
|
4454
|
+
this.stdout += text;
|
|
4455
|
+
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
4456
|
+
if (lines.length > 0) {
|
|
4457
|
+
this.eventEmitter?.({
|
|
4458
|
+
type: COMMAND_EVENT_TYPES.COMMAND_STDOUT,
|
|
4459
|
+
message: lines[lines.length - 1]
|
|
4414
4460
|
});
|
|
4415
|
-
|
|
4461
|
+
}
|
|
4462
|
+
this.checkForInputPrompt(text);
|
|
4463
|
+
}
|
|
4464
|
+
handleStderr(data) {
|
|
4465
|
+
const text = data.toString();
|
|
4466
|
+
this.stderr += text;
|
|
4467
|
+
this.checkForInputPrompt(text);
|
|
4468
|
+
}
|
|
4469
|
+
};
|
|
4470
|
+
async function executeCommandOnce(command, options = {}) {
|
|
4471
|
+
return new Promise((resolve) => {
|
|
4472
|
+
const eventEmitter = getEventEmitter();
|
|
4473
|
+
const { safeCommand, execCommand, error } = prepareCommand(command);
|
|
4474
|
+
if (error) {
|
|
4475
|
+
return resolve({ success: false, output: "", error });
|
|
4416
4476
|
}
|
|
4417
4477
|
eventEmitter?.({
|
|
4418
4478
|
type: COMMAND_EVENT_TYPES.COMMAND_START,
|
|
4419
4479
|
message: `Executing: ${safeCommand.slice(0, DISPLAY_LIMITS.COMMAND_PREVIEW)}${safeCommand.length > DISPLAY_LIMITS.COMMAND_PREVIEW ? "..." : ""}`
|
|
4420
4480
|
});
|
|
4421
|
-
const execCommand = wrapCommandForTor(safeCommand);
|
|
4422
4481
|
const child = spawn3("sh", ["-c", execCommand], {
|
|
4423
4482
|
detached: true,
|
|
4424
4483
|
env: { ...process.env, ...options.env },
|
|
4425
4484
|
cwd: options.cwd
|
|
4426
4485
|
});
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
const killTimer = setTimeout(() => {
|
|
4433
|
-
if (processTerminated) return;
|
|
4434
|
-
timedOut = true;
|
|
4435
|
-
processTerminated = true;
|
|
4436
|
-
try {
|
|
4437
|
-
process.kill(-child.pid, "SIGKILL");
|
|
4438
|
-
} catch {
|
|
4439
|
-
try {
|
|
4440
|
-
child.kill("SIGKILL");
|
|
4441
|
-
} catch {
|
|
4442
|
-
}
|
|
4443
|
-
}
|
|
4444
|
-
}, timeout);
|
|
4445
|
-
const checkForInputPrompt = async (data) => {
|
|
4446
|
-
if (inputHandled || !inputHandler || processTerminated) return;
|
|
4447
|
-
for (const pattern of INPUT_PROMPT_PATTERNS) {
|
|
4448
|
-
if (pattern.test(data)) {
|
|
4449
|
-
inputHandled = true;
|
|
4450
|
-
eventEmitter?.({
|
|
4451
|
-
type: COMMAND_EVENT_TYPES.INPUT_REQUIRED,
|
|
4452
|
-
message: `Input required: ${data.trim().slice(0, SYSTEM_LIMITS.MAX_PROMPT_PREVIEW)}`,
|
|
4453
|
-
detail: "Waiting for user input..."
|
|
4454
|
-
});
|
|
4455
|
-
const promptText = data.trim();
|
|
4456
|
-
try {
|
|
4457
|
-
const userInput = await inputHandler(promptText);
|
|
4458
|
-
if (userInput !== null && !processTerminated && child.stdin?.writable) {
|
|
4459
|
-
child.stdin.write(userInput + "\n");
|
|
4460
|
-
}
|
|
4461
|
-
} catch {
|
|
4462
|
-
}
|
|
4463
|
-
return;
|
|
4464
|
-
}
|
|
4465
|
-
}
|
|
4466
|
-
};
|
|
4467
|
-
child.stdout.on("data", (data) => {
|
|
4468
|
-
const text = data.toString();
|
|
4469
|
-
stdout += text;
|
|
4470
|
-
checkForInputPrompt(text);
|
|
4471
|
-
});
|
|
4472
|
-
child.stderr.on("data", (data) => {
|
|
4473
|
-
const text = data.toString();
|
|
4474
|
-
stderr += text;
|
|
4475
|
-
checkForInputPrompt(text);
|
|
4486
|
+
const processState = { terminated: false, timedOut: false };
|
|
4487
|
+
const timeoutMs = options.timeout ?? TOOL_TIMEOUTS.DEFAULT_COMMAND;
|
|
4488
|
+
const killTimer = setupTimeout(child, timeoutMs, () => {
|
|
4489
|
+
processState.timedOut = true;
|
|
4490
|
+
processState.terminated = true;
|
|
4476
4491
|
});
|
|
4492
|
+
const ioHandler = new ProcessIOHandler(child, processState, eventEmitter, getInputHandler());
|
|
4493
|
+
child.stdout.on("data", (d) => ioHandler.handleStdout(d));
|
|
4494
|
+
child.stderr.on("data", (d) => ioHandler.handleStderr(d));
|
|
4477
4495
|
child.on("close", (code) => {
|
|
4478
4496
|
clearTimeout(killTimer);
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
resolve({
|
|
4497
|
+
processState.terminated = true;
|
|
4498
|
+
const outTrimmed = ioHandler.stdout.trim();
|
|
4499
|
+
const errTrimmed = ioHandler.stderr.trim();
|
|
4500
|
+
if (processState.timedOut) {
|
|
4501
|
+
eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_FAILED, message: `Command timed out after ${Math.round(timeoutMs / 1e3)}s` });
|
|
4502
|
+
return resolve({
|
|
4486
4503
|
success: false,
|
|
4487
|
-
output:
|
|
4488
|
-
error: `Command timed out after ${Math.round(
|
|
4489
|
-
${(stdout + stderr).trim().slice(-500)}`
|
|
4504
|
+
output: outTrimmed,
|
|
4505
|
+
error: `Command timed out after ${Math.round(timeoutMs / 1e3)}s. Output so far:
|
|
4506
|
+
${(ioHandler.stdout + ioHandler.stderr).trim().slice(-500)}`
|
|
4490
4507
|
});
|
|
4491
|
-
return;
|
|
4492
4508
|
}
|
|
4493
4509
|
if (code === 0) {
|
|
4494
|
-
eventEmitter?.({
|
|
4495
|
-
|
|
4496
|
-
message: `Command completed`
|
|
4497
|
-
});
|
|
4498
|
-
resolve({
|
|
4499
|
-
success: true,
|
|
4500
|
-
output: stdout.trim() || stderr.trim()
|
|
4501
|
-
});
|
|
4502
|
-
} else {
|
|
4503
|
-
eventEmitter?.({
|
|
4504
|
-
type: COMMAND_EVENT_TYPES.COMMAND_FAILED,
|
|
4505
|
-
message: `Command failed (exit ${code})`,
|
|
4506
|
-
detail: stderr.slice(0, SYSTEM_LIMITS.MAX_INPUT_SLICE)
|
|
4507
|
-
});
|
|
4508
|
-
resolve({
|
|
4509
|
-
success: false,
|
|
4510
|
-
output: stdout.trim(),
|
|
4511
|
-
error: stderr.trim() || `Process exited with code ${code}`
|
|
4512
|
-
});
|
|
4510
|
+
eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_SUCCESS, message: `Command completed` });
|
|
4511
|
+
return resolve({ success: true, output: outTrimmed || errTrimmed });
|
|
4513
4512
|
}
|
|
4513
|
+
eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_FAILED, message: `Command failed (exit ${code})`, detail: errTrimmed.slice(0, SYSTEM_LIMITS.MAX_INPUT_SLICE) });
|
|
4514
|
+
return resolve({ success: false, output: outTrimmed, error: errTrimmed || `Process exited with code ${code}` });
|
|
4514
4515
|
});
|
|
4515
4516
|
child.on("error", (err) => {
|
|
4516
4517
|
clearTimeout(killTimer);
|
|
4517
|
-
|
|
4518
|
-
eventEmitter?.({
|
|
4519
|
-
|
|
4520
|
-
message: `Command error: ${err.message}`
|
|
4521
|
-
});
|
|
4522
|
-
resolve({
|
|
4523
|
-
success: false,
|
|
4524
|
-
output: "",
|
|
4525
|
-
error: err.message || String(err)
|
|
4526
|
-
});
|
|
4518
|
+
processState.terminated = true;
|
|
4519
|
+
eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_ERROR, message: `Command error: ${err.message}` });
|
|
4520
|
+
resolve({ success: false, output: "", error: err.message || String(err) });
|
|
4527
4521
|
});
|
|
4528
4522
|
});
|
|
4529
4523
|
}
|
|
@@ -4702,6 +4696,13 @@ function startBackgroundProcess(command, options = {}) {
|
|
|
4702
4696
|
bgProcess.hasExited = true;
|
|
4703
4697
|
bgProcess.exitCode = code;
|
|
4704
4698
|
logEvent(processId, PROCESS_EVENTS.DIED, `Process exited with code ${code}`);
|
|
4699
|
+
const emitter = getEventEmitter();
|
|
4700
|
+
if (emitter) {
|
|
4701
|
+
emitter({
|
|
4702
|
+
type: COMMAND_EVENT_TYPES.PROCESS_NOTIFICATION,
|
|
4703
|
+
message: `Process finished: ${bgProcess.description} (exit ${code}). Ready for analysis.`
|
|
4704
|
+
});
|
|
4705
|
+
}
|
|
4705
4706
|
});
|
|
4706
4707
|
setProcess(processId, bgProcess);
|
|
4707
4708
|
logEvent(processId, PROCESS_EVENTS.STARTED, `${role} started: ${command.slice(0, SYSTEM_LIMITS.MAX_DESCRIPTION_LENGTH)} (PID:${child.pid})`);
|
|
@@ -6363,10 +6364,6 @@ Returns recommendations on process status, port conflicts, long-running tasks, e
|
|
|
6363
6364
|
}
|
|
6364
6365
|
];
|
|
6365
6366
|
|
|
6366
|
-
// src/domains/registry.ts
|
|
6367
|
-
import { join as join11, dirname as dirname3 } from "path";
|
|
6368
|
-
import { fileURLToPath } from "url";
|
|
6369
|
-
|
|
6370
6367
|
// src/shared/constants/service-ports.ts
|
|
6371
6368
|
var SERVICE_PORTS = {
|
|
6372
6369
|
SSH: 22,
|
|
@@ -6779,7 +6776,8 @@ Requires root/sudo privileges.`,
|
|
|
6779
6776
|
duration: { type: "number", description: "Capture duration in seconds. Default: 30" },
|
|
6780
6777
|
output_file: { type: "string", description: "Path to save pcap file. Auto-generated if not specified." },
|
|
6781
6778
|
count: { type: "number", description: "Number of packets to capture. Default: 1000" },
|
|
6782
|
-
extract_creds: { type: "boolean", description: "Try to extract credentials from captured traffic. Default: true" }
|
|
6779
|
+
extract_creds: { type: "boolean", description: "Try to extract credentials from captured traffic. Default: true" },
|
|
6780
|
+
background: { type: "boolean", description: "Run the capture in the background and return immediately. Default: false" }
|
|
6783
6781
|
},
|
|
6784
6782
|
execute: async (p) => {
|
|
6785
6783
|
const iface = p.interface || "any";
|
|
@@ -6788,11 +6786,23 @@ Requires root/sudo privileges.`,
|
|
|
6788
6786
|
const outputFile = p.output_file || createTempFile(FILE_EXTENSIONS.PCAP);
|
|
6789
6787
|
const count = p.count || NETWORK_CONFIG.DEFAULT_PACKET_COUNT;
|
|
6790
6788
|
const extractCreds = p.extract_creds !== false;
|
|
6789
|
+
const isBackground = p.background === true;
|
|
6791
6790
|
const filterFlag = filter ? `"${filter}"` : "";
|
|
6792
6791
|
const captureCmd = NETWORK_COMMANDS.TCPDUMP_CAPTURE.replace("${duration}", duration.toString()).replace("${iface}", iface).replace("${count}", count.toString()).replace("${outputFile}", outputFile).replace("${filterFlag}", filterFlag);
|
|
6793
6792
|
const proc = startBackgroundProcess(captureCmd, {
|
|
6794
6793
|
description: `Packet capture on ${iface}`
|
|
6795
6794
|
});
|
|
6795
|
+
if (isBackground) {
|
|
6796
|
+
return {
|
|
6797
|
+
success: true,
|
|
6798
|
+
output: `Packet capture started in background.
|
|
6799
|
+
Process ID: ${proc.id}
|
|
6800
|
+
Duration: ${duration}s
|
|
6801
|
+
Output File: ${outputFile}
|
|
6802
|
+
|
|
6803
|
+
You can continue with other tasks. To analyze the capture later, use run_cmd with tshark or tcpdump against the output file.`
|
|
6804
|
+
};
|
|
6805
|
+
}
|
|
6796
6806
|
await new Promise((r) => setTimeout(r, (duration + NETWORK_CONFIG.WAIT_BUFFER_SECONDS) * 1e3));
|
|
6797
6807
|
const captureOutput = getProcessOutput(proc.id);
|
|
6798
6808
|
await stopBackgroundProcess(proc.id);
|
|
@@ -6958,6 +6968,56 @@ ${dnsQueries.output}
|
|
|
6958
6968
|
}
|
|
6959
6969
|
});
|
|
6960
6970
|
|
|
6971
|
+
// src/domains/network/analyze-pcap.ts
|
|
6972
|
+
var createAnalyzePcapTool = () => ({
|
|
6973
|
+
name: TOOL_NAMES.ANALYZE_PCAP,
|
|
6974
|
+
description: `Perform deep microscopic analysis of a PCAP file using tshark filters.
|
|
6975
|
+
Use this to:
|
|
6976
|
+
- Extract credentials from specific protocols
|
|
6977
|
+
- Reconstruct TCP streams
|
|
6978
|
+
- Analyze specific communication patterns (e.g. HTTP, SMB, DNS)
|
|
6979
|
+
- Identify potential attack vectors from intercepted traffic`,
|
|
6980
|
+
parameters: {
|
|
6981
|
+
file_path: { type: "string", description: "Path to the PCAP file to analyze." },
|
|
6982
|
+
filter: { type: "string", description: 'tshark display filter (e.g., "http.request.method == POST", "tcp.port == 445")' },
|
|
6983
|
+
fields: { type: "string", description: 'Comma-separated list of fields to extract (e.g., "http.host,http.user_agent,http.auth_basic")' },
|
|
6984
|
+
limit: { type: "number", description: "Maximum number of packets to process. Default: 100" }
|
|
6985
|
+
},
|
|
6986
|
+
required: ["file_path"],
|
|
6987
|
+
execute: async (p) => {
|
|
6988
|
+
const filePath = p.file_path;
|
|
6989
|
+
const filter = p.filter || "";
|
|
6990
|
+
const fields = p.fields || "";
|
|
6991
|
+
const limit = p.limit || 100;
|
|
6992
|
+
let cmd = `tshark -r ${filePath}`;
|
|
6993
|
+
if (filter) {
|
|
6994
|
+
cmd += ` -Y "${filter}"`;
|
|
6995
|
+
}
|
|
6996
|
+
if (fields) {
|
|
6997
|
+
const fieldArgs = fields.split(",").map((f) => `-e ${f.trim()}`).join(" ");
|
|
6998
|
+
cmd += ` -T fields ${fieldArgs} -E header=y -E separator=/t`;
|
|
6999
|
+
} else {
|
|
7000
|
+
cmd += ` -c ${limit}`;
|
|
7001
|
+
}
|
|
7002
|
+
const result = await runCommand(cmd);
|
|
7003
|
+
if (!result.success) {
|
|
7004
|
+
return {
|
|
7005
|
+
success: false,
|
|
7006
|
+
output: `Failed to analyze PCAP: ${result.error}`
|
|
7007
|
+
};
|
|
7008
|
+
}
|
|
7009
|
+
let output = `PCAP Analysis Results for ${filePath}:
|
|
7010
|
+
`;
|
|
7011
|
+
if (filter) output += `Filter: ${filter}
|
|
7012
|
+
`;
|
|
7013
|
+
if (fields) output += `Fields: ${fields}
|
|
7014
|
+
`;
|
|
7015
|
+
output += `
|
|
7016
|
+
${result.output}`;
|
|
7017
|
+
return { success: true, output };
|
|
7018
|
+
}
|
|
7019
|
+
});
|
|
7020
|
+
|
|
6961
7021
|
// src/engine/tools/intel-utils/types.ts
|
|
6962
7022
|
var PORT_STATE = {
|
|
6963
7023
|
OPEN: "open",
|
|
@@ -8057,13 +8117,12 @@ var RETRY_CONFIG = {
|
|
|
8057
8117
|
baseDelayMs: 2e3,
|
|
8058
8118
|
maxDelayMs: 6e4,
|
|
8059
8119
|
jitterMs: 1e3,
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
autoRetryMaxAttempts:
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
// Initial delay for rate limit retry (exponential backoff)
|
|
8120
|
+
/** WHY Infinity: Rate limit (429) errors are transient infrastructure delays.
|
|
8121
|
+
* Infinite fixed-interval retries allow the agent to eventually recover
|
|
8122
|
+
* without failing the entire engagement. */
|
|
8123
|
+
autoRetryMaxAttempts: Infinity,
|
|
8124
|
+
autoRetryDelayMs: 1e4
|
|
8125
|
+
// 10s fixed interval for 429/network errors
|
|
8067
8126
|
};
|
|
8068
8127
|
var LLM_LIMITS = {
|
|
8069
8128
|
/** WHY 64K: non-streaming calls (orchestrator, summaries) benefit from
|
|
@@ -8320,7 +8379,8 @@ var NETWORK_TOOLS = [
|
|
|
8320
8379
|
createDnsSpoofTool(),
|
|
8321
8380
|
createMitmProxyTool(),
|
|
8322
8381
|
createPacketSniffTool(),
|
|
8323
|
-
createTrafficInterceptTool()
|
|
8382
|
+
createTrafficInterceptTool(),
|
|
8383
|
+
createAnalyzePcapTool()
|
|
8324
8384
|
];
|
|
8325
8385
|
|
|
8326
8386
|
// src/domains/web/intel.ts
|
|
@@ -10739,98 +10799,82 @@ var INTEL_TOOLS = [
|
|
|
10739
10799
|
];
|
|
10740
10800
|
|
|
10741
10801
|
// src/domains/registry.ts
|
|
10742
|
-
var __dirname = dirname3(fileURLToPath(import.meta.url));
|
|
10743
10802
|
var DOMAINS = {
|
|
10744
10803
|
[SERVICE_CATEGORIES.NETWORK]: {
|
|
10745
10804
|
id: SERVICE_CATEGORIES.NETWORK,
|
|
10746
10805
|
name: "Network Infrastructure",
|
|
10747
|
-
description: "Vulnerability scanning, port mapping, and network service exploitation."
|
|
10748
|
-
promptPath: join11(__dirname, "network/prompt.md")
|
|
10806
|
+
description: "Vulnerability scanning, port mapping, and network service exploitation."
|
|
10749
10807
|
},
|
|
10750
10808
|
[SERVICE_CATEGORIES.WEB]: {
|
|
10751
10809
|
id: SERVICE_CATEGORIES.WEB,
|
|
10752
10810
|
name: "Web Application",
|
|
10753
|
-
description: "Web app security testing, injection attacks, and auth bypass."
|
|
10754
|
-
promptPath: join11(__dirname, "web/prompt.md")
|
|
10811
|
+
description: "Web app security testing, injection attacks, and auth bypass."
|
|
10755
10812
|
},
|
|
10756
10813
|
// Phase 2 domains (using internal categories for now)
|
|
10757
10814
|
"engagement": {
|
|
10758
10815
|
id: "engagement",
|
|
10759
10816
|
name: "Engagement Management",
|
|
10760
|
-
description: "Mission planning, state tracking, and finding management."
|
|
10761
|
-
promptPath: join11(__dirname, "engagement/prompt.md")
|
|
10817
|
+
description: "Mission planning, state tracking, and finding management."
|
|
10762
10818
|
},
|
|
10763
10819
|
"exploit": {
|
|
10764
10820
|
id: "exploit",
|
|
10765
10821
|
name: "Exploitation Utilities",
|
|
10766
|
-
description: "Payload mutation, hash cracking, and wordlists."
|
|
10767
|
-
promptPath: join11(__dirname, "exploit/prompt.md")
|
|
10822
|
+
description: "Payload mutation, hash cracking, and wordlists."
|
|
10768
10823
|
},
|
|
10769
10824
|
"intel": {
|
|
10770
10825
|
id: "intel",
|
|
10771
10826
|
name: "Vulnerability Intelligence",
|
|
10772
|
-
description: "OWASP knowledge, CVE research, and web attack surface."
|
|
10773
|
-
promptPath: join11(__dirname, "intel/prompt.md")
|
|
10827
|
+
description: "OWASP knowledge, CVE research, and web attack surface."
|
|
10774
10828
|
},
|
|
10775
10829
|
[SERVICE_CATEGORIES.DATABASE]: {
|
|
10776
10830
|
id: SERVICE_CATEGORIES.DATABASE,
|
|
10777
10831
|
name: "Database Security",
|
|
10778
|
-
description: "SQL injection, database enumeration, and data extraction."
|
|
10779
|
-
promptPath: join11(__dirname, "database/prompt.md")
|
|
10832
|
+
description: "SQL injection, database enumeration, and data extraction."
|
|
10780
10833
|
},
|
|
10781
10834
|
[SERVICE_CATEGORIES.AD]: {
|
|
10782
10835
|
id: SERVICE_CATEGORIES.AD,
|
|
10783
10836
|
name: "Active Directory",
|
|
10784
|
-
description: "Kerberos, LDAP, and Windows domain privilege escalation."
|
|
10785
|
-
promptPath: join11(__dirname, "ad/prompt.md")
|
|
10837
|
+
description: "Kerberos, LDAP, and Windows domain privilege escalation."
|
|
10786
10838
|
},
|
|
10787
10839
|
[SERVICE_CATEGORIES.EMAIL]: {
|
|
10788
10840
|
id: SERVICE_CATEGORIES.EMAIL,
|
|
10789
10841
|
name: "Email Services",
|
|
10790
|
-
description: "SMTP, IMAP, POP3 security and user enumeration."
|
|
10791
|
-
promptPath: join11(__dirname, "email/prompt.md")
|
|
10842
|
+
description: "SMTP, IMAP, POP3 security and user enumeration."
|
|
10792
10843
|
},
|
|
10793
10844
|
[SERVICE_CATEGORIES.REMOTE_ACCESS]: {
|
|
10794
10845
|
id: SERVICE_CATEGORIES.REMOTE_ACCESS,
|
|
10795
10846
|
name: "Remote Access",
|
|
10796
|
-
description: "SSH, RDP, VNC and other remote control protocols."
|
|
10797
|
-
promptPath: join11(__dirname, "remote-access/prompt.md")
|
|
10847
|
+
description: "SSH, RDP, VNC and other remote control protocols."
|
|
10798
10848
|
},
|
|
10799
10849
|
[SERVICE_CATEGORIES.FILE_SHARING]: {
|
|
10800
10850
|
id: SERVICE_CATEGORIES.FILE_SHARING,
|
|
10801
10851
|
name: "File Sharing",
|
|
10802
|
-
description: "SMB, NFS, FTP and shared resource security."
|
|
10803
|
-
promptPath: join11(__dirname, "file-sharing/prompt.md")
|
|
10852
|
+
description: "SMB, NFS, FTP and shared resource security."
|
|
10804
10853
|
},
|
|
10805
10854
|
[SERVICE_CATEGORIES.CLOUD]: {
|
|
10806
10855
|
id: SERVICE_CATEGORIES.CLOUD,
|
|
10807
10856
|
name: "Cloud Infrastructure",
|
|
10808
|
-
description: "AWS, Azure, and GCP security and misconfiguration."
|
|
10809
|
-
promptPath: join11(__dirname, "cloud/prompt.md")
|
|
10857
|
+
description: "AWS, Azure, and GCP security and misconfiguration."
|
|
10810
10858
|
},
|
|
10811
10859
|
[SERVICE_CATEGORIES.CONTAINER]: {
|
|
10812
10860
|
id: SERVICE_CATEGORIES.CONTAINER,
|
|
10813
10861
|
name: "Container Systems",
|
|
10814
|
-
description: "Docker and Kubernetes security testing."
|
|
10815
|
-
promptPath: join11(__dirname, "container/prompt.md")
|
|
10862
|
+
description: "Docker and Kubernetes security testing."
|
|
10816
10863
|
},
|
|
10817
10864
|
[SERVICE_CATEGORIES.API]: {
|
|
10818
10865
|
id: SERVICE_CATEGORIES.API,
|
|
10819
10866
|
name: "API Security",
|
|
10820
|
-
description: "REST, GraphQL, and SOAP API security testing."
|
|
10821
|
-
promptPath: join11(__dirname, "api/prompt.md")
|
|
10867
|
+
description: "REST, GraphQL, and SOAP API security testing."
|
|
10822
10868
|
},
|
|
10823
10869
|
[SERVICE_CATEGORIES.WIRELESS]: {
|
|
10824
10870
|
id: SERVICE_CATEGORIES.WIRELESS,
|
|
10825
10871
|
name: "Wireless Networks",
|
|
10826
|
-
description: "WiFi and Bluetooth security testing."
|
|
10827
|
-
promptPath: join11(__dirname, "wireless/prompt.md")
|
|
10872
|
+
description: "WiFi and Bluetooth security testing."
|
|
10828
10873
|
},
|
|
10829
10874
|
[SERVICE_CATEGORIES.ICS]: {
|
|
10830
10875
|
id: SERVICE_CATEGORIES.ICS,
|
|
10831
10876
|
name: "Industrial Systems",
|
|
10832
|
-
description: "Critical infrastructure - Modbus, DNP3, ENIP."
|
|
10833
|
-
promptPath: join11(__dirname, "ics/prompt.md")
|
|
10877
|
+
description: "Critical infrastructure - Modbus, DNP3, ENIP."
|
|
10834
10878
|
}
|
|
10835
10879
|
};
|
|
10836
10880
|
function loadAllDomainTools(state, events) {
|
|
@@ -11503,20 +11547,38 @@ function classifyError(error) {
|
|
|
11503
11547
|
}
|
|
11504
11548
|
|
|
11505
11549
|
// src/engine/llm-client/retry-handler.ts
|
|
11550
|
+
var retryUtils = {
|
|
11551
|
+
sleep: (ms, signal) => {
|
|
11552
|
+
return new Promise((resolve, reject) => {
|
|
11553
|
+
if (signal?.aborted) {
|
|
11554
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
11555
|
+
return;
|
|
11556
|
+
}
|
|
11557
|
+
const timer = setTimeout(resolve, ms);
|
|
11558
|
+
signal?.addEventListener("abort", () => {
|
|
11559
|
+
clearTimeout(timer);
|
|
11560
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
11561
|
+
}, { once: true });
|
|
11562
|
+
});
|
|
11563
|
+
}
|
|
11564
|
+
};
|
|
11506
11565
|
async function executeWithRetry(operation, callbacks) {
|
|
11507
11566
|
const signal = callbacks?.abortSignal;
|
|
11508
11567
|
const maxAttempts = RETRY_CONFIG.autoRetryMaxAttempts;
|
|
11509
|
-
|
|
11568
|
+
const delayMs = RETRY_CONFIG.autoRetryDelayMs;
|
|
11569
|
+
let attempt = 0;
|
|
11570
|
+
while (attempt < maxAttempts) {
|
|
11510
11571
|
if (signal?.aborted) throwAbort();
|
|
11511
11572
|
try {
|
|
11512
11573
|
return await operation();
|
|
11513
11574
|
} catch (error) {
|
|
11514
11575
|
if (isAbortError(error, signal)) throwAbort();
|
|
11515
11576
|
const errorInfo = classifyError(error);
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11577
|
+
const isRetryableType = errorInfo.type === LLM_ERROR_TYPES.RATE_LIMIT || errorInfo.type === LLM_ERROR_TYPES.NETWORK_ERROR || errorInfo.type === LLM_ERROR_TYPES.TIMEOUT;
|
|
11578
|
+
if (isRetryableType) {
|
|
11579
|
+
attempt++;
|
|
11580
|
+
callbacks?.onRetry?.(attempt, maxAttempts, delayMs, errorInfo.message);
|
|
11581
|
+
await retryUtils.sleep(delayMs, signal);
|
|
11520
11582
|
continue;
|
|
11521
11583
|
}
|
|
11522
11584
|
throw new LLMError(errorInfo);
|
|
@@ -11524,19 +11586,6 @@ async function executeWithRetry(operation, callbacks) {
|
|
|
11524
11586
|
}
|
|
11525
11587
|
throw new LLMError({ type: LLM_ERROR_TYPES.UNKNOWN, message: "Max retry exceeded", isRetryable: false });
|
|
11526
11588
|
}
|
|
11527
|
-
function sleep(ms, signal) {
|
|
11528
|
-
return new Promise((resolve, reject) => {
|
|
11529
|
-
if (signal?.aborted) {
|
|
11530
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
11531
|
-
return;
|
|
11532
|
-
}
|
|
11533
|
-
const timer = setTimeout(resolve, ms);
|
|
11534
|
-
signal?.addEventListener("abort", () => {
|
|
11535
|
-
clearTimeout(timer);
|
|
11536
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
11537
|
-
}, { once: true });
|
|
11538
|
-
});
|
|
11539
|
-
}
|
|
11540
11589
|
function isAbortError(error, signal) {
|
|
11541
11590
|
return error?.name === "AbortError" || !!signal?.aborted;
|
|
11542
11591
|
}
|
|
@@ -12518,6 +12567,18 @@ function extractHypothesizedReason(failureLine) {
|
|
|
12518
12567
|
}
|
|
12519
12568
|
|
|
12520
12569
|
// src/shared/utils/context-digest/formatters.ts
|
|
12570
|
+
function formatContextForLLM(memo14) {
|
|
12571
|
+
const compact = {};
|
|
12572
|
+
if (memo14.keyFindings.length > 0) compact.findings = memo14.keyFindings;
|
|
12573
|
+
if (memo14.credentials.length > 0) compact.credentials = memo14.credentials;
|
|
12574
|
+
if (memo14.attackVectors.length > 0) compact.attackVectors = memo14.attackVectors;
|
|
12575
|
+
if (memo14.failures.length > 0) compact.failures = memo14.failures;
|
|
12576
|
+
if (memo14.suspicions.length > 0) compact.suspicions = memo14.suspicions;
|
|
12577
|
+
if (memo14.nextSteps.length > 0) compact.nextSteps = memo14.nextSteps;
|
|
12578
|
+
compact.attackValue = memo14.attackValue;
|
|
12579
|
+
if (memo14.reflection) compact.reflection = memo14.reflection;
|
|
12580
|
+
return JSON.stringify(compact);
|
|
12581
|
+
}
|
|
12521
12582
|
function formatAnalystDigest(digest, filePath, originalChars) {
|
|
12522
12583
|
return [
|
|
12523
12584
|
digest,
|
|
@@ -12555,6 +12616,7 @@ async function digestToolOutput(output, toolName, toolInput, analystFn) {
|
|
|
12555
12616
|
if (originalLength < PASSTHROUGH_THRESHOLD) {
|
|
12556
12617
|
return {
|
|
12557
12618
|
digestedOutput: output,
|
|
12619
|
+
contextForLLM: output,
|
|
12558
12620
|
fullOutputPath: null,
|
|
12559
12621
|
analystUsed: false,
|
|
12560
12622
|
memo: null,
|
|
@@ -12574,8 +12636,10 @@ async function digestToolOutput(output, toolName, toolInput, analystFn) {
|
|
|
12574
12636
|
const rawAnalystResponse = await analystFn(preprocessed, context);
|
|
12575
12637
|
const memo14 = parseAnalystMemo(rawAnalystResponse);
|
|
12576
12638
|
const formatted = formatAnalystDigest(rawAnalystResponse, savedOutputPath, originalLength);
|
|
12639
|
+
const contextForLLM = formatContextForLLM(memo14);
|
|
12577
12640
|
return {
|
|
12578
12641
|
digestedOutput: formatted,
|
|
12642
|
+
contextForLLM,
|
|
12579
12643
|
fullOutputPath: savedOutputPath,
|
|
12580
12644
|
analystUsed: true,
|
|
12581
12645
|
memo: memo14,
|
|
@@ -12593,6 +12657,7 @@ async function digestToolOutput(output, toolName, toolInput, analystFn) {
|
|
|
12593
12657
|
const fallback = formatFallbackDigest(preprocessed, savedOutputPath, originalLength);
|
|
12594
12658
|
return {
|
|
12595
12659
|
digestedOutput: fallback,
|
|
12660
|
+
contextForLLM: fallback,
|
|
12596
12661
|
fullOutputPath: savedOutputPath,
|
|
12597
12662
|
analystUsed: false,
|
|
12598
12663
|
memo: null,
|
|
@@ -12696,7 +12761,7 @@ ${text}
|
|
|
12696
12761
|
|
|
12697
12762
|
// src/agents/tool-executor/result-handler/digest.ts
|
|
12698
12763
|
async function digestAndEmit(call, outputText, result, toolStartTime, llm, events) {
|
|
12699
|
-
let
|
|
12764
|
+
let contextOutputForLLM = outputText;
|
|
12700
12765
|
let digestResult = null;
|
|
12701
12766
|
try {
|
|
12702
12767
|
const llmDigestFn = createLLMDigestFn(llm);
|
|
@@ -12706,12 +12771,12 @@ async function digestAndEmit(call, outputText, result, toolStartTime, llm, event
|
|
|
12706
12771
|
JSON.stringify(call.input).slice(0, DISPLAY_LIMITS.OUTPUT_SUMMARY),
|
|
12707
12772
|
llmDigestFn
|
|
12708
12773
|
);
|
|
12709
|
-
|
|
12774
|
+
contextOutputForLLM = digestResult.contextForLLM;
|
|
12710
12775
|
} catch {
|
|
12711
|
-
if (
|
|
12712
|
-
const truncated =
|
|
12713
|
-
const remaining =
|
|
12714
|
-
|
|
12776
|
+
if (contextOutputForLLM.length > AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH) {
|
|
12777
|
+
const truncated = contextOutputForLLM.slice(0, AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH);
|
|
12778
|
+
const remaining = contextOutputForLLM.length - AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH;
|
|
12779
|
+
contextOutputForLLM = `${truncated}
|
|
12715
12780
|
|
|
12716
12781
|
... [TRUNCATED ${remaining} chars] ...`;
|
|
12717
12782
|
}
|
|
@@ -12720,16 +12785,16 @@ async function digestAndEmit(call, outputText, result, toolStartTime, llm, event
|
|
|
12720
12785
|
const tuiOutput = digestResult?.digestedOutput ? `${digestResult.digestedOutput}${outputFilePath ? `
|
|
12721
12786
|
\u{1F4C4} Full output: ${outputFilePath}` : ""}` : outputText.slice(0, DISPLAY_LIMITS.OUTPUT_SUMMARY);
|
|
12722
12787
|
emitToolResult(events, call.name, result.success, tuiOutput, result.error, Date.now() - toolStartTime);
|
|
12723
|
-
return {
|
|
12788
|
+
return { contextOutputForLLM, digestResult };
|
|
12724
12789
|
}
|
|
12725
12790
|
|
|
12726
12791
|
// src/agents/tool-executor/result-handler/journal.ts
|
|
12727
|
-
function recordJournalMemo(call, result,
|
|
12792
|
+
function recordJournalMemo(call, result, contextOutputForLLM, digestResult, turnState, state) {
|
|
12728
12793
|
turnState.toolJournal.push({
|
|
12729
12794
|
name: call.name,
|
|
12730
12795
|
inputSummary: JSON.stringify(call.input),
|
|
12731
12796
|
success: result.success,
|
|
12732
|
-
analystSummary: digestResult?.memo ? digestResult.memo.keyFindings.join("; ") || "No key findings" :
|
|
12797
|
+
analystSummary: digestResult?.memo ? digestResult.memo.keyFindings.join("; ") || "No key findings" : contextOutputForLLM,
|
|
12733
12798
|
outputFile: digestResult?.fullOutputPath ?? null
|
|
12734
12799
|
});
|
|
12735
12800
|
if (digestResult?.memo) {
|
|
@@ -12843,7 +12908,7 @@ async function executeSingle(call, deps, progress) {
|
|
|
12843
12908
|
let outputText = result.output ?? "";
|
|
12844
12909
|
scanForFlags(outputText, deps.state, deps.events);
|
|
12845
12910
|
outputText = handleToolResult(result, call, outputText, progress);
|
|
12846
|
-
const {
|
|
12911
|
+
const { contextOutputForLLM, digestResult } = await digestAndEmit(
|
|
12847
12912
|
call,
|
|
12848
12913
|
outputText,
|
|
12849
12914
|
result,
|
|
@@ -12851,8 +12916,8 @@ async function executeSingle(call, deps, progress) {
|
|
|
12851
12916
|
deps.llm,
|
|
12852
12917
|
deps.events
|
|
12853
12918
|
);
|
|
12854
|
-
recordJournalMemo(call, result,
|
|
12855
|
-
return { toolCallId: call.id, output:
|
|
12919
|
+
recordJournalMemo(call, result, contextOutputForLLM, digestResult, deps.turnState, deps.state);
|
|
12920
|
+
return { toolCallId: call.id, output: contextOutputForLLM, error: result.error };
|
|
12856
12921
|
} catch (error) {
|
|
12857
12922
|
const errorMsg = String(error);
|
|
12858
12923
|
const enrichedError = enrichToolErrorContext({
|
|
@@ -13171,17 +13236,17 @@ var PHASE_TECHNIQUE_MAP = {
|
|
|
13171
13236
|
|
|
13172
13237
|
// src/agents/prompt-builder/prompt-loader.ts
|
|
13173
13238
|
import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
|
|
13174
|
-
import { join as
|
|
13175
|
-
import { fileURLToPath
|
|
13176
|
-
var
|
|
13177
|
-
var PROMPTS_DIR =
|
|
13178
|
-
var TECHNIQUES_DIR =
|
|
13239
|
+
import { join as join11, dirname as dirname3 } from "path";
|
|
13240
|
+
import { fileURLToPath } from "url";
|
|
13241
|
+
var __dirname = dirname3(fileURLToPath(import.meta.url));
|
|
13242
|
+
var PROMPTS_DIR = join11(__dirname, "../prompts");
|
|
13243
|
+
var TECHNIQUES_DIR = join11(PROMPTS_DIR, PROMPT_PATHS.TECHNIQUES_DIR);
|
|
13179
13244
|
function loadPromptFile(filename) {
|
|
13180
|
-
const path2 =
|
|
13245
|
+
const path2 = join11(PROMPTS_DIR, filename);
|
|
13181
13246
|
return existsSync11(path2) ? readFileSync8(path2, PROMPT_CONFIG.ENCODING) : "";
|
|
13182
13247
|
}
|
|
13183
13248
|
function loadTechniqueFile(techniqueName) {
|
|
13184
|
-
const filePath =
|
|
13249
|
+
const filePath = join11(TECHNIQUES_DIR, `${techniqueName}.md`);
|
|
13185
13250
|
try {
|
|
13186
13251
|
if (!existsSync11(filePath)) return "";
|
|
13187
13252
|
return readFileSync8(filePath, PROMPT_CONFIG.ENCODING);
|
|
@@ -13330,11 +13395,11 @@ ${lines.join("\n")}
|
|
|
13330
13395
|
|
|
13331
13396
|
// src/shared/utils/journal/reader.ts
|
|
13332
13397
|
import { readFileSync as readFileSync9, existsSync as existsSync13 } from "fs";
|
|
13333
|
-
import { join as
|
|
13398
|
+
import { join as join13 } from "path";
|
|
13334
13399
|
|
|
13335
13400
|
// src/shared/utils/journal/rotation.ts
|
|
13336
13401
|
import { existsSync as existsSync12, readdirSync as readdirSync3, statSync as statSync4, rmSync as rmSync2 } from "fs";
|
|
13337
|
-
import { join as
|
|
13402
|
+
import { join as join12 } from "path";
|
|
13338
13403
|
function parseTurnNumbers(turnsDir) {
|
|
13339
13404
|
if (!existsSync12(turnsDir)) return [];
|
|
13340
13405
|
return readdirSync3(turnsDir).filter((e) => e.startsWith(TURN_FOLDER_PREFIX) && /^\d+$/.test(e.slice(TURN_FOLDER_PREFIX.length))).map((e) => Number(e.slice(TURN_FOLDER_PREFIX.length)));
|
|
@@ -13343,12 +13408,12 @@ function rotateTurnRecords() {
|
|
|
13343
13408
|
try {
|
|
13344
13409
|
const turnsDir = WORKSPACE.TURNS;
|
|
13345
13410
|
if (!existsSync12(turnsDir)) return;
|
|
13346
|
-
const turnDirs = parseTurnNumbers(turnsDir).map((n) => `${TURN_FOLDER_PREFIX}${n}`).filter((e) => statSync4(
|
|
13411
|
+
const turnDirs = parseTurnNumbers(turnsDir).map((n) => `${TURN_FOLDER_PREFIX}${n}`).filter((e) => statSync4(join12(turnsDir, e)).isDirectory()).sort((a, b) => Number(a.slice(TURN_FOLDER_PREFIX.length)) - Number(b.slice(TURN_FOLDER_PREFIX.length)));
|
|
13347
13412
|
if (turnDirs.length > MEMORY_LIMITS.MAX_TURN_ENTRIES) {
|
|
13348
13413
|
const dirsToDel = turnDirs.slice(0, turnDirs.length - MEMORY_LIMITS.MAX_TURN_ENTRIES);
|
|
13349
13414
|
for (const dir of dirsToDel) {
|
|
13350
13415
|
try {
|
|
13351
|
-
rmSync2(
|
|
13416
|
+
rmSync2(join12(turnsDir, dir), { recursive: true, force: true });
|
|
13352
13417
|
} catch {
|
|
13353
13418
|
}
|
|
13354
13419
|
}
|
|
@@ -13367,7 +13432,7 @@ function readJournalSummary() {
|
|
|
13367
13432
|
const turnsDir = WORKSPACE.TURNS;
|
|
13368
13433
|
const turnDirs = parseTurnNumbers(turnsDir).sort((a, b) => b - a);
|
|
13369
13434
|
for (const turn of turnDirs) {
|
|
13370
|
-
const summaryPath =
|
|
13435
|
+
const summaryPath = join13(WORKSPACE.turnPath(turn), TURN_FILES.SUMMARY);
|
|
13371
13436
|
if (existsSync13(summaryPath)) {
|
|
13372
13437
|
return readFileSync9(summaryPath, "utf-8");
|
|
13373
13438
|
}
|
|
@@ -13384,7 +13449,7 @@ function getRecentEntries(count = MEMORY_LIMITS.MAX_TURN_ENTRIES) {
|
|
|
13384
13449
|
const entries = [];
|
|
13385
13450
|
for (const turn of turnDirs) {
|
|
13386
13451
|
try {
|
|
13387
|
-
const filePath =
|
|
13452
|
+
const filePath = join13(WORKSPACE.turnPath(turn), TURN_FILES.STRUCTURED);
|
|
13388
13453
|
if (existsSync13(filePath)) {
|
|
13389
13454
|
const raw = readFileSync9(filePath, "utf-8");
|
|
13390
13455
|
entries.push(JSON.parse(raw));
|
|
@@ -13410,7 +13475,7 @@ function getNextTurnNumber() {
|
|
|
13410
13475
|
|
|
13411
13476
|
// src/shared/utils/journal/summary.ts
|
|
13412
13477
|
import { writeFileSync as writeFileSync9 } from "fs";
|
|
13413
|
-
import { join as
|
|
13478
|
+
import { join as join14 } from "path";
|
|
13414
13479
|
|
|
13415
13480
|
// src/shared/utils/journal/summary-collector.ts
|
|
13416
13481
|
function collectSummaryBuckets(entries) {
|
|
@@ -13515,7 +13580,7 @@ function regenerateJournalSummary() {
|
|
|
13515
13580
|
const turnDir = WORKSPACE.turnPath(latestTurn);
|
|
13516
13581
|
ensureDirExists(turnDir);
|
|
13517
13582
|
const summary = buildSummaryFromEntries(entries);
|
|
13518
|
-
const summaryPath =
|
|
13583
|
+
const summaryPath = join14(turnDir, TURN_FILES.SUMMARY);
|
|
13519
13584
|
writeFileSync9(summaryPath, summary, "utf-8");
|
|
13520
13585
|
debugLog("general", "Journal summary regenerated", {
|
|
13521
13586
|
entries: entries.length,
|
|
@@ -14109,10 +14174,10 @@ function formatForPrompt(directive, isStale = false) {
|
|
|
14109
14174
|
|
|
14110
14175
|
// src/agents/strategist/prompt-loader.ts
|
|
14111
14176
|
import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
|
|
14112
|
-
import { join as
|
|
14113
|
-
import { fileURLToPath as
|
|
14114
|
-
var
|
|
14115
|
-
var STRATEGIST_PROMPT_PATH =
|
|
14177
|
+
import { join as join15, dirname as dirname4 } from "path";
|
|
14178
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14179
|
+
var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
|
|
14180
|
+
var STRATEGIST_PROMPT_PATH = join15(__dirname2, "../prompts", "strategist-system.md");
|
|
14116
14181
|
function loadSystemPrompt() {
|
|
14117
14182
|
try {
|
|
14118
14183
|
if (existsSync14(STRATEGIST_PROMPT_PATH)) {
|
|
@@ -14513,7 +14578,7 @@ async function processReflection(toolJournal, memo14, phase, reflector) {
|
|
|
14513
14578
|
|
|
14514
14579
|
// src/agents/main-agent/turn-recorder.ts
|
|
14515
14580
|
import { writeFileSync as writeFileSync10, existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
|
|
14516
|
-
import { join as
|
|
14581
|
+
import { join as join16 } from "path";
|
|
14517
14582
|
async function recordTurn(context) {
|
|
14518
14583
|
const { turnCounter, phase, toolJournal, memo: memo14, reflections, summaryRegenerator } = context;
|
|
14519
14584
|
if (toolJournal.length === 0) {
|
|
@@ -14551,8 +14616,8 @@ async function createTurnArchive(turnCounter, ctx) {
|
|
|
14551
14616
|
memo: memo14,
|
|
14552
14617
|
reflection: entry.reflection
|
|
14553
14618
|
});
|
|
14554
|
-
writeFileSync10(
|
|
14555
|
-
writeFileSync10(
|
|
14619
|
+
writeFileSync10(join16(turnDir, TURN_FILES.RECORD), turnContent, "utf-8");
|
|
14620
|
+
writeFileSync10(join16(turnDir, TURN_FILES.STRUCTURED), JSON.stringify(entry, null, 2), "utf-8");
|
|
14556
14621
|
writeAnalystFile(turnDir, memo14);
|
|
14557
14622
|
} catch {
|
|
14558
14623
|
}
|
|
@@ -14566,7 +14631,7 @@ function writeAnalystFile(turnDir, memo14) {
|
|
|
14566
14631
|
if (memo14.suspicions.length > 0) memoLines.push("## Suspicious", ...memo14.suspicions.map((s) => `- ${s}`), "");
|
|
14567
14632
|
if (memo14.nextSteps.length > 0) memoLines.push("## Next Steps", ...memo14.nextSteps.map((n) => `- ${n}`), "");
|
|
14568
14633
|
if (memoLines.length > 0) {
|
|
14569
|
-
writeFileSync10(
|
|
14634
|
+
writeFileSync10(join16(turnDir, TURN_FILES.ANALYST), memoLines.join("\n"), "utf-8");
|
|
14570
14635
|
flowLog(
|
|
14571
14636
|
"\u2462Analyst",
|
|
14572
14637
|
"\u2192",
|
|
@@ -14579,11 +14644,11 @@ async function regenerateSummary(turnCounter, summaryRegenerator, ctx) {
|
|
|
14579
14644
|
const { entry, journalTools, memo: memo14, phase } = ctx;
|
|
14580
14645
|
try {
|
|
14581
14646
|
const turnDir = WORKSPACE.turnPath(turnCounter);
|
|
14582
|
-
const summaryPath =
|
|
14647
|
+
const summaryPath = join16(turnDir, TURN_FILES.SUMMARY);
|
|
14583
14648
|
const prevTurn = turnCounter - 1;
|
|
14584
14649
|
let existingSummary = null;
|
|
14585
14650
|
if (prevTurn >= 1) {
|
|
14586
|
-
const prevSummaryPath =
|
|
14651
|
+
const prevSummaryPath = join16(WORKSPACE.turnPath(prevTurn), TURN_FILES.SUMMARY);
|
|
14587
14652
|
if (existsSync15(prevSummaryPath)) {
|
|
14588
14653
|
existingSummary = readFileSync11(prevSummaryPath, "utf-8");
|
|
14589
14654
|
}
|
|
@@ -15198,6 +15263,10 @@ function createLifecycleHandlers(agent, state, _reasoningBufferRef) {
|
|
|
15198
15263
|
todo: s.getTodo().length
|
|
15199
15264
|
});
|
|
15200
15265
|
};
|
|
15266
|
+
const onNotification = (e) => {
|
|
15267
|
+
const prefix = e.data.level === "error" ? "\u274C" : e.data.level === "warning" ? "\u26A0\uFE0F" : e.data.level === "success" ? "\u2705" : "\u{1F514}";
|
|
15268
|
+
addMessage("system", `${prefix} [${e.data.title}] ${e.data.message}`);
|
|
15269
|
+
};
|
|
15201
15270
|
return {
|
|
15202
15271
|
onComplete,
|
|
15203
15272
|
onRetry,
|
|
@@ -15206,7 +15275,8 @@ function createLifecycleHandlers(agent, state, _reasoningBufferRef) {
|
|
|
15206
15275
|
onAIResponse,
|
|
15207
15276
|
onUsageUpdate,
|
|
15208
15277
|
onFlagFound,
|
|
15209
|
-
onPhaseChange
|
|
15278
|
+
onPhaseChange,
|
|
15279
|
+
onNotification
|
|
15210
15280
|
};
|
|
15211
15281
|
}
|
|
15212
15282
|
|
|
@@ -15276,9 +15346,26 @@ function setupInputHandlers(setInputRequest, addMessage) {
|
|
|
15276
15346
|
}
|
|
15277
15347
|
|
|
15278
15348
|
// src/platform/tui/hooks/useAgentEvents/handlers/command.ts
|
|
15279
|
-
function setupCommandHandlers(addMessage) {
|
|
15349
|
+
function setupCommandHandlers(addMessage, setCurrentStatus) {
|
|
15350
|
+
let lastStatusBase = "";
|
|
15280
15351
|
setCommandEventEmitter((event) => {
|
|
15281
|
-
if (event.type === COMMAND_EVENT_TYPES.COMMAND_START
|
|
15352
|
+
if (event.type === COMMAND_EVENT_TYPES.COMMAND_START) {
|
|
15353
|
+
lastStatusBase = event.message;
|
|
15354
|
+
return;
|
|
15355
|
+
}
|
|
15356
|
+
if (event.type === COMMAND_EVENT_TYPES.COMMAND_SUCCESS) {
|
|
15357
|
+
return;
|
|
15358
|
+
}
|
|
15359
|
+
if (event.type === COMMAND_EVENT_TYPES.COMMAND_STDOUT) {
|
|
15360
|
+
if (lastStatusBase) {
|
|
15361
|
+
const cleanLine = event.message.replace(/\s+/g, " ").slice(0, 100);
|
|
15362
|
+
setCurrentStatus(`${lastStatusBase}
|
|
15363
|
+
[live] ${cleanLine}`);
|
|
15364
|
+
}
|
|
15365
|
+
return;
|
|
15366
|
+
}
|
|
15367
|
+
if (event.type === COMMAND_EVENT_TYPES.PROCESS_NOTIFICATION) {
|
|
15368
|
+
addMessage("system", `\u{1F514} [Background Alert] ${event.message}`);
|
|
15282
15369
|
return;
|
|
15283
15370
|
}
|
|
15284
15371
|
const icon = getCommandEventIcon(event.type);
|
|
@@ -15325,7 +15412,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
|
|
|
15325
15412
|
}, reasoningBufferRef);
|
|
15326
15413
|
const reasoningHandlers = createReasoningHandlers({ addMessage, setCurrentStatus }, reasoningBufferRef);
|
|
15327
15414
|
const cleanupInput = setupInputHandlers(setInputRequest, addMessage);
|
|
15328
|
-
const cleanupCommand = setupCommandHandlers(addMessage);
|
|
15415
|
+
const cleanupCommand = setupCommandHandlers(addMessage, setCurrentStatus);
|
|
15329
15416
|
const updateStats = () => {
|
|
15330
15417
|
const s = agent.getState();
|
|
15331
15418
|
setStats({
|
|
@@ -15344,6 +15431,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
|
|
|
15344
15431
|
events.on(EVENT_TYPES.USAGE_UPDATE, lifecycleHandlers.onUsageUpdate);
|
|
15345
15432
|
events.on(EVENT_TYPES.FLAG_FOUND, lifecycleHandlers.onFlagFound);
|
|
15346
15433
|
events.on(EVENT_TYPES.PHASE_CHANGE, lifecycleHandlers.onPhaseChange);
|
|
15434
|
+
events.on(EVENT_TYPES.NOTIFICATION, lifecycleHandlers.onNotification);
|
|
15347
15435
|
events.on(EVENT_TYPES.STATE_CHANGE, updateStats);
|
|
15348
15436
|
events.on(EVENT_TYPES.START, updateStats);
|
|
15349
15437
|
events.on(EVENT_TYPES.REASONING_START, reasoningHandlers.onStart);
|
|
@@ -16003,7 +16091,7 @@ var useCommands = (props) => {
|
|
|
16003
16091
|
};
|
|
16004
16092
|
|
|
16005
16093
|
// src/platform/tui/hooks/useKeyboardShortcuts.ts
|
|
16006
|
-
import { useCallback as
|
|
16094
|
+
import { useCallback as useCallback6, useEffect as useEffect4 } from "react";
|
|
16007
16095
|
import { useInput } from "ink";
|
|
16008
16096
|
|
|
16009
16097
|
// src/platform/tui/hooks/keyboard/useDoubleTap.ts
|
|
@@ -16093,17 +16181,6 @@ var useEscHandler = ({
|
|
|
16093
16181
|
return { handleEsc };
|
|
16094
16182
|
};
|
|
16095
16183
|
|
|
16096
|
-
// src/platform/tui/hooks/keyboard/useScrollHandler.ts
|
|
16097
|
-
import { useRef as useRef5, useCallback as useCallback6 } from "react";
|
|
16098
|
-
var useScrollHandler = ({ onScroll }) => {
|
|
16099
|
-
const onScrollRef = useRef5(onScroll);
|
|
16100
|
-
onScrollRef.current = onScroll;
|
|
16101
|
-
const handleScroll = useCallback6((delta) => {
|
|
16102
|
-
onScrollRef.current?.(delta);
|
|
16103
|
-
}, []);
|
|
16104
|
-
return { handleScroll };
|
|
16105
|
-
};
|
|
16106
|
-
|
|
16107
16184
|
// src/platform/tui/hooks/useKeyboardShortcuts.ts
|
|
16108
16185
|
var DOUBLE_TAP_WINDOW = 3e3;
|
|
16109
16186
|
var useKeyboardShortcuts = ({
|
|
@@ -16115,8 +16192,7 @@ var useKeyboardShortcuts = ({
|
|
|
16115
16192
|
inputRequestRef,
|
|
16116
16193
|
isModalOpenRef,
|
|
16117
16194
|
inputRef,
|
|
16118
|
-
clearInput
|
|
16119
|
-
onScroll
|
|
16195
|
+
clearInput
|
|
16120
16196
|
}) => {
|
|
16121
16197
|
const { handleCtrlC } = useExitHandler({
|
|
16122
16198
|
handleExit,
|
|
@@ -16135,18 +16211,11 @@ var useKeyboardShortcuts = ({
|
|
|
16135
16211
|
inputRef,
|
|
16136
16212
|
windowMs: DOUBLE_TAP_WINDOW
|
|
16137
16213
|
});
|
|
16138
|
-
|
|
16139
|
-
useInput(useCallback7((ch, key) => {
|
|
16214
|
+
useInput(useCallback6((ch, key) => {
|
|
16140
16215
|
if (isModalOpenRef.current) return;
|
|
16141
16216
|
if (key.escape) handleEsc();
|
|
16142
16217
|
if (key.ctrl && ch === "c") handleCtrlC();
|
|
16143
|
-
|
|
16144
|
-
handleScroll(-10);
|
|
16145
|
-
}
|
|
16146
|
-
if (key.pageDown) {
|
|
16147
|
-
handleScroll(10);
|
|
16148
|
-
}
|
|
16149
|
-
}, [handleEsc, handleCtrlC, handleScroll, isModalOpenRef]));
|
|
16218
|
+
}, [handleEsc, handleCtrlC, isModalOpenRef]));
|
|
16150
16219
|
useEffect4(() => {
|
|
16151
16220
|
const onSignal = () => handleCtrlC();
|
|
16152
16221
|
process.on("SIGINT", onSignal);
|
|
@@ -16177,33 +16246,32 @@ var AnimationProvider = ({ children }) => {
|
|
|
16177
16246
|
var useAnimationTick = () => useContext(AnimationContext);
|
|
16178
16247
|
|
|
16179
16248
|
// src/platform/tui/components/MessageList.tsx
|
|
16180
|
-
import { memo as memo7
|
|
16181
|
-
import { Box as Box8,
|
|
16249
|
+
import { memo as memo7 } from "react";
|
|
16250
|
+
import { Box as Box8, Static } from "ink";
|
|
16182
16251
|
|
|
16183
16252
|
// src/platform/tui/components/messages/ThinkingBlock.tsx
|
|
16184
16253
|
import { memo } from "react";
|
|
16185
16254
|
import { Box, Text } from "ink";
|
|
16186
16255
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
16187
16256
|
var THINKING_PREVIEW_LINES = 3;
|
|
16188
|
-
var ThinkingBlock = memo(({ msg
|
|
16257
|
+
var ThinkingBlock = memo(({ msg }) => {
|
|
16189
16258
|
const lines = msg.content.split("\n");
|
|
16190
16259
|
const charCount = msg.content.length;
|
|
16191
16260
|
const estTokens = Math.round(charCount / LLM_LIMITS.charsPerTokenEstimate);
|
|
16192
16261
|
const hiddenCount = lines.length - THINKING_PREVIEW_LINES;
|
|
16193
|
-
const visibleLines =
|
|
16262
|
+
const visibleLines = lines.slice(0, THINKING_PREVIEW_LINES);
|
|
16194
16263
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [
|
|
16195
16264
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
16196
16265
|
/* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: " \u25C6 " }),
|
|
16197
16266
|
/* @__PURE__ */ jsx2(Text, { color: THEME.gray, children: "Reasoning" }),
|
|
16198
16267
|
/* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: ` (~${estTokens} tokens)` }),
|
|
16199
|
-
|
|
16200
|
-
isExpanded && /* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: " [R to collapse]" })
|
|
16268
|
+
hiddenCount > 0 && /* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: ` [+${hiddenCount} lines]` })
|
|
16201
16269
|
] }),
|
|
16202
16270
|
visibleLines.map((line, i) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
16203
16271
|
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: " \u2502 " }),
|
|
16204
16272
|
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: line })
|
|
16205
16273
|
] }, i)),
|
|
16206
|
-
|
|
16274
|
+
hiddenCount > 0 && /* @__PURE__ */ jsxs(Box, { children: [
|
|
16207
16275
|
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: " \u2502 " }),
|
|
16208
16276
|
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: "..." })
|
|
16209
16277
|
] })
|
|
@@ -16611,23 +16679,22 @@ function classifyResult(content) {
|
|
|
16611
16679
|
if (content.startsWith("\u26A0") || content.startsWith("!")) return RESULT_KINDS.WARNING;
|
|
16612
16680
|
return RESULT_KINDS.NEUTRAL;
|
|
16613
16681
|
}
|
|
16614
|
-
function computeResultDisplay(content
|
|
16682
|
+
function computeResultDisplay(content) {
|
|
16615
16683
|
const kind = classifyResult(content);
|
|
16616
16684
|
const isFailure = kind === RESULT_KINDS.FAILURE;
|
|
16617
16685
|
const lines = content.split("\n");
|
|
16618
16686
|
const firstLine = lines[0] || "";
|
|
16619
16687
|
const restLines = lines.slice(1);
|
|
16620
16688
|
const hiddenCount = restLines.length;
|
|
16621
|
-
const
|
|
16622
|
-
const
|
|
16623
|
-
const
|
|
16624
|
-
|
|
16625
|
-
return { firstLine, visibleRest, hasMore, effectiveExpanded };
|
|
16689
|
+
const previewLines = isFailure ? RESULT_PREVIEW_LINES * 2 : RESULT_PREVIEW_LINES;
|
|
16690
|
+
const visibleRest = restLines.slice(0, previewLines);
|
|
16691
|
+
const hasMore = hiddenCount > previewLines;
|
|
16692
|
+
return { firstLine, visibleRest, hasMore };
|
|
16626
16693
|
}
|
|
16627
16694
|
|
|
16628
16695
|
// src/platform/tui/components/messages/MessageRow.tsx
|
|
16629
16696
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
16630
|
-
var MessageRow = memo4(({ msg
|
|
16697
|
+
var MessageRow = memo4(({ msg }) => {
|
|
16631
16698
|
if (msg.type === "status") {
|
|
16632
16699
|
const statusData = parseStatusContent(msg.content);
|
|
16633
16700
|
if (statusData) {
|
|
@@ -16646,7 +16713,7 @@ var MessageRow = memo4(({ msg, isExpanded }) => {
|
|
|
16646
16713
|
] }, msg.id);
|
|
16647
16714
|
}
|
|
16648
16715
|
if (msg.type === "thinking") {
|
|
16649
|
-
return /* @__PURE__ */ jsx6(ThinkingBlock, { msg
|
|
16716
|
+
return /* @__PURE__ */ jsx6(ThinkingBlock, { msg });
|
|
16650
16717
|
}
|
|
16651
16718
|
if (msg.type === "tool") {
|
|
16652
16719
|
return /* @__PURE__ */ jsx6(Box5, { children: /* @__PURE__ */ jsx6(ToolCard, { content: msg.content }) }, msg.id);
|
|
@@ -16654,15 +16721,13 @@ var MessageRow = memo4(({ msg, isExpanded }) => {
|
|
|
16654
16721
|
if (msg.type === "result") {
|
|
16655
16722
|
const kind = classifyResult(msg.content);
|
|
16656
16723
|
const color = kind === "success" ? THEME.primary : kind === "failure" ? THEME.red : kind === "warning" ? THEME.yellow : THEME.gray;
|
|
16657
|
-
const { firstLine, visibleRest, hasMore
|
|
16724
|
+
const { firstLine, visibleRest, hasMore } = computeResultDisplay(msg.content);
|
|
16658
16725
|
const hiddenCount = msg.content.split("\n").length - 1;
|
|
16659
|
-
const isFailure = kind === "failure";
|
|
16660
16726
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
16661
16727
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16662
16728
|
/* @__PURE__ */ jsx6(Text5, { color: THEME.dimGray, children: " \u2514 " }),
|
|
16663
16729
|
/* @__PURE__ */ jsx6(Text5, { color, children: firstLine }),
|
|
16664
|
-
hasMore && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: ` [+${hiddenCount - RESULT_PREVIEW_LINES}
|
|
16665
|
-
effectiveExpanded && hiddenCount > RESULT_PREVIEW_LINES && !isFailure && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: " [T \u2193]" })
|
|
16730
|
+
hasMore && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: ` [+${hiddenCount - RESULT_PREVIEW_LINES}]` })
|
|
16666
16731
|
] }),
|
|
16667
16732
|
visibleRest.map((line, i) => /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16668
16733
|
/* @__PURE__ */ jsx6(Text5, { color: THEME.dimGray, children: " " }),
|
|
@@ -16677,18 +16742,11 @@ var MessageRow = memo4(({ msg, isExpanded }) => {
|
|
|
16677
16742
|
const eLines = msg.content.split("\n");
|
|
16678
16743
|
const eFirst = eLines[0] || msg.content;
|
|
16679
16744
|
const eHidden = eLines.length - 1;
|
|
16680
|
-
return /* @__PURE__ */
|
|
16681
|
-
/* @__PURE__ */
|
|
16682
|
-
|
|
16683
|
-
|
|
16684
|
-
|
|
16685
|
-
isExpanded && eHidden > 0 && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: " [E \u2191]" })
|
|
16686
|
-
] }),
|
|
16687
|
-
isExpanded && eLines.slice(1).map((line, i) => /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16688
|
-
/* @__PURE__ */ jsx6(Text5, { color: THEME.dimGray, children: " " }),
|
|
16689
|
-
/* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.red, children: line })
|
|
16690
|
-
] }, i))
|
|
16691
|
-
] }, msg.id);
|
|
16745
|
+
return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16746
|
+
/* @__PURE__ */ jsx6(Text5, { color: THEME.red, children: " \u2717 " }),
|
|
16747
|
+
/* @__PURE__ */ jsx6(Text5, { color: THEME.red, children: eFirst }),
|
|
16748
|
+
eHidden > 0 && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: ` [+${eHidden}]` })
|
|
16749
|
+
] }) }, msg.id);
|
|
16692
16750
|
}
|
|
16693
16751
|
if (msg.type === "user") {
|
|
16694
16752
|
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
@@ -16726,11 +16784,9 @@ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
|
16726
16784
|
var EmptyState = memo6(({ modelName, autoApproveMode, version }) => {
|
|
16727
16785
|
const autoLabel = autoApproveMode ? "ON" : "OFF";
|
|
16728
16786
|
const autoColor = autoApproveMode ? THEME.primary : THEME.gray;
|
|
16729
|
-
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", paddingX:
|
|
16787
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
16730
16788
|
/* @__PURE__ */ jsx8(ShimmerBanner, { banner: ASCII_BANNER }),
|
|
16731
|
-
/* @__PURE__ */
|
|
16732
|
-
/* @__PURE__ */ jsxs6(Box7, { gap: 2, children: [
|
|
16733
|
-
/* @__PURE__ */ jsx8(Text7, { color: THEME.primary, bold: true, children: "\u25C8 Pentesting" }),
|
|
16789
|
+
/* @__PURE__ */ jsxs6(Box7, { paddingLeft: 2, gap: 2, children: [
|
|
16734
16790
|
version && /* @__PURE__ */ jsx8(Text7, { color: THEME.dimGray, children: `v${version}` }),
|
|
16735
16791
|
modelName && /* @__PURE__ */ jsxs6(Text7, { color: THEME.dimGray, children: [
|
|
16736
16792
|
"\xB7",
|
|
@@ -16744,99 +16800,35 @@ var EmptyState = memo6(({ modelName, autoApproveMode, version }) => {
|
|
|
16744
16800
|
] })
|
|
16745
16801
|
] }),
|
|
16746
16802
|
/* @__PURE__ */ jsx8(Text7, { children: " " }),
|
|
16747
|
-
/* @__PURE__ */
|
|
16748
|
-
|
|
16749
|
-
|
|
16750
|
-
|
|
16751
|
-
|
|
16752
|
-
|
|
16753
|
-
|
|
16803
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", paddingLeft: 2, children: [
|
|
16804
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.gray, children: "Get started:" }),
|
|
16805
|
+
/* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
|
|
16806
|
+
" ",
|
|
16807
|
+
/* @__PURE__ */ jsxs6(Text7, { color: THEME.white, children: [
|
|
16808
|
+
"/target ",
|
|
16809
|
+
"<ip>"
|
|
16810
|
+
] }),
|
|
16811
|
+
" \u2014 Set target IP or domain"
|
|
16754
16812
|
] }),
|
|
16755
|
-
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
/* @__PURE__ */
|
|
16761
|
-
|
|
16762
|
-
|
|
16763
|
-
|
|
16764
|
-
|
|
16765
|
-
"
|
|
16766
|
-
/* @__PURE__ */ jsx8(Text7, { color: THEME.
|
|
16767
|
-
|
|
16768
|
-
] }),
|
|
16769
|
-
/* @__PURE__ */ jsx8(Text7, { children: " " }),
|
|
16770
|
-
/* @__PURE__ */ jsx8(Text7, { color: THEME.dimGray, children: " Or just type a task and press Enter." })
|
|
16813
|
+
/* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
|
|
16814
|
+
" ",
|
|
16815
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.white, children: "/start" }),
|
|
16816
|
+
" \u2014 Begin autonomous pentest"
|
|
16817
|
+
] }),
|
|
16818
|
+
/* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
|
|
16819
|
+
" ",
|
|
16820
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.white, children: "/help" }),
|
|
16821
|
+
" \u2014 Show all commands"
|
|
16822
|
+
] }),
|
|
16823
|
+
/* @__PURE__ */ jsx8(Text7, { children: " " }),
|
|
16824
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.dimGray, children: "Or just type a task and press Enter." })
|
|
16825
|
+
] })
|
|
16771
16826
|
] });
|
|
16772
16827
|
});
|
|
16773
16828
|
|
|
16774
|
-
// src/platform/tui/components/messages/sliding-window.ts
|
|
16775
|
-
var MAX_VISIBLE_MESSAGES = 200;
|
|
16776
|
-
function computeSlidingWindow(messages, scrollOffset) {
|
|
16777
|
-
const allVisible = messages.length > MAX_VISIBLE_MESSAGES ? messages.slice(-MAX_VISIBLE_MESSAGES) : messages;
|
|
16778
|
-
const clampedOffset = Math.min(scrollOffset, Math.max(0, allVisible.length - 1));
|
|
16779
|
-
const visibleMessages = clampedOffset === 0 ? allVisible : allVisible.slice(0, allVisible.length - clampedOffset);
|
|
16780
|
-
const hiddenAbove = allVisible.length - visibleMessages.length;
|
|
16781
|
-
return { visibleMessages, hiddenAbove, clampedOffset };
|
|
16782
|
-
}
|
|
16783
|
-
|
|
16784
16829
|
// src/platform/tui/components/MessageList.tsx
|
|
16785
|
-
import { jsx as jsx9
|
|
16786
|
-
var MessageList = memo7(({ messages,
|
|
16787
|
-
const [expandedIds, setExpandedIds] = useState4(/* @__PURE__ */ new Set());
|
|
16788
|
-
const messagesRef = useRef6(messages);
|
|
16789
|
-
messagesRef.current = messages;
|
|
16790
|
-
const isModalOpenRef = useRef6(isModalOpen);
|
|
16791
|
-
isModalOpenRef.current = isModalOpen;
|
|
16792
|
-
useInput2(useCallback8((_ch, key) => {
|
|
16793
|
-
if (isModalOpenRef.current || key.ctrl) return;
|
|
16794
|
-
if (_ch === "r") {
|
|
16795
|
-
const msgs = messagesRef.current;
|
|
16796
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
16797
|
-
if (msgs[i].type === "thinking") {
|
|
16798
|
-
const id = msgs[i].id;
|
|
16799
|
-
setExpandedIds((prev) => {
|
|
16800
|
-
const next = new Set(prev);
|
|
16801
|
-
if (next.has(id)) next.delete(id);
|
|
16802
|
-
else next.add(id);
|
|
16803
|
-
return next;
|
|
16804
|
-
});
|
|
16805
|
-
break;
|
|
16806
|
-
}
|
|
16807
|
-
}
|
|
16808
|
-
}
|
|
16809
|
-
if (_ch === "e") {
|
|
16810
|
-
const msgs = messagesRef.current;
|
|
16811
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
16812
|
-
if (msgs[i].type === "error") {
|
|
16813
|
-
const id = msgs[i].id;
|
|
16814
|
-
setExpandedIds((prev) => {
|
|
16815
|
-
const next = new Set(prev);
|
|
16816
|
-
if (next.has(id)) next.delete(id);
|
|
16817
|
-
else next.add(id);
|
|
16818
|
-
return next;
|
|
16819
|
-
});
|
|
16820
|
-
break;
|
|
16821
|
-
}
|
|
16822
|
-
}
|
|
16823
|
-
}
|
|
16824
|
-
if (_ch === "t") {
|
|
16825
|
-
const msgs = messagesRef.current;
|
|
16826
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
16827
|
-
if (msgs[i].type === "result") {
|
|
16828
|
-
const id = msgs[i].id;
|
|
16829
|
-
setExpandedIds((prev) => {
|
|
16830
|
-
const next = new Set(prev);
|
|
16831
|
-
if (next.has(id)) next.delete(id);
|
|
16832
|
-
else next.add(id);
|
|
16833
|
-
return next;
|
|
16834
|
-
});
|
|
16835
|
-
break;
|
|
16836
|
-
}
|
|
16837
|
-
}
|
|
16838
|
-
}
|
|
16839
|
-
}, []));
|
|
16830
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
16831
|
+
var MessageList = memo7(({ messages, modelName, autoApproveMode, version }) => {
|
|
16840
16832
|
if (messages.length === 0) {
|
|
16841
16833
|
return /* @__PURE__ */ jsx9(
|
|
16842
16834
|
EmptyState,
|
|
@@ -16847,30 +16839,19 @@ var MessageList = memo7(({ messages, isModalOpen, modelName, autoApproveMode, ve
|
|
|
16847
16839
|
}
|
|
16848
16840
|
);
|
|
16849
16841
|
}
|
|
16850
|
-
|
|
16851
|
-
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
16852
|
-
clampedOffset > 0 && /* @__PURE__ */ jsx9(Box8, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text8, { dimColor: true, color: THEME.dimGray, children: `\u2191 ${hiddenAbove} message${hiddenAbove !== 1 ? "s" : ""} above \xB7 PgDn to scroll down` }) }),
|
|
16853
|
-
visibleMessages.map((msg) => /* @__PURE__ */ jsx9(
|
|
16854
|
-
MessageRow,
|
|
16855
|
-
{
|
|
16856
|
-
msg,
|
|
16857
|
-
isExpanded: expandedIds.has(msg.id)
|
|
16858
|
-
},
|
|
16859
|
-
msg.id
|
|
16860
|
-
))
|
|
16861
|
-
] });
|
|
16842
|
+
return /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsx9(Static, { items: messages, children: (msg) => /* @__PURE__ */ jsx9(MessageRow, { msg }, msg.id) }) });
|
|
16862
16843
|
});
|
|
16863
16844
|
|
|
16864
16845
|
// src/platform/tui/components/StatusDisplay.tsx
|
|
16865
16846
|
import { memo as memo10 } from "react";
|
|
16866
|
-
import { Box as Box11, Text as
|
|
16847
|
+
import { Box as Box11, Text as Text12 } from "ink";
|
|
16867
16848
|
|
|
16868
16849
|
// src/platform/tui/hooks/useStatusTimer.ts
|
|
16869
|
-
import { useState as
|
|
16850
|
+
import { useState as useState4, useEffect as useEffect6, useRef as useRef5 } from "react";
|
|
16870
16851
|
var useStatusTimer = (currentStatus, isProcessing) => {
|
|
16871
|
-
const [statusElapsed, setStatusElapsed] =
|
|
16872
|
-
const statusTimerRef =
|
|
16873
|
-
const statusStartRef =
|
|
16852
|
+
const [statusElapsed, setStatusElapsed] = useState4(0);
|
|
16853
|
+
const statusTimerRef = useRef5(null);
|
|
16854
|
+
const statusStartRef = useRef5(Date.now());
|
|
16874
16855
|
useEffect6(() => {
|
|
16875
16856
|
if (statusTimerRef.current) clearInterval(statusTimerRef.current);
|
|
16876
16857
|
if (isProcessing && currentStatus) {
|
|
@@ -16891,11 +16872,11 @@ var useStatusTimer = (currentStatus, isProcessing) => {
|
|
|
16891
16872
|
};
|
|
16892
16873
|
|
|
16893
16874
|
// src/platform/tui/components/status/RetryView.tsx
|
|
16894
|
-
import { Box as Box9, Text as
|
|
16875
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
16895
16876
|
|
|
16896
16877
|
// src/platform/tui/components/StarSpinner.tsx
|
|
16897
16878
|
import { memo as memo8 } from "react";
|
|
16898
|
-
import { Text as
|
|
16879
|
+
import { Text as Text8 } from "ink";
|
|
16899
16880
|
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
16900
16881
|
var FRAMES = [
|
|
16901
16882
|
"\xB7",
|
|
@@ -16916,31 +16897,31 @@ var FRAMES = [
|
|
|
16916
16897
|
var StarSpinner = memo8(({ color }) => {
|
|
16917
16898
|
const tick = useAnimationTick();
|
|
16918
16899
|
const index = tick % FRAMES.length;
|
|
16919
|
-
return /* @__PURE__ */ jsx10(
|
|
16900
|
+
return /* @__PURE__ */ jsx10(Text8, { color: color || "yellow", children: FRAMES[index] });
|
|
16920
16901
|
});
|
|
16921
16902
|
|
|
16922
16903
|
// src/platform/tui/components/status/RetryView.tsx
|
|
16923
|
-
import { jsx as jsx11, jsxs as
|
|
16904
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
16924
16905
|
var RetryView = ({ retryState }) => {
|
|
16925
16906
|
const truncateError = (err) => {
|
|
16926
16907
|
return err.length > DISPLAY_LIMITS.RETRY_ERROR_PREVIEW ? err.substring(0, DISPLAY_LIMITS.RETRY_ERROR_TRUNCATED) + "..." : err;
|
|
16927
16908
|
};
|
|
16928
|
-
return /* @__PURE__ */
|
|
16929
|
-
/* @__PURE__ */
|
|
16930
|
-
/* @__PURE__ */ jsx11(
|
|
16931
|
-
/* @__PURE__ */
|
|
16909
|
+
return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", height: 2, children: [
|
|
16910
|
+
/* @__PURE__ */ jsxs7(Box9, { children: [
|
|
16911
|
+
/* @__PURE__ */ jsx11(Text9, { color: THEME.yellow, wrap: "truncate", children: /* @__PURE__ */ jsx11(StarSpinner, { color: THEME.yellow }) }),
|
|
16912
|
+
/* @__PURE__ */ jsxs7(Text9, { color: THEME.yellow, bold: true, wrap: "truncate", children: [
|
|
16932
16913
|
" \u29F3 Retry #",
|
|
16933
16914
|
retryState.attempt,
|
|
16934
16915
|
"/",
|
|
16935
16916
|
retryState.maxRetries
|
|
16936
16917
|
] }),
|
|
16937
|
-
/* @__PURE__ */
|
|
16918
|
+
/* @__PURE__ */ jsxs7(Text9, { color: THEME.gray, wrap: "truncate", children: [
|
|
16938
16919
|
" \u2014 ",
|
|
16939
16920
|
retryState.countdown,
|
|
16940
16921
|
"s"
|
|
16941
16922
|
] })
|
|
16942
16923
|
] }),
|
|
16943
|
-
/* @__PURE__ */ jsx11(Box9, { children: /* @__PURE__ */
|
|
16924
|
+
/* @__PURE__ */ jsx11(Box9, { children: /* @__PURE__ */ jsxs7(Text9, { color: THEME.gray, wrap: "truncate", children: [
|
|
16944
16925
|
" ",
|
|
16945
16926
|
truncateError(retryState.error)
|
|
16946
16927
|
] }) })
|
|
@@ -16948,11 +16929,11 @@ var RetryView = ({ retryState }) => {
|
|
|
16948
16929
|
};
|
|
16949
16930
|
|
|
16950
16931
|
// src/platform/tui/components/status/ProcessingView.tsx
|
|
16951
|
-
import { Box as Box10, Text as
|
|
16932
|
+
import { Box as Box10, Text as Text11 } from "ink";
|
|
16952
16933
|
|
|
16953
16934
|
// src/platform/tui/components/ShimmerText.tsx
|
|
16954
16935
|
import { memo as memo9 } from "react";
|
|
16955
|
-
import { Text as
|
|
16936
|
+
import { Text as Text10 } from "ink";
|
|
16956
16937
|
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
16957
16938
|
var WAVE_SPEED = 0.25 * (120 / ANIM_TICK_MS);
|
|
16958
16939
|
var CHAR_PHASE_GAP = 0.55;
|
|
@@ -16965,16 +16946,16 @@ function sinToColor(sin) {
|
|
|
16965
16946
|
var ShimmerText = memo9(({ children, bold, phase = 0 }) => {
|
|
16966
16947
|
const tick = useAnimationTick();
|
|
16967
16948
|
const globalPhase = tick * WAVE_SPEED + phase;
|
|
16968
|
-
return /* @__PURE__ */ jsx12(
|
|
16949
|
+
return /* @__PURE__ */ jsx12(Text10, { bold, children: Array.from(children).map((char, i) => {
|
|
16969
16950
|
const charPhase = globalPhase - i * CHAR_PHASE_GAP;
|
|
16970
16951
|
const sin = Math.sin(charPhase);
|
|
16971
16952
|
const color = sinToColor(sin);
|
|
16972
|
-
return /* @__PURE__ */ jsx12(
|
|
16953
|
+
return /* @__PURE__ */ jsx12(Text10, { color, children: char }, i);
|
|
16973
16954
|
}) });
|
|
16974
16955
|
});
|
|
16975
16956
|
|
|
16976
16957
|
// src/platform/tui/components/status/ProcessingView.tsx
|
|
16977
|
-
import { jsx as jsx13, jsxs as
|
|
16958
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
16978
16959
|
var ProcessingView = ({
|
|
16979
16960
|
statusMain,
|
|
16980
16961
|
detailText,
|
|
@@ -16993,26 +16974,26 @@ var ProcessingView = ({
|
|
|
16993
16974
|
const parenIdx = statusMain.indexOf("(");
|
|
16994
16975
|
const shimmerPart = parenIdx > -1 ? statusMain.slice(0, parenIdx).trimEnd() : statusMain;
|
|
16995
16976
|
const staticSuffix = parenIdx > -1 ? " " + statusMain.slice(parenIdx) : "";
|
|
16996
|
-
return /* @__PURE__ */
|
|
16997
|
-
/* @__PURE__ */
|
|
16998
|
-
/* @__PURE__ */ jsx13(
|
|
16999
|
-
/* @__PURE__ */ jsx13(
|
|
16977
|
+
return /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", height: 2, children: [
|
|
16978
|
+
/* @__PURE__ */ jsxs8(Box10, { children: [
|
|
16979
|
+
/* @__PURE__ */ jsx13(Text11, { color, wrap: "truncate", children: /* @__PURE__ */ jsx13(StarSpinner, { color }) }),
|
|
16980
|
+
/* @__PURE__ */ jsx13(Text11, { children: " " }),
|
|
17000
16981
|
/* @__PURE__ */ jsx13(ShimmerText, { bold: true, phase: 0, children: shimmerPart }),
|
|
17001
|
-
staticSuffix ? /* @__PURE__ */ jsx13(
|
|
17002
|
-
/* @__PURE__ */
|
|
16982
|
+
staticSuffix ? /* @__PURE__ */ jsx13(Text11, { color: THEME.dimGray, wrap: "truncate", children: staticSuffix }) : null,
|
|
16983
|
+
/* @__PURE__ */ jsxs8(Text11, { color: THEME.dimGray, wrap: "truncate", children: [
|
|
17003
16984
|
" ",
|
|
17004
16985
|
meta
|
|
17005
16986
|
] })
|
|
17006
16987
|
] }),
|
|
17007
|
-
/* @__PURE__ */ jsx13(Box10, { children: detailText ? /* @__PURE__ */
|
|
16988
|
+
/* @__PURE__ */ jsx13(Box10, { children: detailText ? /* @__PURE__ */ jsxs8(Text11, { color: THEME.dimGray, wrap: "truncate", children: [
|
|
17008
16989
|
" ",
|
|
17009
16990
|
detailText
|
|
17010
|
-
] }) : /* @__PURE__ */ jsx13(
|
|
16991
|
+
] }) : /* @__PURE__ */ jsx13(Text11, { children: " " }) })
|
|
17011
16992
|
] });
|
|
17012
16993
|
};
|
|
17013
16994
|
|
|
17014
16995
|
// src/platform/tui/components/StatusDisplay.tsx
|
|
17015
|
-
import { jsx as jsx14, jsxs as
|
|
16996
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
17016
16997
|
var StatusDisplay = memo10(({
|
|
17017
16998
|
retryState,
|
|
17018
16999
|
isProcessing,
|
|
@@ -17026,9 +17007,9 @@ var StatusDisplay = memo10(({
|
|
|
17026
17007
|
return /* @__PURE__ */ jsx14(RetryView, { retryState });
|
|
17027
17008
|
}
|
|
17028
17009
|
if (isProcessing && isWaitingForInput) {
|
|
17029
|
-
return /* @__PURE__ */
|
|
17030
|
-
/* @__PURE__ */ jsx14(
|
|
17031
|
-
/* @__PURE__ */ jsx14(
|
|
17010
|
+
return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", height: 2, children: [
|
|
17011
|
+
/* @__PURE__ */ jsx14(Text12, { children: " " }),
|
|
17012
|
+
/* @__PURE__ */ jsx14(Text12, { children: " " })
|
|
17032
17013
|
] });
|
|
17033
17014
|
}
|
|
17034
17015
|
if (isProcessing) {
|
|
@@ -17047,19 +17028,19 @@ var StatusDisplay = memo10(({
|
|
|
17047
17028
|
}
|
|
17048
17029
|
);
|
|
17049
17030
|
}
|
|
17050
|
-
return /* @__PURE__ */
|
|
17051
|
-
/* @__PURE__ */ jsx14(
|
|
17052
|
-
/* @__PURE__ */ jsx14(
|
|
17031
|
+
return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", height: 2, children: [
|
|
17032
|
+
/* @__PURE__ */ jsx14(Text12, { children: " " }),
|
|
17033
|
+
/* @__PURE__ */ jsx14(Text12, { children: " " })
|
|
17053
17034
|
] });
|
|
17054
17035
|
});
|
|
17055
17036
|
|
|
17056
17037
|
// src/platform/tui/components/ChatInput.tsx
|
|
17057
|
-
import { useMemo, useCallback as
|
|
17058
|
-
import { Box as Box15, Text as
|
|
17038
|
+
import { useMemo, useCallback as useCallback7, useRef as useRef6, memo as memo11, useState as useState5, useEffect as useEffect7 } from "react";
|
|
17039
|
+
import { Box as Box15, Text as Text16, useInput as useInput2 } from "ink";
|
|
17059
17040
|
|
|
17060
17041
|
// src/platform/tui/components/input/AutocompletePreview.tsx
|
|
17061
|
-
import { Box as Box12, Text as
|
|
17062
|
-
import { jsx as jsx15, jsxs as
|
|
17042
|
+
import { Box as Box12, Text as Text13 } from "ink";
|
|
17043
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
17063
17044
|
var AutocompletePreview = ({
|
|
17064
17045
|
suggestions,
|
|
17065
17046
|
clampedIdx
|
|
@@ -17067,26 +17048,26 @@ var AutocompletePreview = ({
|
|
|
17067
17048
|
return /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", paddingX: 1, children: suggestions.map((cmd, i) => {
|
|
17068
17049
|
const isSelected = i === clampedIdx;
|
|
17069
17050
|
const argsText = cmd.args ? ` ${cmd.args}` : "";
|
|
17070
|
-
return /* @__PURE__ */
|
|
17071
|
-
/* @__PURE__ */ jsx15(
|
|
17072
|
-
/* @__PURE__ */
|
|
17051
|
+
return /* @__PURE__ */ jsxs10(Box12, { children: [
|
|
17052
|
+
/* @__PURE__ */ jsx15(Text13, { color: isSelected ? THEME.primary : THEME.dimGray, children: isSelected ? " \u276F " : " " }),
|
|
17053
|
+
/* @__PURE__ */ jsxs10(Text13, { color: isSelected ? THEME.white : THEME.gray, bold: isSelected, children: [
|
|
17073
17054
|
"/",
|
|
17074
17055
|
cmd.name
|
|
17075
17056
|
] }),
|
|
17076
|
-
/* @__PURE__ */ jsx15(
|
|
17077
|
-
/* @__PURE__ */
|
|
17057
|
+
/* @__PURE__ */ jsx15(Text13, { dimColor: true, color: THEME.gray, children: argsText }),
|
|
17058
|
+
/* @__PURE__ */ jsxs10(Text13, { dimColor: true, color: THEME.dimGray, children: [
|
|
17078
17059
|
" \u2014 ",
|
|
17079
17060
|
cmd.description
|
|
17080
17061
|
] }),
|
|
17081
|
-
isSelected && cmd.alias && /* @__PURE__ */ jsx15(
|
|
17062
|
+
isSelected && cmd.alias && /* @__PURE__ */ jsx15(Text13, { dimColor: true, color: THEME.dimGray, children: ` (/${cmd.alias})` })
|
|
17082
17063
|
] }, cmd.name);
|
|
17083
17064
|
}) });
|
|
17084
17065
|
};
|
|
17085
17066
|
|
|
17086
17067
|
// src/platform/tui/components/input/SecretInputArea.tsx
|
|
17087
|
-
import { Box as Box13, Text as
|
|
17068
|
+
import { Box as Box13, Text as Text14, useStdout } from "ink";
|
|
17088
17069
|
import TextInput from "ink-text-input";
|
|
17089
|
-
import { jsx as jsx16, jsxs as
|
|
17070
|
+
import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
17090
17071
|
var OUTER_PADDING = 2;
|
|
17091
17072
|
var SecretInputArea = ({
|
|
17092
17073
|
inputRequest,
|
|
@@ -17097,10 +17078,10 @@ var SecretInputArea = ({
|
|
|
17097
17078
|
const { stdout } = useStdout();
|
|
17098
17079
|
const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING);
|
|
17099
17080
|
const borderLine = "\u2501".repeat(borderWidth);
|
|
17100
|
-
return /* @__PURE__ */
|
|
17101
|
-
/* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(
|
|
17102
|
-
/* @__PURE__ */
|
|
17103
|
-
/* @__PURE__ */ jsx16(
|
|
17081
|
+
return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", children: [
|
|
17082
|
+
/* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(Text14, { color: THEME.yellow, children: borderLine }) }),
|
|
17083
|
+
/* @__PURE__ */ jsxs11(Box13, { paddingX: 1, children: [
|
|
17084
|
+
/* @__PURE__ */ jsx16(Text14, { color: THEME.yellow, bold: true, children: "\u25B8 " }),
|
|
17104
17085
|
/* @__PURE__ */ jsx16(
|
|
17105
17086
|
TextInput,
|
|
17106
17087
|
{
|
|
@@ -17112,14 +17093,14 @@ var SecretInputArea = ({
|
|
|
17112
17093
|
}
|
|
17113
17094
|
)
|
|
17114
17095
|
] }),
|
|
17115
|
-
/* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(
|
|
17096
|
+
/* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(Text14, { color: THEME.yellow, children: borderLine }) })
|
|
17116
17097
|
] });
|
|
17117
17098
|
};
|
|
17118
17099
|
|
|
17119
17100
|
// src/platform/tui/components/input/NormalInputArea.tsx
|
|
17120
|
-
import { Box as Box14, Text as
|
|
17101
|
+
import { Box as Box14, Text as Text15, useStdout as useStdout2 } from "ink";
|
|
17121
17102
|
import TextInput2 from "ink-text-input";
|
|
17122
|
-
import { jsx as jsx17, jsxs as
|
|
17103
|
+
import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
17123
17104
|
var OUTER_PADDING2 = 2;
|
|
17124
17105
|
var NormalInputArea = ({
|
|
17125
17106
|
inputKey,
|
|
@@ -17131,10 +17112,10 @@ var NormalInputArea = ({
|
|
|
17131
17112
|
const { stdout } = useStdout2();
|
|
17132
17113
|
const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING2);
|
|
17133
17114
|
const borderLine = "\u2500".repeat(borderWidth);
|
|
17134
|
-
return /* @__PURE__ */
|
|
17135
|
-
/* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(
|
|
17136
|
-
/* @__PURE__ */
|
|
17137
|
-
/* @__PURE__ */ jsx17(
|
|
17115
|
+
return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", children: [
|
|
17116
|
+
/* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, color: THEME.dimGray, children: borderLine }) }),
|
|
17117
|
+
/* @__PURE__ */ jsxs12(Box14, { paddingX: 1, children: [
|
|
17118
|
+
/* @__PURE__ */ jsx17(Text15, { color: THEME.primary, children: "\u276F " }),
|
|
17138
17119
|
/* @__PURE__ */ jsx17(
|
|
17139
17120
|
TextInput2,
|
|
17140
17121
|
{
|
|
@@ -17146,12 +17127,12 @@ var NormalInputArea = ({
|
|
|
17146
17127
|
inputKey
|
|
17147
17128
|
)
|
|
17148
17129
|
] }),
|
|
17149
|
-
/* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(
|
|
17130
|
+
/* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, color: THEME.dimGray, children: borderLine }) })
|
|
17150
17131
|
] });
|
|
17151
17132
|
};
|
|
17152
17133
|
|
|
17153
17134
|
// src/platform/tui/components/ChatInput.tsx
|
|
17154
|
-
import { jsx as jsx18, jsxs as
|
|
17135
|
+
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
17155
17136
|
var MAX_SUGGESTIONS = 6;
|
|
17156
17137
|
var ChatInput = memo11(({
|
|
17157
17138
|
value,
|
|
@@ -17171,25 +17152,25 @@ var ChatInput = memo11(({
|
|
|
17171
17152
|
return getMatchingCommands(partialCmd).slice(0, MAX_SUGGESTIONS);
|
|
17172
17153
|
}, [isSlashMode, partialCmd, hasArgs]);
|
|
17173
17154
|
const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
|
|
17174
|
-
const [selectedIdx, setSelectedIdx] =
|
|
17155
|
+
const [selectedIdx, setSelectedIdx] = useState5(0);
|
|
17175
17156
|
const clampedIdx = Math.min(selectedIdx, Math.max(0, suggestions.length - 1));
|
|
17176
|
-
const selectedIdxRef =
|
|
17157
|
+
const selectedIdxRef = useRef6(clampedIdx);
|
|
17177
17158
|
selectedIdxRef.current = clampedIdx;
|
|
17178
|
-
const suggestionsRef =
|
|
17159
|
+
const suggestionsRef = useRef6(suggestions);
|
|
17179
17160
|
suggestionsRef.current = suggestions;
|
|
17180
|
-
const isSlashModeRef =
|
|
17161
|
+
const isSlashModeRef = useRef6(isSlashMode);
|
|
17181
17162
|
isSlashModeRef.current = isSlashMode;
|
|
17182
|
-
const hasArgsRef =
|
|
17163
|
+
const hasArgsRef = useRef6(hasArgs);
|
|
17183
17164
|
hasArgsRef.current = hasArgs;
|
|
17184
|
-
const showPreviewRef =
|
|
17165
|
+
const showPreviewRef = useRef6(showPreview);
|
|
17185
17166
|
showPreviewRef.current = showPreview;
|
|
17186
|
-
const inputRequestRef =
|
|
17167
|
+
const inputRequestRef = useRef6(inputRequest);
|
|
17187
17168
|
inputRequestRef.current = inputRequest;
|
|
17188
|
-
const onChangeRef =
|
|
17169
|
+
const onChangeRef = useRef6(onChange);
|
|
17189
17170
|
onChangeRef.current = onChange;
|
|
17190
|
-
const [pastedHint, setPastedHint] =
|
|
17191
|
-
const prevValueRef =
|
|
17192
|
-
const pasteTimerRef =
|
|
17171
|
+
const [pastedHint, setPastedHint] = useState5(null);
|
|
17172
|
+
const prevValueRef = useRef6(value);
|
|
17173
|
+
const pasteTimerRef = useRef6(null);
|
|
17193
17174
|
useEffect7(() => {
|
|
17194
17175
|
const diff = value.length - prevValueRef.current.length;
|
|
17195
17176
|
if (diff > 20) {
|
|
@@ -17202,8 +17183,8 @@ var ChatInput = memo11(({
|
|
|
17202
17183
|
if (pasteTimerRef.current) clearTimeout(pasteTimerRef.current);
|
|
17203
17184
|
};
|
|
17204
17185
|
}, [value]);
|
|
17205
|
-
const [inputKey, setInputKey] =
|
|
17206
|
-
const completeCommand =
|
|
17186
|
+
const [inputKey, setInputKey] = useState5(0);
|
|
17187
|
+
const completeCommand = useCallback7((idx) => {
|
|
17207
17188
|
const sug = suggestionsRef.current;
|
|
17208
17189
|
if (!sug.length) return;
|
|
17209
17190
|
const best = sug[Math.min(idx, sug.length - 1)];
|
|
@@ -17213,9 +17194,9 @@ var ChatInput = memo11(({
|
|
|
17213
17194
|
setSelectedIdx(0);
|
|
17214
17195
|
setInputKey((k) => k + 1);
|
|
17215
17196
|
}, []);
|
|
17216
|
-
const onSubmitRef =
|
|
17197
|
+
const onSubmitRef = useRef6(onSubmit);
|
|
17217
17198
|
onSubmitRef.current = onSubmit;
|
|
17218
|
-
const wrappedOnSubmit =
|
|
17199
|
+
const wrappedOnSubmit = useCallback7((val) => {
|
|
17219
17200
|
if (showPreviewRef.current) {
|
|
17220
17201
|
const sug = suggestionsRef.current;
|
|
17221
17202
|
if (!sug.length) {
|
|
@@ -17234,7 +17215,7 @@ var ChatInput = memo11(({
|
|
|
17234
17215
|
}
|
|
17235
17216
|
onSubmitRef.current(val);
|
|
17236
17217
|
}, [completeCommand]);
|
|
17237
|
-
|
|
17218
|
+
useInput2(useCallback7((_input, key) => {
|
|
17238
17219
|
if (inputRequestRef.current.status === "active") return;
|
|
17239
17220
|
const sug = suggestionsRef.current;
|
|
17240
17221
|
const visible = showPreviewRef.current;
|
|
@@ -17250,7 +17231,7 @@ var ChatInput = memo11(({
|
|
|
17250
17231
|
completeCommand(selectedIdxRef.current);
|
|
17251
17232
|
}
|
|
17252
17233
|
}, [completeCommand]));
|
|
17253
|
-
return /* @__PURE__ */
|
|
17234
|
+
return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", children: [
|
|
17254
17235
|
showPreview && /* @__PURE__ */ jsx18(
|
|
17255
17236
|
AutocompletePreview,
|
|
17256
17237
|
{
|
|
@@ -17282,14 +17263,14 @@ var ChatInput = memo11(({
|
|
|
17282
17263
|
}
|
|
17283
17264
|
)
|
|
17284
17265
|
),
|
|
17285
|
-
pastedHint && /* @__PURE__ */ jsx18(Box15, { paddingX: 2, children: /* @__PURE__ */ jsx18(
|
|
17266
|
+
pastedHint && /* @__PURE__ */ jsx18(Box15, { paddingX: 2, children: /* @__PURE__ */ jsx18(Text16, { dimColor: true, color: THEME.dimGray, children: pastedHint }) })
|
|
17286
17267
|
] });
|
|
17287
17268
|
});
|
|
17288
17269
|
|
|
17289
17270
|
// src/platform/tui/components/footer.tsx
|
|
17290
17271
|
import { memo as memo12 } from "react";
|
|
17291
|
-
import { Box as Box16, Text as
|
|
17292
|
-
import { jsx as jsx19, jsxs as
|
|
17272
|
+
import { Box as Box16, Text as Text17 } from "ink";
|
|
17273
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
17293
17274
|
var CTX_WARN_THRESHOLD = 0.8;
|
|
17294
17275
|
var MAX_CONTEXT_TOKENS = LLM_LIMITS.streamMaxTokens;
|
|
17295
17276
|
var formatElapsed = (totalSeconds) => {
|
|
@@ -17305,7 +17286,7 @@ var formatElapsed = (totalSeconds) => {
|
|
|
17305
17286
|
var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing, totalTokens, turnCount }) => {
|
|
17306
17287
|
const ctxPct = totalTokens > 0 ? Math.round(totalTokens / MAX_CONTEXT_TOKENS * 100) : 0;
|
|
17307
17288
|
const ctxColor = ctxPct >= CTX_WARN_THRESHOLD * 100 ? THEME.yellow : THEME.dimGray;
|
|
17308
|
-
return /* @__PURE__ */
|
|
17289
|
+
return /* @__PURE__ */ jsxs14(
|
|
17309
17290
|
Box16,
|
|
17310
17291
|
{
|
|
17311
17292
|
width: "100%",
|
|
@@ -17313,44 +17294,44 @@ var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing
|
|
|
17313
17294
|
justifyContent: "space-between",
|
|
17314
17295
|
overflow: "hidden",
|
|
17315
17296
|
children: [
|
|
17316
|
-
/* @__PURE__ */
|
|
17317
|
-
/* @__PURE__ */
|
|
17297
|
+
/* @__PURE__ */ jsxs14(Box16, { gap: 2, children: [
|
|
17298
|
+
/* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
|
|
17318
17299
|
"Phase: ",
|
|
17319
|
-
/* @__PURE__ */ jsx19(
|
|
17300
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: phase })
|
|
17320
17301
|
] }),
|
|
17321
|
-
/* @__PURE__ */
|
|
17302
|
+
/* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
|
|
17322
17303
|
"Targets: ",
|
|
17323
|
-
/* @__PURE__ */ jsx19(
|
|
17304
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: targets })
|
|
17324
17305
|
] }),
|
|
17325
|
-
/* @__PURE__ */
|
|
17306
|
+
/* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
|
|
17326
17307
|
"Findings: ",
|
|
17327
|
-
/* @__PURE__ */ jsx19(
|
|
17308
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: findings })
|
|
17328
17309
|
] }),
|
|
17329
|
-
/* @__PURE__ */
|
|
17310
|
+
/* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
|
|
17330
17311
|
"Tasks: ",
|
|
17331
|
-
/* @__PURE__ */ jsx19(
|
|
17312
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: todo })
|
|
17332
17313
|
] })
|
|
17333
17314
|
] }),
|
|
17334
|
-
/* @__PURE__ */
|
|
17335
|
-
isProcessing ? /* @__PURE__ */
|
|
17336
|
-
/* @__PURE__ */ jsx19(
|
|
17337
|
-
/* @__PURE__ */ jsx19(
|
|
17338
|
-
] }) : /* @__PURE__ */ jsx19(
|
|
17339
|
-
turnCount > 0 && /* @__PURE__ */
|
|
17315
|
+
/* @__PURE__ */ jsxs14(Box16, { gap: 2, children: [
|
|
17316
|
+
isProcessing ? /* @__PURE__ */ jsxs14(Box16, { gap: 1, children: [
|
|
17317
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, color: THEME.dimGray, children: "[ESC] abort" }),
|
|
17318
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, color: THEME.dimGray, children: "[^C\xD72] exit" })
|
|
17319
|
+
] }) : /* @__PURE__ */ jsx19(Text17, { dimColor: true, color: THEME.dimGray, children: "[/help] commands" }),
|
|
17320
|
+
turnCount > 0 && /* @__PURE__ */ jsxs14(Text17, { dimColor: true, color: THEME.dimGray, children: [
|
|
17340
17321
|
"turn:",
|
|
17341
17322
|
turnCount
|
|
17342
17323
|
] }),
|
|
17343
|
-
totalTokens > 0 && /* @__PURE__ */
|
|
17324
|
+
totalTokens > 0 && /* @__PURE__ */ jsxs14(Text17, { dimColor: true, color: ctxColor, children: [
|
|
17344
17325
|
"ctx:",
|
|
17345
17326
|
ctxPct,
|
|
17346
17327
|
"%"
|
|
17347
17328
|
] }),
|
|
17348
|
-
totalTokens > 0 && /* @__PURE__ */
|
|
17329
|
+
totalTokens > 0 && /* @__PURE__ */ jsxs14(Text17, { dimColor: true, color: THEME.dimGray, children: [
|
|
17349
17330
|
"\u2191",
|
|
17350
17331
|
formatTokens(totalTokens)
|
|
17351
17332
|
] }),
|
|
17352
|
-
/* @__PURE__ */ jsx19(
|
|
17353
|
-
/* @__PURE__ */ jsx19(
|
|
17333
|
+
/* @__PURE__ */ jsx19(Text17, { color: isProcessing ? THEME.primary : THEME.gray, children: isProcessing ? "Running " : "Idle " }),
|
|
17334
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: formatElapsed(elapsedTime) })
|
|
17354
17335
|
] })
|
|
17355
17336
|
]
|
|
17356
17337
|
}
|
|
@@ -17359,9 +17340,9 @@ var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing
|
|
|
17359
17340
|
var footer_default = Footer;
|
|
17360
17341
|
|
|
17361
17342
|
// src/platform/tui/components/Modal.tsx
|
|
17362
|
-
import { useMemo as useMemo2, memo as memo13, useCallback as
|
|
17363
|
-
import { Box as Box17, Text as
|
|
17364
|
-
import { jsx as jsx20, jsxs as
|
|
17343
|
+
import { useMemo as useMemo2, memo as memo13, useCallback as useCallback8, useRef as useRef7 } from "react";
|
|
17344
|
+
import { Box as Box17, Text as Text18, useStdout as useStdout3, useInput as useInput3 } from "ink";
|
|
17345
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
17365
17346
|
var MODAL_TITLES = {
|
|
17366
17347
|
findings: "\u25C8 FINDINGS \u25C8",
|
|
17367
17348
|
graph: "\u25C8 ATTACK GRAPH \u25C8",
|
|
@@ -17374,9 +17355,9 @@ var Modal = memo13(({
|
|
|
17374
17355
|
onScroll,
|
|
17375
17356
|
onClose
|
|
17376
17357
|
}) => {
|
|
17377
|
-
const onScrollRef =
|
|
17358
|
+
const onScrollRef = useRef7(onScroll);
|
|
17378
17359
|
onScrollRef.current = onScroll;
|
|
17379
|
-
const onCloseRef =
|
|
17360
|
+
const onCloseRef = useRef7(onClose);
|
|
17380
17361
|
onCloseRef.current = onClose;
|
|
17381
17362
|
const { stdout } = useStdout3();
|
|
17382
17363
|
const terminalHeight = stdout?.rows ?? 24;
|
|
@@ -17388,7 +17369,7 @@ var Modal = memo13(({
|
|
|
17388
17369
|
() => lines.slice(scrollOffset, scrollOffset + maxHeight),
|
|
17389
17370
|
[lines, scrollOffset, maxHeight]
|
|
17390
17371
|
);
|
|
17391
|
-
|
|
17372
|
+
useInput3(useCallback8((input, key) => {
|
|
17392
17373
|
if (key.escape || input === "q") {
|
|
17393
17374
|
onCloseRef.current();
|
|
17394
17375
|
} else if (key.upArrow || input === "k") {
|
|
@@ -17405,14 +17386,14 @@ var Modal = memo13(({
|
|
|
17405
17386
|
const endLine = Math.min(scrollOffset + maxHeight, totalLines);
|
|
17406
17387
|
const scrollbarHeight = Math.max(1, Math.floor(maxHeight * (maxHeight / Math.max(totalLines, 1))));
|
|
17407
17388
|
const scrollbarPosition = totalLines > maxHeight ? Math.floor(scrollOffset / (totalLines - maxHeight) * (maxHeight - scrollbarHeight)) : 0;
|
|
17408
|
-
return /* @__PURE__ */
|
|
17389
|
+
return /* @__PURE__ */ jsxs15(
|
|
17409
17390
|
Box17,
|
|
17410
17391
|
{
|
|
17411
17392
|
flexDirection: "column",
|
|
17412
17393
|
width: terminalWidth,
|
|
17413
17394
|
height: terminalHeight,
|
|
17414
17395
|
children: [
|
|
17415
|
-
/* @__PURE__ */ jsx20(Box17, { justifyContent: "center", marginBottom: 0, children: /* @__PURE__ */ jsx20(
|
|
17396
|
+
/* @__PURE__ */ jsx20(Box17, { justifyContent: "center", marginBottom: 0, children: /* @__PURE__ */ jsx20(Text18, { color: THEME.cyan, bold: true, children: (() => {
|
|
17416
17397
|
const title = MODAL_TITLES[type];
|
|
17417
17398
|
const sideWidth = Math.max(3, Math.floor((terminalWidth - title.length - 2) / 2));
|
|
17418
17399
|
return `${"\u2500".repeat(sideWidth)} ${title} ${"\u2500".repeat(sideWidth)}`;
|
|
@@ -17427,17 +17408,17 @@ var Modal = memo13(({
|
|
|
17427
17408
|
flexGrow: 1,
|
|
17428
17409
|
children: visibleLines.map((line, i) => {
|
|
17429
17410
|
const showScrollbar = totalLines > maxHeight && i >= scrollbarPosition && i < scrollbarPosition + scrollbarHeight;
|
|
17430
|
-
return /* @__PURE__ */
|
|
17431
|
-
/* @__PURE__ */ jsx20(
|
|
17411
|
+
return /* @__PURE__ */ jsxs15(Box17, { children: [
|
|
17412
|
+
/* @__PURE__ */ jsx20(Text18, { color: THEME.white, wrap: "truncate", children: line }),
|
|
17432
17413
|
/* @__PURE__ */ jsx20(Box17, { flexGrow: 1 }),
|
|
17433
|
-
totalLines > maxHeight && /* @__PURE__ */ jsx20(
|
|
17414
|
+
totalLines > maxHeight && /* @__PURE__ */ jsx20(Text18, { color: showScrollbar ? THEME.cyan : THEME.dimGray, children: showScrollbar ? "\u2588" : "\u2502" })
|
|
17434
17415
|
] }, i);
|
|
17435
17416
|
})
|
|
17436
17417
|
}
|
|
17437
17418
|
),
|
|
17438
|
-
/* @__PURE__ */
|
|
17439
|
-
/* @__PURE__ */ jsx20(
|
|
17440
|
-
/* @__PURE__ */
|
|
17419
|
+
/* @__PURE__ */ jsxs15(Box17, { justifyContent: "space-between", paddingX: 1, children: [
|
|
17420
|
+
/* @__PURE__ */ jsx20(Text18, { dimColor: true, color: THEME.gray, children: "\u2191\u2193/jk: scroll | g/G: top/bottom | ESC/q: close" }),
|
|
17421
|
+
/* @__PURE__ */ jsxs15(Text18, { dimColor: true, color: THEME.cyan, children: [
|
|
17441
17422
|
startLine,
|
|
17442
17423
|
"-",
|
|
17443
17424
|
endLine,
|
|
@@ -17452,7 +17433,7 @@ var Modal = memo13(({
|
|
|
17452
17433
|
|
|
17453
17434
|
// src/platform/tui/components/app/bottom-region.tsx
|
|
17454
17435
|
import { Box as Box18 } from "ink";
|
|
17455
|
-
import { jsx as jsx21, jsxs as
|
|
17436
|
+
import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
17456
17437
|
var BottomRegion = ({
|
|
17457
17438
|
input,
|
|
17458
17439
|
setInput,
|
|
@@ -17476,7 +17457,7 @@ var BottomRegion = ({
|
|
|
17476
17457
|
const suggestionCount = isSlashMode && !hasArgs && inputRequest.status !== "active" ? Math.min(getMatchingCommands(partialCmd).length, MAX_SUGGESTIONS) : 0;
|
|
17477
17458
|
const previewHeight = suggestionCount;
|
|
17478
17459
|
const bottomMinHeight = 7;
|
|
17479
|
-
return /* @__PURE__ */
|
|
17460
|
+
return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", minHeight: bottomMinHeight, children: [
|
|
17480
17461
|
/* @__PURE__ */ jsx21(
|
|
17481
17462
|
StatusDisplay,
|
|
17482
17463
|
{
|
|
@@ -17517,16 +17498,16 @@ var BottomRegion = ({
|
|
|
17517
17498
|
};
|
|
17518
17499
|
|
|
17519
17500
|
// src/platform/tui/app.tsx
|
|
17520
|
-
import { jsx as jsx22, jsxs as
|
|
17501
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
17521
17502
|
var MODEL_NAME = getModel() || DEFAULT_MODEL;
|
|
17522
17503
|
var App = ({ autoApprove = false, target }) => {
|
|
17523
17504
|
const { exit } = useApp();
|
|
17524
17505
|
const { stdout } = useStdout4();
|
|
17525
17506
|
const terminalWidth = stdout?.columns ?? 80;
|
|
17526
|
-
const [input, setInput] =
|
|
17527
|
-
const [secretInput, setSecretInput] =
|
|
17528
|
-
const [autoApproveMode, setAutoApproveMode] =
|
|
17529
|
-
const [modal, setModal] =
|
|
17507
|
+
const [input, setInput] = useState6("");
|
|
17508
|
+
const [secretInput, setSecretInput] = useState6("");
|
|
17509
|
+
const [autoApproveMode, setAutoApproveMode] = useState6(autoApprove);
|
|
17510
|
+
const [modal, setModal] = useState6({ type: null, content: "", scrollOffset: 0 });
|
|
17530
17511
|
const {
|
|
17531
17512
|
agent,
|
|
17532
17513
|
messages,
|
|
@@ -17546,37 +17527,26 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17546
17527
|
addMessage,
|
|
17547
17528
|
refreshStats
|
|
17548
17529
|
} = useAgent(autoApproveMode, target);
|
|
17549
|
-
const isProcessingRef =
|
|
17530
|
+
const isProcessingRef = useRef8(isProcessing);
|
|
17550
17531
|
isProcessingRef.current = isProcessing;
|
|
17551
|
-
const autoApproveModeRef =
|
|
17532
|
+
const autoApproveModeRef = useRef8(autoApproveMode);
|
|
17552
17533
|
autoApproveModeRef.current = autoApproveMode;
|
|
17553
|
-
const inputRequestRef =
|
|
17534
|
+
const inputRequestRef = useRef8(inputRequest);
|
|
17554
17535
|
inputRequestRef.current = inputRequest;
|
|
17555
|
-
const isModalOpenRef =
|
|
17536
|
+
const isModalOpenRef = useRef8(!!modal.type);
|
|
17556
17537
|
isModalOpenRef.current = !!modal.type;
|
|
17557
|
-
const inputRef =
|
|
17538
|
+
const inputRef = useRef8(input);
|
|
17558
17539
|
inputRef.current = input;
|
|
17559
|
-
const clearInput =
|
|
17540
|
+
const clearInput = useCallback9(() => {
|
|
17560
17541
|
setInput("");
|
|
17561
17542
|
}, []);
|
|
17562
|
-
const
|
|
17563
|
-
const handleScroll = useCallback11((delta) => {
|
|
17564
|
-
setHistoryScrollOffset((prev) => Math.max(0, prev - delta));
|
|
17565
|
-
}, []);
|
|
17566
|
-
const messageCountRef = useRef10(messages.length);
|
|
17567
|
-
if (messages.length !== messageCountRef.current) {
|
|
17568
|
-
messageCountRef.current = messages.length;
|
|
17569
|
-
if (historyScrollOffset > 0) {
|
|
17570
|
-
setHistoryScrollOffset(0);
|
|
17571
|
-
}
|
|
17572
|
-
}
|
|
17573
|
-
const showModal = useCallback11((type, content) => {
|
|
17543
|
+
const showModal = useCallback9((type, content) => {
|
|
17574
17544
|
setModal({ type, content, scrollOffset: 0 });
|
|
17575
17545
|
}, []);
|
|
17576
|
-
const closeModal =
|
|
17546
|
+
const closeModal = useCallback9(() => {
|
|
17577
17547
|
setModal({ type: null, content: "", scrollOffset: 0 });
|
|
17578
17548
|
}, []);
|
|
17579
|
-
const handleModalScroll =
|
|
17549
|
+
const handleModalScroll = useCallback9((delta) => {
|
|
17580
17550
|
setModal((prev) => {
|
|
17581
17551
|
const lines = prev.content.split("\n");
|
|
17582
17552
|
const maxHeight = (stdout?.rows ?? 24) - 6;
|
|
@@ -17585,7 +17555,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17585
17555
|
return { ...prev, scrollOffset: newOffset };
|
|
17586
17556
|
});
|
|
17587
17557
|
}, [stdout?.rows]);
|
|
17588
|
-
const handleExit =
|
|
17558
|
+
const handleExit = useCallback9(() => {
|
|
17589
17559
|
const ir = inputRequestRef.current;
|
|
17590
17560
|
if (ir.status === "active") {
|
|
17591
17561
|
ir.resolve(null);
|
|
@@ -17608,7 +17578,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17608
17578
|
isProcessingRef,
|
|
17609
17579
|
autoApproveModeRef
|
|
17610
17580
|
});
|
|
17611
|
-
const handleSubmit =
|
|
17581
|
+
const handleSubmit = useCallback9(async (value) => {
|
|
17612
17582
|
const trimmed = value.trim();
|
|
17613
17583
|
if (!trimmed) return;
|
|
17614
17584
|
setInput("");
|
|
@@ -17623,7 +17593,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17623
17593
|
await executeTask(trimmed);
|
|
17624
17594
|
}
|
|
17625
17595
|
}, [agent, addMessage, executeTask, handleCommand]);
|
|
17626
|
-
const handleSecretSubmit =
|
|
17596
|
+
const handleSecretSubmit = useCallback9((value) => {
|
|
17627
17597
|
const ir = inputRequestRef.current;
|
|
17628
17598
|
if (ir.status !== "active") return;
|
|
17629
17599
|
const displayText = ir.isPassword ? "\u2022".repeat(Math.min(value.length, 20)) : value;
|
|
@@ -17641,8 +17611,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17641
17611
|
inputRequestRef,
|
|
17642
17612
|
isModalOpenRef,
|
|
17643
17613
|
inputRef,
|
|
17644
|
-
clearInput
|
|
17645
|
-
onScroll: handleScroll
|
|
17614
|
+
clearInput
|
|
17646
17615
|
});
|
|
17647
17616
|
if (modal.type) {
|
|
17648
17617
|
return /* @__PURE__ */ jsx22(Box19, { flexDirection: "column", paddingX: 1, width: terminalWidth, children: /* @__PURE__ */ jsx22(
|
|
@@ -17656,16 +17625,14 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17656
17625
|
}
|
|
17657
17626
|
) });
|
|
17658
17627
|
}
|
|
17659
|
-
return /* @__PURE__ */
|
|
17628
|
+
return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", paddingX: 1, width: terminalWidth, children: [
|
|
17660
17629
|
/* @__PURE__ */ jsx22(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx22(
|
|
17661
17630
|
MessageList,
|
|
17662
17631
|
{
|
|
17663
17632
|
messages,
|
|
17664
|
-
isModalOpen: !!modal.type,
|
|
17665
17633
|
modelName: MODEL_NAME,
|
|
17666
17634
|
autoApproveMode,
|
|
17667
|
-
version: APP_VERSION
|
|
17668
|
-
scrollOffset: historyScrollOffset
|
|
17635
|
+
version: APP_VERSION
|
|
17669
17636
|
}
|
|
17670
17637
|
) }),
|
|
17671
17638
|
/* @__PURE__ */ jsx22(
|