@pensar/apex 0.0.101 → 0.0.103-canary.29795a63
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/README.md +1 -30
- package/bin/pensar.js +43 -0
- package/build/auth.js +1 -1
- package/build/index.js +396 -114
- package/package.json +1 -1
- package/src/core/installation/index.ts +12 -0
- package/src/core/installation/installation.test.ts +34 -2
package/README.md
CHANGED
|
@@ -18,14 +18,6 @@
|
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
21
|
-
### Prerequisites
|
|
22
|
-
|
|
23
|
-
- **API Key** for your chosen AI provider
|
|
24
|
-
|
|
25
|
-
After installing, run `pensar doctor` to check for optional dependencies (like nmap) and install them.
|
|
26
|
-
|
|
27
|
-
### Install Apex
|
|
28
|
-
|
|
29
21
|
#### macOS / Linux (Quick Install)
|
|
30
22
|
|
|
31
23
|
```bash
|
|
@@ -42,7 +34,7 @@ brew install apex
|
|
|
42
34
|
#### Windows (PowerShell)
|
|
43
35
|
|
|
44
36
|
```powershell
|
|
45
|
-
irm https://pensarai.com/apex.ps1 | iex
|
|
37
|
+
irm https://www.pensarai.com/apex.ps1 | iex
|
|
46
38
|
```
|
|
47
39
|
|
|
48
40
|
#### npm
|
|
@@ -51,27 +43,6 @@ irm https://pensarai.com/apex.ps1 | iex
|
|
|
51
43
|
npm install -g @pensar/apex
|
|
52
44
|
```
|
|
53
45
|
|
|
54
|
-
### Configuration
|
|
55
|
-
|
|
56
|
-
Set your AI provider API key as an environment variable:
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
export ANTHROPIC_API_KEY="your-api-key-here"
|
|
60
|
-
# or for other providers:
|
|
61
|
-
export OPENAI_API_KEY="your-api-key-here"
|
|
62
|
-
export OPENROUTER_API_KEY="your-api-key-here"
|
|
63
|
-
|
|
64
|
-
# AWS Bedrock (bearer token auth):
|
|
65
|
-
export BEDROCK_API_KEY="your-bearer-token"
|
|
66
|
-
export AWS_REGION="us-east-1"
|
|
67
|
-
|
|
68
|
-
# AWS Bedrock (IAM credentials):
|
|
69
|
-
export AWS_ACCESS_KEY_ID="..."
|
|
70
|
-
export AWS_SECRET_ACCESS_KEY="..."
|
|
71
|
-
export AWS_SESSION_TOKEN="..." # optional, for temporary credentials
|
|
72
|
-
export AWS_REGION="us-east-1"
|
|
73
|
-
```
|
|
74
|
-
|
|
75
46
|
## Usage
|
|
76
47
|
|
|
77
48
|
Run Apex:
|
package/bin/pensar.js
CHANGED
|
@@ -73,6 +73,26 @@ if (command === "benchmark") {
|
|
|
73
73
|
process.argv = [process.argv[0], uninstallPath, ...args.slice(1)];
|
|
74
74
|
|
|
75
75
|
await import(uninstallPath);
|
|
76
|
+
} else if (command === "projects") {
|
|
77
|
+
const p = join(__dirname, "..", "build", "projects.js");
|
|
78
|
+
process.argv = [process.argv[0], p, ...args.slice(1)];
|
|
79
|
+
await import(p);
|
|
80
|
+
} else if (command === "pentests") {
|
|
81
|
+
const p = join(__dirname, "..", "build", "pentests.js");
|
|
82
|
+
process.argv = [process.argv[0], p, ...args.slice(1)];
|
|
83
|
+
await import(p);
|
|
84
|
+
} else if (command === "issues") {
|
|
85
|
+
const p = join(__dirname, "..", "build", "issues.js");
|
|
86
|
+
process.argv = [process.argv[0], p, ...args.slice(1)];
|
|
87
|
+
await import(p);
|
|
88
|
+
} else if (command === "fixes") {
|
|
89
|
+
const p = join(__dirname, "..", "build", "fixes.js");
|
|
90
|
+
process.argv = [process.argv[0], p, ...args.slice(1)];
|
|
91
|
+
await import(p);
|
|
92
|
+
} else if (command === "logs") {
|
|
93
|
+
const p = join(__dirname, "..", "build", "logs.js");
|
|
94
|
+
process.argv = [process.argv[0], p, ...args.slice(1)];
|
|
95
|
+
await import(p);
|
|
76
96
|
} else if (command === "upgrade" || command === "update") {
|
|
77
97
|
const currentVersion = getCurrentVersion();
|
|
78
98
|
console.log(`Current version: v${currentVersion}`);
|
|
@@ -109,6 +129,21 @@ if (command === "benchmark") {
|
|
|
109
129
|
console.log(
|
|
110
130
|
" pensar auth Connect to Pensar Console for managed inference"
|
|
111
131
|
);
|
|
132
|
+
console.log(
|
|
133
|
+
" pensar projects List workspace projects"
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
" pensar pentests List and manage pentests"
|
|
137
|
+
);
|
|
138
|
+
console.log(
|
|
139
|
+
" pensar issues List and manage security issues"
|
|
140
|
+
);
|
|
141
|
+
console.log(
|
|
142
|
+
" pensar fixes View security fixes"
|
|
143
|
+
);
|
|
144
|
+
console.log(
|
|
145
|
+
" pensar logs View agent execution logs"
|
|
146
|
+
);
|
|
112
147
|
console.log();
|
|
113
148
|
console.log("Options:");
|
|
114
149
|
console.log(" -h, --help Show this help message");
|
|
@@ -226,6 +261,14 @@ if (command === "benchmark") {
|
|
|
226
261
|
console.log(" pensar auth");
|
|
227
262
|
console.log(" pensar auth status");
|
|
228
263
|
console.log(" pensar auth logout");
|
|
264
|
+
console.log();
|
|
265
|
+
console.log("Console API:");
|
|
266
|
+
console.log(" pensar projects");
|
|
267
|
+
console.log(" pensar pentests <projectId>");
|
|
268
|
+
console.log(" pensar issues <projectId>");
|
|
269
|
+
console.log(" pensar issues get <issueId>");
|
|
270
|
+
console.log(" pensar fixes <issueId>");
|
|
271
|
+
console.log(" pensar logs <issueId>");
|
|
229
272
|
} else if (args.length === 0) {
|
|
230
273
|
// No command specified, run the TUI
|
|
231
274
|
const appPath = join(__dirname, "..", "build", "index.js");
|
package/build/auth.js
CHANGED
|
@@ -8,7 +8,7 @@ import fs from "fs/promises";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@pensar/apex",
|
|
11
|
-
version: "0.0.
|
|
11
|
+
version: "0.0.103-canary.29795a63",
|
|
12
12
|
description: "AI-powered penetration testing CLI tool with terminal UI",
|
|
13
13
|
module: "src/tui/index.tsx",
|
|
14
14
|
main: "build/index.js",
|
package/build/index.js
CHANGED
|
@@ -31880,12 +31880,6 @@ var init_openrouter = __esm(() => {
|
|
|
31880
31880
|
var PENSAR_MODELS;
|
|
31881
31881
|
var init_pensar = __esm(() => {
|
|
31882
31882
|
PENSAR_MODELS = [
|
|
31883
|
-
{
|
|
31884
|
-
id: "pensar:anthropic.claude-opus-4-6-v1",
|
|
31885
|
-
name: "Claude Opus 4.6 (Pensar)",
|
|
31886
|
-
provider: "pensar",
|
|
31887
|
-
contextLength: 200000
|
|
31888
|
-
},
|
|
31889
31883
|
{
|
|
31890
31884
|
id: "pensar:anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
31891
31885
|
name: "Claude Sonnet 4.5 (Pensar)",
|
|
@@ -31977,7 +31971,7 @@ var package_default2;
|
|
|
31977
31971
|
var init_package = __esm(() => {
|
|
31978
31972
|
package_default2 = {
|
|
31979
31973
|
name: "@pensar/apex",
|
|
31980
|
-
version: "0.0.
|
|
31974
|
+
version: "0.0.103-canary.29795a63",
|
|
31981
31975
|
description: "AI-powered penetration testing CLI tool with terminal UI",
|
|
31982
31976
|
module: "src/tui/index.tsx",
|
|
31983
31977
|
main: "build/index.js",
|
|
@@ -89913,6 +89907,7 @@ function convertMessagesToUI(messages, baseTime) {
|
|
|
89913
89907
|
const input = part.input;
|
|
89914
89908
|
const toolDescription = typeof input?.toolCallDescription === "string" ? input.toolCallDescription : part.toolName || "tool";
|
|
89915
89909
|
const result = part.toolCallId ? toolResults.get(part.toolCallId) : undefined;
|
|
89910
|
+
const cancelled = typeof result === "string" && result.toLowerCase().includes("cancelled");
|
|
89916
89911
|
uiMessages.push({
|
|
89917
89912
|
role: "tool",
|
|
89918
89913
|
content: toolDescription,
|
|
@@ -89921,7 +89916,7 @@ function convertMessagesToUI(messages, baseTime) {
|
|
|
89921
89916
|
toolName: part.toolName,
|
|
89922
89917
|
args: input,
|
|
89923
89918
|
result,
|
|
89924
|
-
status: "completed"
|
|
89919
|
+
status: cancelled ? "error" : "completed"
|
|
89925
89920
|
});
|
|
89926
89921
|
}
|
|
89927
89922
|
}
|
|
@@ -107298,9 +107293,9 @@ var init_executeCommand = __esm(() => {
|
|
|
107298
107293
|
init_dist5();
|
|
107299
107294
|
init_zod();
|
|
107300
107295
|
executeCommandInputSchema = exports_external.object({
|
|
107296
|
+
toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing (e.g., 'Scanning for open ports on target')"),
|
|
107301
107297
|
command: exports_external.string().describe("The shell command to execute"),
|
|
107302
|
-
timeout: exports_external.number().optional().describe("Timeout in seconds. If omitted, the command runs until completion or abort.")
|
|
107303
|
-
toolCallDescription: exports_external.string().describe("A concise, human-readable description of what this tool call is doing (e.g., 'Scanning for open ports on target')")
|
|
107298
|
+
timeout: exports_external.number().optional().describe("Timeout in seconds. If omitted, the command runs until completion or abort.")
|
|
107304
107299
|
});
|
|
107305
107300
|
});
|
|
107306
107301
|
|
|
@@ -194814,7 +194809,8 @@ var init_operator = __esm(() => {
|
|
|
194814
194809
|
|
|
194815
194810
|
// src/core/agents/offSecAgent/offensiveSecurityAgent.ts
|
|
194816
194811
|
import { join as join22 } from "path";
|
|
194817
|
-
import {
|
|
194812
|
+
import { mkdirSync as mkdirSync10, existsSync as existsSync21 } from "fs";
|
|
194813
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
194818
194814
|
function wrapToolsWithApprovalGate(tools, gate) {
|
|
194819
194815
|
const wrapped = {};
|
|
194820
194816
|
for (const [name26, coreTool] of Object.entries(tools)) {
|
|
@@ -194968,13 +194964,11 @@ var init_offensiveSecurityAgent = __esm(() => {
|
|
|
194968
194964
|
stopWhen,
|
|
194969
194965
|
toolChoice: "auto",
|
|
194970
194966
|
onStepFinish: (event) => {
|
|
194971
|
-
|
|
194972
|
-
|
|
194973
|
-
|
|
194974
|
-
|
|
194975
|
-
|
|
194976
|
-
writeFileSync15(messagesPath, JSON.stringify(allMessages, null, 2));
|
|
194977
|
-
} catch {}
|
|
194967
|
+
const allMessages = [
|
|
194968
|
+
...initialMessagesRef.current,
|
|
194969
|
+
...event.response.messages
|
|
194970
|
+
];
|
|
194971
|
+
writeFile3(messagesPath, JSON.stringify(allMessages, null, 2)).catch(() => {});
|
|
194978
194972
|
input.onStepFinish?.(event);
|
|
194979
194973
|
},
|
|
194980
194974
|
onSummarized: () => {
|
|
@@ -195498,7 +195492,7 @@ var init_agent4 = __esm(() => {
|
|
|
195498
195492
|
});
|
|
195499
195493
|
|
|
195500
195494
|
// src/core/session/execution-metrics.ts
|
|
195501
|
-
import { existsSync as existsSync24, readFileSync as readFileSync11, writeFileSync as
|
|
195495
|
+
import { existsSync as existsSync24, readFileSync as readFileSync11, writeFileSync as writeFileSync15 } from "fs";
|
|
195502
195496
|
import { join as join25 } from "path";
|
|
195503
195497
|
function toNonNegativeInteger(value) {
|
|
195504
195498
|
const n = Number(value);
|
|
@@ -195544,7 +195538,7 @@ function writeSessionJsonTokenTotals(sessionRootPath, tokenUsage) {
|
|
|
195544
195538
|
const parsed = JSON.parse(readFileSync11(path6, "utf-8"));
|
|
195545
195539
|
parsed.tokensIn = tokenUsage.inputTokens;
|
|
195546
195540
|
parsed.tokensOut = tokenUsage.outputTokens;
|
|
195547
|
-
|
|
195541
|
+
writeFileSync15(path6, JSON.stringify(parsed, null, 2));
|
|
195548
195542
|
} catch {}
|
|
195549
195543
|
}
|
|
195550
195544
|
function writeExecutionMetrics(input) {
|
|
@@ -195559,7 +195553,7 @@ function writeExecutionMetrics(input) {
|
|
|
195559
195553
|
runtime: input.runtime ?? existing?.runtime,
|
|
195560
195554
|
updatedAt: new Date().toISOString()
|
|
195561
195555
|
};
|
|
195562
|
-
|
|
195556
|
+
writeFileSync15(metricsPath(input.sessionRootPath), JSON.stringify(next, null, 2), "utf-8");
|
|
195563
195557
|
writeSessionJsonTokenTotals(input.sessionRootPath, next.tokenUsage);
|
|
195564
195558
|
return next;
|
|
195565
195559
|
}
|
|
@@ -196154,7 +196148,7 @@ __export(exports_pentest, {
|
|
|
196154
196148
|
runPentestSwarm: () => runPentestSwarm,
|
|
196155
196149
|
DEFAULT_CONCURRENCY: () => DEFAULT_CONCURRENCY4
|
|
196156
196150
|
});
|
|
196157
|
-
import { existsSync as existsSync25, readdirSync as readdirSync5, readFileSync as readFileSync12, writeFileSync as
|
|
196151
|
+
import { existsSync as existsSync25, readdirSync as readdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync16 } from "fs";
|
|
196158
196152
|
import { join as join26 } from "path";
|
|
196159
196153
|
function addUsageTotals(totals, usage) {
|
|
196160
196154
|
if (!usage)
|
|
@@ -196324,8 +196318,8 @@ async function runPentestWorkflow(input) {
|
|
|
196324
196318
|
});
|
|
196325
196319
|
const mdPath2 = join26(session.rootPath, REPORT_FILENAME_MD);
|
|
196326
196320
|
const jsonPath2 = join26(session.rootPath, REPORT_FILENAME_JSON);
|
|
196327
|
-
|
|
196328
|
-
|
|
196321
|
+
writeFileSync16(mdPath2, renderMarkdown(report2));
|
|
196322
|
+
writeFileSync16(jsonPath2, renderJson(report2));
|
|
196329
196323
|
return {
|
|
196330
196324
|
findings: [],
|
|
196331
196325
|
findingsPath: session.findingsPath,
|
|
@@ -196364,8 +196358,8 @@ async function runPentestWorkflow(input) {
|
|
|
196364
196358
|
});
|
|
196365
196359
|
const mdPath = join26(session.rootPath, REPORT_FILENAME_MD);
|
|
196366
196360
|
const jsonPath = join26(session.rootPath, REPORT_FILENAME_JSON);
|
|
196367
|
-
|
|
196368
|
-
|
|
196361
|
+
writeFileSync16(mdPath, renderMarkdown(report));
|
|
196362
|
+
writeFileSync16(jsonPath, renderJson(report));
|
|
196369
196363
|
return {
|
|
196370
196364
|
findings,
|
|
196371
196365
|
findingsPath: session.findingsPath,
|
|
@@ -273074,7 +273068,7 @@ var useTerminalDimensions = () => {
|
|
|
273074
273068
|
};
|
|
273075
273069
|
|
|
273076
273070
|
// src/tui/index.tsx
|
|
273077
|
-
var
|
|
273071
|
+
var import_react90 = __toESM(require_react(), 1);
|
|
273078
273072
|
|
|
273079
273073
|
// src/tui/components/footer.tsx
|
|
273080
273074
|
import os6 from "os";
|
|
@@ -274517,7 +274511,27 @@ function CommandProvider({
|
|
|
274517
274511
|
description: skill.description || "Skill"
|
|
274518
274512
|
});
|
|
274519
274513
|
}
|
|
274520
|
-
|
|
274514
|
+
const priorityOrder = [
|
|
274515
|
+
"/pentest",
|
|
274516
|
+
"/operator",
|
|
274517
|
+
"/auth",
|
|
274518
|
+
"/models",
|
|
274519
|
+
"/sessions",
|
|
274520
|
+
"/themes",
|
|
274521
|
+
"/help"
|
|
274522
|
+
];
|
|
274523
|
+
return options.sort((a, b2) => {
|
|
274524
|
+
const aIndex = priorityOrder.indexOf(a.value);
|
|
274525
|
+
const bIndex = priorityOrder.indexOf(b2.value);
|
|
274526
|
+
if (aIndex !== -1 && bIndex !== -1) {
|
|
274527
|
+
return aIndex - bIndex;
|
|
274528
|
+
}
|
|
274529
|
+
if (aIndex !== -1)
|
|
274530
|
+
return -1;
|
|
274531
|
+
if (bIndex !== -1)
|
|
274532
|
+
return 1;
|
|
274533
|
+
return a.value.localeCompare(b2.value);
|
|
274534
|
+
});
|
|
274521
274535
|
}, [router, skills]);
|
|
274522
274536
|
const executeCommand = import_react17.useCallback(async (input) => {
|
|
274523
274537
|
return await router.execute(input, ctx3);
|
|
@@ -275734,6 +275748,40 @@ function computeTab(suggestions, selectedIndex) {
|
|
|
275734
275748
|
function shouldResetHistory(historyIndex, isNavigatingHistory) {
|
|
275735
275749
|
return historyIndex !== -1 && !isNavigatingHistory;
|
|
275736
275750
|
}
|
|
275751
|
+
function computeVisibleWindow(suggestions, selectedIndex, maxVisible) {
|
|
275752
|
+
if (suggestions.length === 0) {
|
|
275753
|
+
return {
|
|
275754
|
+
start: 0,
|
|
275755
|
+
end: 0,
|
|
275756
|
+
visibleSuggestions: [],
|
|
275757
|
+
hasMore: false,
|
|
275758
|
+
hasMoreBelow: false
|
|
275759
|
+
};
|
|
275760
|
+
}
|
|
275761
|
+
if (suggestions.length <= maxVisible) {
|
|
275762
|
+
return {
|
|
275763
|
+
start: 0,
|
|
275764
|
+
end: suggestions.length,
|
|
275765
|
+
visibleSuggestions: suggestions,
|
|
275766
|
+
hasMore: false,
|
|
275767
|
+
hasMoreBelow: false
|
|
275768
|
+
};
|
|
275769
|
+
}
|
|
275770
|
+
const safeSelectedIndex = Math.max(0, selectedIndex);
|
|
275771
|
+
let start = Math.max(0, safeSelectedIndex - Math.floor(maxVisible / 2));
|
|
275772
|
+
let end = start + maxVisible;
|
|
275773
|
+
if (end > suggestions.length) {
|
|
275774
|
+
end = suggestions.length;
|
|
275775
|
+
start = Math.max(0, end - maxVisible);
|
|
275776
|
+
}
|
|
275777
|
+
return {
|
|
275778
|
+
start,
|
|
275779
|
+
end,
|
|
275780
|
+
visibleSuggestions: suggestions.slice(start, end),
|
|
275781
|
+
hasMore: start > 0,
|
|
275782
|
+
hasMoreBelow: end < suggestions.length
|
|
275783
|
+
};
|
|
275784
|
+
}
|
|
275737
275785
|
|
|
275738
275786
|
// src/tui/components/shared/use-paste-extmarks.ts
|
|
275739
275787
|
var import_react30 = __toESM(require_react(), 1);
|
|
@@ -275827,6 +275875,7 @@ var PromptInput = import_react31.forwardRef(function PromptInput2({
|
|
|
275827
275875
|
enableAutocomplete = false,
|
|
275828
275876
|
autocompleteOptions = [],
|
|
275829
275877
|
maxSuggestions = 10,
|
|
275878
|
+
maxVisibleSuggestions = 6,
|
|
275830
275879
|
enableCommands = false,
|
|
275831
275880
|
onCommandExecute,
|
|
275832
275881
|
commandHistory = [],
|
|
@@ -275992,34 +276041,72 @@ var PromptInput = import_react31.forwardRef(function PromptInput2({
|
|
|
275992
276041
|
setHistoryIndex(-1);
|
|
275993
276042
|
}
|
|
275994
276043
|
};
|
|
276044
|
+
const windowedView = import_react31.useMemo(() => computeVisibleWindow(suggestions, selectedSuggestionIndex, maxVisibleSuggestions), [suggestions, selectedSuggestionIndex, maxVisibleSuggestions]);
|
|
275995
276045
|
const suggestionsBox = suggestions.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
275996
276046
|
flexDirection: "column",
|
|
275997
276047
|
...autocompletePlacement === "above" ? { marginBottom: 1 } : { marginTop: 1 },
|
|
275998
|
-
children:
|
|
275999
|
-
|
|
276000
|
-
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
276048
|
+
children: [
|
|
276049
|
+
windowedView.hasMore && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
276001
276050
|
flexDirection: "row",
|
|
276002
276051
|
gap: 1,
|
|
276003
276052
|
children: [
|
|
276004
276053
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
276005
|
-
fg:
|
|
276006
|
-
children:
|
|
276054
|
+
fg: colors2.textMuted,
|
|
276055
|
+
children: " ↑"
|
|
276007
276056
|
}, undefined, false, undefined, this),
|
|
276008
276057
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
276009
|
-
fg:
|
|
276010
|
-
children:
|
|
276058
|
+
fg: colors2.textMuted,
|
|
276059
|
+
children: [
|
|
276060
|
+
windowedView.start,
|
|
276061
|
+
" more above..."
|
|
276062
|
+
]
|
|
276063
|
+
}, undefined, true, undefined, this)
|
|
276064
|
+
]
|
|
276065
|
+
}, undefined, true, undefined, this),
|
|
276066
|
+
windowedView.visibleSuggestions.map((suggestion, windowIndex) => {
|
|
276067
|
+
const actualIndex = windowedView.start + windowIndex;
|
|
276068
|
+
const isSelected = actualIndex === selectedSuggestionIndex;
|
|
276069
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
276070
|
+
flexDirection: "row",
|
|
276071
|
+
gap: 1,
|
|
276072
|
+
children: [
|
|
276073
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
276074
|
+
fg: isSelected ? colors2.primary : colors2.textMuted,
|
|
276075
|
+
children: isSelected ? " ▸" : " "
|
|
276076
|
+
}, undefined, false, undefined, this),
|
|
276077
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
276078
|
+
fg: isSelected ? colors2.text : colors2.textMuted,
|
|
276079
|
+
children: suggestion.label
|
|
276080
|
+
}, undefined, false, undefined, this),
|
|
276081
|
+
suggestion.description && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
276082
|
+
fg: colors2.textMuted,
|
|
276083
|
+
children: [
|
|
276084
|
+
" ",
|
|
276085
|
+
suggestion.description
|
|
276086
|
+
]
|
|
276087
|
+
}, undefined, true, undefined, this)
|
|
276088
|
+
]
|
|
276089
|
+
}, suggestion.value, true, undefined, this);
|
|
276090
|
+
}),
|
|
276091
|
+
windowedView.hasMoreBelow && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
276092
|
+
flexDirection: "row",
|
|
276093
|
+
gap: 1,
|
|
276094
|
+
children: [
|
|
276095
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
276096
|
+
fg: colors2.textMuted,
|
|
276097
|
+
children: " ↓"
|
|
276011
276098
|
}, undefined, false, undefined, this),
|
|
276012
|
-
|
|
276099
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
276013
276100
|
fg: colors2.textMuted,
|
|
276014
276101
|
children: [
|
|
276015
|
-
|
|
276016
|
-
|
|
276102
|
+
suggestions.length - windowedView.end,
|
|
276103
|
+
" more below..."
|
|
276017
276104
|
]
|
|
276018
276105
|
}, undefined, true, undefined, this)
|
|
276019
276106
|
]
|
|
276020
|
-
},
|
|
276021
|
-
|
|
276022
|
-
}, undefined,
|
|
276107
|
+
}, undefined, true, undefined, this)
|
|
276108
|
+
]
|
|
276109
|
+
}, undefined, true, undefined, this);
|
|
276023
276110
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
276024
276111
|
flexDirection: "column",
|
|
276025
276112
|
children: [
|
|
@@ -279763,7 +279850,7 @@ function ResponsibleUseDisclosure({
|
|
|
279763
279850
|
}, undefined, false, undefined, this),
|
|
279764
279851
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
279765
279852
|
fg: colors2.text,
|
|
279766
|
-
children: "This penetration testing tool is
|
|
279853
|
+
children: "This penetration testing tool is designed for AUTHORIZED security testing only."
|
|
279767
279854
|
}, undefined, false, undefined, this),
|
|
279768
279855
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
279769
279856
|
flexDirection: "column",
|
|
@@ -279806,7 +279893,7 @@ function ResponsibleUseDisclosure({
|
|
|
279806
279893
|
flexDirection: "column",
|
|
279807
279894
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
279808
279895
|
fg: colors2.error,
|
|
279809
|
-
children: "Unauthorized access to computer systems is
|
|
279896
|
+
children: "Unauthorized access to computer systems is illegal and may result in criminal prosecution."
|
|
279810
279897
|
}, undefined, false, undefined, this)
|
|
279811
279898
|
}, undefined, false, undefined, this),
|
|
279812
279899
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
@@ -280113,18 +280200,23 @@ function HelpDialog() {
|
|
|
280113
280200
|
});
|
|
280114
280201
|
};
|
|
280115
280202
|
useKeyboard((evt) => {
|
|
280203
|
+
if (evt.name === "escape") {
|
|
280204
|
+
evt.preventDefault();
|
|
280205
|
+
if (showDetail) {
|
|
280206
|
+
setShowDetail(false);
|
|
280207
|
+
} else {
|
|
280208
|
+
handleClose();
|
|
280209
|
+
}
|
|
280210
|
+
return;
|
|
280211
|
+
}
|
|
280116
280212
|
if (showDetail) {
|
|
280117
|
-
if (evt.name === "
|
|
280213
|
+
if (evt.name === "return") {
|
|
280118
280214
|
evt.preventDefault();
|
|
280119
280215
|
setShowDetail(false);
|
|
280120
280216
|
}
|
|
280121
280217
|
return;
|
|
280122
280218
|
}
|
|
280123
280219
|
switch (evt.name) {
|
|
280124
|
-
case "escape":
|
|
280125
|
-
evt.preventDefault();
|
|
280126
|
-
handleClose();
|
|
280127
|
-
break;
|
|
280128
280220
|
case "up":
|
|
280129
280221
|
case "k":
|
|
280130
280222
|
evt.preventDefault();
|
|
@@ -281127,6 +281219,98 @@ var actionsByKey = new Map(allActions.map((action) => [action.key, action]));
|
|
|
281127
281219
|
var actionsById = new Map(allActions.map((action) => [action.id, action]));
|
|
281128
281220
|
// src/tui/keybindings/keybind.tsx
|
|
281129
281221
|
var LeaderKeyContext = import_react62.createContext(null);
|
|
281222
|
+
// src/tui/terminal-focus.ts
|
|
281223
|
+
var bracketedFocusModeEnabled = false;
|
|
281224
|
+
function setupTerminalFocusHandling(renderer, options = {}) {
|
|
281225
|
+
const { onTerminalFocus, debug = false } = options;
|
|
281226
|
+
const log2 = (...args) => {
|
|
281227
|
+
if (debug)
|
|
281228
|
+
console.error("[TerminalFocus]", ...args);
|
|
281229
|
+
};
|
|
281230
|
+
const enableBracketedFocus = () => {
|
|
281231
|
+
if (process.stdout.isTTY) {
|
|
281232
|
+
process.stdout.write("\x1B[?1004h");
|
|
281233
|
+
bracketedFocusModeEnabled = true;
|
|
281234
|
+
log2("Enabled bracketed focus mode");
|
|
281235
|
+
}
|
|
281236
|
+
};
|
|
281237
|
+
const disableBracketedFocus = () => {
|
|
281238
|
+
if (process.stdout.isTTY && bracketedFocusModeEnabled) {
|
|
281239
|
+
process.stdout.write("\x1B[?1004l");
|
|
281240
|
+
bracketedFocusModeEnabled = false;
|
|
281241
|
+
log2("Disabled bracketed focus mode");
|
|
281242
|
+
}
|
|
281243
|
+
};
|
|
281244
|
+
const showCursor = () => {
|
|
281245
|
+
if (process.stdout.isTTY) {
|
|
281246
|
+
process.stdout.write("\x1B[?25h");
|
|
281247
|
+
log2("Showed cursor");
|
|
281248
|
+
}
|
|
281249
|
+
};
|
|
281250
|
+
const handleFocusIn = () => {
|
|
281251
|
+
log2("Terminal gained focus");
|
|
281252
|
+
showCursor();
|
|
281253
|
+
renderer.requestRender();
|
|
281254
|
+
if (onTerminalFocus) {
|
|
281255
|
+
setTimeout(() => {
|
|
281256
|
+
onTerminalFocus();
|
|
281257
|
+
log2("Re-focused prompt input");
|
|
281258
|
+
}, 10);
|
|
281259
|
+
}
|
|
281260
|
+
};
|
|
281261
|
+
const handleFocusOut = () => {
|
|
281262
|
+
log2("Terminal lost focus");
|
|
281263
|
+
};
|
|
281264
|
+
const handleSigCont = () => {
|
|
281265
|
+
log2("Received SIGCONT (terminal resumed)");
|
|
281266
|
+
showCursor();
|
|
281267
|
+
renderer.requestRender();
|
|
281268
|
+
if (onTerminalFocus) {
|
|
281269
|
+
setTimeout(() => {
|
|
281270
|
+
onTerminalFocus();
|
|
281271
|
+
log2("Re-focused prompt input after SIGCONT");
|
|
281272
|
+
}, 10);
|
|
281273
|
+
}
|
|
281274
|
+
};
|
|
281275
|
+
let stdinListenerActive = false;
|
|
281276
|
+
const handleStdinData = (data) => {
|
|
281277
|
+
const str = data.toString();
|
|
281278
|
+
if (str.includes("\x1B[I")) {
|
|
281279
|
+
handleFocusIn();
|
|
281280
|
+
}
|
|
281281
|
+
if (str.includes("\x1B[O")) {
|
|
281282
|
+
handleFocusOut();
|
|
281283
|
+
}
|
|
281284
|
+
};
|
|
281285
|
+
const setupListeners = () => {
|
|
281286
|
+
enableBracketedFocus();
|
|
281287
|
+
showCursor();
|
|
281288
|
+
process.on("SIGCONT", handleSigCont);
|
|
281289
|
+
if (process.stdin.isTTY && !stdinListenerActive) {
|
|
281290
|
+
process.stdin.on("data", handleStdinData);
|
|
281291
|
+
stdinListenerActive = true;
|
|
281292
|
+
log2("Set up stdin listener for bracketed focus events");
|
|
281293
|
+
}
|
|
281294
|
+
};
|
|
281295
|
+
const cleanup = () => {
|
|
281296
|
+
log2("Cleaning up terminal focus handling");
|
|
281297
|
+
disableBracketedFocus();
|
|
281298
|
+
process.off("SIGCONT", handleSigCont);
|
|
281299
|
+
if (stdinListenerActive) {
|
|
281300
|
+
process.stdin.off("data", handleStdinData);
|
|
281301
|
+
stdinListenerActive = false;
|
|
281302
|
+
}
|
|
281303
|
+
};
|
|
281304
|
+
setupListeners();
|
|
281305
|
+
return cleanup;
|
|
281306
|
+
}
|
|
281307
|
+
function cleanupTerminalFocusMode() {
|
|
281308
|
+
if (process.stdout.isTTY && bracketedFocusModeEnabled) {
|
|
281309
|
+
process.stdout.write("\x1B[?1004l");
|
|
281310
|
+
bracketedFocusModeEnabled = false;
|
|
281311
|
+
}
|
|
281312
|
+
}
|
|
281313
|
+
|
|
281130
281314
|
// src/tui/keybindings/registry.ts
|
|
281131
281315
|
function createKeybindings(deps) {
|
|
281132
281316
|
const {
|
|
@@ -281155,6 +281339,7 @@ function createKeybindings(deps) {
|
|
|
281155
281339
|
const now2 = Date.now();
|
|
281156
281340
|
const lastPress = ctrlCPressTime;
|
|
281157
281341
|
if (lastPress && now2 - lastPress < 1000) {
|
|
281342
|
+
cleanupTerminalFocusMode();
|
|
281158
281343
|
renderer.destroy();
|
|
281159
281344
|
process.exit(0);
|
|
281160
281345
|
} else {
|
|
@@ -281179,9 +281364,10 @@ function createKeybindings(deps) {
|
|
|
281179
281364
|
return;
|
|
281180
281365
|
}
|
|
281181
281366
|
const isHome = route.data.type === "base" && route.data.path === "home";
|
|
281367
|
+
const isHelp = route.data.type === "base" && route.data.path === "help";
|
|
281182
281368
|
const isOperator = route.data.type === "base" && route.data.path === "operator";
|
|
281183
281369
|
const isSession = route.data.type === "pentest" || route.data.type === "operator";
|
|
281184
|
-
if (!isHome && !isOperator && !isSession) {
|
|
281370
|
+
if (!isHome && !isHelp && !isOperator && !isSession) {
|
|
281185
281371
|
route.navigate({
|
|
281186
281372
|
type: "base",
|
|
281187
281373
|
path: "home"
|
|
@@ -282925,6 +283111,15 @@ function getToolSummary(toolName, args) {
|
|
|
282925
283111
|
const firstArg = Object.entries(args).filter(([k3]) => k3 !== "toolCallDescription").map(([, v3]) => typeof v3 === "string" ? v3 : JSON.stringify(v3)).find((v3) => v3 && v3.length > 0);
|
|
282926
283112
|
return firstArg ? `${toolName} ${String(firstArg).slice(0, 50)}` : toolName;
|
|
282927
283113
|
}
|
|
283114
|
+
function getToolDisplayLabel(toolName, args, options = {}) {
|
|
283115
|
+
if (options.preferDescription && toolName === "execute_command") {
|
|
283116
|
+
const description = args.toolCallDescription;
|
|
283117
|
+
if (typeof description === "string" && description.trim().length > 0) {
|
|
283118
|
+
return description.trim();
|
|
283119
|
+
}
|
|
283120
|
+
}
|
|
283121
|
+
return getToolSummary(toolName, args);
|
|
283122
|
+
}
|
|
282928
283123
|
function getArgsPreview(toolName, args, maxLength = 60) {
|
|
282929
283124
|
const filteredArgs = Object.entries(args).filter(([k3]) => !k3.toLowerCase().includes("description"));
|
|
282930
283125
|
if (filteredArgs.length === 0)
|
|
@@ -283758,7 +283953,9 @@ var ToolRenderer = import_react68.memo(function ToolRenderer2({
|
|
|
283758
283953
|
const isCompleted = message.status === "completed";
|
|
283759
283954
|
const isError = message.status === "error";
|
|
283760
283955
|
const { toolName, args, result, logs, subagentLogs } = message;
|
|
283761
|
-
const summary =
|
|
283956
|
+
const summary = getToolDisplayLabel(toolName, args, {
|
|
283957
|
+
preferDescription: isPending
|
|
283958
|
+
});
|
|
283762
283959
|
const resultDisplay = isCompleted || isError ? getResultSummary(result, toolName, args) : null;
|
|
283763
283960
|
const borderColor = isError ? colors2.error : isPending ? colors2.warning : colors2.info;
|
|
283764
283961
|
const hasSubagentLogs = subagentLogs && Object.keys(subagentLogs).length > 0;
|
|
@@ -286146,7 +286343,7 @@ function MessageList({
|
|
|
286146
286343
|
}, undefined, false, undefined, this),
|
|
286147
286344
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286148
286345
|
fg: colors2.textMuted,
|
|
286149
|
-
children: " -
|
|
286346
|
+
children: " - Switch between Plan or Default mode"
|
|
286150
286347
|
}, undefined, false, undefined, this)
|
|
286151
286348
|
]
|
|
286152
286349
|
}, undefined, true, undefined, this)
|
|
@@ -286749,7 +286946,7 @@ function navigateDown(selectedIndex, queueLength) {
|
|
|
286749
286946
|
}
|
|
286750
286947
|
|
|
286751
286948
|
// src/tui/components/operator-dashboard/index.tsx
|
|
286752
|
-
import { existsSync as existsSync27, readFileSync as readFileSync14 } from "fs";
|
|
286949
|
+
import { existsSync as existsSync27, readFileSync as readFileSync14, writeFileSync as writeFileSync17 } from "fs";
|
|
286753
286950
|
import { join as join28 } from "path";
|
|
286754
286951
|
function OperatorDashboard({
|
|
286755
286952
|
sessionId,
|
|
@@ -286789,6 +286986,8 @@ function OperatorDashboard({
|
|
|
286789
286986
|
return filterOperatorAutocomplete(allAutocompleteOptions, skillSlugs);
|
|
286790
286987
|
}, [allAutocompleteOptions, skills]);
|
|
286791
286988
|
const [session, setSession] = import_react79.useState(null);
|
|
286989
|
+
const sessionRef = import_react79.useRef(null);
|
|
286990
|
+
sessionRef.current = session;
|
|
286792
286991
|
const [loading, setLoading] = import_react79.useState(true);
|
|
286793
286992
|
const [error40, setError] = import_react79.useState(null);
|
|
286794
286993
|
const pendingNameRef = import_react79.useRef(null);
|
|
@@ -286800,6 +286999,8 @@ function OperatorDashboard({
|
|
|
286800
286999
|
});
|
|
286801
287000
|
const commandCancelledRef = import_react79.useRef(false);
|
|
286802
287001
|
const [messages, setMessages] = import_react79.useState([]);
|
|
287002
|
+
const displayMessagesRef = import_react79.useRef([]);
|
|
287003
|
+
displayMessagesRef.current = messages;
|
|
286803
287004
|
const textRef = import_react79.useRef("");
|
|
286804
287005
|
const conversationRef = import_react79.useRef([]);
|
|
286805
287006
|
const [inputValue, setInputValue] = import_react79.useState("");
|
|
@@ -287183,6 +287384,12 @@ function OperatorDashboard({
|
|
|
287183
287384
|
{ role: "user", content: prompt }
|
|
287184
287385
|
];
|
|
287185
287386
|
conversationRef.current = nextMessages;
|
|
287387
|
+
if (sessionRef.current) {
|
|
287388
|
+
try {
|
|
287389
|
+
const mp = join28(sessionRef.current.rootPath, "messages.json");
|
|
287390
|
+
writeFileSync17(mp, JSON.stringify(nextMessages, null, 2));
|
|
287391
|
+
} catch {}
|
|
287392
|
+
}
|
|
287186
287393
|
const onStepFinish = (event) => {
|
|
287187
287394
|
const nextUsage = accumulateTokenUsage(tokenUsageRef.current, event.usage?.inputTokens ?? 0, event.usage?.outputTokens ?? 0);
|
|
287188
287395
|
if (!nextUsage)
|
|
@@ -287200,22 +287407,32 @@ function OperatorDashboard({
|
|
|
287200
287407
|
};
|
|
287201
287408
|
const callbacks = {
|
|
287202
287409
|
onTextDelta: (d3) => {
|
|
287410
|
+
if (gen !== generationRef.current)
|
|
287411
|
+
return;
|
|
287203
287412
|
setThinking(false);
|
|
287204
287413
|
appendText(d3.text);
|
|
287205
287414
|
},
|
|
287206
287415
|
onToolCallStreaming: (d3) => {
|
|
287416
|
+
if (gen !== generationRef.current)
|
|
287417
|
+
return;
|
|
287207
287418
|
setThinking(false);
|
|
287208
287419
|
addStreamingToolCall(d3.toolCallId, d3.toolName);
|
|
287209
287420
|
},
|
|
287210
287421
|
onToolCallDelta: (d3) => {
|
|
287422
|
+
if (gen !== generationRef.current)
|
|
287423
|
+
return;
|
|
287211
287424
|
appendToolCallDelta(d3.toolCallId, d3.argsTextDelta);
|
|
287212
287425
|
},
|
|
287213
287426
|
onToolCall: (d3) => {
|
|
287427
|
+
if (gen !== generationRef.current)
|
|
287428
|
+
return;
|
|
287214
287429
|
setThinking(false);
|
|
287215
287430
|
commandCancelledRef.current = false;
|
|
287216
287431
|
addToolCall(d3.toolCallId, d3.toolName, d3.input);
|
|
287217
287432
|
},
|
|
287218
287433
|
onToolResult: (d3) => {
|
|
287434
|
+
if (gen !== generationRef.current)
|
|
287435
|
+
return;
|
|
287219
287436
|
flushCommandOutput();
|
|
287220
287437
|
if (cmdFlushTimerRef.current) {
|
|
287221
287438
|
clearInterval(cmdFlushTimerRef.current);
|
|
@@ -287275,6 +287492,8 @@ function OperatorDashboard({
|
|
|
287275
287492
|
callbacks,
|
|
287276
287493
|
onSessionReady: (s2) => {
|
|
287277
287494
|
setSessionCwd(s2.rootPath);
|
|
287495
|
+
sessionRef.current = s2;
|
|
287496
|
+
setSession((prev) => prev ?? s2);
|
|
287278
287497
|
}
|
|
287279
287498
|
};
|
|
287280
287499
|
try {
|
|
@@ -287520,19 +287739,61 @@ function OperatorDashboard({
|
|
|
287520
287739
|
setQueuedMessages([]);
|
|
287521
287740
|
queuedMessagesRef.current = [];
|
|
287522
287741
|
setSelectedQueueIndex(-1);
|
|
287742
|
+
approvalGateRef.current.denyAll();
|
|
287523
287743
|
setStatus("idle");
|
|
287524
287744
|
setThinking(false);
|
|
287525
287745
|
setIsExecuting(false);
|
|
287526
|
-
|
|
287527
|
-
if (
|
|
287746
|
+
const activeSession = sessionRef.current;
|
|
287747
|
+
if (activeSession) {
|
|
287528
287748
|
try {
|
|
287529
|
-
const messagesPath = join28(
|
|
287749
|
+
const messagesPath = join28(activeSession.rootPath, "messages.json");
|
|
287530
287750
|
if (existsSync27(messagesPath)) {
|
|
287531
287751
|
const raw = JSON.parse(readFileSync14(messagesPath, "utf-8"));
|
|
287532
287752
|
if (Array.isArray(raw) && raw.length > 0) {
|
|
287533
287753
|
conversationRef.current = sessions.getResumeMessages(raw);
|
|
287534
287754
|
}
|
|
287535
287755
|
}
|
|
287756
|
+
const last = conversationRef.current[conversationRef.current.length - 1];
|
|
287757
|
+
if (last?.role === "user") {
|
|
287758
|
+
const partial2 = textRef.current.trim();
|
|
287759
|
+
const pendingTools = displayMessagesRef.current.filter((m4) => isToolMessage(m4) && (m4.status === "pending" || m4.status === "streaming"));
|
|
287760
|
+
const assistantContent = [
|
|
287761
|
+
{
|
|
287762
|
+
type: "text",
|
|
287763
|
+
text: partial2 || "[Response interrupted by user.]"
|
|
287764
|
+
}
|
|
287765
|
+
];
|
|
287766
|
+
for (const t3 of pendingTools) {
|
|
287767
|
+
assistantContent.push({
|
|
287768
|
+
type: "tool-call",
|
|
287769
|
+
toolCallId: t3.toolCallId,
|
|
287770
|
+
toolName: t3.toolName,
|
|
287771
|
+
input: t3.args ?? {}
|
|
287772
|
+
});
|
|
287773
|
+
}
|
|
287774
|
+
conversationRef.current = [
|
|
287775
|
+
...conversationRef.current,
|
|
287776
|
+
{
|
|
287777
|
+
role: "assistant",
|
|
287778
|
+
content: assistantContent
|
|
287779
|
+
}
|
|
287780
|
+
];
|
|
287781
|
+
if (pendingTools.length > 0) {
|
|
287782
|
+
conversationRef.current = [
|
|
287783
|
+
...conversationRef.current,
|
|
287784
|
+
{
|
|
287785
|
+
role: "tool",
|
|
287786
|
+
content: pendingTools.map((t3) => ({
|
|
287787
|
+
type: "tool-result",
|
|
287788
|
+
toolCallId: t3.toolCallId,
|
|
287789
|
+
toolName: t3.toolName ?? "unknown",
|
|
287790
|
+
output: "Cancelled by user."
|
|
287791
|
+
}))
|
|
287792
|
+
}
|
|
287793
|
+
];
|
|
287794
|
+
}
|
|
287795
|
+
writeFileSync17(join28(activeSession.rootPath, "messages.json"), JSON.stringify(conversationRef.current, null, 2));
|
|
287796
|
+
}
|
|
287536
287797
|
} catch {}
|
|
287537
287798
|
}
|
|
287538
287799
|
setMessages((prev) => {
|
|
@@ -287546,7 +287807,7 @@ function OperatorDashboard({
|
|
|
287546
287807
|
}
|
|
287547
287808
|
];
|
|
287548
287809
|
});
|
|
287549
|
-
}, [
|
|
287810
|
+
}, [setThinking, setIsExecuting]);
|
|
287550
287811
|
const toggleApproval = import_react79.useCallback(() => {
|
|
287551
287812
|
setOperatorState((prev) => {
|
|
287552
287813
|
const newVal = !prev.requireApproval;
|
|
@@ -290935,74 +291196,92 @@ function setupAutoCopy(renderer, copyToClipboard) {
|
|
|
290935
291196
|
});
|
|
290936
291197
|
}
|
|
290937
291198
|
|
|
291199
|
+
// src/tui/components/terminal-focus-handler.tsx
|
|
291200
|
+
var import_react87 = __toESM(require_react(), 1);
|
|
291201
|
+
function TerminalFocusHandler() {
|
|
291202
|
+
const { refocusPrompt } = useFocus();
|
|
291203
|
+
const renderer = useRenderer();
|
|
291204
|
+
import_react87.useEffect(() => {
|
|
291205
|
+
const cleanup = setupTerminalFocusHandling(renderer, {
|
|
291206
|
+
onTerminalFocus: refocusPrompt,
|
|
291207
|
+
debug: false
|
|
291208
|
+
});
|
|
291209
|
+
return cleanup;
|
|
291210
|
+
}, [renderer, refocusPrompt]);
|
|
291211
|
+
return null;
|
|
291212
|
+
}
|
|
291213
|
+
|
|
290938
291214
|
// src/tui/index.tsx
|
|
290939
291215
|
function App({ appConfig }) {
|
|
290940
|
-
const [focusIndex, setFocusIndex] =
|
|
290941
|
-
const [cwd, setCwd] =
|
|
290942
|
-
const [ctrlCPressTime, setCtrlCPressTime] =
|
|
290943
|
-
const [showExitWarning, setShowExitWarning] =
|
|
290944
|
-
const [inputKey, setInputKey] =
|
|
290945
|
-
const [showSessionsDialog, setShowSessionsDialog] =
|
|
290946
|
-
const [showShortcutsDialog, setShowShortcutsDialog] =
|
|
290947
|
-
const [showThemeDialog, setShowThemeDialog] =
|
|
290948
|
-
const [showAuthDialog, setShowAuthDialog] =
|
|
290949
|
-
const [showPentestDialog, setShowPentestDialog] =
|
|
290950
|
-
const [pendingPentestFlags, setPendingPentestFlags] =
|
|
291216
|
+
const [focusIndex, setFocusIndex] = import_react90.useState(0);
|
|
291217
|
+
const [cwd, setCwd] = import_react90.useState(process.cwd());
|
|
291218
|
+
const [ctrlCPressTime, setCtrlCPressTime] = import_react90.useState(null);
|
|
291219
|
+
const [showExitWarning, setShowExitWarning] = import_react90.useState(false);
|
|
291220
|
+
const [inputKey, setInputKey] = import_react90.useState(0);
|
|
291221
|
+
const [showSessionsDialog, setShowSessionsDialog] = import_react90.useState(false);
|
|
291222
|
+
const [showShortcutsDialog, setShowShortcutsDialog] = import_react90.useState(false);
|
|
291223
|
+
const [showThemeDialog, setShowThemeDialog] = import_react90.useState(false);
|
|
291224
|
+
const [showAuthDialog, setShowAuthDialog] = import_react90.useState(false);
|
|
291225
|
+
const [showPentestDialog, setShowPentestDialog] = import_react90.useState(false);
|
|
291226
|
+
const [pendingPentestFlags, setPendingPentestFlags] = import_react90.useState(undefined);
|
|
290951
291227
|
const navigableItems = ["command-input"];
|
|
290952
291228
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ConfigProvider, {
|
|
290953
291229
|
config: appConfig,
|
|
290954
291230
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SessionProvider, {
|
|
290955
291231
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteProvider, {
|
|
290956
291232
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(FocusProvider, {
|
|
290957
|
-
children:
|
|
290958
|
-
|
|
290959
|
-
|
|
290960
|
-
|
|
290961
|
-
|
|
290962
|
-
|
|
290963
|
-
|
|
290964
|
-
|
|
290965
|
-
|
|
290966
|
-
|
|
290967
|
-
|
|
290968
|
-
|
|
290969
|
-
deps: {
|
|
290970
|
-
ctrlCPressTime,
|
|
290971
|
-
setCtrlCPressTime,
|
|
290972
|
-
setShowExitWarning,
|
|
290973
|
-
setInputKey,
|
|
290974
|
-
setShowSessionsDialog,
|
|
290975
|
-
setShowShortcutsDialog,
|
|
290976
|
-
setFocusIndex,
|
|
290977
|
-
navigableItems
|
|
291233
|
+
children: [
|
|
291234
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(TerminalFocusHandler, {}, undefined, false, undefined, this),
|
|
291235
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(InputProvider, {
|
|
291236
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(DialogProvider, {
|
|
291237
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AgentProvider, {
|
|
291238
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(CommandProvider, {
|
|
291239
|
+
onOpenSessionsDialog: () => setShowSessionsDialog(true),
|
|
291240
|
+
onOpenThemeDialog: () => setShowThemeDialog(true),
|
|
291241
|
+
onOpenAuthDialog: () => setShowAuthDialog(true),
|
|
291242
|
+
onOpenPentestDialog: (flags) => {
|
|
291243
|
+
setPendingPentestFlags(flags);
|
|
291244
|
+
setShowPentestDialog(true);
|
|
290978
291245
|
},
|
|
290979
|
-
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(
|
|
290980
|
-
|
|
290981
|
-
|
|
290982
|
-
|
|
290983
|
-
|
|
290984
|
-
|
|
290985
|
-
|
|
290986
|
-
|
|
290987
|
-
|
|
290988
|
-
|
|
290989
|
-
|
|
290990
|
-
|
|
290991
|
-
|
|
290992
|
-
|
|
290993
|
-
|
|
290994
|
-
|
|
290995
|
-
|
|
290996
|
-
|
|
290997
|
-
|
|
290998
|
-
|
|
291246
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(KeybindingProvider, {
|
|
291247
|
+
deps: {
|
|
291248
|
+
ctrlCPressTime,
|
|
291249
|
+
setCtrlCPressTime,
|
|
291250
|
+
setShowExitWarning,
|
|
291251
|
+
setInputKey,
|
|
291252
|
+
setShowSessionsDialog,
|
|
291253
|
+
setShowShortcutsDialog,
|
|
291254
|
+
setFocusIndex,
|
|
291255
|
+
navigableItems
|
|
291256
|
+
},
|
|
291257
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AppContent, {
|
|
291258
|
+
focusIndex,
|
|
291259
|
+
showSessionsDialog,
|
|
291260
|
+
setShowSessionsDialog,
|
|
291261
|
+
showShortcutsDialog,
|
|
291262
|
+
setShowShortcutsDialog,
|
|
291263
|
+
showThemeDialog,
|
|
291264
|
+
setShowThemeDialog,
|
|
291265
|
+
showAuthDialog,
|
|
291266
|
+
setShowAuthDialog,
|
|
291267
|
+
showPentestDialog,
|
|
291268
|
+
setShowPentestDialog,
|
|
291269
|
+
pendingPentestFlags,
|
|
291270
|
+
setPendingPentestFlags,
|
|
291271
|
+
cwd,
|
|
291272
|
+
setCtrlCPressTime,
|
|
291273
|
+
showExitWarning,
|
|
291274
|
+
setShowExitWarning,
|
|
291275
|
+
inputKey,
|
|
291276
|
+
setInputKey
|
|
291277
|
+
}, undefined, false, undefined, this)
|
|
290999
291278
|
}, undefined, false, undefined, this)
|
|
291000
291279
|
}, undefined, false, undefined, this)
|
|
291001
291280
|
}, undefined, false, undefined, this)
|
|
291002
291281
|
}, undefined, false, undefined, this)
|
|
291003
291282
|
}, undefined, false, undefined, this)
|
|
291004
|
-
|
|
291005
|
-
}, undefined,
|
|
291283
|
+
]
|
|
291284
|
+
}, undefined, true, undefined, this)
|
|
291006
291285
|
}, undefined, false, undefined, this)
|
|
291007
291286
|
}, undefined, false, undefined, this)
|
|
291008
291287
|
}, undefined, false, undefined, this);
|
|
@@ -291034,14 +291313,14 @@ function AppContent({
|
|
|
291034
291313
|
const { toast } = useToast();
|
|
291035
291314
|
const { refocusPrompt } = useFocus();
|
|
291036
291315
|
const { setExternalDialogOpen } = useDialog();
|
|
291037
|
-
|
|
291316
|
+
import_react90.useEffect(() => {
|
|
291038
291317
|
checkForUpdate().then(({ updateAvailable, currentVersion, latestVersion }) => {
|
|
291039
291318
|
if (!updateAvailable)
|
|
291040
291319
|
return;
|
|
291041
291320
|
toast(`Update available: v${currentVersion} → v${latestVersion}. Run: pensar upgrade`, "warn", 8000);
|
|
291042
291321
|
});
|
|
291043
291322
|
}, []);
|
|
291044
|
-
|
|
291323
|
+
import_react90.useEffect(() => {
|
|
291045
291324
|
if (route.data.type !== "base")
|
|
291046
291325
|
return;
|
|
291047
291326
|
if (!config3.data.responsibleUseAccepted && route.data.path !== "disclosure") {
|
|
@@ -291050,12 +291329,12 @@ function AppContent({
|
|
|
291050
291329
|
route.navigate({ type: "base", path: "providers" });
|
|
291051
291330
|
}
|
|
291052
291331
|
}, [config3.data.responsibleUseAccepted, route.data]);
|
|
291053
|
-
|
|
291332
|
+
import_react90.useEffect(() => {
|
|
291054
291333
|
if (showThemeDialog || showAuthDialog || showPentestDialog) {
|
|
291055
291334
|
setExternalDialogOpen(true);
|
|
291056
291335
|
}
|
|
291057
291336
|
}, [showThemeDialog, showAuthDialog, showPentestDialog]);
|
|
291058
|
-
|
|
291337
|
+
import_react90.useEffect(() => {
|
|
291059
291338
|
if (showExitWarning) {
|
|
291060
291339
|
const timer = setTimeout(() => {
|
|
291061
291340
|
setShowExitWarning(false);
|
|
@@ -291287,18 +291566,21 @@ async function main2() {
|
|
|
291287
291566
|
const { copyToClipboard } = createClipboardManager(renderer);
|
|
291288
291567
|
setupAutoCopy(renderer, copyToClipboard);
|
|
291289
291568
|
const cleanup = () => {
|
|
291569
|
+
cleanupTerminalFocusMode();
|
|
291290
291570
|
renderer.destroy();
|
|
291291
291571
|
process.exit(0);
|
|
291292
291572
|
};
|
|
291293
291573
|
process.on("SIGINT", cleanup);
|
|
291294
291574
|
process.on("SIGTERM", cleanup);
|
|
291295
291575
|
process.on("uncaughtException", (err) => {
|
|
291576
|
+
cleanupTerminalFocusMode();
|
|
291296
291577
|
renderer.destroy();
|
|
291297
291578
|
console.error("Uncaught exception:", err);
|
|
291298
291579
|
writeErrorLog(err, "UNCAUGHT");
|
|
291299
291580
|
process.exit(1);
|
|
291300
291581
|
});
|
|
291301
291582
|
process.on("unhandledRejection", (reason) => {
|
|
291583
|
+
cleanupTerminalFocusMode();
|
|
291302
291584
|
renderer.destroy();
|
|
291303
291585
|
console.error("Unhandled rejection:", reason);
|
|
291304
291586
|
writeErrorLog(reason, "UNHANDLED_REJECTION");
|
package/package.json
CHANGED
|
@@ -66,6 +66,18 @@ export function detectInstallMethod(): InstallMethod {
|
|
|
66
66
|
return "npm";
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// Determine if we're running as a compiled binary vs via an interpreter.
|
|
70
|
+
// Compiled Bun binaries have process.execPath pointing to the binary itself
|
|
71
|
+
// (e.g. ~/.local/bin/pensar), while interpreted scripts have it pointing to
|
|
72
|
+
// the runtime (e.g. ~/.bun/bin/bun or /usr/local/bin/node).
|
|
73
|
+
const execName = execPath.split("/").pop()?.replace(/\.exe$/, "") ?? "";
|
|
74
|
+
const isInterpreter =
|
|
75
|
+
execName === "bun" || execName === "node" || execName === "bun-debug";
|
|
76
|
+
|
|
77
|
+
if (!isInterpreter) {
|
|
78
|
+
return "binary";
|
|
79
|
+
}
|
|
80
|
+
|
|
69
81
|
const npmCheck = spawnSync(
|
|
70
82
|
"npm",
|
|
71
83
|
["list", "-g", "@pensar/apex", "--depth=0"],
|
|
@@ -177,7 +177,7 @@ describe("detectInstallMethod", () => {
|
|
|
177
177
|
expect(detectInstallMethod()).toBe("npm");
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
it("detects npm via spawnSync fallback when
|
|
180
|
+
it("detects npm via spawnSync fallback when running under interpreter", async () => {
|
|
181
181
|
Object.defineProperty(process, "execPath", {
|
|
182
182
|
value: "/usr/local/bin/node",
|
|
183
183
|
writable: true,
|
|
@@ -203,7 +203,7 @@ describe("detectInstallMethod", () => {
|
|
|
203
203
|
);
|
|
204
204
|
});
|
|
205
205
|
|
|
206
|
-
it("returns binary when all heuristics fail", async () => {
|
|
206
|
+
it("returns binary when all heuristics fail under interpreter", async () => {
|
|
207
207
|
Object.defineProperty(process, "execPath", {
|
|
208
208
|
value: "/usr/local/bin/node",
|
|
209
209
|
writable: true,
|
|
@@ -223,6 +223,38 @@ describe("detectInstallMethod", () => {
|
|
|
223
223
|
|
|
224
224
|
expect(detectInstallMethod()).toBe("binary");
|
|
225
225
|
});
|
|
226
|
+
|
|
227
|
+
it("returns binary for compiled binary even when npm global package exists", async () => {
|
|
228
|
+
Object.defineProperty(process, "execPath", {
|
|
229
|
+
value: "/home/user/.local/bin/pensar",
|
|
230
|
+
writable: true,
|
|
231
|
+
});
|
|
232
|
+
process.argv[1] = "upgrade";
|
|
233
|
+
|
|
234
|
+
const { spawnSync } = await import("child_process");
|
|
235
|
+
const mockedSpawnSync = vi.mocked(spawnSync);
|
|
236
|
+
mockedSpawnSync.mockReturnValue({
|
|
237
|
+
status: 0,
|
|
238
|
+
stdout: "└── @pensar/apex@0.1.0",
|
|
239
|
+
stderr: "",
|
|
240
|
+
pid: 0,
|
|
241
|
+
output: [],
|
|
242
|
+
signal: null,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect(detectInstallMethod()).toBe("binary");
|
|
246
|
+
expect(mockedSpawnSync).not.toHaveBeenCalled();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("returns binary for compiled binary without npm fallback", () => {
|
|
250
|
+
Object.defineProperty(process, "execPath", {
|
|
251
|
+
value: "/usr/local/bin/pensar",
|
|
252
|
+
writable: true,
|
|
253
|
+
});
|
|
254
|
+
process.argv[1] = "uninstall";
|
|
255
|
+
|
|
256
|
+
expect(detectInstallMethod()).toBe("binary");
|
|
257
|
+
});
|
|
226
258
|
});
|
|
227
259
|
|
|
228
260
|
// ---------------------------------------------------------------------------
|