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