pentesting 0.70.4 → 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 +630 -646
- 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
|
|
|
@@ -15237,11 +15310,12 @@ ${firstLine}`);
|
|
|
15237
15310
|
}
|
|
15238
15311
|
|
|
15239
15312
|
// src/platform/tui/hooks/useAgentEvents/handlers/input.ts
|
|
15240
|
-
function setupInputHandlers(setInputRequest) {
|
|
15313
|
+
function setupInputHandlers(setInputRequest, addMessage) {
|
|
15241
15314
|
setInputHandler((p) => {
|
|
15242
15315
|
return new Promise((resolve) => {
|
|
15243
15316
|
const isPassword = /password|passphrase/i.test(p);
|
|
15244
15317
|
const inputType = /sudo/i.test(p) ? INPUT_TYPES.SUDO_PASSWORD : isPassword ? INPUT_TYPES.PASSWORD : INPUT_TYPES.TEXT;
|
|
15318
|
+
addMessage("ai", `\u{1F512} ${p.trim()}`);
|
|
15245
15319
|
setInputRequest({
|
|
15246
15320
|
status: "active",
|
|
15247
15321
|
prompt: p.trim(),
|
|
@@ -15255,6 +15329,7 @@ function setupInputHandlers(setInputRequest) {
|
|
|
15255
15329
|
return new Promise((resolve) => {
|
|
15256
15330
|
const isPassword = SENSITIVE_INPUT_TYPES.includes(request.type);
|
|
15257
15331
|
const displayPrompt = buildCredentialPrompt(request);
|
|
15332
|
+
addMessage("ai", `\u{1F512} ${displayPrompt}`);
|
|
15258
15333
|
setInputRequest({
|
|
15259
15334
|
status: "active",
|
|
15260
15335
|
prompt: displayPrompt,
|
|
@@ -15274,9 +15349,26 @@ function setupInputHandlers(setInputRequest) {
|
|
|
15274
15349
|
}
|
|
15275
15350
|
|
|
15276
15351
|
// src/platform/tui/hooks/useAgentEvents/handlers/command.ts
|
|
15277
|
-
function setupCommandHandlers(addMessage) {
|
|
15352
|
+
function setupCommandHandlers(addMessage, setCurrentStatus) {
|
|
15353
|
+
let lastStatusBase = "";
|
|
15278
15354
|
setCommandEventEmitter((event) => {
|
|
15279
|
-
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}`);
|
|
15280
15372
|
return;
|
|
15281
15373
|
}
|
|
15282
15374
|
const icon = getCommandEventIcon(event.type);
|
|
@@ -15322,8 +15414,8 @@ var useAgentEvents = (agent, eventsRef, state) => {
|
|
|
15322
15414
|
lastStepTokensRef
|
|
15323
15415
|
}, reasoningBufferRef);
|
|
15324
15416
|
const reasoningHandlers = createReasoningHandlers({ addMessage, setCurrentStatus }, reasoningBufferRef);
|
|
15325
|
-
const cleanupInput = setupInputHandlers(setInputRequest);
|
|
15326
|
-
const cleanupCommand = setupCommandHandlers(addMessage);
|
|
15417
|
+
const cleanupInput = setupInputHandlers(setInputRequest, addMessage);
|
|
15418
|
+
const cleanupCommand = setupCommandHandlers(addMessage, setCurrentStatus);
|
|
15327
15419
|
const updateStats = () => {
|
|
15328
15420
|
const s = agent.getState();
|
|
15329
15421
|
setStats({
|
|
@@ -15342,6 +15434,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
|
|
|
15342
15434
|
events.on(EVENT_TYPES.USAGE_UPDATE, lifecycleHandlers.onUsageUpdate);
|
|
15343
15435
|
events.on(EVENT_TYPES.FLAG_FOUND, lifecycleHandlers.onFlagFound);
|
|
15344
15436
|
events.on(EVENT_TYPES.PHASE_CHANGE, lifecycleHandlers.onPhaseChange);
|
|
15437
|
+
events.on(EVENT_TYPES.NOTIFICATION, lifecycleHandlers.onNotification);
|
|
15345
15438
|
events.on(EVENT_TYPES.STATE_CHANGE, updateStats);
|
|
15346
15439
|
events.on(EVENT_TYPES.START, updateStats);
|
|
15347
15440
|
events.on(EVENT_TYPES.REASONING_START, reasoningHandlers.onStart);
|
|
@@ -16001,7 +16094,7 @@ var useCommands = (props) => {
|
|
|
16001
16094
|
};
|
|
16002
16095
|
|
|
16003
16096
|
// src/platform/tui/hooks/useKeyboardShortcuts.ts
|
|
16004
|
-
import { useCallback as
|
|
16097
|
+
import { useCallback as useCallback6, useEffect as useEffect4 } from "react";
|
|
16005
16098
|
import { useInput } from "ink";
|
|
16006
16099
|
|
|
16007
16100
|
// src/platform/tui/hooks/keyboard/useDoubleTap.ts
|
|
@@ -16091,17 +16184,6 @@ var useEscHandler = ({
|
|
|
16091
16184
|
return { handleEsc };
|
|
16092
16185
|
};
|
|
16093
16186
|
|
|
16094
|
-
// src/platform/tui/hooks/keyboard/useScrollHandler.ts
|
|
16095
|
-
import { useRef as useRef5, useCallback as useCallback6 } from "react";
|
|
16096
|
-
var useScrollHandler = ({ onScroll }) => {
|
|
16097
|
-
const onScrollRef = useRef5(onScroll);
|
|
16098
|
-
onScrollRef.current = onScroll;
|
|
16099
|
-
const handleScroll = useCallback6((delta) => {
|
|
16100
|
-
onScrollRef.current?.(delta);
|
|
16101
|
-
}, []);
|
|
16102
|
-
return { handleScroll };
|
|
16103
|
-
};
|
|
16104
|
-
|
|
16105
16187
|
// src/platform/tui/hooks/useKeyboardShortcuts.ts
|
|
16106
16188
|
var DOUBLE_TAP_WINDOW = 3e3;
|
|
16107
16189
|
var useKeyboardShortcuts = ({
|
|
@@ -16113,8 +16195,7 @@ var useKeyboardShortcuts = ({
|
|
|
16113
16195
|
inputRequestRef,
|
|
16114
16196
|
isModalOpenRef,
|
|
16115
16197
|
inputRef,
|
|
16116
|
-
clearInput
|
|
16117
|
-
onScroll
|
|
16198
|
+
clearInput
|
|
16118
16199
|
}) => {
|
|
16119
16200
|
const { handleCtrlC } = useExitHandler({
|
|
16120
16201
|
handleExit,
|
|
@@ -16133,18 +16214,11 @@ var useKeyboardShortcuts = ({
|
|
|
16133
16214
|
inputRef,
|
|
16134
16215
|
windowMs: DOUBLE_TAP_WINDOW
|
|
16135
16216
|
});
|
|
16136
|
-
|
|
16137
|
-
useInput(useCallback7((ch, key) => {
|
|
16217
|
+
useInput(useCallback6((ch, key) => {
|
|
16138
16218
|
if (isModalOpenRef.current) return;
|
|
16139
16219
|
if (key.escape) handleEsc();
|
|
16140
16220
|
if (key.ctrl && ch === "c") handleCtrlC();
|
|
16141
|
-
|
|
16142
|
-
handleScroll(-10);
|
|
16143
|
-
}
|
|
16144
|
-
if (key.pageDown) {
|
|
16145
|
-
handleScroll(10);
|
|
16146
|
-
}
|
|
16147
|
-
}, [handleEsc, handleCtrlC, handleScroll, isModalOpenRef]));
|
|
16221
|
+
}, [handleEsc, handleCtrlC, isModalOpenRef]));
|
|
16148
16222
|
useEffect4(() => {
|
|
16149
16223
|
const onSignal = () => handleCtrlC();
|
|
16150
16224
|
process.on("SIGINT", onSignal);
|
|
@@ -16157,36 +16231,52 @@ var useKeyboardShortcuts = ({
|
|
|
16157
16231
|
return { handleCtrlC };
|
|
16158
16232
|
};
|
|
16159
16233
|
|
|
16234
|
+
// src/platform/tui/hooks/useAnimationTick.tsx
|
|
16235
|
+
import { createContext, useContext, useState as useState3, useEffect as useEffect5 } from "react";
|
|
16236
|
+
import { jsx } from "react/jsx-runtime";
|
|
16237
|
+
var ANIM_TICK_MS = 100;
|
|
16238
|
+
var AnimationContext = createContext(0);
|
|
16239
|
+
var AnimationProvider = ({ children }) => {
|
|
16240
|
+
const [tick, setTick] = useState3(0);
|
|
16241
|
+
useEffect5(() => {
|
|
16242
|
+
const timer = setInterval(() => {
|
|
16243
|
+
setTick((t) => t + 1);
|
|
16244
|
+
}, ANIM_TICK_MS);
|
|
16245
|
+
return () => clearInterval(timer);
|
|
16246
|
+
}, []);
|
|
16247
|
+
return /* @__PURE__ */ jsx(AnimationContext.Provider, { value: tick, children });
|
|
16248
|
+
};
|
|
16249
|
+
var useAnimationTick = () => useContext(AnimationContext);
|
|
16250
|
+
|
|
16160
16251
|
// src/platform/tui/components/MessageList.tsx
|
|
16161
|
-
import { memo as memo7
|
|
16162
|
-
import { Box as Box8,
|
|
16252
|
+
import { memo as memo7 } from "react";
|
|
16253
|
+
import { Box as Box8, Static } from "ink";
|
|
16163
16254
|
|
|
16164
16255
|
// src/platform/tui/components/messages/ThinkingBlock.tsx
|
|
16165
16256
|
import { memo } from "react";
|
|
16166
16257
|
import { Box, Text } from "ink";
|
|
16167
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16258
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
16168
16259
|
var THINKING_PREVIEW_LINES = 3;
|
|
16169
|
-
var ThinkingBlock = memo(({ msg
|
|
16260
|
+
var ThinkingBlock = memo(({ msg }) => {
|
|
16170
16261
|
const lines = msg.content.split("\n");
|
|
16171
16262
|
const charCount = msg.content.length;
|
|
16172
16263
|
const estTokens = Math.round(charCount / LLM_LIMITS.charsPerTokenEstimate);
|
|
16173
16264
|
const hiddenCount = lines.length - THINKING_PREVIEW_LINES;
|
|
16174
|
-
const visibleLines =
|
|
16265
|
+
const visibleLines = lines.slice(0, THINKING_PREVIEW_LINES);
|
|
16175
16266
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [
|
|
16176
16267
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
16177
|
-
/* @__PURE__ */
|
|
16178
|
-
/* @__PURE__ */
|
|
16179
|
-
/* @__PURE__ */
|
|
16180
|
-
|
|
16181
|
-
isExpanded && /* @__PURE__ */ jsx(Text, { color: THEME.dimGray, children: " [R to collapse]" })
|
|
16268
|
+
/* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: " \u25C6 " }),
|
|
16269
|
+
/* @__PURE__ */ jsx2(Text, { color: THEME.gray, children: "Reasoning" }),
|
|
16270
|
+
/* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: ` (~${estTokens} tokens)` }),
|
|
16271
|
+
hiddenCount > 0 && /* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: ` [+${hiddenCount} lines]` })
|
|
16182
16272
|
] }),
|
|
16183
16273
|
visibleLines.map((line, i) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
16184
|
-
/* @__PURE__ */
|
|
16185
|
-
/* @__PURE__ */
|
|
16274
|
+
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: " \u2502 " }),
|
|
16275
|
+
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: line })
|
|
16186
16276
|
] }, i)),
|
|
16187
|
-
|
|
16188
|
-
/* @__PURE__ */
|
|
16189
|
-
/* @__PURE__ */
|
|
16277
|
+
hiddenCount > 0 && /* @__PURE__ */ jsxs(Box, { children: [
|
|
16278
|
+
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: " \u2502 " }),
|
|
16279
|
+
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: "..." })
|
|
16190
16280
|
] })
|
|
16191
16281
|
] });
|
|
16192
16282
|
});
|
|
@@ -16233,7 +16323,7 @@ var MESSAGE_STYLES = {
|
|
|
16233
16323
|
|
|
16234
16324
|
// src/platform/tui/components/inline-status.tsx
|
|
16235
16325
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
16236
|
-
import { Fragment, jsx as
|
|
16326
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
16237
16327
|
function formatDuration3(ms) {
|
|
16238
16328
|
const seconds = Math.floor(ms / 1e3);
|
|
16239
16329
|
if (seconds < 60) return `${seconds}s`;
|
|
@@ -16281,13 +16371,13 @@ function ProcessRow({ proc, isCompact }) {
|
|
|
16281
16371
|
const purpose = proc.purpose || proc.description || "";
|
|
16282
16372
|
const truncatedPurpose = isCompact && purpose.length > TUI_DISPLAY_LIMITS.purposeMaxLength ? purpose.slice(0, TUI_DISPLAY_LIMITS.purposeTruncated) + "..." : purpose;
|
|
16283
16373
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
16284
|
-
/* @__PURE__ */
|
|
16374
|
+
/* @__PURE__ */ jsx3(StatusIndicator, { isRunning: proc.isRunning, exitCode: proc.exitCode }),
|
|
16285
16375
|
/* @__PURE__ */ jsxs2(Text2, { color: THEME.gray, children: [
|
|
16286
16376
|
"[",
|
|
16287
16377
|
proc.id,
|
|
16288
16378
|
"]"
|
|
16289
16379
|
] }),
|
|
16290
|
-
/* @__PURE__ */
|
|
16380
|
+
/* @__PURE__ */ jsx3(Text2, { children: " " }),
|
|
16291
16381
|
/* @__PURE__ */ jsxs2(Text2, { color: getRoleColor(proc.role), bold: true, children: [
|
|
16292
16382
|
proc.role,
|
|
16293
16383
|
port
|
|
@@ -16298,8 +16388,8 @@ function ProcessRow({ proc, isCompact }) {
|
|
|
16298
16388
|
")"
|
|
16299
16389
|
] }),
|
|
16300
16390
|
truncatedPurpose && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
16301
|
-
/* @__PURE__ */
|
|
16302
|
-
/* @__PURE__ */
|
|
16391
|
+
/* @__PURE__ */ jsx3(Text2, { color: THEME.gray, children: " - " }),
|
|
16392
|
+
/* @__PURE__ */ jsx3(Text2, { color: THEME.gray, children: truncatedPurpose })
|
|
16303
16393
|
] })
|
|
16304
16394
|
] });
|
|
16305
16395
|
}
|
|
@@ -16310,7 +16400,7 @@ var InlineStatus = ({
|
|
|
16310
16400
|
isCompact = true
|
|
16311
16401
|
}) => {
|
|
16312
16402
|
if (processes.length === 0 && zombies.length === 0) {
|
|
16313
|
-
return /* @__PURE__ */
|
|
16403
|
+
return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx3(Text2, { color: THEME.gray, children: "\u2022 No active background processes" }) });
|
|
16314
16404
|
}
|
|
16315
16405
|
const running = processes.filter((p) => p.isRunning);
|
|
16316
16406
|
const stopped = processes.filter((p) => !p.isRunning);
|
|
@@ -16327,7 +16417,7 @@ var InlineStatus = ({
|
|
|
16327
16417
|
running.length,
|
|
16328
16418
|
")"
|
|
16329
16419
|
] }),
|
|
16330
|
-
running.map((proc) => /* @__PURE__ */
|
|
16420
|
+
running.map((proc) => /* @__PURE__ */ jsx3(ProcessRow, { proc, isCompact }, proc.id))
|
|
16331
16421
|
] }),
|
|
16332
16422
|
stopped.length > 0 && !isCompact && /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: running.length > 0 ? 1 : 0, children: [
|
|
16333
16423
|
/* @__PURE__ */ jsxs2(Text2, { color: THEME.gray, children: [
|
|
@@ -16336,7 +16426,7 @@ var InlineStatus = ({
|
|
|
16336
16426
|
stopped.length,
|
|
16337
16427
|
")"
|
|
16338
16428
|
] }),
|
|
16339
|
-
stopped.slice(0, TUI_DISPLAY_LIMITS.maxStoppedProcesses).map((proc) => /* @__PURE__ */
|
|
16429
|
+
stopped.slice(0, TUI_DISPLAY_LIMITS.maxStoppedProcesses).map((proc) => /* @__PURE__ */ jsx3(ProcessRow, { proc, isCompact }, proc.id)),
|
|
16340
16430
|
stopped.length > TUI_DISPLAY_LIMITS.maxStoppedProcesses && /* @__PURE__ */ jsxs2(Text2, { color: THEME.gray, children: [
|
|
16341
16431
|
" ... and ",
|
|
16342
16432
|
stopped.length - TUI_DISPLAY_LIMITS.maxStoppedProcesses,
|
|
@@ -16366,11 +16456,11 @@ var InlineStatus = ({
|
|
|
16366
16456
|
" orphaned children"
|
|
16367
16457
|
] })
|
|
16368
16458
|
] }, z.processId)),
|
|
16369
|
-
/* @__PURE__ */
|
|
16459
|
+
/* @__PURE__ */ jsx3(Text2, { color: THEME.gray, children: " Run /cleanup to terminate" })
|
|
16370
16460
|
] }),
|
|
16371
16461
|
/* @__PURE__ */ jsxs2(Box2, { marginTop: running.length > 0 ? 1 : 0, children: [
|
|
16372
|
-
/* @__PURE__ */
|
|
16373
|
-
/* @__PURE__ */
|
|
16462
|
+
/* @__PURE__ */ jsx3(Text2, { color: THEME.gray, children: "Health: " }),
|
|
16463
|
+
/* @__PURE__ */ jsx3(Text2, { color: healthColor, bold: true, children: health.toUpperCase() })
|
|
16374
16464
|
] })
|
|
16375
16465
|
] });
|
|
16376
16466
|
};
|
|
@@ -16378,7 +16468,7 @@ var InlineStatus = ({
|
|
|
16378
16468
|
// src/platform/tui/components/ToolCard.tsx
|
|
16379
16469
|
import { memo as memo2 } from "react";
|
|
16380
16470
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
16381
|
-
import { Fragment as Fragment2, jsx as
|
|
16471
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
16382
16472
|
var ARGS_MAX = 72;
|
|
16383
16473
|
function truncate(s, max) {
|
|
16384
16474
|
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
@@ -16423,12 +16513,12 @@ function parseToolContent(content) {
|
|
|
16423
16513
|
var ToolCard = memo2(({ content }) => {
|
|
16424
16514
|
const { name, args } = parseToolContent(content);
|
|
16425
16515
|
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
16426
|
-
/* @__PURE__ */
|
|
16427
|
-
/* @__PURE__ */
|
|
16516
|
+
/* @__PURE__ */ jsx4(Text3, { color: THEME.primary, children: " \u2295 " }),
|
|
16517
|
+
/* @__PURE__ */ jsx4(Text3, { color: THEME.white, bold: true, children: name }),
|
|
16428
16518
|
args && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
16429
|
-
/* @__PURE__ */
|
|
16430
|
-
/* @__PURE__ */
|
|
16431
|
-
/* @__PURE__ */
|
|
16519
|
+
/* @__PURE__ */ jsx4(Text3, { color: THEME.dimGray, children: "(" }),
|
|
16520
|
+
/* @__PURE__ */ jsx4(Text3, { color: THEME.gray, children: args }),
|
|
16521
|
+
/* @__PURE__ */ jsx4(Text3, { color: THEME.dimGray, children: ")" })
|
|
16432
16522
|
] })
|
|
16433
16523
|
] });
|
|
16434
16524
|
});
|
|
@@ -16436,7 +16526,7 @@ var ToolCard = memo2(({ content }) => {
|
|
|
16436
16526
|
// src/platform/tui/components/MarkdownText.tsx
|
|
16437
16527
|
import { memo as memo3 } from "react";
|
|
16438
16528
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
16439
|
-
import { Fragment as Fragment3, jsx as
|
|
16529
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
16440
16530
|
function tokenizeLine(line) {
|
|
16441
16531
|
const tokens = [];
|
|
16442
16532
|
let i = 0;
|
|
@@ -16460,14 +16550,14 @@ function tokenizeLine(line) {
|
|
|
16460
16550
|
}
|
|
16461
16551
|
return tokens;
|
|
16462
16552
|
}
|
|
16463
|
-
var InlineContent = ({ tokens }) => /* @__PURE__ */
|
|
16553
|
+
var InlineContent = ({ tokens }) => /* @__PURE__ */ jsx5(Fragment3, { children: tokens.map((tok, i) => {
|
|
16464
16554
|
if (tok.type === "bold") {
|
|
16465
|
-
return /* @__PURE__ */
|
|
16555
|
+
return /* @__PURE__ */ jsx5(Text4, { bold: true, color: THEME.white, children: tok.value }, i);
|
|
16466
16556
|
}
|
|
16467
16557
|
if (tok.type === "code") {
|
|
16468
|
-
return /* @__PURE__ */
|
|
16558
|
+
return /* @__PURE__ */ jsx5(Text4, { color: THEME.cyan, children: `\`${tok.value}\`` }, i);
|
|
16469
16559
|
}
|
|
16470
|
-
return /* @__PURE__ */
|
|
16560
|
+
return /* @__PURE__ */ jsx5(Text4, { color: THEME.white, children: tok.value }, i);
|
|
16471
16561
|
}) });
|
|
16472
16562
|
function parseBlocks(lines) {
|
|
16473
16563
|
const blocks = [];
|
|
@@ -16517,12 +16607,12 @@ function parseBlocks(lines) {
|
|
|
16517
16607
|
}
|
|
16518
16608
|
var BlockRenderer = ({ block, blockIdx }) => {
|
|
16519
16609
|
if (block.type === "blank") {
|
|
16520
|
-
return /* @__PURE__ */
|
|
16610
|
+
return /* @__PURE__ */ jsx5(Text4, { children: " " }, blockIdx);
|
|
16521
16611
|
}
|
|
16522
16612
|
if (block.type === "heading") {
|
|
16523
16613
|
const prefixes = { 1: "\u25B8 ", 2: " \u25B9 ", 3: " \xB7 " };
|
|
16524
16614
|
const colors = { 1: THEME.primary, 2: THEME.cyan, 3: THEME.white };
|
|
16525
|
-
return /* @__PURE__ */
|
|
16615
|
+
return /* @__PURE__ */ jsx5(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: colors[block.level], bold: true, children: [
|
|
16526
16616
|
prefixes[block.level],
|
|
16527
16617
|
block.content
|
|
16528
16618
|
] }) }, blockIdx);
|
|
@@ -16534,7 +16624,7 @@ var BlockRenderer = ({ block, blockIdx }) => {
|
|
|
16534
16624
|
bullet,
|
|
16535
16625
|
" "
|
|
16536
16626
|
] }),
|
|
16537
|
-
/* @__PURE__ */
|
|
16627
|
+
/* @__PURE__ */ jsx5(InlineContent, { tokens: tokenizeLine(block.content) })
|
|
16538
16628
|
] }, blockIdx);
|
|
16539
16629
|
}
|
|
16540
16630
|
if (block.type === "codeBlock") {
|
|
@@ -16547,19 +16637,19 @@ var BlockRenderer = ({ block, blockIdx }) => {
|
|
|
16547
16637
|
paddingX: 1,
|
|
16548
16638
|
marginY: 0,
|
|
16549
16639
|
children: [
|
|
16550
|
-
block.lang && /* @__PURE__ */
|
|
16551
|
-
block.lines.map((l, li) => /* @__PURE__ */
|
|
16640
|
+
block.lang && /* @__PURE__ */ jsx5(Text4, { color: THEME.dimGray, children: block.lang }),
|
|
16641
|
+
block.lines.map((l, li) => /* @__PURE__ */ jsx5(Text4, { color: THEME.cyan, children: l }, li))
|
|
16552
16642
|
]
|
|
16553
16643
|
},
|
|
16554
16644
|
blockIdx
|
|
16555
16645
|
);
|
|
16556
16646
|
}
|
|
16557
|
-
return /* @__PURE__ */
|
|
16647
|
+
return /* @__PURE__ */ jsx5(Box4, { children: /* @__PURE__ */ jsx5(InlineContent, { tokens: tokenizeLine(block.content) }) }, blockIdx);
|
|
16558
16648
|
};
|
|
16559
16649
|
var MarkdownText = memo3(({ content }) => {
|
|
16560
16650
|
const lines = content.split("\n");
|
|
16561
16651
|
const blocks = parseBlocks(lines);
|
|
16562
|
-
return /* @__PURE__ */
|
|
16652
|
+
return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", children: blocks.map((block, i) => /* @__PURE__ */ jsx5(BlockRenderer, { block, blockIdx: i }, i)) });
|
|
16563
16653
|
});
|
|
16564
16654
|
|
|
16565
16655
|
// src/platform/tui/components/messages/parseStatusContent.ts
|
|
@@ -16592,27 +16682,26 @@ function classifyResult(content) {
|
|
|
16592
16682
|
if (content.startsWith("\u26A0") || content.startsWith("!")) return RESULT_KINDS.WARNING;
|
|
16593
16683
|
return RESULT_KINDS.NEUTRAL;
|
|
16594
16684
|
}
|
|
16595
|
-
function computeResultDisplay(content
|
|
16685
|
+
function computeResultDisplay(content) {
|
|
16596
16686
|
const kind = classifyResult(content);
|
|
16597
16687
|
const isFailure = kind === RESULT_KINDS.FAILURE;
|
|
16598
16688
|
const lines = content.split("\n");
|
|
16599
16689
|
const firstLine = lines[0] || "";
|
|
16600
16690
|
const restLines = lines.slice(1);
|
|
16601
16691
|
const hiddenCount = restLines.length;
|
|
16602
|
-
const
|
|
16603
|
-
const
|
|
16604
|
-
const
|
|
16605
|
-
|
|
16606
|
-
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 };
|
|
16607
16696
|
}
|
|
16608
16697
|
|
|
16609
16698
|
// src/platform/tui/components/messages/MessageRow.tsx
|
|
16610
|
-
import { jsx as
|
|
16611
|
-
var MessageRow = memo4(({ msg
|
|
16699
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
16700
|
+
var MessageRow = memo4(({ msg }) => {
|
|
16612
16701
|
if (msg.type === "status") {
|
|
16613
16702
|
const statusData = parseStatusContent(msg.content);
|
|
16614
16703
|
if (statusData) {
|
|
16615
|
-
return /* @__PURE__ */
|
|
16704
|
+
return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsx6(
|
|
16616
16705
|
InlineStatus,
|
|
16617
16706
|
{
|
|
16618
16707
|
processes: statusData.processes,
|
|
@@ -16622,70 +16711,61 @@ var MessageRow = memo4(({ msg, isExpanded }) => {
|
|
|
16622
16711
|
) }, msg.id);
|
|
16623
16712
|
}
|
|
16624
16713
|
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16625
|
-
/* @__PURE__ */
|
|
16626
|
-
/* @__PURE__ */
|
|
16714
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: " \xB7 " }),
|
|
16715
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.gray, children: msg.content })
|
|
16627
16716
|
] }, msg.id);
|
|
16628
16717
|
}
|
|
16629
16718
|
if (msg.type === "thinking") {
|
|
16630
|
-
return /* @__PURE__ */
|
|
16719
|
+
return /* @__PURE__ */ jsx6(ThinkingBlock, { msg });
|
|
16631
16720
|
}
|
|
16632
16721
|
if (msg.type === "tool") {
|
|
16633
|
-
return /* @__PURE__ */
|
|
16722
|
+
return /* @__PURE__ */ jsx6(Box5, { children: /* @__PURE__ */ jsx6(ToolCard, { content: msg.content }) }, msg.id);
|
|
16634
16723
|
}
|
|
16635
16724
|
if (msg.type === "result") {
|
|
16636
16725
|
const kind = classifyResult(msg.content);
|
|
16637
16726
|
const color = kind === "success" ? THEME.primary : kind === "failure" ? THEME.red : kind === "warning" ? THEME.yellow : THEME.gray;
|
|
16638
|
-
const { firstLine, visibleRest, hasMore
|
|
16727
|
+
const { firstLine, visibleRest, hasMore } = computeResultDisplay(msg.content);
|
|
16639
16728
|
const hiddenCount = msg.content.split("\n").length - 1;
|
|
16640
|
-
const isFailure = kind === "failure";
|
|
16641
16729
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
16642
16730
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16643
|
-
/* @__PURE__ */
|
|
16644
|
-
/* @__PURE__ */
|
|
16645
|
-
hasMore && /* @__PURE__ */
|
|
16646
|
-
effectiveExpanded && hiddenCount > RESULT_PREVIEW_LINES && !isFailure && /* @__PURE__ */ jsx5(Text5, { dimColor: true, color: THEME.dimGray, children: " [T \u2193]" })
|
|
16731
|
+
/* @__PURE__ */ jsx6(Text5, { color: THEME.dimGray, children: " \u2514 " }),
|
|
16732
|
+
/* @__PURE__ */ jsx6(Text5, { color, children: firstLine }),
|
|
16733
|
+
hasMore && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: ` [+${hiddenCount - RESULT_PREVIEW_LINES}]` })
|
|
16647
16734
|
] }),
|
|
16648
16735
|
visibleRest.map((line, i) => /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16649
|
-
/* @__PURE__ */
|
|
16650
|
-
/* @__PURE__ */
|
|
16736
|
+
/* @__PURE__ */ jsx6(Text5, { color: THEME.dimGray, children: " " }),
|
|
16737
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.gray, children: line })
|
|
16651
16738
|
] }, i))
|
|
16652
16739
|
] }, msg.id);
|
|
16653
16740
|
}
|
|
16654
16741
|
if (msg.type === "ai" || msg.type === "assistant") {
|
|
16655
|
-
return /* @__PURE__ */
|
|
16742
|
+
return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", marginBottom: 0, children: /* @__PURE__ */ jsx6(MarkdownText, { content: msg.content }) }, msg.id);
|
|
16656
16743
|
}
|
|
16657
16744
|
if (msg.type === "error") {
|
|
16658
16745
|
const eLines = msg.content.split("\n");
|
|
16659
16746
|
const eFirst = eLines[0] || msg.content;
|
|
16660
16747
|
const eHidden = eLines.length - 1;
|
|
16661
|
-
return /* @__PURE__ */
|
|
16662
|
-
/* @__PURE__ */
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
isExpanded && eHidden > 0 && /* @__PURE__ */ jsx5(Text5, { dimColor: true, color: THEME.dimGray, children: " [E \u2191]" })
|
|
16667
|
-
] }),
|
|
16668
|
-
isExpanded && eLines.slice(1).map((line, i) => /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16669
|
-
/* @__PURE__ */ jsx5(Text5, { color: THEME.dimGray, children: " " }),
|
|
16670
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, color: THEME.red, children: line })
|
|
16671
|
-
] }, i))
|
|
16672
|
-
] }, 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);
|
|
16673
16753
|
}
|
|
16674
16754
|
if (msg.type === "user") {
|
|
16675
16755
|
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16676
|
-
/* @__PURE__ */
|
|
16677
|
-
/* @__PURE__ */
|
|
16756
|
+
/* @__PURE__ */ jsx6(Text5, { color: THEME.primary, children: "\u276F " }),
|
|
16757
|
+
/* @__PURE__ */ jsx6(Text5, { color: THEME.white, bold: true, children: msg.content })
|
|
16678
16758
|
] }, msg.id);
|
|
16679
16759
|
}
|
|
16680
16760
|
if (msg.type === "system") {
|
|
16681
16761
|
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16682
|
-
/* @__PURE__ */
|
|
16683
|
-
/* @__PURE__ */
|
|
16762
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: " \xB7 " }),
|
|
16763
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.gray, children: msg.content })
|
|
16684
16764
|
] }, msg.id);
|
|
16685
16765
|
}
|
|
16686
16766
|
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
16687
|
-
/* @__PURE__ */
|
|
16688
|
-
/* @__PURE__ */
|
|
16767
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: " \xB7 " }),
|
|
16768
|
+
/* @__PURE__ */ jsx6(Text5, { color: MESSAGE_STYLES.colors[msg.type] ?? THEME.gray, children: msg.content })
|
|
16689
16769
|
] }, msg.id);
|
|
16690
16770
|
});
|
|
16691
16771
|
|
|
@@ -16696,36 +16776,36 @@ import { Box as Box7, Text as Text7 } from "ink";
|
|
|
16696
16776
|
// src/platform/tui/components/ShimmerBanner.tsx
|
|
16697
16777
|
import { memo as memo5 } from "react";
|
|
16698
16778
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
16699
|
-
import { jsx as
|
|
16779
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
16700
16780
|
var ShimmerBanner = memo5(({ banner }) => {
|
|
16701
16781
|
const lines = banner.split("\n").filter((l) => l.length > 0);
|
|
16702
|
-
return /* @__PURE__ */
|
|
16782
|
+
return /* @__PURE__ */ jsx7(Box6, { flexDirection: "column", children: lines.map((line, row) => /* @__PURE__ */ jsx7(Text6, { color: HEX.primary, children: line }, row)) });
|
|
16703
16783
|
});
|
|
16704
16784
|
|
|
16705
16785
|
// src/platform/tui/components/messages/EmptyState.tsx
|
|
16706
|
-
import { jsx as
|
|
16786
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
16707
16787
|
var EmptyState = memo6(({ modelName, autoApproveMode, version }) => {
|
|
16708
16788
|
const autoLabel = autoApproveMode ? "ON" : "OFF";
|
|
16709
16789
|
const autoColor = autoApproveMode ? THEME.primary : THEME.gray;
|
|
16710
16790
|
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [
|
|
16711
|
-
/* @__PURE__ */
|
|
16712
|
-
/* @__PURE__ */
|
|
16791
|
+
/* @__PURE__ */ jsx8(ShimmerBanner, { banner: ASCII_BANNER }),
|
|
16792
|
+
/* @__PURE__ */ jsx8(Text7, { children: " " }),
|
|
16713
16793
|
/* @__PURE__ */ jsxs6(Box7, { gap: 2, children: [
|
|
16714
|
-
/* @__PURE__ */
|
|
16715
|
-
version && /* @__PURE__ */
|
|
16794
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.primary, bold: true, children: "\u25C8 Pentesting" }),
|
|
16795
|
+
version && /* @__PURE__ */ jsx8(Text7, { color: THEME.dimGray, children: `v${version}` }),
|
|
16716
16796
|
modelName && /* @__PURE__ */ jsxs6(Text7, { color: THEME.dimGray, children: [
|
|
16717
16797
|
"\xB7",
|
|
16718
16798
|
" ",
|
|
16719
|
-
/* @__PURE__ */
|
|
16799
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.gray, children: modelName })
|
|
16720
16800
|
] }),
|
|
16721
16801
|
/* @__PURE__ */ jsxs6(Text7, { color: THEME.dimGray, children: [
|
|
16722
16802
|
"\xB7",
|
|
16723
16803
|
" Auto: ",
|
|
16724
|
-
/* @__PURE__ */
|
|
16804
|
+
/* @__PURE__ */ jsx8(Text7, { color: autoColor, children: autoLabel })
|
|
16725
16805
|
] })
|
|
16726
16806
|
] }),
|
|
16727
|
-
/* @__PURE__ */
|
|
16728
|
-
/* @__PURE__ */
|
|
16807
|
+
/* @__PURE__ */ jsx8(Text7, { children: " " }),
|
|
16808
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.gray, children: " Get started:" }),
|
|
16729
16809
|
/* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
|
|
16730
16810
|
" ",
|
|
16731
16811
|
" ",
|
|
@@ -16738,88 +16818,25 @@ var EmptyState = memo6(({ modelName, autoApproveMode, version }) => {
|
|
|
16738
16818
|
/* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
|
|
16739
16819
|
" ",
|
|
16740
16820
|
" ",
|
|
16741
|
-
/* @__PURE__ */
|
|
16821
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.white, children: "/start" }),
|
|
16742
16822
|
" \u2014 Begin autonomous pentest"
|
|
16743
16823
|
] }),
|
|
16744
16824
|
/* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
|
|
16745
16825
|
" ",
|
|
16746
16826
|
" ",
|
|
16747
|
-
/* @__PURE__ */
|
|
16827
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.white, children: "/help" }),
|
|
16748
16828
|
" \u2014 Show all commands"
|
|
16749
16829
|
] }),
|
|
16750
|
-
/* @__PURE__ */
|
|
16751
|
-
/* @__PURE__ */
|
|
16830
|
+
/* @__PURE__ */ jsx8(Text7, { children: " " }),
|
|
16831
|
+
/* @__PURE__ */ jsx8(Text7, { color: THEME.dimGray, children: " Or just type a task and press Enter." })
|
|
16752
16832
|
] });
|
|
16753
16833
|
});
|
|
16754
16834
|
|
|
16755
|
-
// src/platform/tui/components/messages/sliding-window.ts
|
|
16756
|
-
var MAX_VISIBLE_MESSAGES = 200;
|
|
16757
|
-
function computeSlidingWindow(messages, scrollOffset) {
|
|
16758
|
-
const allVisible = messages.length > MAX_VISIBLE_MESSAGES ? messages.slice(-MAX_VISIBLE_MESSAGES) : messages;
|
|
16759
|
-
const clampedOffset = Math.min(scrollOffset, Math.max(0, allVisible.length - 1));
|
|
16760
|
-
const visibleMessages = clampedOffset === 0 ? allVisible : allVisible.slice(0, allVisible.length - clampedOffset);
|
|
16761
|
-
const hiddenAbove = allVisible.length - visibleMessages.length;
|
|
16762
|
-
return { visibleMessages, hiddenAbove, clampedOffset };
|
|
16763
|
-
}
|
|
16764
|
-
|
|
16765
16835
|
// src/platform/tui/components/MessageList.tsx
|
|
16766
|
-
import { jsx as
|
|
16767
|
-
var MessageList = memo7(({ messages,
|
|
16768
|
-
const [expandedIds, setExpandedIds] = useState3(/* @__PURE__ */ new Set());
|
|
16769
|
-
const messagesRef = useRef6(messages);
|
|
16770
|
-
messagesRef.current = messages;
|
|
16771
|
-
const isModalOpenRef = useRef6(isModalOpen);
|
|
16772
|
-
isModalOpenRef.current = isModalOpen;
|
|
16773
|
-
useInput2(useCallback8((_ch, key) => {
|
|
16774
|
-
if (isModalOpenRef.current || key.ctrl) return;
|
|
16775
|
-
if (_ch === "r") {
|
|
16776
|
-
const msgs = messagesRef.current;
|
|
16777
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
16778
|
-
if (msgs[i].type === "thinking") {
|
|
16779
|
-
const id = msgs[i].id;
|
|
16780
|
-
setExpandedIds((prev) => {
|
|
16781
|
-
const next = new Set(prev);
|
|
16782
|
-
if (next.has(id)) next.delete(id);
|
|
16783
|
-
else next.add(id);
|
|
16784
|
-
return next;
|
|
16785
|
-
});
|
|
16786
|
-
break;
|
|
16787
|
-
}
|
|
16788
|
-
}
|
|
16789
|
-
}
|
|
16790
|
-
if (_ch === "e") {
|
|
16791
|
-
const msgs = messagesRef.current;
|
|
16792
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
16793
|
-
if (msgs[i].type === "error") {
|
|
16794
|
-
const id = msgs[i].id;
|
|
16795
|
-
setExpandedIds((prev) => {
|
|
16796
|
-
const next = new Set(prev);
|
|
16797
|
-
if (next.has(id)) next.delete(id);
|
|
16798
|
-
else next.add(id);
|
|
16799
|
-
return next;
|
|
16800
|
-
});
|
|
16801
|
-
break;
|
|
16802
|
-
}
|
|
16803
|
-
}
|
|
16804
|
-
}
|
|
16805
|
-
if (_ch === "t") {
|
|
16806
|
-
const msgs = messagesRef.current;
|
|
16807
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
16808
|
-
if (msgs[i].type === "result") {
|
|
16809
|
-
const id = msgs[i].id;
|
|
16810
|
-
setExpandedIds((prev) => {
|
|
16811
|
-
const next = new Set(prev);
|
|
16812
|
-
if (next.has(id)) next.delete(id);
|
|
16813
|
-
else next.add(id);
|
|
16814
|
-
return next;
|
|
16815
|
-
});
|
|
16816
|
-
break;
|
|
16817
|
-
}
|
|
16818
|
-
}
|
|
16819
|
-
}
|
|
16820
|
-
}, []));
|
|
16836
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
16837
|
+
var MessageList = memo7(({ messages, modelName, autoApproveMode, version }) => {
|
|
16821
16838
|
if (messages.length === 0) {
|
|
16822
|
-
return /* @__PURE__ */
|
|
16839
|
+
return /* @__PURE__ */ jsx9(
|
|
16823
16840
|
EmptyState,
|
|
16824
16841
|
{
|
|
16825
16842
|
modelName,
|
|
@@ -16828,31 +16845,20 @@ var MessageList = memo7(({ messages, isModalOpen, modelName, autoApproveMode, ve
|
|
|
16828
16845
|
}
|
|
16829
16846
|
);
|
|
16830
16847
|
}
|
|
16831
|
-
|
|
16832
|
-
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
16833
|
-
clampedOffset > 0 && /* @__PURE__ */ jsx8(Box8, { paddingX: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, color: THEME.dimGray, children: `\u2191 ${hiddenAbove} message${hiddenAbove !== 1 ? "s" : ""} above \xB7 PgDn to scroll down` }) }),
|
|
16834
|
-
visibleMessages.map((msg) => /* @__PURE__ */ jsx8(
|
|
16835
|
-
MessageRow,
|
|
16836
|
-
{
|
|
16837
|
-
msg,
|
|
16838
|
-
isExpanded: expandedIds.has(msg.id)
|
|
16839
|
-
},
|
|
16840
|
-
msg.id
|
|
16841
|
-
))
|
|
16842
|
-
] });
|
|
16848
|
+
return /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsx9(Static, { items: messages, children: (msg) => /* @__PURE__ */ jsx9(MessageRow, { msg }, msg.id) }) });
|
|
16843
16849
|
});
|
|
16844
16850
|
|
|
16845
16851
|
// src/platform/tui/components/StatusDisplay.tsx
|
|
16846
16852
|
import { memo as memo10 } from "react";
|
|
16847
|
-
import { Box as Box11, Text as
|
|
16853
|
+
import { Box as Box11, Text as Text12 } from "ink";
|
|
16848
16854
|
|
|
16849
16855
|
// src/platform/tui/hooks/useStatusTimer.ts
|
|
16850
|
-
import { useState as useState4, useEffect as
|
|
16856
|
+
import { useState as useState4, useEffect as useEffect6, useRef as useRef5 } from "react";
|
|
16851
16857
|
var useStatusTimer = (currentStatus, isProcessing) => {
|
|
16852
16858
|
const [statusElapsed, setStatusElapsed] = useState4(0);
|
|
16853
|
-
const statusTimerRef =
|
|
16854
|
-
const statusStartRef =
|
|
16855
|
-
|
|
16859
|
+
const statusTimerRef = useRef5(null);
|
|
16860
|
+
const statusStartRef = useRef5(Date.now());
|
|
16861
|
+
useEffect6(() => {
|
|
16856
16862
|
if (statusTimerRef.current) clearInterval(statusTimerRef.current);
|
|
16857
16863
|
if (isProcessing && currentStatus) {
|
|
16858
16864
|
statusStartRef.current = Date.now();
|
|
@@ -16872,12 +16878,12 @@ var useStatusTimer = (currentStatus, isProcessing) => {
|
|
|
16872
16878
|
};
|
|
16873
16879
|
|
|
16874
16880
|
// src/platform/tui/components/status/RetryView.tsx
|
|
16875
|
-
import { Box as Box9, Text as
|
|
16881
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
16876
16882
|
|
|
16877
|
-
// src/platform/tui/components/
|
|
16878
|
-
import {
|
|
16879
|
-
import { Text as
|
|
16880
|
-
import { jsx as
|
|
16883
|
+
// src/platform/tui/components/StarSpinner.tsx
|
|
16884
|
+
import { memo as memo8 } from "react";
|
|
16885
|
+
import { Text as Text8 } from "ink";
|
|
16886
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
16881
16887
|
var FRAMES = [
|
|
16882
16888
|
"\xB7",
|
|
16883
16889
|
"\u2726",
|
|
@@ -16894,40 +16900,34 @@ var FRAMES = [
|
|
|
16894
16900
|
"\u2727",
|
|
16895
16901
|
"\u2726"
|
|
16896
16902
|
];
|
|
16897
|
-
var
|
|
16898
|
-
|
|
16899
|
-
const
|
|
16900
|
-
|
|
16901
|
-
const timer = setInterval(() => {
|
|
16902
|
-
setIndex((i) => (i + 1) % FRAMES.length);
|
|
16903
|
-
}, INTERVAL);
|
|
16904
|
-
return () => clearInterval(timer);
|
|
16905
|
-
}, []);
|
|
16906
|
-
return /* @__PURE__ */ jsx9(Text9, { color: color || "yellow", children: FRAMES[index] });
|
|
16903
|
+
var StarSpinner = memo8(({ color }) => {
|
|
16904
|
+
const tick = useAnimationTick();
|
|
16905
|
+
const index = tick % FRAMES.length;
|
|
16906
|
+
return /* @__PURE__ */ jsx10(Text8, { color: color || "yellow", children: FRAMES[index] });
|
|
16907
16907
|
});
|
|
16908
16908
|
|
|
16909
16909
|
// src/platform/tui/components/status/RetryView.tsx
|
|
16910
|
-
import { jsx as
|
|
16910
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
16911
16911
|
var RetryView = ({ retryState }) => {
|
|
16912
16912
|
const truncateError = (err) => {
|
|
16913
16913
|
return err.length > DISPLAY_LIMITS.RETRY_ERROR_PREVIEW ? err.substring(0, DISPLAY_LIMITS.RETRY_ERROR_TRUNCATED) + "..." : err;
|
|
16914
16914
|
};
|
|
16915
|
-
return /* @__PURE__ */
|
|
16916
|
-
/* @__PURE__ */
|
|
16917
|
-
/* @__PURE__ */
|
|
16918
|
-
/* @__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: [
|
|
16919
16919
|
" \u29F3 Retry #",
|
|
16920
16920
|
retryState.attempt,
|
|
16921
16921
|
"/",
|
|
16922
16922
|
retryState.maxRetries
|
|
16923
16923
|
] }),
|
|
16924
|
-
/* @__PURE__ */
|
|
16924
|
+
/* @__PURE__ */ jsxs7(Text9, { color: THEME.gray, wrap: "truncate", children: [
|
|
16925
16925
|
" \u2014 ",
|
|
16926
16926
|
retryState.countdown,
|
|
16927
16927
|
"s"
|
|
16928
16928
|
] })
|
|
16929
16929
|
] }),
|
|
16930
|
-
/* @__PURE__ */
|
|
16930
|
+
/* @__PURE__ */ jsx11(Box9, { children: /* @__PURE__ */ jsxs7(Text9, { color: THEME.gray, wrap: "truncate", children: [
|
|
16931
16931
|
" ",
|
|
16932
16932
|
truncateError(retryState.error)
|
|
16933
16933
|
] }) })
|
|
@@ -16935,14 +16935,13 @@ var RetryView = ({ retryState }) => {
|
|
|
16935
16935
|
};
|
|
16936
16936
|
|
|
16937
16937
|
// src/platform/tui/components/status/ProcessingView.tsx
|
|
16938
|
-
import { Box as Box10, Text as
|
|
16938
|
+
import { Box as Box10, Text as Text11 } from "ink";
|
|
16939
16939
|
|
|
16940
16940
|
// src/platform/tui/components/ShimmerText.tsx
|
|
16941
|
-
import {
|
|
16942
|
-
import { Text as
|
|
16943
|
-
import { jsx as
|
|
16944
|
-
var
|
|
16945
|
-
var WAVE_SPEED = 0.25;
|
|
16941
|
+
import { memo as memo9 } from "react";
|
|
16942
|
+
import { Text as Text10 } from "ink";
|
|
16943
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
16944
|
+
var WAVE_SPEED = 0.25 * (120 / ANIM_TICK_MS);
|
|
16946
16945
|
var CHAR_PHASE_GAP = 0.55;
|
|
16947
16946
|
function sinToColor(sin) {
|
|
16948
16947
|
const t = (sin + 1) / 2;
|
|
@@ -16951,24 +16950,18 @@ function sinToColor(sin) {
|
|
|
16951
16950
|
return `#${hex}${hex}${hex}`;
|
|
16952
16951
|
}
|
|
16953
16952
|
var ShimmerText = memo9(({ children, bold, phase = 0 }) => {
|
|
16954
|
-
const
|
|
16955
|
-
useEffect7(() => {
|
|
16956
|
-
const timer = setInterval(() => {
|
|
16957
|
-
setTick((t) => t + 1);
|
|
16958
|
-
}, FRAME_INTERVAL);
|
|
16959
|
-
return () => clearInterval(timer);
|
|
16960
|
-
}, []);
|
|
16953
|
+
const tick = useAnimationTick();
|
|
16961
16954
|
const globalPhase = tick * WAVE_SPEED + phase;
|
|
16962
|
-
return /* @__PURE__ */
|
|
16955
|
+
return /* @__PURE__ */ jsx12(Text10, { bold, children: Array.from(children).map((char, i) => {
|
|
16963
16956
|
const charPhase = globalPhase - i * CHAR_PHASE_GAP;
|
|
16964
16957
|
const sin = Math.sin(charPhase);
|
|
16965
16958
|
const color = sinToColor(sin);
|
|
16966
|
-
return /* @__PURE__ */
|
|
16959
|
+
return /* @__PURE__ */ jsx12(Text10, { color, children: char }, i);
|
|
16967
16960
|
}) });
|
|
16968
16961
|
});
|
|
16969
16962
|
|
|
16970
16963
|
// src/platform/tui/components/status/ProcessingView.tsx
|
|
16971
|
-
import { jsx as
|
|
16964
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
16972
16965
|
var ProcessingView = ({
|
|
16973
16966
|
statusMain,
|
|
16974
16967
|
detailText,
|
|
@@ -16984,41 +16977,53 @@ var ProcessingView = ({
|
|
|
16984
16977
|
return parts.length > 0 ? `(${parts.join(" \xB7 ")})` : "";
|
|
16985
16978
|
};
|
|
16986
16979
|
const meta = buildMeta();
|
|
16987
|
-
|
|
16988
|
-
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
/* @__PURE__ */
|
|
16980
|
+
const parenIdx = statusMain.indexOf("(");
|
|
16981
|
+
const shimmerPart = parenIdx > -1 ? statusMain.slice(0, parenIdx).trimEnd() : statusMain;
|
|
16982
|
+
const staticSuffix = parenIdx > -1 ? " " + statusMain.slice(parenIdx) : "";
|
|
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: " " }),
|
|
16987
|
+
/* @__PURE__ */ jsx13(ShimmerText, { bold: true, phase: 0, children: shimmerPart }),
|
|
16988
|
+
staticSuffix ? /* @__PURE__ */ jsx13(Text11, { color: THEME.dimGray, wrap: "truncate", children: staticSuffix }) : null,
|
|
16989
|
+
/* @__PURE__ */ jsxs8(Text11, { color: THEME.dimGray, wrap: "truncate", children: [
|
|
16993
16990
|
" ",
|
|
16994
16991
|
meta
|
|
16995
16992
|
] })
|
|
16996
16993
|
] }),
|
|
16997
|
-
/* @__PURE__ */
|
|
16994
|
+
/* @__PURE__ */ jsx13(Box10, { children: detailText ? /* @__PURE__ */ jsxs8(Text11, { color: THEME.dimGray, wrap: "truncate", children: [
|
|
16998
16995
|
" ",
|
|
16999
16996
|
detailText
|
|
17000
|
-
] }) : /* @__PURE__ */
|
|
16997
|
+
] }) : /* @__PURE__ */ jsx13(Text11, { children: " " }) })
|
|
17001
16998
|
] });
|
|
17002
16999
|
};
|
|
17003
17000
|
|
|
17004
17001
|
// src/platform/tui/components/StatusDisplay.tsx
|
|
17005
|
-
import { jsx as
|
|
17002
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
17006
17003
|
var StatusDisplay = memo10(({
|
|
17007
17004
|
retryState,
|
|
17008
17005
|
isProcessing,
|
|
17009
17006
|
currentStatus,
|
|
17010
|
-
currentTokens
|
|
17007
|
+
currentTokens,
|
|
17008
|
+
inputRequest
|
|
17011
17009
|
}) => {
|
|
17012
|
-
const
|
|
17010
|
+
const isWaitingForInput = inputRequest?.status === "active";
|
|
17011
|
+
const statusElapsed = useStatusTimer(currentStatus, isProcessing && !isWaitingForInput);
|
|
17013
17012
|
if (retryState && retryState.status === "retrying") {
|
|
17014
|
-
return /* @__PURE__ */
|
|
17013
|
+
return /* @__PURE__ */ jsx14(RetryView, { retryState });
|
|
17014
|
+
}
|
|
17015
|
+
if (isProcessing && isWaitingForInput) {
|
|
17016
|
+
return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", height: 2, children: [
|
|
17017
|
+
/* @__PURE__ */ jsx14(Text12, { children: " " }),
|
|
17018
|
+
/* @__PURE__ */ jsx14(Text12, { children: " " })
|
|
17019
|
+
] });
|
|
17015
17020
|
}
|
|
17016
17021
|
if (isProcessing) {
|
|
17017
17022
|
const isThinkingStatus = currentStatus.startsWith("Reasoning");
|
|
17018
17023
|
const statusLines = currentStatus ? currentStatus.split("\n").filter(Boolean) : [];
|
|
17019
17024
|
const statusMain = statusLines[0] || "Processing...";
|
|
17020
17025
|
const detailText = isThinkingStatus && statusLines.length > 1 ? statusLines[statusLines.length - 1].slice(0, 120) : "";
|
|
17021
|
-
return /* @__PURE__ */
|
|
17026
|
+
return /* @__PURE__ */ jsx14(
|
|
17022
17027
|
ProcessingView,
|
|
17023
17028
|
{
|
|
17024
17029
|
statusMain,
|
|
@@ -17029,46 +17034,46 @@ var StatusDisplay = memo10(({
|
|
|
17029
17034
|
}
|
|
17030
17035
|
);
|
|
17031
17036
|
}
|
|
17032
|
-
return /* @__PURE__ */
|
|
17033
|
-
/* @__PURE__ */
|
|
17034
|
-
/* @__PURE__ */
|
|
17037
|
+
return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", height: 2, children: [
|
|
17038
|
+
/* @__PURE__ */ jsx14(Text12, { children: " " }),
|
|
17039
|
+
/* @__PURE__ */ jsx14(Text12, { children: " " })
|
|
17035
17040
|
] });
|
|
17036
17041
|
});
|
|
17037
17042
|
|
|
17038
17043
|
// src/platform/tui/components/ChatInput.tsx
|
|
17039
|
-
import { useMemo, useCallback as
|
|
17040
|
-
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";
|
|
17041
17046
|
|
|
17042
17047
|
// src/platform/tui/components/input/AutocompletePreview.tsx
|
|
17043
|
-
import { Box as Box12, Text as
|
|
17044
|
-
import { jsx as
|
|
17048
|
+
import { Box as Box12, Text as Text13 } from "ink";
|
|
17049
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
17045
17050
|
var AutocompletePreview = ({
|
|
17046
17051
|
suggestions,
|
|
17047
17052
|
clampedIdx
|
|
17048
17053
|
}) => {
|
|
17049
|
-
return /* @__PURE__ */
|
|
17054
|
+
return /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", paddingX: 1, children: suggestions.map((cmd, i) => {
|
|
17050
17055
|
const isSelected = i === clampedIdx;
|
|
17051
17056
|
const argsText = cmd.args ? ` ${cmd.args}` : "";
|
|
17052
|
-
return /* @__PURE__ */
|
|
17053
|
-
/* @__PURE__ */
|
|
17054
|
-
/* @__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: [
|
|
17055
17060
|
"/",
|
|
17056
17061
|
cmd.name
|
|
17057
17062
|
] }),
|
|
17058
|
-
/* @__PURE__ */
|
|
17059
|
-
/* @__PURE__ */
|
|
17063
|
+
/* @__PURE__ */ jsx15(Text13, { dimColor: true, color: THEME.gray, children: argsText }),
|
|
17064
|
+
/* @__PURE__ */ jsxs10(Text13, { dimColor: true, color: THEME.dimGray, children: [
|
|
17060
17065
|
" \u2014 ",
|
|
17061
17066
|
cmd.description
|
|
17062
17067
|
] }),
|
|
17063
|
-
isSelected && cmd.alias && /* @__PURE__ */
|
|
17068
|
+
isSelected && cmd.alias && /* @__PURE__ */ jsx15(Text13, { dimColor: true, color: THEME.dimGray, children: ` (/${cmd.alias})` })
|
|
17064
17069
|
] }, cmd.name);
|
|
17065
17070
|
}) });
|
|
17066
17071
|
};
|
|
17067
17072
|
|
|
17068
17073
|
// src/platform/tui/components/input/SecretInputArea.tsx
|
|
17069
|
-
import { Box as Box13, Text as
|
|
17074
|
+
import { Box as Box13, Text as Text14, useStdout } from "ink";
|
|
17070
17075
|
import TextInput from "ink-text-input";
|
|
17071
|
-
import { jsx as
|
|
17076
|
+
import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
17072
17077
|
var OUTER_PADDING = 2;
|
|
17073
17078
|
var SecretInputArea = ({
|
|
17074
17079
|
inputRequest,
|
|
@@ -17079,35 +17084,29 @@ var SecretInputArea = ({
|
|
|
17079
17084
|
const { stdout } = useStdout();
|
|
17080
17085
|
const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING);
|
|
17081
17086
|
const borderLine = "\u2501".repeat(borderWidth);
|
|
17082
|
-
return /* @__PURE__ */
|
|
17083
|
-
/* @__PURE__ */
|
|
17084
|
-
/* @__PURE__ */
|
|
17085
|
-
/* @__PURE__ */
|
|
17086
|
-
|
|
17087
|
-
|
|
17088
|
-
|
|
17089
|
-
|
|
17090
|
-
|
|
17091
|
-
|
|
17092
|
-
|
|
17093
|
-
|
|
17094
|
-
|
|
17095
|
-
|
|
17096
|
-
onSubmit: onSecretSubmit,
|
|
17097
|
-
placeholder: "(type and press Enter)",
|
|
17098
|
-
mask: inputRequest.isPassword ? "\u2022" : void 0
|
|
17099
|
-
}
|
|
17100
|
-
)
|
|
17101
|
-
] })
|
|
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 " }),
|
|
17091
|
+
/* @__PURE__ */ jsx16(
|
|
17092
|
+
TextInput,
|
|
17093
|
+
{
|
|
17094
|
+
value: secretInput,
|
|
17095
|
+
onChange: setSecretInput,
|
|
17096
|
+
onSubmit: onSecretSubmit,
|
|
17097
|
+
placeholder: inputRequest.isPassword ? "(hidden \xB7 press Enter)" : "(type and press Enter)",
|
|
17098
|
+
mask: inputRequest.isPassword ? "\u2022" : void 0
|
|
17099
|
+
}
|
|
17100
|
+
)
|
|
17102
17101
|
] }),
|
|
17103
|
-
/* @__PURE__ */
|
|
17102
|
+
/* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(Text14, { color: THEME.yellow, children: borderLine }) })
|
|
17104
17103
|
] });
|
|
17105
17104
|
};
|
|
17106
17105
|
|
|
17107
17106
|
// src/platform/tui/components/input/NormalInputArea.tsx
|
|
17108
|
-
import { Box as Box14, Text as
|
|
17107
|
+
import { Box as Box14, Text as Text15, useStdout as useStdout2 } from "ink";
|
|
17109
17108
|
import TextInput2 from "ink-text-input";
|
|
17110
|
-
import { jsx as
|
|
17109
|
+
import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
17111
17110
|
var OUTER_PADDING2 = 2;
|
|
17112
17111
|
var NormalInputArea = ({
|
|
17113
17112
|
inputKey,
|
|
@@ -17119,11 +17118,11 @@ var NormalInputArea = ({
|
|
|
17119
17118
|
const { stdout } = useStdout2();
|
|
17120
17119
|
const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING2);
|
|
17121
17120
|
const borderLine = "\u2500".repeat(borderWidth);
|
|
17122
|
-
return /* @__PURE__ */
|
|
17123
|
-
/* @__PURE__ */
|
|
17124
|
-
/* @__PURE__ */
|
|
17125
|
-
/* @__PURE__ */
|
|
17126
|
-
/* @__PURE__ */
|
|
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 " }),
|
|
17125
|
+
/* @__PURE__ */ jsx17(
|
|
17127
17126
|
TextInput2,
|
|
17128
17127
|
{
|
|
17129
17128
|
value,
|
|
@@ -17134,12 +17133,12 @@ var NormalInputArea = ({
|
|
|
17134
17133
|
inputKey
|
|
17135
17134
|
)
|
|
17136
17135
|
] }),
|
|
17137
|
-
/* @__PURE__ */
|
|
17136
|
+
/* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, color: THEME.dimGray, children: borderLine }) })
|
|
17138
17137
|
] });
|
|
17139
17138
|
};
|
|
17140
17139
|
|
|
17141
17140
|
// src/platform/tui/components/ChatInput.tsx
|
|
17142
|
-
import { jsx as
|
|
17141
|
+
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
17143
17142
|
var MAX_SUGGESTIONS = 6;
|
|
17144
17143
|
var ChatInput = memo11(({
|
|
17145
17144
|
value,
|
|
@@ -17159,26 +17158,26 @@ var ChatInput = memo11(({
|
|
|
17159
17158
|
return getMatchingCommands(partialCmd).slice(0, MAX_SUGGESTIONS);
|
|
17160
17159
|
}, [isSlashMode, partialCmd, hasArgs]);
|
|
17161
17160
|
const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
|
|
17162
|
-
const [selectedIdx, setSelectedIdx] =
|
|
17161
|
+
const [selectedIdx, setSelectedIdx] = useState5(0);
|
|
17163
17162
|
const clampedIdx = Math.min(selectedIdx, Math.max(0, suggestions.length - 1));
|
|
17164
|
-
const selectedIdxRef =
|
|
17163
|
+
const selectedIdxRef = useRef6(clampedIdx);
|
|
17165
17164
|
selectedIdxRef.current = clampedIdx;
|
|
17166
|
-
const suggestionsRef =
|
|
17165
|
+
const suggestionsRef = useRef6(suggestions);
|
|
17167
17166
|
suggestionsRef.current = suggestions;
|
|
17168
|
-
const isSlashModeRef =
|
|
17167
|
+
const isSlashModeRef = useRef6(isSlashMode);
|
|
17169
17168
|
isSlashModeRef.current = isSlashMode;
|
|
17170
|
-
const hasArgsRef =
|
|
17169
|
+
const hasArgsRef = useRef6(hasArgs);
|
|
17171
17170
|
hasArgsRef.current = hasArgs;
|
|
17172
|
-
const showPreviewRef =
|
|
17171
|
+
const showPreviewRef = useRef6(showPreview);
|
|
17173
17172
|
showPreviewRef.current = showPreview;
|
|
17174
|
-
const inputRequestRef =
|
|
17173
|
+
const inputRequestRef = useRef6(inputRequest);
|
|
17175
17174
|
inputRequestRef.current = inputRequest;
|
|
17176
|
-
const onChangeRef =
|
|
17175
|
+
const onChangeRef = useRef6(onChange);
|
|
17177
17176
|
onChangeRef.current = onChange;
|
|
17178
|
-
const [pastedHint, setPastedHint] =
|
|
17179
|
-
const prevValueRef =
|
|
17180
|
-
const pasteTimerRef =
|
|
17181
|
-
|
|
17177
|
+
const [pastedHint, setPastedHint] = useState5(null);
|
|
17178
|
+
const prevValueRef = useRef6(value);
|
|
17179
|
+
const pasteTimerRef = useRef6(null);
|
|
17180
|
+
useEffect7(() => {
|
|
17182
17181
|
const diff = value.length - prevValueRef.current.length;
|
|
17183
17182
|
if (diff > 20) {
|
|
17184
17183
|
if (pasteTimerRef.current) clearTimeout(pasteTimerRef.current);
|
|
@@ -17190,8 +17189,8 @@ var ChatInput = memo11(({
|
|
|
17190
17189
|
if (pasteTimerRef.current) clearTimeout(pasteTimerRef.current);
|
|
17191
17190
|
};
|
|
17192
17191
|
}, [value]);
|
|
17193
|
-
const [inputKey, setInputKey] =
|
|
17194
|
-
const completeCommand =
|
|
17192
|
+
const [inputKey, setInputKey] = useState5(0);
|
|
17193
|
+
const completeCommand = useCallback7((idx) => {
|
|
17195
17194
|
const sug = suggestionsRef.current;
|
|
17196
17195
|
if (!sug.length) return;
|
|
17197
17196
|
const best = sug[Math.min(idx, sug.length - 1)];
|
|
@@ -17201,9 +17200,9 @@ var ChatInput = memo11(({
|
|
|
17201
17200
|
setSelectedIdx(0);
|
|
17202
17201
|
setInputKey((k) => k + 1);
|
|
17203
17202
|
}, []);
|
|
17204
|
-
const onSubmitRef =
|
|
17203
|
+
const onSubmitRef = useRef6(onSubmit);
|
|
17205
17204
|
onSubmitRef.current = onSubmit;
|
|
17206
|
-
const wrappedOnSubmit =
|
|
17205
|
+
const wrappedOnSubmit = useCallback7((val) => {
|
|
17207
17206
|
if (showPreviewRef.current) {
|
|
17208
17207
|
const sug = suggestionsRef.current;
|
|
17209
17208
|
if (!sug.length) {
|
|
@@ -17222,7 +17221,7 @@ var ChatInput = memo11(({
|
|
|
17222
17221
|
}
|
|
17223
17222
|
onSubmitRef.current(val);
|
|
17224
17223
|
}, [completeCommand]);
|
|
17225
|
-
|
|
17224
|
+
useInput2(useCallback7((_input, key) => {
|
|
17226
17225
|
if (inputRequestRef.current.status === "active") return;
|
|
17227
17226
|
const sug = suggestionsRef.current;
|
|
17228
17227
|
const visible = showPreviewRef.current;
|
|
@@ -17238,8 +17237,8 @@ var ChatInput = memo11(({
|
|
|
17238
17237
|
completeCommand(selectedIdxRef.current);
|
|
17239
17238
|
}
|
|
17240
17239
|
}, [completeCommand]));
|
|
17241
|
-
return /* @__PURE__ */
|
|
17242
|
-
showPreview && /* @__PURE__ */
|
|
17240
|
+
return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", children: [
|
|
17241
|
+
showPreview && /* @__PURE__ */ jsx18(
|
|
17243
17242
|
AutocompletePreview,
|
|
17244
17243
|
{
|
|
17245
17244
|
suggestions,
|
|
@@ -17248,7 +17247,7 @@ var ChatInput = memo11(({
|
|
|
17248
17247
|
),
|
|
17249
17248
|
inputRequest.status === "active" ? (
|
|
17250
17249
|
/* Active input request — yellow top/bottom border */
|
|
17251
|
-
/* @__PURE__ */
|
|
17250
|
+
/* @__PURE__ */ jsx18(
|
|
17252
17251
|
SecretInputArea,
|
|
17253
17252
|
{
|
|
17254
17253
|
inputRequest,
|
|
@@ -17259,7 +17258,7 @@ var ChatInput = memo11(({
|
|
|
17259
17258
|
)
|
|
17260
17259
|
) : (
|
|
17261
17260
|
/* Normal input — dim top/bottom border */
|
|
17262
|
-
/* @__PURE__ */
|
|
17261
|
+
/* @__PURE__ */ jsx18(
|
|
17263
17262
|
NormalInputArea,
|
|
17264
17263
|
{
|
|
17265
17264
|
inputKey,
|
|
@@ -17270,14 +17269,14 @@ var ChatInput = memo11(({
|
|
|
17270
17269
|
}
|
|
17271
17270
|
)
|
|
17272
17271
|
),
|
|
17273
|
-
pastedHint && /* @__PURE__ */
|
|
17272
|
+
pastedHint && /* @__PURE__ */ jsx18(Box15, { paddingX: 2, children: /* @__PURE__ */ jsx18(Text16, { dimColor: true, color: THEME.dimGray, children: pastedHint }) })
|
|
17274
17273
|
] });
|
|
17275
17274
|
});
|
|
17276
17275
|
|
|
17277
17276
|
// src/platform/tui/components/footer.tsx
|
|
17278
17277
|
import { memo as memo12 } from "react";
|
|
17279
|
-
import { Box as Box16, Text as
|
|
17280
|
-
import { jsx as
|
|
17278
|
+
import { Box as Box16, Text as Text17 } from "ink";
|
|
17279
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
17281
17280
|
var CTX_WARN_THRESHOLD = 0.8;
|
|
17282
17281
|
var MAX_CONTEXT_TOKENS = LLM_LIMITS.streamMaxTokens;
|
|
17283
17282
|
var formatElapsed = (totalSeconds) => {
|
|
@@ -17293,7 +17292,7 @@ var formatElapsed = (totalSeconds) => {
|
|
|
17293
17292
|
var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing, totalTokens, turnCount }) => {
|
|
17294
17293
|
const ctxPct = totalTokens > 0 ? Math.round(totalTokens / MAX_CONTEXT_TOKENS * 100) : 0;
|
|
17295
17294
|
const ctxColor = ctxPct >= CTX_WARN_THRESHOLD * 100 ? THEME.yellow : THEME.dimGray;
|
|
17296
|
-
return /* @__PURE__ */
|
|
17295
|
+
return /* @__PURE__ */ jsxs14(
|
|
17297
17296
|
Box16,
|
|
17298
17297
|
{
|
|
17299
17298
|
width: "100%",
|
|
@@ -17301,44 +17300,44 @@ var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing
|
|
|
17301
17300
|
justifyContent: "space-between",
|
|
17302
17301
|
overflow: "hidden",
|
|
17303
17302
|
children: [
|
|
17304
|
-
/* @__PURE__ */
|
|
17305
|
-
/* @__PURE__ */
|
|
17303
|
+
/* @__PURE__ */ jsxs14(Box16, { gap: 2, children: [
|
|
17304
|
+
/* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
|
|
17306
17305
|
"Phase: ",
|
|
17307
|
-
/* @__PURE__ */
|
|
17306
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: phase })
|
|
17308
17307
|
] }),
|
|
17309
|
-
/* @__PURE__ */
|
|
17308
|
+
/* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
|
|
17310
17309
|
"Targets: ",
|
|
17311
|
-
/* @__PURE__ */
|
|
17310
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: targets })
|
|
17312
17311
|
] }),
|
|
17313
|
-
/* @__PURE__ */
|
|
17312
|
+
/* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
|
|
17314
17313
|
"Findings: ",
|
|
17315
|
-
/* @__PURE__ */
|
|
17314
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: findings })
|
|
17316
17315
|
] }),
|
|
17317
|
-
/* @__PURE__ */
|
|
17316
|
+
/* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
|
|
17318
17317
|
"Tasks: ",
|
|
17319
|
-
/* @__PURE__ */
|
|
17318
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: todo })
|
|
17320
17319
|
] })
|
|
17321
17320
|
] }),
|
|
17322
|
-
/* @__PURE__ */
|
|
17323
|
-
isProcessing ? /* @__PURE__ */
|
|
17324
|
-
/* @__PURE__ */
|
|
17325
|
-
/* @__PURE__ */
|
|
17326
|
-
] }) : /* @__PURE__ */
|
|
17327
|
-
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: [
|
|
17328
17327
|
"turn:",
|
|
17329
17328
|
turnCount
|
|
17330
17329
|
] }),
|
|
17331
|
-
totalTokens > 0 && /* @__PURE__ */
|
|
17330
|
+
totalTokens > 0 && /* @__PURE__ */ jsxs14(Text17, { dimColor: true, color: ctxColor, children: [
|
|
17332
17331
|
"ctx:",
|
|
17333
17332
|
ctxPct,
|
|
17334
17333
|
"%"
|
|
17335
17334
|
] }),
|
|
17336
|
-
totalTokens > 0 && /* @__PURE__ */
|
|
17335
|
+
totalTokens > 0 && /* @__PURE__ */ jsxs14(Text17, { dimColor: true, color: THEME.dimGray, children: [
|
|
17337
17336
|
"\u2191",
|
|
17338
17337
|
formatTokens(totalTokens)
|
|
17339
17338
|
] }),
|
|
17340
|
-
/* @__PURE__ */
|
|
17341
|
-
/* @__PURE__ */
|
|
17339
|
+
/* @__PURE__ */ jsx19(Text17, { color: isProcessing ? THEME.primary : THEME.gray, children: isProcessing ? "Running " : "Idle " }),
|
|
17340
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: formatElapsed(elapsedTime) })
|
|
17342
17341
|
] })
|
|
17343
17342
|
]
|
|
17344
17343
|
}
|
|
@@ -17347,9 +17346,9 @@ var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing
|
|
|
17347
17346
|
var footer_default = Footer;
|
|
17348
17347
|
|
|
17349
17348
|
// src/platform/tui/components/Modal.tsx
|
|
17350
|
-
import { useMemo as useMemo2, memo as memo13, useCallback as
|
|
17351
|
-
import { Box as Box17, Text as
|
|
17352
|
-
import { jsx 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";
|
|
17353
17352
|
var MODAL_TITLES = {
|
|
17354
17353
|
findings: "\u25C8 FINDINGS \u25C8",
|
|
17355
17354
|
graph: "\u25C8 ATTACK GRAPH \u25C8",
|
|
@@ -17362,9 +17361,9 @@ var Modal = memo13(({
|
|
|
17362
17361
|
onScroll,
|
|
17363
17362
|
onClose
|
|
17364
17363
|
}) => {
|
|
17365
|
-
const onScrollRef =
|
|
17364
|
+
const onScrollRef = useRef7(onScroll);
|
|
17366
17365
|
onScrollRef.current = onScroll;
|
|
17367
|
-
const onCloseRef =
|
|
17366
|
+
const onCloseRef = useRef7(onClose);
|
|
17368
17367
|
onCloseRef.current = onClose;
|
|
17369
17368
|
const { stdout } = useStdout3();
|
|
17370
17369
|
const terminalHeight = stdout?.rows ?? 24;
|
|
@@ -17376,7 +17375,7 @@ var Modal = memo13(({
|
|
|
17376
17375
|
() => lines.slice(scrollOffset, scrollOffset + maxHeight),
|
|
17377
17376
|
[lines, scrollOffset, maxHeight]
|
|
17378
17377
|
);
|
|
17379
|
-
|
|
17378
|
+
useInput3(useCallback8((input, key) => {
|
|
17380
17379
|
if (key.escape || input === "q") {
|
|
17381
17380
|
onCloseRef.current();
|
|
17382
17381
|
} else if (key.upArrow || input === "k") {
|
|
@@ -17393,19 +17392,19 @@ var Modal = memo13(({
|
|
|
17393
17392
|
const endLine = Math.min(scrollOffset + maxHeight, totalLines);
|
|
17394
17393
|
const scrollbarHeight = Math.max(1, Math.floor(maxHeight * (maxHeight / Math.max(totalLines, 1))));
|
|
17395
17394
|
const scrollbarPosition = totalLines > maxHeight ? Math.floor(scrollOffset / (totalLines - maxHeight) * (maxHeight - scrollbarHeight)) : 0;
|
|
17396
|
-
return /* @__PURE__ */
|
|
17395
|
+
return /* @__PURE__ */ jsxs15(
|
|
17397
17396
|
Box17,
|
|
17398
17397
|
{
|
|
17399
17398
|
flexDirection: "column",
|
|
17400
17399
|
width: terminalWidth,
|
|
17401
17400
|
height: terminalHeight,
|
|
17402
17401
|
children: [
|
|
17403
|
-
/* @__PURE__ */
|
|
17402
|
+
/* @__PURE__ */ jsx20(Box17, { justifyContent: "center", marginBottom: 0, children: /* @__PURE__ */ jsx20(Text18, { color: THEME.cyan, bold: true, children: (() => {
|
|
17404
17403
|
const title = MODAL_TITLES[type];
|
|
17405
17404
|
const sideWidth = Math.max(3, Math.floor((terminalWidth - title.length - 2) / 2));
|
|
17406
17405
|
return `${"\u2500".repeat(sideWidth)} ${title} ${"\u2500".repeat(sideWidth)}`;
|
|
17407
17406
|
})() }) }),
|
|
17408
|
-
/* @__PURE__ */
|
|
17407
|
+
/* @__PURE__ */ jsx20(
|
|
17409
17408
|
Box17,
|
|
17410
17409
|
{
|
|
17411
17410
|
flexDirection: "column",
|
|
@@ -17415,17 +17414,17 @@ var Modal = memo13(({
|
|
|
17415
17414
|
flexGrow: 1,
|
|
17416
17415
|
children: visibleLines.map((line, i) => {
|
|
17417
17416
|
const showScrollbar = totalLines > maxHeight && i >= scrollbarPosition && i < scrollbarPosition + scrollbarHeight;
|
|
17418
|
-
return /* @__PURE__ */
|
|
17419
|
-
/* @__PURE__ */
|
|
17420
|
-
/* @__PURE__ */
|
|
17421
|
-
totalLines > maxHeight && /* @__PURE__ */
|
|
17417
|
+
return /* @__PURE__ */ jsxs15(Box17, { children: [
|
|
17418
|
+
/* @__PURE__ */ jsx20(Text18, { color: THEME.white, wrap: "truncate", children: line }),
|
|
17419
|
+
/* @__PURE__ */ jsx20(Box17, { flexGrow: 1 }),
|
|
17420
|
+
totalLines > maxHeight && /* @__PURE__ */ jsx20(Text18, { color: showScrollbar ? THEME.cyan : THEME.dimGray, children: showScrollbar ? "\u2588" : "\u2502" })
|
|
17422
17421
|
] }, i);
|
|
17423
17422
|
})
|
|
17424
17423
|
}
|
|
17425
17424
|
),
|
|
17426
|
-
/* @__PURE__ */
|
|
17427
|
-
/* @__PURE__ */
|
|
17428
|
-
/* @__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: [
|
|
17429
17428
|
startLine,
|
|
17430
17429
|
"-",
|
|
17431
17430
|
endLine,
|
|
@@ -17440,7 +17439,7 @@ var Modal = memo13(({
|
|
|
17440
17439
|
|
|
17441
17440
|
// src/platform/tui/components/app/bottom-region.tsx
|
|
17442
17441
|
import { Box as Box18 } from "ink";
|
|
17443
|
-
import { jsx as
|
|
17442
|
+
import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
17444
17443
|
var BottomRegion = ({
|
|
17445
17444
|
input,
|
|
17446
17445
|
setInput,
|
|
@@ -17463,19 +17462,19 @@ var BottomRegion = ({
|
|
|
17463
17462
|
const hasArgs = isSlashMode && input.includes(" ");
|
|
17464
17463
|
const suggestionCount = isSlashMode && !hasArgs && inputRequest.status !== "active" ? Math.min(getMatchingCommands(partialCmd).length, MAX_SUGGESTIONS) : 0;
|
|
17465
17464
|
const previewHeight = suggestionCount;
|
|
17466
|
-
const
|
|
17467
|
-
|
|
17468
|
-
|
|
17469
|
-
/* @__PURE__ */ jsx20(
|
|
17465
|
+
const bottomMinHeight = 7;
|
|
17466
|
+
return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", minHeight: bottomMinHeight, children: [
|
|
17467
|
+
/* @__PURE__ */ jsx21(
|
|
17470
17468
|
StatusDisplay,
|
|
17471
17469
|
{
|
|
17472
17470
|
retryState,
|
|
17473
17471
|
isProcessing,
|
|
17474
17472
|
currentStatus,
|
|
17475
|
-
currentTokens
|
|
17473
|
+
currentTokens,
|
|
17474
|
+
inputRequest
|
|
17476
17475
|
}
|
|
17477
17476
|
),
|
|
17478
|
-
/* @__PURE__ */
|
|
17477
|
+
/* @__PURE__ */ jsx21(
|
|
17479
17478
|
ChatInput,
|
|
17480
17479
|
{
|
|
17481
17480
|
value: input,
|
|
@@ -17488,7 +17487,7 @@ var BottomRegion = ({
|
|
|
17488
17487
|
onSecretSubmit: handleSecretSubmit
|
|
17489
17488
|
}
|
|
17490
17489
|
),
|
|
17491
|
-
/* @__PURE__ */
|
|
17490
|
+
/* @__PURE__ */ jsx21(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx21(
|
|
17492
17491
|
footer_default,
|
|
17493
17492
|
{
|
|
17494
17493
|
phase: stats.phase,
|
|
@@ -17505,16 +17504,16 @@ var BottomRegion = ({
|
|
|
17505
17504
|
};
|
|
17506
17505
|
|
|
17507
17506
|
// src/platform/tui/app.tsx
|
|
17508
|
-
import { jsx as
|
|
17507
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
17509
17508
|
var MODEL_NAME = getModel() || DEFAULT_MODEL;
|
|
17510
17509
|
var App = ({ autoApprove = false, target }) => {
|
|
17511
17510
|
const { exit } = useApp();
|
|
17512
17511
|
const { stdout } = useStdout4();
|
|
17513
17512
|
const terminalWidth = stdout?.columns ?? 80;
|
|
17514
|
-
const [input, setInput] =
|
|
17515
|
-
const [secretInput, setSecretInput] =
|
|
17516
|
-
const [autoApproveMode, setAutoApproveMode] =
|
|
17517
|
-
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 });
|
|
17518
17517
|
const {
|
|
17519
17518
|
agent,
|
|
17520
17519
|
messages,
|
|
@@ -17534,37 +17533,26 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17534
17533
|
addMessage,
|
|
17535
17534
|
refreshStats
|
|
17536
17535
|
} = useAgent(autoApproveMode, target);
|
|
17537
|
-
const isProcessingRef =
|
|
17536
|
+
const isProcessingRef = useRef8(isProcessing);
|
|
17538
17537
|
isProcessingRef.current = isProcessing;
|
|
17539
|
-
const autoApproveModeRef =
|
|
17538
|
+
const autoApproveModeRef = useRef8(autoApproveMode);
|
|
17540
17539
|
autoApproveModeRef.current = autoApproveMode;
|
|
17541
|
-
const inputRequestRef =
|
|
17540
|
+
const inputRequestRef = useRef8(inputRequest);
|
|
17542
17541
|
inputRequestRef.current = inputRequest;
|
|
17543
|
-
const isModalOpenRef =
|
|
17542
|
+
const isModalOpenRef = useRef8(!!modal.type);
|
|
17544
17543
|
isModalOpenRef.current = !!modal.type;
|
|
17545
|
-
const inputRef =
|
|
17544
|
+
const inputRef = useRef8(input);
|
|
17546
17545
|
inputRef.current = input;
|
|
17547
|
-
const clearInput =
|
|
17546
|
+
const clearInput = useCallback9(() => {
|
|
17548
17547
|
setInput("");
|
|
17549
17548
|
}, []);
|
|
17550
|
-
const
|
|
17551
|
-
const handleScroll = useCallback11((delta) => {
|
|
17552
|
-
setHistoryScrollOffset((prev) => Math.max(0, prev - delta));
|
|
17553
|
-
}, []);
|
|
17554
|
-
const messageCountRef = useRef10(messages.length);
|
|
17555
|
-
if (messages.length !== messageCountRef.current) {
|
|
17556
|
-
messageCountRef.current = messages.length;
|
|
17557
|
-
if (historyScrollOffset > 0) {
|
|
17558
|
-
setHistoryScrollOffset(0);
|
|
17559
|
-
}
|
|
17560
|
-
}
|
|
17561
|
-
const showModal = useCallback11((type, content) => {
|
|
17549
|
+
const showModal = useCallback9((type, content) => {
|
|
17562
17550
|
setModal({ type, content, scrollOffset: 0 });
|
|
17563
17551
|
}, []);
|
|
17564
|
-
const closeModal =
|
|
17552
|
+
const closeModal = useCallback9(() => {
|
|
17565
17553
|
setModal({ type: null, content: "", scrollOffset: 0 });
|
|
17566
17554
|
}, []);
|
|
17567
|
-
const handleModalScroll =
|
|
17555
|
+
const handleModalScroll = useCallback9((delta) => {
|
|
17568
17556
|
setModal((prev) => {
|
|
17569
17557
|
const lines = prev.content.split("\n");
|
|
17570
17558
|
const maxHeight = (stdout?.rows ?? 24) - 6;
|
|
@@ -17573,7 +17561,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17573
17561
|
return { ...prev, scrollOffset: newOffset };
|
|
17574
17562
|
});
|
|
17575
17563
|
}, [stdout?.rows]);
|
|
17576
|
-
const handleExit =
|
|
17564
|
+
const handleExit = useCallback9(() => {
|
|
17577
17565
|
const ir = inputRequestRef.current;
|
|
17578
17566
|
if (ir.status === "active") {
|
|
17579
17567
|
ir.resolve(null);
|
|
@@ -17596,7 +17584,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17596
17584
|
isProcessingRef,
|
|
17597
17585
|
autoApproveModeRef
|
|
17598
17586
|
});
|
|
17599
|
-
const handleSubmit =
|
|
17587
|
+
const handleSubmit = useCallback9(async (value) => {
|
|
17600
17588
|
const trimmed = value.trim();
|
|
17601
17589
|
if (!trimmed) return;
|
|
17602
17590
|
setInput("");
|
|
@@ -17611,12 +17599,11 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17611
17599
|
await executeTask(trimmed);
|
|
17612
17600
|
}
|
|
17613
17601
|
}, [agent, addMessage, executeTask, handleCommand]);
|
|
17614
|
-
const handleSecretSubmit =
|
|
17602
|
+
const handleSecretSubmit = useCallback9((value) => {
|
|
17615
17603
|
const ir = inputRequestRef.current;
|
|
17616
17604
|
if (ir.status !== "active") return;
|
|
17617
|
-
const displayText = ir.isPassword ? "\u2022".repeat(value.length) : value;
|
|
17618
|
-
|
|
17619
|
-
addMessage("system", `\u21B3 ${promptLabel} ${displayText}`);
|
|
17605
|
+
const displayText = ir.isPassword ? "\u2022".repeat(Math.min(value.length, 20)) : value;
|
|
17606
|
+
addMessage("system", `\u21B3 ${displayText}`);
|
|
17620
17607
|
ir.resolve(value);
|
|
17621
17608
|
setInputRequest({ status: "inactive" });
|
|
17622
17609
|
setSecretInput("");
|
|
@@ -17630,11 +17617,10 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17630
17617
|
inputRequestRef,
|
|
17631
17618
|
isModalOpenRef,
|
|
17632
17619
|
inputRef,
|
|
17633
|
-
clearInput
|
|
17634
|
-
onScroll: handleScroll
|
|
17620
|
+
clearInput
|
|
17635
17621
|
});
|
|
17636
17622
|
if (modal.type) {
|
|
17637
|
-
return /* @__PURE__ */
|
|
17623
|
+
return /* @__PURE__ */ jsx22(Box19, { flexDirection: "column", paddingX: 1, width: terminalWidth, children: /* @__PURE__ */ jsx22(
|
|
17638
17624
|
Modal,
|
|
17639
17625
|
{
|
|
17640
17626
|
type: modal.type,
|
|
@@ -17645,19 +17631,17 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17645
17631
|
}
|
|
17646
17632
|
) });
|
|
17647
17633
|
}
|
|
17648
|
-
return /* @__PURE__ */
|
|
17649
|
-
/* @__PURE__ */
|
|
17634
|
+
return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", paddingX: 1, width: terminalWidth, children: [
|
|
17635
|
+
/* @__PURE__ */ jsx22(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx22(
|
|
17650
17636
|
MessageList,
|
|
17651
17637
|
{
|
|
17652
17638
|
messages,
|
|
17653
|
-
isModalOpen: !!modal.type,
|
|
17654
17639
|
modelName: MODEL_NAME,
|
|
17655
17640
|
autoApproveMode,
|
|
17656
|
-
version: APP_VERSION
|
|
17657
|
-
scrollOffset: historyScrollOffset
|
|
17641
|
+
version: APP_VERSION
|
|
17658
17642
|
}
|
|
17659
17643
|
) }),
|
|
17660
|
-
/* @__PURE__ */
|
|
17644
|
+
/* @__PURE__ */ jsx22(
|
|
17661
17645
|
BottomRegion,
|
|
17662
17646
|
{
|
|
17663
17647
|
input,
|
|
@@ -17682,7 +17666,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17682
17666
|
var app_default = App;
|
|
17683
17667
|
|
|
17684
17668
|
// src/platform/tui/cli/commands/interactive.tsx
|
|
17685
|
-
import { jsx as
|
|
17669
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
17686
17670
|
async function interactiveAction(options) {
|
|
17687
17671
|
const { dangerouslySkipPermissions: skipPermissions = false, target } = options;
|
|
17688
17672
|
console.clear();
|
|
@@ -17691,13 +17675,13 @@ async function interactiveAction(options) {
|
|
|
17691
17675
|
console.log(chalk.hex(HEX.red)("[!] All tool executions will be auto-approved!\n"));
|
|
17692
17676
|
}
|
|
17693
17677
|
const { waitUntilExit } = render(
|
|
17694
|
-
/* @__PURE__ */
|
|
17678
|
+
/* @__PURE__ */ jsx23(AnimationProvider, { children: /* @__PURE__ */ jsx23(
|
|
17695
17679
|
app_default,
|
|
17696
17680
|
{
|
|
17697
17681
|
autoApprove: skipPermissions,
|
|
17698
17682
|
target
|
|
17699
17683
|
}
|
|
17700
|
-
)
|
|
17684
|
+
) })
|
|
17701
17685
|
);
|
|
17702
17686
|
await waitUntilExit();
|
|
17703
17687
|
}
|