@pensar/apex 0.0.77 → 0.0.78-canary.224df24d

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.
Files changed (2) hide show
  1. package/build/index.js +2817 -372
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -31716,6 +31716,55 @@ var init_openrouter = __esm(() => {
31716
31716
  ];
31717
31717
  });
31718
31718
 
31719
+ // src/core/ai/models/pensar.ts
31720
+ var PENSAR_MODELS;
31721
+ var init_pensar = __esm(() => {
31722
+ PENSAR_MODELS = [
31723
+ {
31724
+ id: "pensar:anthropic.claude-sonnet-4-5-20250929-v1:0",
31725
+ name: "Claude Sonnet 4.5 (Pensar)",
31726
+ provider: "pensar",
31727
+ contextLength: 200000
31728
+ },
31729
+ {
31730
+ id: "pensar:anthropic.claude-opus-4-1-20250805-v1:0",
31731
+ name: "Claude Opus 4.1 (Pensar)",
31732
+ provider: "pensar",
31733
+ contextLength: 200000
31734
+ },
31735
+ {
31736
+ id: "pensar:anthropic.claude-haiku-4-5-20251001-v1:0",
31737
+ name: "Claude Haiku 4.5 (Pensar)",
31738
+ provider: "pensar",
31739
+ contextLength: 200000
31740
+ },
31741
+ {
31742
+ id: "pensar:anthropic.claude-sonnet-4-20250514-v1:0",
31743
+ name: "Claude Sonnet 4 (Pensar)",
31744
+ provider: "pensar",
31745
+ contextLength: 200000
31746
+ },
31747
+ {
31748
+ id: "pensar:anthropic.claude-opus-4-20250514-v1:0",
31749
+ name: "Claude Opus 4 (Pensar)",
31750
+ provider: "pensar",
31751
+ contextLength: 200000
31752
+ },
31753
+ {
31754
+ id: "pensar:anthropic.claude-3-7-sonnet-20250219-v1:0",
31755
+ name: "Claude 3.7 Sonnet (Pensar)",
31756
+ provider: "pensar",
31757
+ contextLength: 200000
31758
+ },
31759
+ {
31760
+ id: "pensar:anthropic.claude-3-5-haiku-20241022-v1:0",
31761
+ name: "Claude 3.5 Haiku (Pensar)",
31762
+ provider: "pensar",
31763
+ contextLength: 200000
31764
+ }
31765
+ ];
31766
+ });
31767
+
31719
31768
  // src/core/ai/models/index.ts
31720
31769
  function getModelInfo(model) {
31721
31770
  return AVAILABLE_MODELS.find((m2) => m2.id === model) ?? {
@@ -31730,14 +31779,231 @@ var init_models = __esm(() => {
31730
31779
  init_openai();
31731
31780
  init_bedrock();
31732
31781
  init_openrouter();
31782
+ init_pensar();
31733
31783
  AVAILABLE_MODELS = [
31734
31784
  ...ANTHROPIC_MODELS,
31735
31785
  ...OPENAI_MODELS,
31736
31786
  ...BEDROCK_MODELS,
31737
- ...OPENROUTER_MODELS
31787
+ ...OPENROUTER_MODELS,
31788
+ ...PENSAR_MODELS
31738
31789
  ];
31739
31790
  });
31740
31791
 
31792
+ // package.json
31793
+ var package_default2;
31794
+ var init_package = __esm(() => {
31795
+ package_default2 = {
31796
+ name: "@pensar/apex",
31797
+ version: "0.0.78-canary.224df24d",
31798
+ description: "AI-powered penetration testing CLI tool with terminal UI",
31799
+ module: "src/tui/index.tsx",
31800
+ main: "build/index.js",
31801
+ type: "module",
31802
+ repository: {
31803
+ type: "git",
31804
+ url: "https://github.com/pensarai/apex.git"
31805
+ },
31806
+ bin: {
31807
+ pensar: "./bin/pensar.js"
31808
+ },
31809
+ files: [
31810
+ "build",
31811
+ "bin",
31812
+ "src/core/installation",
31813
+ "pensar.svg",
31814
+ "LICENSE"
31815
+ ],
31816
+ scripts: {
31817
+ build: "bun build src/tui/index.tsx --outdir build --target node --format esm --external sharp",
31818
+ "generate:ascii": "bun run scripts/generate-ascii-art.ts",
31819
+ "generate:models": "bun run scripts/generate-models.ts",
31820
+ "build:binary": "bun run generate:ascii && bun build src/cli.ts --compile --outfile pensar",
31821
+ "build:binary:macos-arm64": "bun build src/cli.ts --compile --target=bun-darwin-arm64 --outfile dist/pensar-darwin-arm64",
31822
+ "build:binary:macos-x64": "bun build src/cli.ts --compile --target=bun-darwin-x64 --outfile dist/pensar-darwin-x64",
31823
+ "build:binary:linux-x64": "bun build src/cli.ts --compile --target=bun-linux-x64 --outfile dist/pensar-linux-x64",
31824
+ "build:binary:linux-arm64": "bun build src/cli.ts --compile --target=bun-linux-arm64 --outfile dist/pensar-linux-arm64",
31825
+ "build:binaries": "bun run generate:ascii && mkdir -p dist && bun run build:binary:macos-arm64 && bun run build:binary:macos-x64 && bun run build:binary:linux-x64 && bun run build:binary:linux-arm64",
31826
+ dev: "bun run scripts/watch.ts",
31827
+ "dev:debug": "SHOW_CONSOLE=true bun run scripts/watch.ts",
31828
+ start: "bun run src/tui/index.tsx",
31829
+ pensar: "node bin/pensar.js",
31830
+ tsc: "tsc --noEmit",
31831
+ "daytona-benchmark": "bun run scripts/daytona-benchmark.ts",
31832
+ "local-benchmark": "bun run scripts/local-benchmark.ts",
31833
+ test: "vitest run",
31834
+ "test:watch": "vitest",
31835
+ lint: "eslint src/",
31836
+ format: "prettier --write .",
31837
+ "format:check": "prettier --check .",
31838
+ prepublishOnly: "npm run build"
31839
+ },
31840
+ keywords: [
31841
+ "penetration-testing",
31842
+ "security",
31843
+ "pentesting",
31844
+ "ai",
31845
+ "cli",
31846
+ "terminal",
31847
+ "tui"
31848
+ ],
31849
+ author: "Pensar",
31850
+ license: "MIT",
31851
+ engines: {
31852
+ node: ">=18.0.0",
31853
+ bun: ">=1.0.0"
31854
+ },
31855
+ devDependencies: {
31856
+ "@eslint/js": "^10.0.1",
31857
+ "@playwright/mcp": "^0.0.54",
31858
+ "@types/bun": "^1.3.0",
31859
+ "@types/mailparser": "^3.4.6",
31860
+ "@types/react": "^19.2.6",
31861
+ "@typescript-eslint/eslint-plugin": "^8.55.0",
31862
+ "@typescript-eslint/parser": "^8.55.0",
31863
+ dotenv: "^17.2.3",
31864
+ eslint: "^10.0.0",
31865
+ "eslint-config-prettier": "^10.1.8",
31866
+ "eslint-plugin-unused-imports": "^4.4.1",
31867
+ prettier: "^3.8.1",
31868
+ "typescript-eslint": "^8.55.0",
31869
+ vitest: "^2.1.8"
31870
+ },
31871
+ peerDependencies: {
31872
+ typescript: "^5.9.3"
31873
+ },
31874
+ dependencies: {
31875
+ "@ai-sdk/amazon-bedrock": "^4.0.69",
31876
+ "@ai-sdk/anthropic": "^3.0.50",
31877
+ "@ai-sdk/openai": "^3.0.37",
31878
+ "@daytonaio/sdk": "^0.112.1",
31879
+ "@googleapis/gmail": "^16.1.1",
31880
+ "@microsoft/microsoft-graph-client": "^3.0.7",
31881
+ "@modelcontextprotocol/sdk": "^1.0.0",
31882
+ "@openrouter/ai-sdk-provider": "^2.2.3",
31883
+ "@opentui/core": "^0.1.80",
31884
+ "@opentui/react": "^0.1.80",
31885
+ ai: "^6.0.105",
31886
+ glob: "^13.0.0",
31887
+ "google-auth-library": "^10.6.1",
31888
+ ignore: "^7.0.5",
31889
+ imapflow: "^1.2.10",
31890
+ mailparser: "^3.9.3",
31891
+ marked: "^16.4.0",
31892
+ nanoid: "^5.1.6",
31893
+ "p-limit": "^7.2.0",
31894
+ react: "^19.2.0",
31895
+ sharp: "^0.34.4",
31896
+ yaml: "^2.8.2",
31897
+ zod: "^3.25.76"
31898
+ },
31899
+ packageManager: "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
31900
+ };
31901
+ });
31902
+
31903
+ // src/core/installation/index.ts
31904
+ function getCurrentVersion() {
31905
+ return package_default2.version;
31906
+ }
31907
+ function isNewerVersion(current, latest) {
31908
+ const parse2 = (v2) => v2.split(".").map((n) => parseInt(n, 10) || 0);
31909
+ const c = parse2(current);
31910
+ const l = parse2(latest);
31911
+ for (let i = 0;i < Math.max(c.length, l.length); i++) {
31912
+ const cv = c[i] ?? 0;
31913
+ const lv = l[i] ?? 0;
31914
+ if (lv > cv)
31915
+ return true;
31916
+ if (lv < cv)
31917
+ return false;
31918
+ }
31919
+ return false;
31920
+ }
31921
+ async function getLatestVersion() {
31922
+ const res = await fetch("https://registry.npmjs.org/@pensar/apex/latest");
31923
+ if (!res.ok)
31924
+ throw new Error(`Failed to fetch latest version: ${res.statusText}`);
31925
+ const data = await res.json();
31926
+ return String(data.version);
31927
+ }
31928
+ async function checkForUpdate() {
31929
+ const currentVersion = getCurrentVersion();
31930
+ let latestVersion;
31931
+ try {
31932
+ latestVersion = await getLatestVersion();
31933
+ } catch {
31934
+ return {
31935
+ updateAvailable: false,
31936
+ currentVersion,
31937
+ latestVersion: currentVersion
31938
+ };
31939
+ }
31940
+ return {
31941
+ updateAvailable: isNewerVersion(currentVersion, latestVersion),
31942
+ currentVersion,
31943
+ latestVersion
31944
+ };
31945
+ }
31946
+ var init_installation = __esm(() => {
31947
+ init_package();
31948
+ });
31949
+
31950
+ // src/core/config/config.ts
31951
+ import os2 from "os";
31952
+ import path2 from "path";
31953
+ import fs2 from "fs/promises";
31954
+ async function init() {
31955
+ const folder = path2.join(os2.homedir(), ".pensar");
31956
+ const file = path2.join(folder, "config.json");
31957
+ const dirExists = await fs2.access(folder).then(() => true).catch(() => false);
31958
+ if (!dirExists) {
31959
+ await fs2.mkdir(folder, { recursive: true });
31960
+ }
31961
+ const fileExists = await fs2.access(file).then(() => true).catch(() => false);
31962
+ if (!fileExists) {
31963
+ await fs2.writeFile(file, JSON.stringify(DEFAULT_CONFIG));
31964
+ }
31965
+ const version = getCurrentVersion();
31966
+ return { ...DEFAULT_CONFIG, version };
31967
+ }
31968
+ async function get() {
31969
+ const folder = path2.join(os2.homedir(), ".pensar");
31970
+ const file = path2.join(folder, "config.json");
31971
+ const exists = await fs2.access(file).then(() => true).catch(() => false);
31972
+ if (!exists) {
31973
+ return await init();
31974
+ }
31975
+ const config = await fs2.readFile(file, "utf8");
31976
+ const parsedConfig = JSON.parse(config);
31977
+ const version = getCurrentVersion();
31978
+ return {
31979
+ ...parsedConfig,
31980
+ version,
31981
+ openAiAPIKey: process.env.OPENAI_API_KEY ?? parsedConfig.openAiAPIKey,
31982
+ anthropicAPIKey: process.env.ANTHROPIC_API_KEY ?? parsedConfig.anthropicAPIKey,
31983
+ openRouterAPIKey: process.env.OPENROUTER_API_KEY ?? parsedConfig.openRouterAPIKey,
31984
+ bedrockAPIKey: process.env.BEDROCK_API_KEY ?? parsedConfig.bedrockAPIKey,
31985
+ pensarAPIKey: process.env.PENSAR_API_KEY ?? parsedConfig.pensarAPIKey,
31986
+ pensarApiUrl: process.env.PENSAR_API_URL ?? parsedConfig.pensarApiUrl,
31987
+ daytonaAPIKey: process.env.DAYTONA_API_KEY ?? parsedConfig.daytonaAPIKey,
31988
+ daytonaOrgId: process.env.DAYTONA_ORG_ID ?? parsedConfig.daytonaOrgId,
31989
+ runloopAPIKey: process.env.RUNLOOP_API_KEY ?? parsedConfig.runloopAPIKey
31990
+ };
31991
+ }
31992
+ async function update(config) {
31993
+ const currentConfig = await get();
31994
+ const newConfig = { ...currentConfig, ...config };
31995
+ const folder = path2.join(os2.homedir(), ".pensar");
31996
+ const file = path2.join(folder, "config.json");
31997
+ await fs2.writeFile(file, JSON.stringify(newConfig));
31998
+ }
31999
+ var DEFAULT_CONFIG;
32000
+ var init_config = __esm(() => {
32001
+ init_installation();
32002
+ DEFAULT_CONFIG = {
32003
+ responsibleUseAccepted: false
32004
+ };
32005
+ });
32006
+
31741
32007
  // node_modules/zod/v3/helpers/util.js
31742
32008
  var util3, objectUtil, ZodParsedType, getParsedType = (data) => {
31743
32009
  const t2 = typeof data;
@@ -36239,6 +36505,119 @@ var init_toolset = __esm(() => {
36239
36505
  });
36240
36506
  });
36241
36507
 
36508
+ // src/core/config/index.ts
36509
+ var config;
36510
+ var init_config2 = __esm(() => {
36511
+ init_config();
36512
+ config = {
36513
+ get,
36514
+ init,
36515
+ update
36516
+ };
36517
+ });
36518
+
36519
+ // src/core/api/constants.ts
36520
+ var exports_constants = {};
36521
+ __export(exports_constants, {
36522
+ getPensarConsoleUrl: () => getPensarConsoleUrl,
36523
+ getPensarApiUrl: () => getPensarApiUrl,
36524
+ PENSAR_CONSOLE_BASE_URL: () => PENSAR_CONSOLE_BASE_URL,
36525
+ PENSAR_API_BASE_URL: () => PENSAR_API_BASE_URL
36526
+ });
36527
+ function getPensarApiUrl(config2) {
36528
+ return config2?.pensarApiUrl || process.env.PENSAR_API_URL || PENSAR_API_BASE_URL;
36529
+ }
36530
+ function getPensarConsoleUrl() {
36531
+ return process.env.PENSAR_CONSOLE_URL || PENSAR_CONSOLE_BASE_URL;
36532
+ }
36533
+ var PENSAR_API_BASE_URL = "https://api.console.pensar.dev", PENSAR_CONSOLE_BASE_URL = "https://console.pensar.dev";
36534
+
36535
+ // src/core/api/tokenRefresh.ts
36536
+ function decodeJwtPayload(token) {
36537
+ try {
36538
+ const parts = token.split(".");
36539
+ if (parts.length !== 3)
36540
+ return null;
36541
+ const payload = Buffer.from(parts[1], "base64url").toString("utf-8");
36542
+ return JSON.parse(payload);
36543
+ } catch {
36544
+ return null;
36545
+ }
36546
+ }
36547
+ function isTokenExpired(token, bufferSeconds = 60) {
36548
+ const payload = decodeJwtPayload(token);
36549
+ if (!payload || typeof payload.exp !== "number")
36550
+ return true;
36551
+ const nowSeconds = Math.floor(Date.now() / 1000);
36552
+ return payload.exp - nowSeconds < bufferSeconds;
36553
+ }
36554
+ async function refreshAccessToken(clientId, refreshToken) {
36555
+ try {
36556
+ const response = await fetch("https://api.workos.com/user_management/authenticate", {
36557
+ method: "POST",
36558
+ headers: { "Content-Type": "application/json" },
36559
+ body: JSON.stringify({
36560
+ client_id: clientId,
36561
+ grant_type: "refresh_token",
36562
+ refresh_token: refreshToken
36563
+ })
36564
+ });
36565
+ if (!response.ok) {
36566
+ console.error(`[pensar] Token refresh failed: ${response.status} ${response.statusText}`);
36567
+ return null;
36568
+ }
36569
+ const data = await response.json();
36570
+ await config.update({
36571
+ accessToken: data.access_token,
36572
+ refreshToken: data.refresh_token
36573
+ });
36574
+ return data.access_token;
36575
+ } catch (err) {
36576
+ console.error("[pensar] Token refresh error:", err);
36577
+ return null;
36578
+ }
36579
+ }
36580
+ async function ensureValidToken(cfg) {
36581
+ if (cfg.accessToken) {
36582
+ if (!isTokenExpired(cfg.accessToken)) {
36583
+ return { token: cfg.accessToken, type: "workos" };
36584
+ }
36585
+ if (cfg.refreshToken) {
36586
+ const clientId = await fetchWorkOSClientId(cfg);
36587
+ if (clientId) {
36588
+ const newToken = await refreshAccessToken(clientId, cfg.refreshToken);
36589
+ if (newToken) {
36590
+ return { token: newToken, type: "workos" };
36591
+ }
36592
+ }
36593
+ }
36594
+ }
36595
+ if (cfg.pensarAPIKey) {
36596
+ return { token: cfg.pensarAPIKey, type: "legacy" };
36597
+ }
36598
+ return null;
36599
+ }
36600
+ async function fetchWorkOSClientId(cfg) {
36601
+ if (cachedClientId)
36602
+ return cachedClientId;
36603
+ try {
36604
+ const { getPensarApiUrl: getPensarApiUrl2 } = await Promise.resolve().then(() => exports_constants);
36605
+ const apiUrl = getPensarApiUrl2(cfg);
36606
+ const response = await fetch(`${apiUrl}/api/cli/config`);
36607
+ if (!response.ok)
36608
+ return null;
36609
+ const data = await response.json();
36610
+ cachedClientId = data.workosClientId;
36611
+ return cachedClientId;
36612
+ } catch {
36613
+ return null;
36614
+ }
36615
+ }
36616
+ var cachedClientId = null;
36617
+ var init_tokenRefresh = __esm(() => {
36618
+ init_config2();
36619
+ });
36620
+
36242
36621
  // node_modules/@ai-sdk/provider/dist/index.mjs
36243
36622
  function getErrorMessage(error) {
36244
36623
  if (error == null) {
@@ -60666,7 +61045,7 @@ Run shell commands for reconnaissance. Use for: dig, curl, nmap, whois, and all
60666
61045
  Record a discovered asset to the session's assets directory. Use extensively — every significant discovery should be documented.
60667
61046
 
60668
61047
  ## create_attack_surface_report
60669
- Submit the final structured report. Call this ONCE at the very end with complete results.
61048
+ Submit the final structured report. Call this ONCE at the very end with complete results. This ends the run.
60670
61049
 
60671
61050
  ## Browser tools
60672
61051
  - \`browser_navigate\` — Load a URL in the browser
@@ -80023,7 +80402,345 @@ var init_dist9 = __esm(() => {
80023
80402
  anthropic = createAnthropic();
80024
80403
  });
80025
80404
 
80405
+ // src/core/ai/providers/pensarFormatters.ts
80406
+ function convertToBedrockFormat(modelId, options) {
80407
+ if (modelId.includes("anthropic.claude")) {
80408
+ return convertToAnthropicFormat(options);
80409
+ }
80410
+ return convertToAnthropicFormat(options);
80411
+ }
80412
+ function convertToAnthropicFormat(options) {
80413
+ const messages = [];
80414
+ let systemPrompt;
80415
+ if (options.prompt) {
80416
+ for (const part of options.prompt) {
80417
+ if (part.role === "system") {
80418
+ systemPrompt = part.content;
80419
+ } else if (part.role === "user") {
80420
+ const content = part.content.map((c) => {
80421
+ if (c.type === "text")
80422
+ return c.text;
80423
+ if (c.type === "file")
80424
+ return "[file]";
80425
+ return "";
80426
+ }).join("");
80427
+ messages.push({ role: "user", content });
80428
+ } else if (part.role === "assistant") {
80429
+ const assistantContent = part.content;
80430
+ const hasToolCalls = assistantContent.some((c) => c.type === "tool-call");
80431
+ if (hasToolCalls) {
80432
+ const content = [];
80433
+ for (const c of assistantContent) {
80434
+ if (c.type === "text" && c.text) {
80435
+ content.push({ type: "text", text: c.text });
80436
+ } else if (c.type === "tool-call") {
80437
+ const parsedInput = typeof c.input === "string" ? JSON.parse(c.input) : c.input;
80438
+ content.push({
80439
+ type: "tool_use",
80440
+ id: c.toolCallId,
80441
+ name: c.toolName,
80442
+ input: parsedInput
80443
+ });
80444
+ }
80445
+ }
80446
+ messages.push({ role: "assistant", content });
80447
+ } else {
80448
+ const text2 = assistantContent.map((c) => c.type === "text" ? c.text : "").join("");
80449
+ messages.push({ role: "assistant", content: text2 });
80450
+ }
80451
+ } else if (part.role === "tool") {
80452
+ const toolResults = part.content.map((c) => {
80453
+ let resultContent;
80454
+ if (c.output.type === "text" || c.output.type === "error-text") {
80455
+ resultContent = c.output.value;
80456
+ } else {
80457
+ resultContent = JSON.stringify(c.output.value);
80458
+ }
80459
+ return {
80460
+ type: "tool_result",
80461
+ tool_use_id: c.toolCallId,
80462
+ content: resultContent
80463
+ };
80464
+ });
80465
+ messages.push({ role: "user", content: toolResults });
80466
+ }
80467
+ }
80468
+ }
80469
+ if (options.responseFormat?.type === "json") {
80470
+ const jsonInstruction = options.responseFormat.schema ? `
80471
+
80472
+ You MUST respond with ONLY valid JSON matching this schema (no markdown, no explanation, no code fences):
80473
+ ${JSON.stringify(options.responseFormat.schema)}` : `
80474
+
80475
+ You MUST respond with ONLY valid JSON (no markdown, no explanation, no code fences).`;
80476
+ systemPrompt = (systemPrompt || "") + jsonInstruction;
80477
+ }
80478
+ const body = {
80479
+ anthropic_version: "bedrock-2023-05-31",
80480
+ messages,
80481
+ max_tokens: options.maxOutputTokens ?? 4096
80482
+ };
80483
+ if (systemPrompt) {
80484
+ body.system = systemPrompt;
80485
+ }
80486
+ if (options.temperature != null) {
80487
+ body.temperature = options.temperature;
80488
+ }
80489
+ if (options.topP != null) {
80490
+ body.top_p = options.topP;
80491
+ }
80492
+ if (options.stopSequences && options.stopSequences.length > 0) {
80493
+ body.stop_sequences = options.stopSequences;
80494
+ }
80495
+ if (options.tools && options.tools.length > 0) {
80496
+ body.tools = options.tools.filter((tool2) => tool2.type === "function").map((tool2) => ({
80497
+ name: tool2.name,
80498
+ description: tool2.description,
80499
+ input_schema: tool2.inputSchema
80500
+ }));
80501
+ }
80502
+ if (options.toolChoice) {
80503
+ if (options.toolChoice.type === "auto") {
80504
+ body.tool_choice = { type: "auto" };
80505
+ } else if (options.toolChoice.type === "required") {
80506
+ body.tool_choice = { type: "any" };
80507
+ } else if (options.toolChoice.type === "none") {
80508
+ delete body.tools;
80509
+ } else if (options.toolChoice.type === "tool") {
80510
+ body.tool_choice = {
80511
+ type: "tool",
80512
+ name: options.toolChoice.toolName
80513
+ };
80514
+ }
80515
+ }
80516
+ return body;
80517
+ }
80518
+ function parseBedrockResponse(modelId, response, usage) {
80519
+ if (modelId.includes("anthropic.claude")) {
80520
+ return parseAnthropicResponse(response, usage);
80521
+ }
80522
+ return parseAnthropicResponse(response, usage);
80523
+ }
80524
+ function parseAnthropicResponse(response, usage) {
80525
+ const rawContent = response.content;
80526
+ const content = [];
80527
+ if (rawContent) {
80528
+ for (const block of rawContent) {
80529
+ if (block.type === "text") {
80530
+ content.push({
80531
+ type: "text",
80532
+ text: block.text
80533
+ });
80534
+ } else if (block.type === "tool_use") {
80535
+ content.push({
80536
+ type: "tool-call",
80537
+ toolCallId: block.id,
80538
+ toolName: block.name,
80539
+ input: typeof block.input === "string" ? block.input : JSON.stringify(block.input)
80540
+ });
80541
+ }
80542
+ }
80543
+ }
80544
+ const stopReason = response.stop_reason;
80545
+ let finishReason = "unknown";
80546
+ switch (stopReason) {
80547
+ case "end_turn":
80548
+ finishReason = "stop";
80549
+ break;
80550
+ case "tool_use":
80551
+ finishReason = "tool-calls";
80552
+ break;
80553
+ case "max_tokens":
80554
+ finishReason = "length";
80555
+ break;
80556
+ case "stop_sequence":
80557
+ finishReason = "stop";
80558
+ break;
80559
+ }
80560
+ const respUsage = response.usage;
80561
+ const finalUsage = {
80562
+ inputTokens: respUsage?.input_tokens ?? usage.inputTokens,
80563
+ outputTokens: respUsage?.output_tokens ?? usage.outputTokens,
80564
+ totalTokens: (respUsage?.input_tokens ?? usage.inputTokens) + (respUsage?.output_tokens ?? usage.outputTokens)
80565
+ };
80566
+ return { content, finishReason, usage: finalUsage };
80567
+ }
80568
+
80569
+ // src/core/ai/providers/pensar.ts
80570
+ function log(...args) {
80571
+ if (DEBUG)
80572
+ console.log("[pensar]", ...args);
80573
+ }
80574
+ function logError(...args) {
80575
+ console.error("[pensar]", ...args);
80576
+ }
80577
+ function createPensarModel(bedrockModelId, config3) {
80578
+ const modelId = `pensar:${bedrockModelId}`;
80579
+ async function buildHeaders() {
80580
+ const headers = {
80581
+ "Content-Type": "application/json"
80582
+ };
80583
+ if (config3.getToken) {
80584
+ const result = await config3.getToken();
80585
+ if (!result) {
80586
+ throw new Error("Pensar authentication failed. Run /auth to reconnect.");
80587
+ }
80588
+ headers.Authorization = `Bearer ${result.token}`;
80589
+ if (result.type === "workos" && config3.workspaceId) {
80590
+ headers["X-Workspace-Id"] = config3.workspaceId;
80591
+ }
80592
+ } else {
80593
+ headers.Authorization = `Bearer ${config3.apiKey}`;
80594
+ if (config3.workspaceId && !config3.apiKey.startsWith("sk-")) {
80595
+ headers["X-Workspace-Id"] = config3.workspaceId;
80596
+ }
80597
+ }
80598
+ return headers;
80599
+ }
80600
+ const model = {
80601
+ specificationVersion: "v2",
80602
+ provider: "pensar",
80603
+ modelId,
80604
+ supportedUrls: {},
80605
+ async doGenerate(options) {
80606
+ const body = convertToBedrockFormat(bedrockModelId, options);
80607
+ const url2 = `${config3.baseUrl}/bedrock/invoke`;
80608
+ log(`doGenerate → ${bedrockModelId}`);
80609
+ log(` URL: ${url2}`);
80610
+ log(` messages: ${body.messages?.length ?? 0}, tools: ${body.tools?.length ?? 0}`);
80611
+ const startTime = Date.now();
80612
+ const headers = await buildHeaders();
80613
+ const response = await fetch(url2, {
80614
+ method: "POST",
80615
+ headers,
80616
+ signal: options.abortSignal,
80617
+ body: JSON.stringify({
80618
+ modelId: bedrockModelId,
80619
+ body,
80620
+ stream: false
80621
+ })
80622
+ });
80623
+ log(` response: ${response.status} (${Date.now() - startTime}ms)`);
80624
+ if (!response.ok) {
80625
+ const errorBody = await response.text();
80626
+ let errorMessage;
80627
+ try {
80628
+ const parsed2 = JSON.parse(errorBody);
80629
+ errorMessage = parsed2.error || parsed2.message || errorBody;
80630
+ } catch {
80631
+ errorMessage = errorBody;
80632
+ }
80633
+ logError(` FAILED ${response.status}: ${errorMessage}`);
80634
+ if (response.status === 402) {
80635
+ throw new Error(`Insufficient Pensar credits: ${errorMessage}. ` + `Top up at https://console.pensar.dev`);
80636
+ }
80637
+ throw new Error(`Pensar API error (${response.status}): ${errorMessage}`);
80638
+ }
80639
+ const result = await response.json();
80640
+ const usageFromProxy = result.usage ?? {
80641
+ inputTokens: 0,
80642
+ outputTokens: 0
80643
+ };
80644
+ const parsed = parseBedrockResponse(bedrockModelId, result.response, {
80645
+ inputTokens: usageFromProxy.inputTokens,
80646
+ outputTokens: usageFromProxy.outputTokens
80647
+ });
80648
+ log(` finish: ${parsed.finishReason}, content: ${parsed.content.length} parts`);
80649
+ log(` usage: ${parsed.usage.inputTokens}in / ${parsed.usage.outputTokens}out`);
80650
+ if (result.usage?.totalCost != null) {
80651
+ log(` cost: $${result.usage.totalCost.toFixed(6)}`);
80652
+ }
80653
+ return {
80654
+ content: parsed.content,
80655
+ finishReason: parsed.finishReason,
80656
+ usage: parsed.usage,
80657
+ request: {
80658
+ body
80659
+ },
80660
+ response: {
80661
+ body: result
80662
+ },
80663
+ warnings: []
80664
+ };
80665
+ },
80666
+ async doStream(options) {
80667
+ log(`doStream → wrapping doGenerate for ${bedrockModelId}`);
80668
+ const generateResult = await model.doGenerate(options);
80669
+ const parts = [];
80670
+ parts.push({
80671
+ type: "stream-start",
80672
+ warnings: generateResult.warnings
80673
+ });
80674
+ for (const item of generateResult.content) {
80675
+ if (item.type === "text") {
80676
+ const id = `text-${Date.now()}`;
80677
+ parts.push({ type: "text-start", id });
80678
+ parts.push({ type: "text-delta", id, delta: item.text });
80679
+ parts.push({ type: "text-end", id });
80680
+ } else if (item.type === "tool-call") {
80681
+ const id = item.toolCallId;
80682
+ parts.push({
80683
+ type: "tool-input-start",
80684
+ id,
80685
+ toolName: item.toolName
80686
+ });
80687
+ parts.push({
80688
+ type: "tool-input-delta",
80689
+ id,
80690
+ delta: item.input
80691
+ });
80692
+ parts.push({ type: "tool-input-end", id });
80693
+ parts.push({
80694
+ type: "tool-call",
80695
+ toolCallId: id,
80696
+ toolName: item.toolName,
80697
+ input: item.input
80698
+ });
80699
+ }
80700
+ }
80701
+ parts.push({
80702
+ type: "finish",
80703
+ finishReason: generateResult.finishReason,
80704
+ usage: generateResult.usage
80705
+ });
80706
+ const stream2 = new ReadableStream({
80707
+ start(controller) {
80708
+ for (const part of parts) {
80709
+ controller.enqueue(part);
80710
+ }
80711
+ controller.close();
80712
+ }
80713
+ });
80714
+ return {
80715
+ stream: stream2,
80716
+ request: {
80717
+ body: generateResult.request?.body
80718
+ }
80719
+ };
80720
+ }
80721
+ };
80722
+ return model;
80723
+ }
80724
+ var DEBUG;
80725
+ var init_pensar2 = __esm(() => {
80726
+ DEBUG = process.env.PENSAR_DEBUG === "1" || process.env.PENSAR_DEBUG === "true";
80727
+ });
80728
+
80026
80729
  // src/core/ai/utils.ts
80730
+ function buildAuthConfig(cfg) {
80731
+ return {
80732
+ anthropicAPIKey: cfg.anthropicAPIKey ?? undefined,
80733
+ openAiAPIKey: cfg.openAiAPIKey ?? undefined,
80734
+ openRouterAPIKey: cfg.openRouterAPIKey ?? undefined,
80735
+ pensarAPIKey: cfg.pensarAPIKey ?? undefined,
80736
+ pensarApiUrl: cfg.pensarApiUrl ?? undefined,
80737
+ accessToken: cfg.accessToken ?? undefined,
80738
+ refreshToken: cfg.refreshToken ?? undefined,
80739
+ workspaceId: cfg.workspaceId ?? undefined,
80740
+ bedrock: cfg.bedrockAPIKey ? { apiKey: cfg.bedrockAPIKey } : undefined,
80741
+ local: cfg.localModelUrl ? { baseURL: cfg.localModelUrl } : undefined
80742
+ };
80743
+ }
80027
80744
  function getProviderModel(model, authConfig) {
80028
80745
  const { provider } = getModelInfo(model);
80029
80746
  const openAiAPIKey = authConfig?.openAiAPIKey || process.env.OPENAI_API_KEY;
@@ -80068,6 +80785,36 @@ function getProviderModel(model, authConfig) {
80068
80785
  apiKey: anthropicAPIKey
80069
80786
  }).chat(model);
80070
80787
  break;
80788
+ case "pensar": {
80789
+ const pensarApiKey = authConfig?.pensarAPIKey || process.env.PENSAR_API_KEY;
80790
+ const hasWorkOSAuth = !!authConfig?.accessToken;
80791
+ if (!pensarApiKey && !hasWorkOSAuth) {
80792
+ throw new Error("Pensar not configured. Run /auth to connect to Pensar Console.");
80793
+ }
80794
+ const pensarApiUrl = authConfig?.pensarApiUrl || getPensarApiUrl();
80795
+ const bedrockModelId = model.startsWith("pensar:") ? model.slice(7) : model;
80796
+ if (process.env.PENSAR_DEBUG === "1" || process.env.PENSAR_DEBUG === "true") {
80797
+ console.log(`[pensar] getProviderModel: ${model} → bedrock:${bedrockModelId} via ${pensarApiUrl}`);
80798
+ }
80799
+ const modelConfig = {
80800
+ apiKey: pensarApiKey || authConfig?.accessToken || "",
80801
+ baseUrl: pensarApiUrl,
80802
+ workspaceId: authConfig?.workspaceId
80803
+ };
80804
+ if (hasWorkOSAuth) {
80805
+ modelConfig.getToken = async () => {
80806
+ const freshConfig = await config.get();
80807
+ return ensureValidToken({
80808
+ accessToken: freshConfig.accessToken,
80809
+ refreshToken: freshConfig.refreshToken,
80810
+ pensarAPIKey: freshConfig.pensarAPIKey,
80811
+ pensarApiUrl: freshConfig.pensarApiUrl
80812
+ });
80813
+ };
80814
+ }
80815
+ providerModel = createPensarModel(bedrockModelId, modelConfig);
80816
+ break;
80817
+ }
80071
80818
  case "local":
80072
80819
  providerModel = createOpenAI({
80073
80820
  baseURL: localBaseURL,
@@ -80239,6 +80986,9 @@ var init_utils2 = __esm(() => {
80239
80986
  init_dist8();
80240
80987
  init_dist9();
80241
80988
  init_models();
80989
+ init_pensar2();
80990
+ init_tokenRefresh();
80991
+ init_config2();
80242
80992
  init_dist5();
80243
80993
  });
80244
80994
 
@@ -86652,11 +87402,11 @@ var require_core = __commonJS((exports) => {
86652
87402
  Ajv.ValidationError = validation_error_1.default;
86653
87403
  Ajv.MissingRefError = ref_error_1.default;
86654
87404
  exports.default = Ajv;
86655
- function checkOptions(checkOpts, options, msg, log = "error") {
87405
+ function checkOptions(checkOpts, options, msg, log2 = "error") {
86656
87406
  for (const key in checkOpts) {
86657
87407
  const opt = key;
86658
87408
  if (opt in options)
86659
- this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`);
87409
+ this.logger[log2](`${msg}: option ${key}. ${checkOpts[opt]}`);
86660
87410
  }
86661
87411
  }
86662
87412
  function getSchEnv(keyRef) {
@@ -92294,11 +93044,11 @@ var require_core3 = __commonJS((exports) => {
92294
93044
  Ajv.ValidationError = validation_error_1.default;
92295
93045
  Ajv.MissingRefError = ref_error_1.default;
92296
93046
  exports.default = Ajv;
92297
- function checkOptions(checkOpts, options, msg, log = "error") {
93047
+ function checkOptions(checkOpts, options, msg, log2 = "error") {
92298
93048
  for (const key in checkOpts) {
92299
93049
  const opt = key;
92300
93050
  if (opt in options)
92301
- this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`);
93051
+ this.logger[log2](`${msg}: option ${key}. ${checkOpts[opt]}`);
92302
93052
  }
92303
93053
  }
92304
93054
  function getSchEnv(keyRef) {
@@ -95700,6 +96450,20 @@ var init_stdio2 = __esm(() => {
95700
96450
  import { createRequire as createRequire2 } from "module";
95701
96451
  import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync10 } from "fs";
95702
96452
  import { join as join3, dirname as dirname2 } from "path";
96453
+ function transformScriptToFunction(script) {
96454
+ const trimmed = script.trim();
96455
+ const iifeTrailing = /\(\s*\)\s*;?\s*$/.test(trimmed);
96456
+ const iifeLeading = /^\s*\((?:async\s+)?(?:function|\()/.test(trimmed);
96457
+ if (iifeLeading && iifeTrailing) {
96458
+ let extracted = trimmed.replace(/\(\s*\)\s*;?\s*$/, "").trim();
96459
+ if (extracted.startsWith("(") && extracted.endsWith(")")) {
96460
+ extracted = extracted.slice(1, -1).trim();
96461
+ }
96462
+ return extracted;
96463
+ }
96464
+ const isFunction = /^\s*(async\s+)?\(/.test(script) || /^\s*(async\s+)?function\s*\(/.test(script);
96465
+ return isFunction ? script : `() => (${script})`;
96466
+ }
95703
96467
  function withTimeout(promise3, ms, message) {
95704
96468
  let timer;
95705
96469
  const timeout = new Promise((_2, reject) => {
@@ -96041,8 +96805,7 @@ IMPORTANT: For reliable form filling, first call browser_snapshot to get element
96041
96805
  toolCallDescription
96042
96806
  }) => {
96043
96807
  try {
96044
- const isFunction = /^\s*(async\s+)?\(/.test(script) || /^\s*(async\s+)?function\s*\(/.test(script);
96045
- const fnScript = isFunction ? script : `() => (${script})`;
96808
+ const fnScript = transformScriptToFunction(script);
96046
96809
  const result = await session.callTool("browser_evaluate", { function: fnScript }, abortSignal);
96047
96810
  return { success: true, script, result };
96048
96811
  } catch (error40) {
@@ -96795,6 +97558,852 @@ var init_httpRequest = __esm(() => {
96795
97558
  });
96796
97559
  });
96797
97560
 
97561
+ // src/lib/cvss/types.ts
97562
+ function getSeverityFromScore(score) {
97563
+ if (score === 0)
97564
+ return "NONE";
97565
+ if (score <= 3.9)
97566
+ return "LOW";
97567
+ if (score <= 6.9)
97568
+ return "MEDIUM";
97569
+ if (score <= 8.9)
97570
+ return "HIGH";
97571
+ return "CRITICAL";
97572
+ }
97573
+ var init_types4 = () => {};
97574
+
97575
+ // src/lib/cvss/macrovector-scores.ts
97576
+ var MACROVECTOR_LOOKUP, METRIC_LEVELS, MAX_SEVERITY, STEP = 0.1, EPSILON;
97577
+ var init_macrovector_scores = __esm(() => {
97578
+ MACROVECTOR_LOOKUP = {
97579
+ "000000": 10,
97580
+ "000001": 9.9,
97581
+ "000010": 9.8,
97582
+ "000011": 9.5,
97583
+ "000020": 9.5,
97584
+ "000021": 9.2,
97585
+ "000100": 10,
97586
+ "000101": 9.6,
97587
+ "000110": 9.3,
97588
+ "000111": 8.7,
97589
+ "000120": 9.1,
97590
+ "000121": 8.1,
97591
+ "000200": 9.3,
97592
+ "000201": 9,
97593
+ "000210": 8.9,
97594
+ "000211": 8,
97595
+ "000220": 8.1,
97596
+ "000221": 6.8,
97597
+ "001000": 9.8,
97598
+ "001001": 9.5,
97599
+ "001010": 9.5,
97600
+ "001011": 9.2,
97601
+ "001020": 9,
97602
+ "001021": 8.4,
97603
+ "001100": 9.3,
97604
+ "001101": 9.2,
97605
+ "001110": 8.9,
97606
+ "001111": 8.1,
97607
+ "001120": 8.1,
97608
+ "001121": 6.5,
97609
+ "001200": 8.8,
97610
+ "001201": 8,
97611
+ "001210": 7.8,
97612
+ "001211": 7,
97613
+ "001220": 6.9,
97614
+ "001221": 4.8,
97615
+ "002001": 9.2,
97616
+ "002011": 8.2,
97617
+ "002021": 7.2,
97618
+ "002101": 7.9,
97619
+ "002111": 6.9,
97620
+ "002121": 5,
97621
+ "002201": 6.9,
97622
+ "002211": 5.5,
97623
+ "002221": 2.7,
97624
+ "010000": 9.9,
97625
+ "010001": 9.7,
97626
+ "010010": 9.5,
97627
+ "010011": 9.2,
97628
+ "010020": 9.2,
97629
+ "010021": 8.5,
97630
+ "010100": 9.5,
97631
+ "010101": 9.1,
97632
+ "010110": 9,
97633
+ "010111": 8.3,
97634
+ "010120": 8.4,
97635
+ "010121": 7.1,
97636
+ "010200": 9.2,
97637
+ "010201": 8.1,
97638
+ "010210": 8.2,
97639
+ "010211": 7.1,
97640
+ "010220": 7.2,
97641
+ "010221": 5.3,
97642
+ "011000": 9.5,
97643
+ "011001": 9.3,
97644
+ "011010": 9.2,
97645
+ "011011": 8.5,
97646
+ "011020": 8.5,
97647
+ "011021": 7.3,
97648
+ "011100": 9.2,
97649
+ "011101": 8.2,
97650
+ "011110": 8,
97651
+ "011111": 7.2,
97652
+ "011120": 7,
97653
+ "011121": 5.9,
97654
+ "011200": 8.4,
97655
+ "011201": 7,
97656
+ "011210": 7.1,
97657
+ "011211": 5.2,
97658
+ "011220": 5,
97659
+ "011221": 3,
97660
+ "012001": 8.6,
97661
+ "012011": 7.5,
97662
+ "012021": 5.2,
97663
+ "012101": 7.1,
97664
+ "012111": 5.2,
97665
+ "012121": 2.9,
97666
+ "012201": 6.3,
97667
+ "012211": 2.9,
97668
+ "012221": 1.7,
97669
+ "100000": 9.8,
97670
+ "100001": 9.5,
97671
+ "100010": 9.4,
97672
+ "100011": 8.7,
97673
+ "100020": 9.1,
97674
+ "100021": 8.1,
97675
+ "100100": 9.4,
97676
+ "100101": 8.9,
97677
+ "100110": 8.6,
97678
+ "100111": 7.4,
97679
+ "100120": 7.7,
97680
+ "100121": 6.4,
97681
+ "100200": 8.7,
97682
+ "100201": 7.5,
97683
+ "100210": 7.4,
97684
+ "100211": 6.3,
97685
+ "100220": 6.3,
97686
+ "100221": 4.9,
97687
+ "101000": 9.4,
97688
+ "101001": 8.9,
97689
+ "101010": 8.8,
97690
+ "101011": 7.7,
97691
+ "101020": 7.6,
97692
+ "101021": 6.7,
97693
+ "101100": 8.6,
97694
+ "101101": 7.6,
97695
+ "101110": 7.4,
97696
+ "101111": 5.8,
97697
+ "101120": 5.9,
97698
+ "101121": 5,
97699
+ "101200": 7.2,
97700
+ "101201": 5.7,
97701
+ "101210": 5.7,
97702
+ "101211": 5.2,
97703
+ "101220": 5.2,
97704
+ "101221": 2.5,
97705
+ "102001": 8.3,
97706
+ "102011": 7,
97707
+ "102021": 5.4,
97708
+ "102101": 6.5,
97709
+ "102111": 5.8,
97710
+ "102121": 2.6,
97711
+ "102201": 5.3,
97712
+ "102211": 2.1,
97713
+ "102221": 1.3,
97714
+ "110000": 9.5,
97715
+ "110001": 9,
97716
+ "110010": 8.8,
97717
+ "110011": 7.6,
97718
+ "110020": 7.6,
97719
+ "110021": 7,
97720
+ "110100": 9,
97721
+ "110101": 7.7,
97722
+ "110110": 7.5,
97723
+ "110111": 6.2,
97724
+ "110120": 6.1,
97725
+ "110121": 5.3,
97726
+ "110200": 7.7,
97727
+ "110201": 6.6,
97728
+ "110210": 6.8,
97729
+ "110211": 5.9,
97730
+ "110220": 5.2,
97731
+ "110221": 3,
97732
+ "111000": 8.9,
97733
+ "111001": 7.8,
97734
+ "111010": 7.6,
97735
+ "111011": 6.7,
97736
+ "111020": 6.2,
97737
+ "111021": 5.8,
97738
+ "111100": 7.4,
97739
+ "111101": 5.9,
97740
+ "111110": 5.7,
97741
+ "111111": 5.7,
97742
+ "111120": 4.7,
97743
+ "111121": 2.3,
97744
+ "111200": 6.1,
97745
+ "111201": 5.2,
97746
+ "111210": 5.7,
97747
+ "111211": 2.9,
97748
+ "111220": 2.4,
97749
+ "111221": 1.6,
97750
+ "112001": 7.1,
97751
+ "112011": 5.9,
97752
+ "112021": 3,
97753
+ "112101": 5.8,
97754
+ "112111": 2.6,
97755
+ "112121": 1.5,
97756
+ "112201": 2.3,
97757
+ "112211": 1.3,
97758
+ "112221": 0.6,
97759
+ "200000": 9.3,
97760
+ "200001": 8.7,
97761
+ "200010": 8.6,
97762
+ "200011": 7.2,
97763
+ "200020": 7.5,
97764
+ "200021": 5.8,
97765
+ "200100": 8.6,
97766
+ "200101": 7.4,
97767
+ "200110": 7.4,
97768
+ "200111": 6.1,
97769
+ "200120": 5.6,
97770
+ "200121": 3.4,
97771
+ "200200": 7,
97772
+ "200201": 5.4,
97773
+ "200210": 5.2,
97774
+ "200211": 4,
97775
+ "200220": 4,
97776
+ "200221": 2.2,
97777
+ "201000": 8.5,
97778
+ "201001": 7.5,
97779
+ "201010": 7.4,
97780
+ "201011": 5.5,
97781
+ "201020": 6.2,
97782
+ "201021": 5.1,
97783
+ "201100": 7.2,
97784
+ "201101": 5.7,
97785
+ "201110": 5.5,
97786
+ "201111": 4.1,
97787
+ "201120": 4.6,
97788
+ "201121": 1.9,
97789
+ "201200": 5.3,
97790
+ "201201": 3.6,
97791
+ "201210": 3.4,
97792
+ "201211": 1.9,
97793
+ "201220": 1.9,
97794
+ "201221": 0.8,
97795
+ "202001": 6.4,
97796
+ "202011": 5.1,
97797
+ "202021": 2,
97798
+ "202101": 4.7,
97799
+ "202111": 2.1,
97800
+ "202121": 1.1,
97801
+ "202201": 2.4,
97802
+ "202211": 0.9,
97803
+ "202221": 0.4,
97804
+ "210000": 8.8,
97805
+ "210001": 7.5,
97806
+ "210010": 7.3,
97807
+ "210011": 5.3,
97808
+ "210020": 6,
97809
+ "210021": 5,
97810
+ "210100": 7.3,
97811
+ "210101": 5.5,
97812
+ "210110": 5.9,
97813
+ "210111": 4,
97814
+ "210120": 4.1,
97815
+ "210121": 2,
97816
+ "210200": 5.4,
97817
+ "210201": 4.3,
97818
+ "210210": 4.5,
97819
+ "210211": 2.2,
97820
+ "210220": 2,
97821
+ "210221": 1.1,
97822
+ "211000": 7.5,
97823
+ "211001": 5.5,
97824
+ "211010": 5.8,
97825
+ "211011": 4.5,
97826
+ "211020": 4,
97827
+ "211021": 2.1,
97828
+ "211100": 6.1,
97829
+ "211101": 5.1,
97830
+ "211110": 4.8,
97831
+ "211111": 1.8,
97832
+ "211120": 2,
97833
+ "211121": 0.9,
97834
+ "211200": 4.6,
97835
+ "211201": 1.8,
97836
+ "211210": 1.7,
97837
+ "211211": 0.7,
97838
+ "211220": 0.8,
97839
+ "211221": 0.2,
97840
+ "212001": 5.3,
97841
+ "212011": 2.4,
97842
+ "212021": 1.4,
97843
+ "212101": 2.4,
97844
+ "212111": 1.2,
97845
+ "212121": 0.5,
97846
+ "212201": 1,
97847
+ "212211": 0.3,
97848
+ "212221": 0.1
97849
+ };
97850
+ METRIC_LEVELS = {
97851
+ AV: { N: 0, A: 0.1, L: 0.2, P: 0.3 },
97852
+ PR: { N: 0, L: 0.1, H: 0.2 },
97853
+ UI: { N: 0, P: 0.1, A: 0.2 },
97854
+ AC: { L: 0, H: 0.1 },
97855
+ AT: { N: 0, P: 0.1 },
97856
+ VC: { H: 0, L: 0.1, N: 0.2 },
97857
+ VI: { H: 0, L: 0.1, N: 0.2 },
97858
+ VA: { H: 0, L: 0.1, N: 0.2 },
97859
+ SC: { H: 0.1, L: 0.2, N: 0.3 },
97860
+ SI: { S: 0, H: 0.1, L: 0.2, N: 0.3 },
97861
+ SA: { S: 0, H: 0.1, L: 0.2, N: 0.3 },
97862
+ CR: { H: 0, M: 0.1, L: 0.2 },
97863
+ IR: { H: 0, M: 0.1, L: 0.2 },
97864
+ AR: { H: 0, M: 0.1, L: 0.2 },
97865
+ E: { A: 0, P: 0.1, U: 0.2 }
97866
+ };
97867
+ MAX_SEVERITY = {
97868
+ eq1: { 0: 1, 1: 4, 2: 5 },
97869
+ eq2: { 0: 1, 1: 2 },
97870
+ eq3eq6: {
97871
+ 0: { 0: 7, 1: 6 },
97872
+ 1: { 0: 8, 1: 8 },
97873
+ 2: { 1: 10 }
97874
+ },
97875
+ eq4: { 0: 6, 1: 5, 2: 4 },
97876
+ eq5: { 0: 1, 1: 1, 2: 1 }
97877
+ };
97878
+ EPSILON = Math.pow(10, -6);
97879
+ });
97880
+
97881
+ // src/lib/cvss/calculator.ts
97882
+ function buildVectorString(metrics) {
97883
+ const parts = ["CVSS:4.0"];
97884
+ for (const metric of BASE_METRICS) {
97885
+ const value = metrics[metric];
97886
+ if (value !== undefined) {
97887
+ parts.push(`${metric}:${value}`);
97888
+ }
97889
+ }
97890
+ for (const metric of THREAT_METRICS) {
97891
+ const value = metrics[metric];
97892
+ if (value !== undefined && value !== "X") {
97893
+ parts.push(`${metric}:${value}`);
97894
+ }
97895
+ }
97896
+ for (const metric of ENVIRONMENTAL_METRICS) {
97897
+ const value = metrics[metric];
97898
+ if (value !== undefined && value !== "X") {
97899
+ parts.push(`${metric}:${value}`);
97900
+ }
97901
+ }
97902
+ for (const metric of SUPPLEMENTAL_METRICS) {
97903
+ const value = metrics[metric];
97904
+ if (value !== undefined && value !== "X") {
97905
+ parts.push(`${metric}:${value}`);
97906
+ }
97907
+ }
97908
+ return parts.join("/");
97909
+ }
97910
+ function getEffectiveValue(metrics, baseMetric, modifiedMetric) {
97911
+ if (modifiedMetric) {
97912
+ const modValue = metrics[modifiedMetric];
97913
+ if (modValue && modValue !== "X") {
97914
+ return modValue;
97915
+ }
97916
+ }
97917
+ return metrics[baseMetric] ?? "";
97918
+ }
97919
+ function computeEQ1(metrics) {
97920
+ const av = getEffectiveValue(metrics, "AV", "MAV");
97921
+ const pr = getEffectiveValue(metrics, "PR", "MPR");
97922
+ const ui = getEffectiveValue(metrics, "UI", "MUI");
97923
+ if (av === "N" && pr === "N" && ui === "N") {
97924
+ return 0;
97925
+ }
97926
+ if (!(av === "P" || pr === "H" || ui === "A")) {
97927
+ return 1;
97928
+ }
97929
+ return 2;
97930
+ }
97931
+ function computeEQ2(metrics) {
97932
+ const ac = getEffectiveValue(metrics, "AC", "MAC");
97933
+ const at = getEffectiveValue(metrics, "AT", "MAT");
97934
+ if (ac === "L" && at === "N") {
97935
+ return 0;
97936
+ }
97937
+ return 1;
97938
+ }
97939
+ function computeEQ3(metrics) {
97940
+ const vc = getEffectiveValue(metrics, "VC", "MVC");
97941
+ const vi = getEffectiveValue(metrics, "VI", "MVI");
97942
+ const va = getEffectiveValue(metrics, "VA", "MVA");
97943
+ if (vc === "H" && vi === "H") {
97944
+ return 0;
97945
+ }
97946
+ if (vc === "H" || vi === "H" || va === "H") {
97947
+ return 1;
97948
+ }
97949
+ return 2;
97950
+ }
97951
+ function computeEQ4(metrics) {
97952
+ const msi = metrics.MSI || "X";
97953
+ const msa = metrics.MSA || "X";
97954
+ const sc = getEffectiveValue(metrics, "SC", "MSC");
97955
+ const si = msi !== "X" ? msi : metrics.SI;
97956
+ const sa = msa !== "X" ? msa : metrics.SA;
97957
+ if (msi === "S" || msa === "S") {
97958
+ return 0;
97959
+ }
97960
+ if (sc === "H" || si === "H" || sa === "H") {
97961
+ return 1;
97962
+ }
97963
+ return 2;
97964
+ }
97965
+ function computeEQ5(metrics) {
97966
+ const e = metrics.E || "A";
97967
+ if (e === "A" || e === "X") {
97968
+ return 0;
97969
+ }
97970
+ if (e === "P") {
97971
+ return 1;
97972
+ }
97973
+ return 2;
97974
+ }
97975
+ function computeEQ6(metrics) {
97976
+ const cr = metrics.CR || "H";
97977
+ const ir = metrics.IR || "H";
97978
+ const ar = metrics.AR || "H";
97979
+ const vc = getEffectiveValue(metrics, "VC", "MVC");
97980
+ const vi = getEffectiveValue(metrics, "VI", "MVI");
97981
+ const va = getEffectiveValue(metrics, "VA", "MVA");
97982
+ if (cr === "H" && vc === "H" || ir === "H" && vi === "H" || ar === "H" && va === "H") {
97983
+ return 0;
97984
+ }
97985
+ return 1;
97986
+ }
97987
+ function computeMacroVector(metrics) {
97988
+ const eq1 = computeEQ1(metrics);
97989
+ const eq2 = computeEQ2(metrics);
97990
+ const eq3 = computeEQ3(metrics);
97991
+ const eq4 = computeEQ4(metrics);
97992
+ const eq5 = computeEQ5(metrics);
97993
+ const eq6 = computeEQ6(metrics);
97994
+ return `${eq1}${eq2}${eq3}${eq4}${eq5}${eq6}`;
97995
+ }
97996
+ function hasNoImpact(metrics) {
97997
+ const vc = getEffectiveValue(metrics, "VC", "MVC");
97998
+ const vi = getEffectiveValue(metrics, "VI", "MVI");
97999
+ const va = getEffectiveValue(metrics, "VA", "MVA");
98000
+ const sc = getEffectiveValue(metrics, "SC", "MSC");
98001
+ const si = metrics.MSI !== "X" && metrics.MSI ? metrics.MSI : metrics.SI;
98002
+ const sa = metrics.MSA !== "X" && metrics.MSA ? metrics.MSA : metrics.SA;
98003
+ return vc === "N" && vi === "N" && va === "N" && sc === "N" && si === "N" && sa === "N";
98004
+ }
98005
+ function getMetricDistance(metrics, metric) {
98006
+ const levels = METRIC_LEVELS[metric];
98007
+ if (!levels)
98008
+ return 0;
98009
+ let value;
98010
+ const modifiedMetric = "M" + metric;
98011
+ const metricsRecord = metrics;
98012
+ if (metricsRecord[modifiedMetric] && metricsRecord[modifiedMetric] !== "X") {
98013
+ value = metricsRecord[modifiedMetric] ?? "";
98014
+ } else {
98015
+ value = metricsRecord[metric] ?? "";
98016
+ }
98017
+ if (metric === "E" && (!value || value === "X")) {
98018
+ value = "A";
98019
+ }
98020
+ if ((metric === "CR" || metric === "IR" || metric === "AR") && (!value || value === "X")) {
98021
+ value = "H";
98022
+ }
98023
+ return levels[value] || 0;
98024
+ }
98025
+ function getNextLowerScore(macroVector, position) {
98026
+ const digits = macroVector.split("").map(Number);
98027
+ digits[position]++;
98028
+ const nextMacroVector = digits.join("");
98029
+ return MACROVECTOR_LOOKUP[nextMacroVector] ?? null;
98030
+ }
98031
+ function interpolateScore(metrics, macroVector, baseScore) {
98032
+ const eq1 = parseInt(macroVector[0]);
98033
+ const eq2 = parseInt(macroVector[1]);
98034
+ const eq3 = parseInt(macroVector[2]);
98035
+ const eq4 = parseInt(macroVector[3]);
98036
+ const eq5 = parseInt(macroVector[4]);
98037
+ const eq6 = parseInt(macroVector[5]);
98038
+ let meanDistance = 0;
98039
+ let eqCount = 0;
98040
+ const eq1NextLower = getNextLowerScore(macroVector, 0);
98041
+ if (eq1NextLower !== null) {
98042
+ const msd = baseScore - eq1NextLower;
98043
+ const maxDepth = MAX_SEVERITY.eq1[eq1] || 1;
98044
+ const avDist = getMetricDistance(metrics, "AV");
98045
+ const prDist = getMetricDistance(metrics, "PR");
98046
+ const uiDist = getMetricDistance(metrics, "UI");
98047
+ const severityDist = avDist + prDist + uiDist;
98048
+ const normalizedDist = severityDist / (maxDepth * STEP);
98049
+ meanDistance += msd * normalizedDist;
98050
+ eqCount++;
98051
+ }
98052
+ const eq2NextLower = getNextLowerScore(macroVector, 1);
98053
+ if (eq2NextLower !== null) {
98054
+ const msd = baseScore - eq2NextLower;
98055
+ const maxDepth = MAX_SEVERITY.eq2[eq2] || 1;
98056
+ const acDist = getMetricDistance(metrics, "AC");
98057
+ const atDist = getMetricDistance(metrics, "AT");
98058
+ const severityDist = acDist + atDist;
98059
+ const normalizedDist = severityDist / (maxDepth * STEP);
98060
+ meanDistance += msd * normalizedDist;
98061
+ eqCount++;
98062
+ }
98063
+ const eq3eq6MaxSeverity = MAX_SEVERITY.eq3eq6;
98064
+ if (eq3eq6MaxSeverity[eq3] && eq3eq6MaxSeverity[eq3][eq6] !== undefined) {
98065
+ const eq3NextLower = getNextLowerScore(macroVector, 2);
98066
+ if (eq3NextLower !== null) {
98067
+ const msd = baseScore - eq3NextLower;
98068
+ const maxDepth = eq3eq6MaxSeverity[eq3][eq6] || 1;
98069
+ const vcDist = getMetricDistance(metrics, "VC");
98070
+ const viDist = getMetricDistance(metrics, "VI");
98071
+ const vaDist = getMetricDistance(metrics, "VA");
98072
+ const crDist = getMetricDistance(metrics, "CR");
98073
+ const irDist = getMetricDistance(metrics, "IR");
98074
+ const arDist = getMetricDistance(metrics, "AR");
98075
+ const severityDist = vcDist + viDist + vaDist + crDist + irDist + arDist;
98076
+ const normalizedDist = severityDist / (maxDepth * STEP);
98077
+ meanDistance += msd * normalizedDist;
98078
+ eqCount++;
98079
+ }
98080
+ }
98081
+ const eq4NextLower = getNextLowerScore(macroVector, 3);
98082
+ if (eq4NextLower !== null) {
98083
+ const msd = baseScore - eq4NextLower;
98084
+ const maxDepth = MAX_SEVERITY.eq4[eq4] || 1;
98085
+ const scDist = getMetricDistance(metrics, "SC");
98086
+ const siDist = getMetricDistance(metrics, "SI");
98087
+ const saDist = getMetricDistance(metrics, "SA");
98088
+ const severityDist = scDist + siDist + saDist;
98089
+ const normalizedDist = severityDist / (maxDepth * STEP);
98090
+ meanDistance += msd * normalizedDist;
98091
+ eqCount++;
98092
+ }
98093
+ const eq5NextLower = getNextLowerScore(macroVector, 4);
98094
+ if (eq5NextLower !== null) {
98095
+ const msd = baseScore - eq5NextLower;
98096
+ const maxDepth = MAX_SEVERITY.eq5[eq5] || 1;
98097
+ const eDist = getMetricDistance(metrics, "E");
98098
+ const normalizedDist = eDist / (maxDepth * STEP);
98099
+ meanDistance += msd * normalizedDist;
98100
+ eqCount++;
98101
+ }
98102
+ const avgDistance = eqCount > 0 ? meanDistance / eqCount : 0;
98103
+ let finalScore = baseScore - avgDistance;
98104
+ finalScore = Math.max(0, Math.min(10, finalScore));
98105
+ finalScore = Math.round(finalScore * 10) / 10;
98106
+ return finalScore;
98107
+ }
98108
+ function calculateCVSS4Score(metrics) {
98109
+ if (hasNoImpact(metrics)) {
98110
+ return {
98111
+ score: 0,
98112
+ severity: "NONE",
98113
+ vectorString: buildVectorString(metrics),
98114
+ metrics,
98115
+ scoreType: getScoreType(metrics)
98116
+ };
98117
+ }
98118
+ const macroVector = computeMacroVector(metrics);
98119
+ const baseScore = MACROVECTOR_LOOKUP[macroVector];
98120
+ if (baseScore === undefined) {
98121
+ throw new Error(`Invalid MacroVector: ${macroVector}`);
98122
+ }
98123
+ const score = interpolateScore(metrics, macroVector, baseScore);
98124
+ const severity = getSeverityFromScore(score);
98125
+ const scoreType = getScoreType(metrics);
98126
+ return {
98127
+ score,
98128
+ severity,
98129
+ vectorString: buildVectorString(metrics),
98130
+ metrics,
98131
+ scoreType
98132
+ };
98133
+ }
98134
+ function getScoreType(metrics) {
98135
+ const hasThreat = metrics.E !== undefined && metrics.E !== "X";
98136
+ const hasEnvironmental = metrics.CR !== undefined && metrics.CR !== "X" || metrics.IR !== undefined && metrics.IR !== "X" || metrics.AR !== undefined && metrics.AR !== "X" || metrics.MAV !== undefined && metrics.MAV !== "X" || metrics.MAC !== undefined && metrics.MAC !== "X" || metrics.MAT !== undefined && metrics.MAT !== "X" || metrics.MPR !== undefined && metrics.MPR !== "X" || metrics.MUI !== undefined && metrics.MUI !== "X" || metrics.MVC !== undefined && metrics.MVC !== "X" || metrics.MVI !== undefined && metrics.MVI !== "X" || metrics.MVA !== undefined && metrics.MVA !== "X" || metrics.MSC !== undefined && metrics.MSC !== "X" || metrics.MSI !== undefined && metrics.MSI !== "X" || metrics.MSA !== undefined && metrics.MSA !== "X";
98137
+ if (hasThreat && hasEnvironmental)
98138
+ return "CVSS-BTE";
98139
+ if (hasEnvironmental)
98140
+ return "CVSS-BE";
98141
+ if (hasThreat)
98142
+ return "CVSS-BT";
98143
+ return "CVSS-B";
98144
+ }
98145
+ var BASE_METRICS, THREAT_METRICS, ENVIRONMENTAL_METRICS, SUPPLEMENTAL_METRICS;
98146
+ var init_calculator = __esm(() => {
98147
+ init_types4();
98148
+ init_macrovector_scores();
98149
+ BASE_METRICS = [
98150
+ "AV",
98151
+ "AC",
98152
+ "AT",
98153
+ "PR",
98154
+ "UI",
98155
+ "VC",
98156
+ "VI",
98157
+ "VA",
98158
+ "SC",
98159
+ "SI",
98160
+ "SA"
98161
+ ];
98162
+ THREAT_METRICS = ["E"];
98163
+ ENVIRONMENTAL_METRICS = [
98164
+ "CR",
98165
+ "IR",
98166
+ "AR",
98167
+ "MAV",
98168
+ "MAC",
98169
+ "MAT",
98170
+ "MPR",
98171
+ "MUI",
98172
+ "MVC",
98173
+ "MVI",
98174
+ "MVA",
98175
+ "MSC",
98176
+ "MSI",
98177
+ "MSA"
98178
+ ];
98179
+ SUPPLEMENTAL_METRICS = ["S", "AU", "R", "V", "RE", "U"];
98180
+ });
98181
+
98182
+ // src/lib/cvss/index.ts
98183
+ var init_cvss = __esm(() => {
98184
+ init_types4();
98185
+ init_calculator();
98186
+ init_macrovector_scores();
98187
+ });
98188
+
98189
+ // src/core/agents/specialized/cvssScorer/index.ts
98190
+ async function scoreFindingWithCVSS(input, model, authConfig) {
98191
+ const prompt = buildScoringPrompt(input);
98192
+ const assessment = await generateObjectResponse({
98193
+ model,
98194
+ schema: CVSSMetricsOutputSchema,
98195
+ prompt,
98196
+ system: CVSS_SCORER_SYSTEM_PROMPT,
98197
+ authConfig
98198
+ });
98199
+ const cvssResult = calculateCVSS4Score({
98200
+ ...assessment.metrics
98201
+ });
98202
+ return {
98203
+ score: cvssResult.score,
98204
+ severity: cvssResult.severity,
98205
+ vectorString: cvssResult.vectorString,
98206
+ metrics: cvssResult.metrics,
98207
+ scoreType: cvssResult.scoreType,
98208
+ reasoning: assessment.reasoning
98209
+ };
98210
+ }
98211
+ function buildScoringPrompt(input) {
98212
+ const { finding, agentMessages } = input;
98213
+ let prompt = `# Vulnerability Finding to Score
98214
+
98215
+ ## Finding Details
98216
+
98217
+ **Title:** ${finding.title}
98218
+ **Vulnerability Class:** ${finding.vulnerabilityClass || "Unknown"}
98219
+ **Endpoint:** ${finding.endpoint}
98220
+
98221
+ ### Description
98222
+ ${finding.description}
98223
+
98224
+ ### Impact Assessment
98225
+ ${finding.impact}
98226
+
98227
+ ### Evidence (POC Output)
98228
+ \`\`\`
98229
+ ${finding.evidence}
98230
+ \`\`\`
98231
+
98232
+ `;
98233
+ if (agentMessages && agentMessages.length > 0) {
98234
+ prompt += `## Discovery Context
98235
+
98236
+ The following is a summary of how this vulnerability was discovered (from the testing agent's conversation):
98237
+
98238
+ `;
98239
+ const contextSummary = extractContextSummary(agentMessages);
98240
+ prompt += contextSummary;
98241
+ }
98242
+ prompt += `
98243
+ ## Task
98244
+
98245
+ Analyze this vulnerability finding and determine the appropriate CVSS 4.0 metrics.
98246
+
98247
+ Consider:
98248
+ 1. How the vulnerability is exploited (attack vector, complexity, requirements)
98249
+ 2. What privileges/authentication were needed
98250
+ 3. Whether user interaction is required
98251
+ 4. The actual impact demonstrated in the evidence
98252
+ 5. Potential for lateral movement or subsequent system compromise
98253
+
98254
+ Provide your metrics assessment and brief reasoning.
98255
+ `;
98256
+ return prompt;
98257
+ }
98258
+ function extractContextSummary(messages) {
98259
+ const contextParts = [];
98260
+ let foundToolCalls = 0;
98261
+ const maxToolCalls = 5;
98262
+ for (const message of messages) {
98263
+ if (foundToolCalls >= maxToolCalls)
98264
+ break;
98265
+ if (message.role === "assistant" && typeof message.content === "string") {
98266
+ const hypothesisMatch = message.content.match(/HYPOTHESIS:[\s\S]*?(?=VALIDATION:|$)/);
98267
+ const validationMatch = message.content.match(/VALIDATION:[\s\S]*?(?=HYPOTHESIS:|$)/);
98268
+ if (hypothesisMatch) {
98269
+ contextParts.push(`- ${hypothesisMatch[0].substring(0, 300)}...`);
98270
+ }
98271
+ if (validationMatch) {
98272
+ contextParts.push(`- ${validationMatch[0].substring(0, 300)}...`);
98273
+ }
98274
+ }
98275
+ if (message.role === "assistant" && Array.isArray(message.content)) {
98276
+ for (const rawPart of message.content) {
98277
+ const part = rawPart;
98278
+ if (part.type === "tool-call" && part.toolName) {
98279
+ const input = part.input;
98280
+ const desc = input?.toolCallDescription || `Used ${part.toolName}`;
98281
+ contextParts.push(`- Tool: ${String(desc)}`);
98282
+ foundToolCalls++;
98283
+ }
98284
+ }
98285
+ }
98286
+ }
98287
+ if (contextParts.length === 0) {
98288
+ return `No additional context available from testing conversation.
98289
+ `;
98290
+ }
98291
+ return contextParts.join(`
98292
+ `) + `
98293
+ `;
98294
+ }
98295
+ var CVSSMetricsOutputSchema, CVSS_SCORER_SYSTEM_PROMPT = `You are a CVSS 4.0 scoring specialist. Your task is to analyze vulnerability findings and determine the appropriate CVSS 4.0 Base metrics.
98296
+
98297
+ ## CVSS 4.0 Metrics Guide
98298
+
98299
+ ### Attack Vector (AV)
98300
+ - **N (Network)**: Remotely exploitable over the internet (web app vulns, network services)
98301
+ - **A (Adjacent)**: Requires shared physical or logical network (same WiFi, VLAN)
98302
+ - **L (Local)**: Requires local access or user interaction to deliver payload
98303
+ - **P (Physical)**: Requires physical hardware access
98304
+
98305
+ ### Attack Complexity (AC)
98306
+ - **L (Low)**: No special preparation needed, works reliably
98307
+ - **H (High)**: Requires race conditions, bypassing defenses, or specific configurations
98308
+
98309
+ ### Attack Requirements (AT)
98310
+ - **N (None)**: Works under normal conditions
98311
+ - **P (Present)**: Requires specific deployment conditions (race window, man-in-the-middle position)
98312
+
98313
+ ### Privileges Required (PR)
98314
+ - **N (None)**: Unauthenticated attack
98315
+ - **L (Low)**: Requires basic user-level privileges
98316
+ - **H (High)**: Requires administrative/root privileges
98317
+
98318
+ ### User Interaction (UI)
98319
+ - **N (None)**: No user action required
98320
+ - **P (Passive)**: User visits a page, opens a file, or is on a vulnerable session
98321
+ - **A (Active)**: User must click a link, dismiss warnings, or actively interact
98322
+
98323
+ ### Confidentiality Impact (VC - Vulnerable System, SC - Subsequent Systems)
98324
+ - **H (High)**: Complete loss of confidentiality (full data access, credential theft)
98325
+ - **L (Low)**: Limited data exposure (some info leak but not critical)
98326
+ - **N (None)**: No confidentiality impact
98327
+
98328
+ ### Integrity Impact (VI - Vulnerable System, SI - Subsequent Systems)
98329
+ - **H (High)**: Complete loss of integrity (arbitrary modification, code execution)
98330
+ - **L (Low)**: Limited modification capability
98331
+ - **N (None)**: No integrity impact
98332
+
98333
+ ### Availability Impact (VA - Vulnerable System, SA - Subsequent Systems)
98334
+ - **H (High)**: Complete denial of service
98335
+ - **L (Low)**: Reduced performance or intermittent availability
98336
+ - **N (None)**: No availability impact
98337
+
98338
+ ### Exploit Maturity (E)
98339
+ - **A (Attacked)**: Working exploit exists (POC confirmed vulnerability)
98340
+ - **P (POC)**: Proof-of-concept code exists but may not be weaponized
98341
+ - **U (Unreported)**: No known public exploit
98342
+
98343
+ ## Vulnerability Class Guidelines
98344
+
98345
+ ### SQL Injection (sqli)
98346
+ - Typically: AV:N, AC:L, AT:N, PR varies, UI:N
98347
+ - VC:H (data access), VI:H (data modification), VA:L-H (depending on impact)
98348
+ - SC/SI/SA: Usually N unless database is shared
98349
+
98350
+ ### Cross-Site Scripting (xss)
98351
+ - Reflected: AV:N, AC:L, AT:N, PR:N, UI:A (user must click)
98352
+ - Stored: AV:N, AC:L, AT:N, PR varies, UI:P (user visits page)
98353
+ - VC:L (session theft), VI:L (DOM modification), VA:N
98354
+ - SC/SI/SA: Usually N (client-side only)
98355
+
98356
+ ### Command Injection / RCE
98357
+ - Typically: AV:N, AC:L, AT:N, PR varies, UI:N
98358
+ - VC:H, VI:H, VA:H (complete system compromise)
98359
+ - SC/SI/SA: Potentially H if can pivot
98360
+
98361
+ ### IDOR / Access Control
98362
+ - Typically: AV:N, AC:L, AT:N, PR:L (needs some access), UI:N
98363
+ - Impact varies based on what data is accessed
98364
+
98365
+ ### SSRF
98366
+ - Typically: AV:N, AC:L, AT:N, PR varies, UI:N
98367
+ - VC on vulnerable: L-N, SC on subsequent: H (internal network access)
98368
+
98369
+ ### Path Traversal / LFI
98370
+ - Typically: AV:N, AC:L, AT:N, PR varies, UI:N
98371
+ - VC:H (file read), VI:N (unless write), VA:N
98372
+
98373
+ ## Analysis Instructions
98374
+
98375
+ 1. Read the finding description and evidence carefully
98376
+ 2. Consider the attack vector based on how the vulnerability was exploited
98377
+ 3. Assess complexity based on whether special conditions were needed
98378
+ 4. Determine privileges based on authentication requirements
98379
+ 5. Evaluate user interaction based on exploit mechanics
98380
+ 6. Assess impact on both the vulnerable system AND potential subsequent systems
98381
+ 7. Since a POC exists and confirmed the vulnerability, E should typically be 'A'
98382
+
98383
+ Always provide brief reasoning explaining your key decisions.`;
98384
+ var init_cvssScorer = __esm(() => {
98385
+ init_zod();
98386
+ init_ai2();
98387
+ init_cvss();
98388
+ CVSSMetricsOutputSchema = exports_external.object({
98389
+ metrics: exports_external.object({
98390
+ AV: exports_external.enum(["N", "A", "L", "P"]).describe("Attack Vector: N=Network (remotely exploitable), A=Adjacent network, L=Local access required, P=Physical access required"),
98391
+ AC: exports_external.enum(["L", "H"]).describe("Attack Complexity: L=Low (no special conditions), H=High (requires specific conditions/bypassing)"),
98392
+ AT: exports_external.enum(["N", "P"]).describe("Attack Requirements: N=None (works in most configs), P=Present (requires race conditions/specific setup)"),
98393
+ PR: exports_external.enum(["N", "L", "H"]).describe("Privileges Required: N=None (unauthenticated), L=Low (basic user), H=High (admin)"),
98394
+ UI: exports_external.enum(["N", "P", "A"]).describe("User Interaction: N=None, P=Passive (user visits page), A=Active (user must click/interact)"),
98395
+ VC: exports_external.enum(["H", "L", "N"]).describe("Confidentiality Impact on Vulnerable System: H=High (total loss), L=Low (partial), N=None"),
98396
+ VI: exports_external.enum(["H", "L", "N"]).describe("Integrity Impact on Vulnerable System: H=High (total loss), L=Low (partial), N=None"),
98397
+ VA: exports_external.enum(["H", "L", "N"]).describe("Availability Impact on Vulnerable System: H=High (total loss), L=Low (partial), N=None"),
98398
+ SC: exports_external.enum(["H", "L", "N"]).describe("Confidentiality Impact on Subsequent Systems: H=High, L=Low, N=None (no pivoting)"),
98399
+ SI: exports_external.enum(["H", "L", "N"]).describe("Integrity Impact on Subsequent Systems: H=High, L=Low, N=None"),
98400
+ SA: exports_external.enum(["H", "L", "N"]).describe("Availability Impact on Subsequent Systems: H=High, L=Low, N=None"),
98401
+ E: exports_external.enum(["A", "P", "U"]).describe("Exploit Maturity: A=Attacked (working exploit exists), P=POC available, U=Unreported")
98402
+ }),
98403
+ reasoning: exports_external.string().describe("Brief explanation (2-3 sentences) of the key factors that influenced the metric choices")
98404
+ });
98405
+ });
98406
+
96798
98407
  // src/core/agents/offSecAgent/tools/documentFinding.ts
96799
98408
  import { join as join5 } from "path";
96800
98409
  import { writeFileSync as writeFileSync4, appendFileSync as appendFileSync2 } from "fs";
@@ -96840,11 +98449,37 @@ FINDING STRUCTURE:
96840
98449
  }
96841
98450
  }
96842
98451
  const timestamp = new Date().toISOString();
98452
+ let cvssResult;
98453
+ try {
98454
+ cvssResult = await scoreFindingWithCVSS({
98455
+ finding: {
98456
+ title: finding.title,
98457
+ description: finding.description,
98458
+ impact: finding.impact,
98459
+ evidence: finding.evidence,
98460
+ endpoint: finding.endpoint,
98461
+ remediation: finding.remediation
98462
+ },
98463
+ agentMessages: []
98464
+ }, ctx4.model, ctx4.authConfig);
98465
+ } catch (err) {
98466
+ console.warn("CVSS scoring failed, proceeding without score:", err instanceof Error ? err.message : err);
98467
+ }
96843
98468
  const findingWithMeta = {
96844
98469
  ...finding,
96845
98470
  timestamp,
96846
98471
  sessionId: session.id,
96847
- target: session.targets[0]
98472
+ target: session.targets[0],
98473
+ ...cvssResult && {
98474
+ cvss: {
98475
+ score: cvssResult.score,
98476
+ severity: cvssResult.severity,
98477
+ vectorString: cvssResult.vectorString,
98478
+ metrics: cvssResult.metrics,
98479
+ scoreType: cvssResult.scoreType,
98480
+ reasoning: cvssResult.reasoning
98481
+ }
98482
+ }
96848
98483
  };
96849
98484
  const safeTitle = finding.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").substring(0, 50);
96850
98485
  const findingId = `${timestamp.split("T")[0]}-${safeTitle}`;
@@ -96854,9 +98489,21 @@ FINDING STRUCTURE:
96854
98489
  const mdPath = join5(session.findingsPath, mdFilename);
96855
98490
  try {
96856
98491
  writeFileSync4(jsonPath, JSON.stringify(findingWithMeta, null, 2));
98492
+ const cvssLine = cvssResult ? `
98493
+ **CVSS 4.0 Score:** ${cvssResult.score} (${cvssResult.severity})
98494
+ **Vector:** \`${cvssResult.vectorString}\` ` : "";
98495
+ const cvssSection = cvssResult ? `
98496
+ ## CVSS 4.0 Assessment
98497
+
98498
+ **Score:** ${cvssResult.score} / 10.0 (${cvssResult.severity})
98499
+ **Vector:** \`${cvssResult.vectorString}\`
98500
+ **Score Type:** ${cvssResult.scoreType}
98501
+
98502
+ **Reasoning:** ${cvssResult.reasoning}
98503
+ ` : "";
96857
98504
  const markdown = `# ${finding.title}
96858
98505
 
96859
- **Severity:** ${finding.severity}
98506
+ **Severity:** ${finding.severity} ${cvssLine}
96860
98507
  **Target:** ${session.targets[0]}
96861
98508
  **Endpoint:** ${finding.endpoint}
96862
98509
  **Date:** ${timestamp}
@@ -96869,7 +98516,7 @@ ${finding.description}
96869
98516
  ## Impact
96870
98517
 
96871
98518
  ${finding.impact}
96872
-
98519
+ ${cvssSection}
96873
98520
  ## Evidence
96874
98521
 
96875
98522
  \`\`\`
@@ -96894,7 +98541,8 @@ ${finding.references}` : ""}
96894
98541
  `;
96895
98542
  writeFileSync4(mdPath, markdown);
96896
98543
  const summaryPath = join5(session.rootPath, "findings-summary.md");
96897
- const summaryEntry = `- [${finding.severity}] ${finding.title} - \`findings/${mdFilename}\`
98544
+ const cvssTag = cvssResult ? ` (CVSS ${cvssResult.score})` : "";
98545
+ const summaryEntry = `- [${finding.severity}]${cvssTag} ${finding.title} - \`findings/${mdFilename}\`
96898
98546
  `;
96899
98547
  try {
96900
98548
  appendFileSync2(summaryPath, summaryEntry);
@@ -96936,6 +98584,7 @@ var documentVulnerabilityInputSchema;
96936
98584
  var init_documentFinding = __esm(() => {
96937
98585
  init_dist5();
96938
98586
  init_zod();
98587
+ init_cvssScorer();
96939
98588
  documentVulnerabilityInputSchema = exports_external.object({
96940
98589
  title: exports_external.string().describe("Finding title"),
96941
98590
  severity: exports_external.enum(["CRITICAL", "HIGH", "MEDIUM", "LOW"]),
@@ -99103,7 +100752,7 @@ For each app you identified, spawn a coding agent with a detailed objective. The
99103
100752
 
99104
100753
  // src/core/agents/specialized/whiteboxAttackSurface/types.ts
99105
100754
  var EndpointSchema, AppSchema, WhiteboxAttackSurfaceResultSchema;
99106
- var init_types4 = __esm(() => {
100755
+ var init_types5 = __esm(() => {
99107
100756
  init_zod();
99108
100757
  EndpointSchema = exports_external.object({
99109
100758
  method: exports_external.string().describe("HTTP method (GET, POST, PUT, DELETE, etc.) or 'PAGE' for web pages"),
@@ -99165,7 +100814,7 @@ var init_agent2 = __esm(() => {
99165
100814
  init_dist5();
99166
100815
  init_dist5();
99167
100816
  init_offensiveSecurityAgent();
99168
- init_types4();
100817
+ init_types5();
99169
100818
  WhiteboxAttackSurfaceAgent = class WhiteboxAttackSurfaceAgent extends OffensiveSecurityAgent {
99170
100819
  constructor(opts) {
99171
100820
  const {
@@ -99718,6 +101367,13 @@ var PentestResponseSchema, TargetedPentestAgent, PENTEST_SYSTEM_PROMPT = `You ar
99718
101367
 
99719
101368
  You are given a specific target and specific objectives. Do NOT perform broad reconnaissance or service/endpoint discovery — that has already been done for you. Your job is to deeply test the provided target against the provided objectives.
99720
101369
 
101370
+ CRITICAL — Source Code Prohibition:
101371
+ - You are performing a BLACKBOX penetration test. You must NEVER read, view, access, or analyze source code under any circumstances.
101372
+ - Do NOT use execute_command to read files on the local filesystem (no cat, less, more, head, tail, find, ls on source directories, strings, xxd, or any other file-reading command targeting application source code).
101373
+ - Do NOT attempt to download, fetch, or retrieve source code from the target server (e.g. via .git exposure, backup files, directory traversal to read source, or source map files) for the purpose of analyzing application logic. If you discover such an exposure, document it as a finding but do NOT read or analyze the contents.
101374
+ - Do NOT reference, assume, or reason about internal implementation details. Treat the target as a completely opaque black box — you can only observe its external behavior through HTTP responses, browser rendering, and error messages.
101375
+ - Your testing must rely exclusively on external interaction: sending requests, observing responses, and analyzing observable behavior.
101376
+
99721
101377
  Your methodology:
99722
101378
  1. PLAN — Begin by stating the objectives you have been given and outlining your testing plan. For each objective, describe which attack techniques, payloads, and tools you intend to use. Output this plan as text before making any tool calls.
99723
101379
  2. VERIFY — Confirm the target endpoint exists and is reachable. Understand its basic behavior (response format, parameters, auth requirements).
@@ -138829,7 +140485,7 @@ var require_node3 = __commonJS((exports, module2) => {
138829
140485
  var tty = __require("tty");
138830
140486
  var util4 = __require("util");
138831
140487
  exports.init = init2;
138832
- exports.log = log;
140488
+ exports.log = log2;
138833
140489
  exports.formatArgs = formatArgs;
138834
140490
  exports.save = save;
138835
140491
  exports.load = load;
@@ -138961,7 +140617,7 @@ var require_node3 = __commonJS((exports, module2) => {
138961
140617
  }
138962
140618
  return new Date().toISOString() + " ";
138963
140619
  }
138964
- function log(...args) {
140620
+ function log2(...args) {
138965
140621
  return process.stderr.write(util4.formatWithOptions(exports.inspectOpts, ...args) + `
138966
140622
  `);
138967
140623
  }
@@ -147926,7 +149582,7 @@ var require_logging_utils = __commonJS((exports) => {
147926
149582
  exports.getDebugBackend = getDebugBackend;
147927
149583
  exports.getStructuredBackend = getStructuredBackend;
147928
149584
  exports.setBackend = setBackend;
147929
- exports.log = log;
149585
+ exports.log = log2;
147930
149586
  var events_1 = __require("events");
147931
149587
  var process3 = __importStar(__require("process"));
147932
149588
  var util4 = __importStar(__require("util"));
@@ -147953,7 +149609,7 @@ var require_logging_utils = __commonJS((exports) => {
147953
149609
  this.func.info = (...args) => this.invokeSeverity(LogSeverity.INFO, ...args);
147954
149610
  this.func.warn = (...args) => this.invokeSeverity(LogSeverity.WARNING, ...args);
147955
149611
  this.func.error = (...args) => this.invokeSeverity(LogSeverity.ERROR, ...args);
147956
- this.func.sublog = (namespace2) => log(namespace2, this.func);
149612
+ this.func.sublog = (namespace2) => log2(namespace2, this.func);
147957
149613
  }
147958
149614
  invoke(fields, ...args) {
147959
149615
  if (this.upstream) {
@@ -148114,7 +149770,7 @@ var require_logging_utils = __commonJS((exports) => {
148114
149770
  cachedBackend = backend;
148115
149771
  loggerCache.clear();
148116
149772
  }
148117
- function log(namespace, parent) {
149773
+ function log2(namespace, parent) {
148118
149774
  if (!cachedBackend) {
148119
149775
  const enablesFlag = process3.env[exports.env.nodeEnables];
148120
149776
  if (!enablesFlag) {
@@ -148254,7 +149910,7 @@ var require_src6 = __commonJS((exports) => {
148254
149910
  exports.HEADER_NAME = "Metadata-Flavor";
148255
149911
  exports.HEADER_VALUE = "Google";
148256
149912
  exports.HEADERS = Object.freeze({ [exports.HEADER_NAME]: exports.HEADER_VALUE });
148257
- var log = logger.log("gcp-metadata");
149913
+ var log2 = logger.log("gcp-metadata");
148258
149914
  exports.METADATA_SERVER_DETECTION = Object.freeze({
148259
149915
  "assume-present": "don't try to ping the metadata server, but assume it's present",
148260
149916
  none: "don't try to ping the metadata server, but don't try to use it either",
@@ -148317,9 +149973,9 @@ var require_src6 = __commonJS((exports) => {
148317
149973
  responseType: "text",
148318
149974
  timeout: requestTimeout()
148319
149975
  };
148320
- log.info("instance request %j", req);
149976
+ log2.info("instance request %j", req);
148321
149977
  const res = await requestMethod(req);
148322
- log.info("instance metadata is %s", res.data);
149978
+ log2.info("instance metadata is %s", res.data);
148323
149979
  const metadataFlavor = res.headers.get(exports.HEADER_NAME);
148324
149980
  if (metadataFlavor !== exports.HEADER_VALUE) {
148325
149981
  throw new RangeError(`Invalid response from metadata service: incorrect ${exports.HEADER_NAME} header. Expected '${exports.HEADER_VALUE}', got ${metadataFlavor ? `'${metadataFlavor}'` : "no header"}`);
@@ -156447,7 +158103,7 @@ var require_http2 = __commonJS((exports) => {
156447
158103
  var process3 = __require("process");
156448
158104
  var util_1 = require_util7();
156449
158105
  var { HTTP2_HEADER_CONTENT_ENCODING, HTTP2_HEADER_CONTENT_TYPE, HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_STATUS } = http22.constants;
156450
- var DEBUG = !!process3.env.HTTP2_DEBUG;
158106
+ var DEBUG2 = !!process3.env.HTTP2_DEBUG;
156451
158107
  exports.sessions = {};
156452
158108
  async function request(config3) {
156453
158109
  const opts = extend3(true, {}, config3);
@@ -156561,7 +158217,7 @@ var require_http2 = __commonJS((exports) => {
156561
158217
  }
156562
158218
  function _getClient(host) {
156563
158219
  if (!exports.sessions[host]) {
156564
- if (DEBUG) {
158220
+ if (DEBUG2) {
156565
158221
  console.log(`Creating client for ${host}`);
156566
158222
  }
156567
158223
  const session = http22.connect(`https://${host}`);
@@ -156574,7 +158230,7 @@ var require_http2 = __commonJS((exports) => {
156574
158230
  });
156575
158231
  exports.sessions[host] = { session };
156576
158232
  } else {
156577
- if (DEBUG) {
158233
+ if (DEBUG2) {
156578
158234
  console.log(`Used cached client for ${host}`);
156579
158235
  }
156580
158236
  }
@@ -156587,17 +158243,17 @@ var require_http2 = __commonJS((exports) => {
156587
158243
  }
156588
158244
  const { session } = sessionData;
156589
158245
  delete exports.sessions[url2.host];
156590
- if (DEBUG) {
158246
+ if (DEBUG2) {
156591
158247
  console.error(`Closing ${url2.host}`);
156592
158248
  }
156593
158249
  session.close(() => {
156594
- if (DEBUG) {
158250
+ if (DEBUG2) {
156595
158251
  console.error(`Closed ${url2.host}`);
156596
158252
  }
156597
158253
  });
156598
158254
  setTimeout(() => {
156599
158255
  if (session && !session.destroyed) {
156600
- if (DEBUG) {
158256
+ if (DEBUG2) {
156601
158257
  console.log(`Forcing close ${url2.host}`);
156602
158258
  }
156603
158259
  if (session) {
@@ -166108,11 +167764,11 @@ var require_tools = __commonJS((exports, module2) => {
166108
167764
  }
166109
167765
  }
166110
167766
  }
166111
- function buildFormatters(level, bindings, log) {
167767
+ function buildFormatters(level, bindings, log2) {
166112
167768
  return {
166113
167769
  level,
166114
167770
  bindings,
166115
- log
167771
+ log: log2
166116
167772
  };
166117
167773
  }
166118
167774
  function normalizeDestFileDescriptor(destination) {
@@ -166457,8 +168113,8 @@ var require_proto = __commonJS((exports, module2) => {
166457
168113
  } else
166458
168114
  instance[serializersSym] = serializers;
166459
168115
  if (options.hasOwnProperty("formatters")) {
166460
- const { level, bindings: chindings, log } = options.formatters;
166461
- instance[formattersSym] = buildFormatters(level || formatters.level, chindings || resetChildingsFormatter, log || formatters.log);
168116
+ const { level, bindings: chindings, log: log2 } = options.formatters;
168117
+ instance[formattersSym] = buildFormatters(level || formatters.level, chindings || resetChildingsFormatter, log2 || formatters.log);
166462
168118
  } else {
166463
168119
  instance[formattersSym] = buildFormatters(formatters.level, resetChildingsFormatter, formatters.log);
166464
168120
  }
@@ -209469,219 +211125,16 @@ var useTerminalDimensions = () => {
209469
211125
  };
209470
211126
 
209471
211127
  // src/tui/index.tsx
209472
- var import_react83 = __toESM(require_react(), 1);
211128
+ var import_react87 = __toESM(require_react(), 1);
209473
211129
 
209474
211130
  // src/tui/components/footer.tsx
209475
211131
  import os5 from "os";
209476
211132
 
209477
211133
  // src/tui/context/agent.tsx
209478
211134
  init_models();
211135
+ init_config();
209479
211136
  var import_react11 = __toESM(require_react(), 1);
209480
211137
 
209481
- // src/core/config/config.ts
209482
- import os2 from "os";
209483
- import path2 from "path";
209484
- import fs2 from "fs/promises";
209485
- // package.json
209486
- var package_default2 = {
209487
- name: "@pensar/apex",
209488
- version: "0.0.77",
209489
- description: "AI-powered penetration testing CLI tool with terminal UI",
209490
- module: "src/tui/index.tsx",
209491
- main: "build/index.js",
209492
- type: "module",
209493
- repository: {
209494
- type: "git",
209495
- url: "https://github.com/pensarai/apex.git"
209496
- },
209497
- bin: {
209498
- pensar: "./bin/pensar.js"
209499
- },
209500
- files: [
209501
- "build",
209502
- "bin",
209503
- "src/core/installation",
209504
- "pensar.svg",
209505
- "LICENSE"
209506
- ],
209507
- scripts: {
209508
- build: "bun build src/tui/index.tsx --outdir build --target node --format esm --external sharp",
209509
- "generate:ascii": "bun run scripts/generate-ascii-art.ts",
209510
- "generate:models": "bun run scripts/generate-models.ts",
209511
- "build:binary": "bun run generate:ascii && bun build src/cli.ts --compile --outfile pensar",
209512
- "build:binary:macos-arm64": "bun build src/cli.ts --compile --target=bun-darwin-arm64 --outfile dist/pensar-darwin-arm64",
209513
- "build:binary:macos-x64": "bun build src/cli.ts --compile --target=bun-darwin-x64 --outfile dist/pensar-darwin-x64",
209514
- "build:binary:linux-x64": "bun build src/cli.ts --compile --target=bun-linux-x64 --outfile dist/pensar-linux-x64",
209515
- "build:binary:linux-arm64": "bun build src/cli.ts --compile --target=bun-linux-arm64 --outfile dist/pensar-linux-arm64",
209516
- "build:binaries": "bun run generate:ascii && mkdir -p dist && bun run build:binary:macos-arm64 && bun run build:binary:macos-x64 && bun run build:binary:linux-x64 && bun run build:binary:linux-arm64",
209517
- dev: "bun run scripts/watch.ts",
209518
- "dev:debug": "SHOW_CONSOLE=true bun run scripts/watch.ts",
209519
- start: "bun run src/tui/index.tsx",
209520
- pensar: "node bin/pensar.js",
209521
- tsc: "tsc --noEmit",
209522
- "daytona-benchmark": "bun run scripts/daytona-benchmark.ts",
209523
- "local-benchmark": "bun run scripts/local-benchmark.ts",
209524
- test: "vitest run",
209525
- "test:watch": "vitest",
209526
- lint: "eslint src/",
209527
- format: "prettier --write .",
209528
- "format:check": "prettier --check .",
209529
- prepublishOnly: "npm run build"
209530
- },
209531
- keywords: [
209532
- "penetration-testing",
209533
- "security",
209534
- "pentesting",
209535
- "ai",
209536
- "cli",
209537
- "terminal",
209538
- "tui"
209539
- ],
209540
- author: "Pensar",
209541
- license: "MIT",
209542
- engines: {
209543
- node: ">=18.0.0",
209544
- bun: ">=1.0.0"
209545
- },
209546
- devDependencies: {
209547
- "@eslint/js": "^10.0.1",
209548
- "@playwright/mcp": "^0.0.54",
209549
- "@types/bun": "^1.3.0",
209550
- "@types/mailparser": "^3.4.6",
209551
- "@types/react": "^19.2.6",
209552
- "@typescript-eslint/eslint-plugin": "^8.55.0",
209553
- "@typescript-eslint/parser": "^8.55.0",
209554
- dotenv: "^17.2.3",
209555
- eslint: "^10.0.0",
209556
- "eslint-config-prettier": "^10.1.8",
209557
- "eslint-plugin-unused-imports": "^4.4.1",
209558
- prettier: "^3.8.1",
209559
- "typescript-eslint": "^8.55.0",
209560
- vitest: "^2.1.8"
209561
- },
209562
- peerDependencies: {
209563
- typescript: "^5.9.3"
209564
- },
209565
- dependencies: {
209566
- "@ai-sdk/amazon-bedrock": "^4.0.69",
209567
- "@ai-sdk/anthropic": "^3.0.50",
209568
- "@ai-sdk/openai": "^3.0.37",
209569
- "@daytonaio/sdk": "^0.112.1",
209570
- "@googleapis/gmail": "^16.1.1",
209571
- "@microsoft/microsoft-graph-client": "^3.0.7",
209572
- "@modelcontextprotocol/sdk": "^1.0.0",
209573
- "@openrouter/ai-sdk-provider": "^2.2.3",
209574
- "@opentui/core": "^0.1.80",
209575
- "@opentui/react": "^0.1.80",
209576
- ai: "^6.0.105",
209577
- glob: "^13.0.0",
209578
- "google-auth-library": "^10.6.1",
209579
- ignore: "^7.0.5",
209580
- imapflow: "^1.2.10",
209581
- mailparser: "^3.9.3",
209582
- marked: "^16.4.0",
209583
- nanoid: "^5.1.6",
209584
- "p-limit": "^7.2.0",
209585
- react: "^19.2.0",
209586
- sharp: "^0.34.4",
209587
- yaml: "^2.8.2",
209588
- zod: "^3.25.76"
209589
- },
209590
- packageManager: "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
209591
- };
209592
-
209593
- // src/core/installation/index.ts
209594
- function getCurrentVersion() {
209595
- return package_default2.version;
209596
- }
209597
- function isNewerVersion(current, latest) {
209598
- const parse2 = (v2) => v2.split(".").map((n) => parseInt(n, 10) || 0);
209599
- const c = parse2(current);
209600
- const l = parse2(latest);
209601
- for (let i = 0;i < Math.max(c.length, l.length); i++) {
209602
- const cv = c[i] ?? 0;
209603
- const lv = l[i] ?? 0;
209604
- if (lv > cv)
209605
- return true;
209606
- if (lv < cv)
209607
- return false;
209608
- }
209609
- return false;
209610
- }
209611
- async function getLatestVersion() {
209612
- const res = await fetch("https://registry.npmjs.org/@pensar/apex/latest");
209613
- if (!res.ok)
209614
- throw new Error(`Failed to fetch latest version: ${res.statusText}`);
209615
- const data = await res.json();
209616
- return String(data.version);
209617
- }
209618
- async function checkForUpdate() {
209619
- const currentVersion = getCurrentVersion();
209620
- let latestVersion;
209621
- try {
209622
- latestVersion = await getLatestVersion();
209623
- } catch {
209624
- return {
209625
- updateAvailable: false,
209626
- currentVersion,
209627
- latestVersion: currentVersion
209628
- };
209629
- }
209630
- return {
209631
- updateAvailable: isNewerVersion(currentVersion, latestVersion),
209632
- currentVersion,
209633
- latestVersion
209634
- };
209635
- }
209636
-
209637
- // src/core/config/config.ts
209638
- var DEFAULT_CONFIG = {
209639
- responsibleUseAccepted: false
209640
- };
209641
- async function init() {
209642
- const folder = path2.join(os2.homedir(), ".pensar");
209643
- const file = path2.join(folder, "config.json");
209644
- const dirExists = await fs2.access(folder).then(() => true).catch(() => false);
209645
- if (!dirExists) {
209646
- await fs2.mkdir(folder, { recursive: true });
209647
- }
209648
- const fileExists = await fs2.access(file).then(() => true).catch(() => false);
209649
- if (!fileExists) {
209650
- await fs2.writeFile(file, JSON.stringify(DEFAULT_CONFIG));
209651
- }
209652
- const version = getCurrentVersion();
209653
- return { ...DEFAULT_CONFIG, version };
209654
- }
209655
- async function get() {
209656
- const folder = path2.join(os2.homedir(), ".pensar");
209657
- const file = path2.join(folder, "config.json");
209658
- const exists = await fs2.access(file).then(() => true).catch(() => false);
209659
- if (!exists) {
209660
- return await init();
209661
- }
209662
- const config = await fs2.readFile(file, "utf8");
209663
- const parsedConfig = JSON.parse(config);
209664
- const version = getCurrentVersion();
209665
- return {
209666
- ...parsedConfig,
209667
- version,
209668
- openAiAPIKey: process.env.OPENAI_API_KEY ?? parsedConfig.openAiAPIKey,
209669
- anthropicAPIKey: process.env.ANTHROPIC_API_KEY ?? parsedConfig.anthropicAPIKey,
209670
- openRouterAPIKey: process.env.OPENROUTER_API_KEY ?? parsedConfig.openRouterAPIKey,
209671
- bedrockAPIKey: process.env.BEDROCK_API_KEY ?? parsedConfig.bedrockAPIKey,
209672
- daytonaAPIKey: process.env.DAYTONA_API_KEY ?? parsedConfig.daytonaAPIKey,
209673
- daytonaOrgId: process.env.DAYTONA_ORG_ID ?? parsedConfig.daytonaOrgId,
209674
- runloopAPIKey: process.env.RUNLOOP_API_KEY ?? parsedConfig.runloopAPIKey
209675
- };
209676
- }
209677
- async function update(config) {
209678
- const currentConfig = await get();
209679
- const newConfig = { ...currentConfig, ...config };
209680
- const folder = path2.join(os2.homedir(), ".pensar");
209681
- const file = path2.join(folder, "config.json");
209682
- await fs2.writeFile(file, JSON.stringify(newConfig));
209683
- }
209684
-
209685
211138
  // src/core/providers/utils.ts
209686
211139
  init_models();
209687
211140
 
@@ -209716,6 +211169,12 @@ var AVAILABLE_PROVIDERS = [
209716
211169
  name: "Local LLM",
209717
211170
  description: "OpenAI-compatible local model (vLLM, LM Studio, Ollama)",
209718
211171
  requiresAPIKey: false
211172
+ },
211173
+ {
211174
+ id: "pensar",
211175
+ name: "Pensar",
211176
+ description: "Managed inference via Pensar Console (usage-based billing)",
211177
+ requiresAPIKey: true
209719
211178
  }
209720
211179
  ];
209721
211180
 
@@ -209742,12 +211201,14 @@ function isProviderConfigured(providerId, config) {
209742
211201
  return !!config.bedrockAPIKey;
209743
211202
  case "local":
209744
211203
  return !!(config.localModelUrl || config.localModelName || process.env.LOCAL_MODEL_URL);
211204
+ case "pensar":
211205
+ return !!(config.pensarAPIKey || config.accessToken);
209745
211206
  default:
209746
211207
  return false;
209747
211208
  }
209748
211209
  }
209749
211210
  function hasAnyProviderConfigured(config) {
209750
- return !!config.anthropicAPIKey || !!config.openAiAPIKey || !!config.openRouterAPIKey || !!config.bedrockAPIKey || !!config.localModelUrl || !!config.localModelName || !!process.env.LOCAL_MODEL_URL;
211211
+ return !!config.anthropicAPIKey || !!config.openAiAPIKey || !!config.openRouterAPIKey || !!config.bedrockAPIKey || !!config.localModelUrl || !!config.localModelName || !!process.env.LOCAL_MODEL_URL || !!config.pensarAPIKey || !!config.accessToken;
209751
211212
  }
209752
211213
  function getAvailableModels(config) {
209753
211214
  const models = AVAILABLE_MODELS.filter((model) => {
@@ -209768,10 +211229,17 @@ var import_jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
209768
211229
 
209769
211230
  // src/tui/context/agent.tsx
209770
211231
  var PREFERRED_DEFAULTS = {
211232
+ pensar: "pensar:anthropic.claude-haiku-4-5-20251001-v1:0",
209771
211233
  anthropic: "claude-haiku-4-5",
209772
211234
  openai: "gpt-4o-mini"
209773
211235
  };
209774
- var PROVIDER_PREFERENCE = ["anthropic", "openai", "openrouter", "bedrock"];
211236
+ var PROVIDER_PREFERENCE = [
211237
+ "pensar",
211238
+ "anthropic",
211239
+ "openai",
211240
+ "openrouter",
211241
+ "bedrock"
211242
+ ];
209775
211243
  var AgentContext = import_react11.createContext(null);
209776
211244
  function useAgent() {
209777
211245
  const context = import_react11.useContext(AgentContext);
@@ -210091,6 +211559,9 @@ function create(prefix, descending2, timestamp) {
210091
211559
  return prefixes[prefix] + "_" + timeBytes.toString("hex") + randomBase62(LENGTH - 12);
210092
211560
  }
210093
211561
 
211562
+ // src/core/session/index.ts
211563
+ init_installation();
211564
+
210094
211565
  // src/core/storage/index.ts
210095
211566
  init_zod();
210096
211567
  import os3 from "os";
@@ -210509,8 +211980,6 @@ var SessionConfigObject = zod_default.object({
210509
211980
  authenticationInstructions: zod_default.string().optional(),
210510
211981
  requestsPerSecond: zod_default.number().optional(),
210511
211982
  operatorSettings: OperatorSettingsObject.optional(),
210512
- enableCvssScoring: zod_default.boolean().optional(),
210513
- cvssModel: zod_default.string().optional(),
210514
211983
  toolsetState: ToolsetStateSchema.optional(),
210515
211984
  enumerateSubdomains: zod_default.boolean().optional(),
210516
211985
  cwd: zod_default.string().optional(),
@@ -211356,14 +212825,8 @@ async function createSwarmSessionFromFlags(flags) {
211356
212825
  return session;
211357
212826
  }
211358
212827
 
211359
- // src/core/config/index.ts
211360
- var config = {
211361
- get,
211362
- init,
211363
- update
211364
- };
211365
-
211366
212828
  // src/tui/command-registry.ts
212829
+ init_config2();
211367
212830
  var commands = [
211368
212831
  {
211369
212832
  name: "pentest",
@@ -211648,6 +213111,29 @@ var commands = [
211648
213111
  handler: async () => {
211649
213112
  process.kill(process.pid, "SIGINT");
211650
213113
  }
213114
+ },
213115
+ {
213116
+ name: "auth",
213117
+ description: "Connect to Pensar Console for managed inference",
213118
+ category: "General",
213119
+ handler: async (args, ctx3) => {
213120
+ ctx3.navigate({
213121
+ type: "base",
213122
+ path: "auth"
213123
+ });
213124
+ }
213125
+ },
213126
+ {
213127
+ name: "credits",
213128
+ aliases: ["buy"],
213129
+ description: "Buy credits / check balance",
213130
+ category: "General",
213131
+ handler: async (args, ctx3) => {
213132
+ ctx3.navigate({
213133
+ type: "base",
213134
+ path: "credits"
213135
+ });
213136
+ }
211651
213137
  }
211652
213138
  ];
211653
213139
  var commandRegistry = commands.map((config2) => (ctx3) => ({
@@ -212325,6 +213811,7 @@ function AlertDialog({
212325
213811
  }
212326
213812
 
212327
213813
  // src/tui/components/commands/config-dialog.tsx
213814
+ init_config2();
212328
213815
  function ConfigDialog() {
212329
213816
  const route = useRoute();
212330
213817
  const [open, setOpen] = import_react25.useState(false);
@@ -212408,6 +213895,7 @@ function ConfigForm({ appConfig }) {
212408
213895
  var import_react37 = __toESM(require_react(), 1);
212409
213896
 
212410
213897
  // src/tui/context/config.tsx
213898
+ init_config2();
212411
213899
  var import_react26 = __toESM(require_react(), 1);
212412
213900
  var ctx3 = import_react26.createContext(null);
212413
213901
  function ConfigProvider({ children, config: config2 }) {
@@ -213006,9 +214494,17 @@ var providerNames = {
213006
214494
  openai: "OpenAI",
213007
214495
  openrouter: "OpenRouter",
213008
214496
  bedrock: "Bedrock",
214497
+ pensar: "Pensar",
213009
214498
  local: "Local LLM"
213010
214499
  };
213011
- var providerOrder = ["anthropic", "openai", "openrouter", "bedrock", "local"];
214500
+ var providerOrder = [
214501
+ "pensar",
214502
+ "anthropic",
214503
+ "openai",
214504
+ "openrouter",
214505
+ "bedrock",
214506
+ "local"
214507
+ ];
213012
214508
  function ModelPicker({
213013
214509
  config: config2,
213014
214510
  selectedModel,
@@ -216338,6 +217834,7 @@ function SessionsBrowser() {
216338
217834
 
216339
217835
  // src/tui/components/commands/provider-manager.tsx
216340
217836
  var import_react50 = __toESM(require_react(), 1);
217837
+ init_config2();
216341
217838
  // src/tui/components/commands/provider-selection.tsx
216342
217839
  var import_react47 = __toESM(require_react(), 1);
216343
217840
  function ProviderSelection({
@@ -216525,6 +218022,8 @@ function APIKeyInput({
216525
218022
  return "Get your API key from openrouter.ai/keys";
216526
218023
  case "bedrock":
216527
218024
  return "Enter your AWS Access Key ID (configure region separately) or AWS Bedrock API Key";
218025
+ case "pensar":
218026
+ return "Get your API key from console.pensar.dev/connect (or run /auth)";
216528
218027
  default:
216529
218028
  return "Enter your API key";
216530
218029
  }
@@ -216678,6 +218177,9 @@ function ProviderManager() {
216678
218177
  }, undefined, true, undefined, this);
216679
218178
  }
216680
218179
 
218180
+ // src/tui/index.tsx
218181
+ init_config2();
218182
+
216681
218183
  // src/tui/components/switch.tsx
216682
218184
  var import_react51 = __toESM(require_react(), 1);
216683
218185
  var CaseSymbol = Symbol("Switch.Case");
@@ -217015,6 +218517,9 @@ function ErrorBoundary2({ children }) {
217015
218517
  return import_react55.default.createElement(ErrorBoundaryInner, { onError: handleError }, children);
217016
218518
  }
217017
218519
 
218520
+ // src/tui/index.tsx
218521
+ init_installation();
218522
+
217018
218523
  // src/tui/keybindings-registry.ts
217019
218524
  var keybindings = [
217020
218525
  {
@@ -217582,11 +219087,946 @@ function ModelsDisplay() {
217582
219087
  }, undefined, true, undefined, this);
217583
219088
  }
217584
219089
 
219090
+ // src/tui/components/commands/auth-flow.tsx
219091
+ var import_react60 = __toESM(require_react(), 1);
219092
+ init_config2();
219093
+ function AuthFlow() {
219094
+ const route = useRoute();
219095
+ const appConfig = useConfig();
219096
+ const isConnected = !!(appConfig.data.accessToken || appConfig.data.pensarAPIKey);
219097
+ const [step, setStep] = import_react60.useState(isConnected ? "success" : "start");
219098
+ const [error, setError] = import_react60.useState(null);
219099
+ const [authMode, setAuthMode] = import_react60.useState(null);
219100
+ const [deviceInfo, setDeviceInfo] = import_react60.useState(null);
219101
+ const [legacyDeviceInfo, setLegacyDeviceInfo] = import_react60.useState(null);
219102
+ const [workspaces, setWorkspaces] = import_react60.useState([]);
219103
+ const [selectedWorkspace, setSelectedWorkspace] = import_react60.useState(null);
219104
+ const [selectedIndex, setSelectedIndex] = import_react60.useState(0);
219105
+ const [billingUrl, setBillingUrl] = import_react60.useState(null);
219106
+ const [balance, setBalance] = import_react60.useState(null);
219107
+ const pollingRef = import_react60.useRef(null);
219108
+ const cancelledRef = import_react60.useRef(false);
219109
+ const connectedWorkspace = appConfig.data.workspaceSlug ? { name: appConfig.data.workspaceSlug, slug: appConfig.data.workspaceSlug } : null;
219110
+ const goHome = () => {
219111
+ route.navigate({ type: "base", path: "home" });
219112
+ };
219113
+ const cleanup = () => {
219114
+ cancelledRef.current = true;
219115
+ if (pollingRef.current) {
219116
+ clearTimeout(pollingRef.current);
219117
+ pollingRef.current = null;
219118
+ }
219119
+ };
219120
+ import_react60.useEffect(() => {
219121
+ return cleanup;
219122
+ }, []);
219123
+ const openUrl = (url) => {
219124
+ try {
219125
+ const platform = process.platform;
219126
+ if (platform === "darwin") {
219127
+ Bun.spawn(["open", url]);
219128
+ } else if (platform === "win32") {
219129
+ Bun.spawn(["cmd", "/c", "start", url]);
219130
+ } else {
219131
+ Bun.spawn(["xdg-open", url]);
219132
+ }
219133
+ } catch {}
219134
+ };
219135
+ const startDeviceFlow = async () => {
219136
+ setStep("requesting");
219137
+ setError(null);
219138
+ cancelledRef.current = false;
219139
+ const apiUrl = getPensarApiUrl(appConfig.data);
219140
+ try {
219141
+ const configResponse = await fetch(`${apiUrl}/api/cli/config`);
219142
+ if (configResponse.ok) {
219143
+ const cliConfig = await configResponse.json();
219144
+ const response = await fetch("https://api.workos.com/user_management/authorize/device", {
219145
+ method: "POST",
219146
+ headers: { "Content-Type": "application/json" },
219147
+ body: JSON.stringify({ client_id: cliConfig.workosClientId })
219148
+ });
219149
+ if (response.ok) {
219150
+ const data = await response.json();
219151
+ setAuthMode("workos");
219152
+ setDeviceInfo(data);
219153
+ openUrl(data.verification_uri_complete);
219154
+ setStep("polling");
219155
+ pollForToken(apiUrl, cliConfig.workosClientId, data.device_code, data.interval, data.expires_in);
219156
+ return;
219157
+ }
219158
+ }
219159
+ } catch {}
219160
+ try {
219161
+ const response = await fetch(`${apiUrl}/auth/device/code`, {
219162
+ method: "POST",
219163
+ headers: { "Content-Type": "application/json" }
219164
+ });
219165
+ if (!response.ok) {
219166
+ throw new Error("Failed to start device authorization");
219167
+ }
219168
+ const data = await response.json();
219169
+ setAuthMode("legacy");
219170
+ setLegacyDeviceInfo(data);
219171
+ openUrl(data.verificationUriComplete);
219172
+ setStep("polling");
219173
+ pollForLegacyToken(apiUrl, data.deviceCode, data.interval, data.expiresIn);
219174
+ } catch (err) {
219175
+ setError(err instanceof Error ? err.message : "Failed to start authorization");
219176
+ setStep("error");
219177
+ }
219178
+ };
219179
+ const pollForToken = (apiUrl, clientId, deviceCode, interval, expiresIn) => {
219180
+ const deadline = Date.now() + expiresIn * 1000;
219181
+ const poll = async () => {
219182
+ if (cancelledRef.current)
219183
+ return;
219184
+ if (Date.now() > deadline) {
219185
+ setError("Authorization timed out. Please try again.");
219186
+ setStep("error");
219187
+ return;
219188
+ }
219189
+ try {
219190
+ const response = await fetch("https://api.workos.com/user_management/authenticate", {
219191
+ method: "POST",
219192
+ headers: { "Content-Type": "application/json" },
219193
+ body: JSON.stringify({
219194
+ client_id: clientId,
219195
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
219196
+ device_code: deviceCode
219197
+ })
219198
+ });
219199
+ if (cancelledRef.current)
219200
+ return;
219201
+ if (response.status === 400) {
219202
+ pollingRef.current = setTimeout(poll, interval * 1000);
219203
+ return;
219204
+ }
219205
+ if (!response.ok) {
219206
+ throw new Error("Authentication failed");
219207
+ }
219208
+ const data = await response.json();
219209
+ await config.update({
219210
+ accessToken: data.access_token,
219211
+ refreshToken: data.refresh_token
219212
+ });
219213
+ await appConfig.reload();
219214
+ await fetchWorkspaces(apiUrl, data.access_token);
219215
+ } catch (err) {
219216
+ if (cancelledRef.current)
219217
+ return;
219218
+ pollingRef.current = setTimeout(poll, interval * 1000);
219219
+ }
219220
+ };
219221
+ pollingRef.current = setTimeout(poll, interval * 1000);
219222
+ };
219223
+ const pollForLegacyToken = (apiUrl, deviceCode, interval, expiresIn) => {
219224
+ const deadline = Date.now() + expiresIn * 1000;
219225
+ const poll = async () => {
219226
+ if (cancelledRef.current)
219227
+ return;
219228
+ if (Date.now() > deadline) {
219229
+ setError("Authorization timed out. Please try again.");
219230
+ setStep("error");
219231
+ return;
219232
+ }
219233
+ try {
219234
+ const response = await fetch(`${apiUrl}/auth/device/token`, {
219235
+ method: "POST",
219236
+ headers: { "Content-Type": "application/json" },
219237
+ body: JSON.stringify({ deviceCode })
219238
+ });
219239
+ if (!response.ok) {
219240
+ throw new Error("Failed to check authorization status");
219241
+ }
219242
+ const data = await response.json();
219243
+ if (cancelledRef.current)
219244
+ return;
219245
+ if (data.status === "complete" && data.apiKey) {
219246
+ await config.update({ pensarAPIKey: data.apiKey });
219247
+ if (data.workspace) {
219248
+ await config.update({
219249
+ workspaceId: data.workspace.id,
219250
+ workspaceSlug: data.workspace.slug
219251
+ });
219252
+ }
219253
+ appConfig.reload();
219254
+ setSelectedWorkspace(data.workspace ? {
219255
+ ...data.workspace,
219256
+ balance: data.credits?.balance ?? 0,
219257
+ hasPaymentMethod: true
219258
+ } : null);
219259
+ setBalance(data.credits?.balance ?? null);
219260
+ setStep("success");
219261
+ return;
219262
+ }
219263
+ if (data.status === "expired") {
219264
+ setError("Authorization expired. Please try again.");
219265
+ setStep("error");
219266
+ return;
219267
+ }
219268
+ if (data.status === "not_found") {
219269
+ setError("Invalid authorization session. Please try again.");
219270
+ setStep("error");
219271
+ return;
219272
+ }
219273
+ pollingRef.current = setTimeout(poll, interval * 1000);
219274
+ } catch (err) {
219275
+ if (cancelledRef.current)
219276
+ return;
219277
+ pollingRef.current = setTimeout(poll, interval * 1000);
219278
+ }
219279
+ };
219280
+ pollingRef.current = setTimeout(poll, interval * 1000);
219281
+ };
219282
+ const fetchWorkspaces = async (apiUrl, accessToken) => {
219283
+ try {
219284
+ const response = await fetch(`${apiUrl}/api/cli/workspaces`, {
219285
+ headers: { Authorization: `Bearer ${accessToken}` }
219286
+ });
219287
+ if (!response.ok) {
219288
+ throw new Error("Failed to fetch workspaces");
219289
+ }
219290
+ const data = await response.json();
219291
+ if (data.workspaces.length === 0) {
219292
+ setError("No workspaces found. Create one at console.pensar.dev");
219293
+ setStep("error");
219294
+ return;
219295
+ }
219296
+ if (data.workspaces.length === 1) {
219297
+ await selectWorkspace(apiUrl, accessToken, data.workspaces[0]);
219298
+ return;
219299
+ }
219300
+ setWorkspaces(data.workspaces);
219301
+ setSelectedIndex(0);
219302
+ setStep("select-workspace");
219303
+ } catch (err) {
219304
+ setError(err instanceof Error ? err.message : "Failed to fetch workspaces");
219305
+ setStep("error");
219306
+ }
219307
+ };
219308
+ const selectWorkspace = async (apiUrl, accessToken, workspace) => {
219309
+ setSelectedWorkspace(workspace);
219310
+ setStep("checking-billing");
219311
+ try {
219312
+ const response = await fetch(`${apiUrl}/api/cli/select-workspace`, {
219313
+ method: "POST",
219314
+ headers: {
219315
+ "Content-Type": "application/json",
219316
+ Authorization: `Bearer ${accessToken}`
219317
+ },
219318
+ body: JSON.stringify({ workspaceId: workspace.id })
219319
+ });
219320
+ if (!response.ok) {
219321
+ throw new Error("Failed to select workspace");
219322
+ }
219323
+ const data = await response.json();
219324
+ await config.update({
219325
+ workspaceId: workspace.id,
219326
+ workspaceSlug: workspace.slug
219327
+ });
219328
+ appConfig.reload();
219329
+ setBalance(data.billing.balance);
219330
+ if (!data.confirmed && data.billingUrl) {
219331
+ setBillingUrl(data.billingUrl);
219332
+ }
219333
+ setStep("success");
219334
+ } catch (err) {
219335
+ setError(err instanceof Error ? err.message : "Failed to select workspace");
219336
+ setStep("error");
219337
+ }
219338
+ };
219339
+ const handleDisconnect = async () => {
219340
+ await config.update({
219341
+ pensarAPIKey: null,
219342
+ accessToken: null,
219343
+ refreshToken: null,
219344
+ workspaceId: null,
219345
+ workspaceSlug: null
219346
+ });
219347
+ appConfig.reload();
219348
+ setAuthMode(null);
219349
+ setSelectedWorkspace(null);
219350
+ setBalance(null);
219351
+ setBillingUrl(null);
219352
+ setDeviceInfo(null);
219353
+ setLegacyDeviceInfo(null);
219354
+ setStep("start");
219355
+ };
219356
+ const hasLowBalance = balance !== null && balance < 1;
219357
+ const effectiveBillingUrl = billingUrl || (selectedWorkspace?.slug ? `${getPensarConsoleUrl()}/${selectedWorkspace.slug}/settings/billing` : connectedWorkspace?.slug ? `${getPensarConsoleUrl()}/${connectedWorkspace.slug}/settings/billing` : `${getPensarConsoleUrl()}/credits`);
219358
+ const openBillingPage = () => {
219359
+ openUrl(effectiveBillingUrl);
219360
+ goHome();
219361
+ };
219362
+ useKeyboard((key) => {
219363
+ if (key.name === "escape") {
219364
+ cleanup();
219365
+ goHome();
219366
+ return;
219367
+ }
219368
+ if (step === "start") {
219369
+ if (key.name === "return") {
219370
+ startDeviceFlow();
219371
+ }
219372
+ }
219373
+ if (step === "select-workspace") {
219374
+ if (key.name === "up" && selectedIndex > 0) {
219375
+ setSelectedIndex((i) => i - 1);
219376
+ }
219377
+ if (key.name === "down" && selectedIndex < workspaces.length - 1) {
219378
+ setSelectedIndex((i) => i + 1);
219379
+ }
219380
+ if (key.name === "return" && workspaces[selectedIndex]) {
219381
+ const currentConfig = appConfig.data;
219382
+ const apiUrl = getPensarApiUrl(currentConfig);
219383
+ const accessToken = currentConfig.accessToken;
219384
+ selectWorkspace(apiUrl, accessToken, workspaces[selectedIndex]);
219385
+ }
219386
+ }
219387
+ if (step === "error") {
219388
+ if (key.name === "return") {
219389
+ startDeviceFlow();
219390
+ }
219391
+ }
219392
+ if (step === "success") {
219393
+ if (key.name === "return") {
219394
+ if (hasLowBalance || billingUrl) {
219395
+ openBillingPage();
219396
+ } else {
219397
+ goHome();
219398
+ }
219399
+ }
219400
+ if (key.raw === "d" || key.raw === "D") {
219401
+ handleDisconnect();
219402
+ }
219403
+ }
219404
+ });
219405
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219406
+ flexDirection: "column",
219407
+ width: "100%",
219408
+ maxWidth: 80,
219409
+ alignItems: "flex-start",
219410
+ padding: 1,
219411
+ children: [
219412
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219413
+ marginBottom: 1,
219414
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219415
+ fg: "green",
219416
+ children: "Pensar Console — Managed Inference"
219417
+ }, undefined, false, undefined, this)
219418
+ }, undefined, false, undefined, this),
219419
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219420
+ marginBottom: 1,
219421
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219422
+ fg: "gray",
219423
+ children: [
219424
+ "Connect to Pensar Console for usage-based AI inference.",
219425
+ `
219426
+ `,
219427
+ "No API keys needed — just a Pensar account with credits."
219428
+ ]
219429
+ }, undefined, true, undefined, this)
219430
+ }, undefined, false, undefined, this),
219431
+ step === "start" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219432
+ flexDirection: "column",
219433
+ gap: 1,
219434
+ children: [
219435
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219436
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219437
+ fg: "white",
219438
+ children: [
219439
+ "Press ",
219440
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219441
+ fg: "green",
219442
+ children: "[ENTER]"
219443
+ }, undefined, false, undefined, this),
219444
+ " to authorize via your browser."
219445
+ ]
219446
+ }, undefined, true, undefined, this)
219447
+ }, undefined, false, undefined, this),
219448
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219449
+ marginTop: 1,
219450
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219451
+ fg: "gray",
219452
+ children: [
219453
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219454
+ fg: "green",
219455
+ children: "[ENTER]"
219456
+ }, undefined, false, undefined, this),
219457
+ " Connect ·",
219458
+ " ",
219459
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219460
+ fg: "green",
219461
+ children: "[ESC]"
219462
+ }, undefined, false, undefined, this),
219463
+ " Cancel"
219464
+ ]
219465
+ }, undefined, true, undefined, this)
219466
+ }, undefined, false, undefined, this)
219467
+ ]
219468
+ }, undefined, true, undefined, this),
219469
+ step === "requesting" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219470
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219471
+ fg: "yellow",
219472
+ children: "Starting authorization..."
219473
+ }, undefined, false, undefined, this)
219474
+ }, undefined, false, undefined, this),
219475
+ step === "polling" && (deviceInfo || legacyDeviceInfo) && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219476
+ flexDirection: "column",
219477
+ gap: 1,
219478
+ children: [
219479
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219480
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219481
+ fg: "yellow",
219482
+ children: "Waiting for browser authorization..."
219483
+ }, undefined, false, undefined, this)
219484
+ }, undefined, false, undefined, this),
219485
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219486
+ marginTop: 1,
219487
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219488
+ fg: "white",
219489
+ children: [
219490
+ "Your code:",
219491
+ " ",
219492
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219493
+ fg: "green",
219494
+ children: deviceInfo?.user_code || legacyDeviceInfo?.userCode
219495
+ }, undefined, false, undefined, this)
219496
+ ]
219497
+ }, undefined, true, undefined, this)
219498
+ }, undefined, false, undefined, this),
219499
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219500
+ marginTop: 1,
219501
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219502
+ fg: "gray",
219503
+ children: [
219504
+ "If the browser didn't open, visit:",
219505
+ `
219506
+ `,
219507
+ deviceInfo?.verification_uri_complete || legacyDeviceInfo?.verificationUriComplete
219508
+ ]
219509
+ }, undefined, true, undefined, this)
219510
+ }, undefined, false, undefined, this),
219511
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219512
+ marginTop: 1,
219513
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219514
+ fg: "gray",
219515
+ children: [
219516
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219517
+ fg: "green",
219518
+ children: "[ESC]"
219519
+ }, undefined, false, undefined, this),
219520
+ " Cancel"
219521
+ ]
219522
+ }, undefined, true, undefined, this)
219523
+ }, undefined, false, undefined, this)
219524
+ ]
219525
+ }, undefined, true, undefined, this),
219526
+ step === "select-workspace" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219527
+ flexDirection: "column",
219528
+ gap: 1,
219529
+ children: [
219530
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219531
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219532
+ fg: "white",
219533
+ children: "Select a workspace:"
219534
+ }, undefined, false, undefined, this)
219535
+ }, undefined, false, undefined, this),
219536
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219537
+ flexDirection: "column",
219538
+ marginTop: 1,
219539
+ children: workspaces.map((ws, i) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219540
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219541
+ fg: i === selectedIndex ? "green" : "gray",
219542
+ children: [
219543
+ i === selectedIndex ? "▸ " : " ",
219544
+ ws.name,
219545
+ " ",
219546
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219547
+ fg: "gray",
219548
+ children: [
219549
+ "(",
219550
+ ws.slug,
219551
+ ") — $",
219552
+ ws.balance.toFixed(2)
219553
+ ]
219554
+ }, undefined, true, undefined, this)
219555
+ ]
219556
+ }, undefined, true, undefined, this)
219557
+ }, ws.id, false, undefined, this))
219558
+ }, undefined, false, undefined, this),
219559
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219560
+ marginTop: 1,
219561
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219562
+ fg: "gray",
219563
+ children: [
219564
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219565
+ fg: "green",
219566
+ children: "[↑/↓]"
219567
+ }, undefined, false, undefined, this),
219568
+ " Navigate ·",
219569
+ " ",
219570
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219571
+ fg: "green",
219572
+ children: "[ENTER]"
219573
+ }, undefined, false, undefined, this),
219574
+ " Select ·",
219575
+ " ",
219576
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219577
+ fg: "green",
219578
+ children: "[ESC]"
219579
+ }, undefined, false, undefined, this),
219580
+ " Cancel"
219581
+ ]
219582
+ }, undefined, true, undefined, this)
219583
+ }, undefined, false, undefined, this)
219584
+ ]
219585
+ }, undefined, true, undefined, this),
219586
+ step === "checking-billing" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219587
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219588
+ fg: "yellow",
219589
+ children: [
219590
+ "Checking billing for ",
219591
+ selectedWorkspace?.name,
219592
+ "..."
219593
+ ]
219594
+ }, undefined, true, undefined, this)
219595
+ }, undefined, false, undefined, this),
219596
+ step === "success" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219597
+ flexDirection: "column",
219598
+ gap: 1,
219599
+ children: [
219600
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219601
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219602
+ fg: "green",
219603
+ children: "Connected to Pensar Console"
219604
+ }, undefined, false, undefined, this)
219605
+ }, undefined, false, undefined, this),
219606
+ (selectedWorkspace || connectedWorkspace) && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219607
+ flexDirection: "column",
219608
+ children: [
219609
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219610
+ fg: "white",
219611
+ children: [
219612
+ "Workspace: ",
219613
+ selectedWorkspace?.name || connectedWorkspace?.name
219614
+ ]
219615
+ }, undefined, true, undefined, this),
219616
+ balance !== null && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219617
+ fg: "white",
219618
+ children: [
219619
+ "Credits:",
219620
+ " ",
219621
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219622
+ fg: hasLowBalance ? "yellow" : "white",
219623
+ children: [
219624
+ "$",
219625
+ balance.toFixed(2)
219626
+ ]
219627
+ }, undefined, true, undefined, this)
219628
+ ]
219629
+ }, undefined, true, undefined, this)
219630
+ ]
219631
+ }, undefined, true, undefined, this),
219632
+ (hasLowBalance || billingUrl) && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219633
+ marginTop: 1,
219634
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219635
+ fg: "yellow",
219636
+ children: [
219637
+ billingUrl ? "Your workspace needs credits to use Apex CLI." : "Your credit balance is very low. We recommend at least $30 to run",
219638
+ `
219639
+ `,
219640
+ billingUrl ? "Press ENTER to open billing and add credits." : "pentests without interruptions. Press ENTER to open billing."
219641
+ ]
219642
+ }, undefined, true, undefined, this)
219643
+ }, undefined, false, undefined, this),
219644
+ !selectedWorkspace && !connectedWorkspace && appConfig.data.pensarAPIKey && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219645
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219646
+ fg: "gray",
219647
+ children: "Already connected (legacy key saved in config)"
219648
+ }, undefined, false, undefined, this)
219649
+ }, undefined, false, undefined, this),
219650
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219651
+ marginTop: 1,
219652
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219653
+ fg: "gray",
219654
+ children: "Pensar models are now available in the model selector."
219655
+ }, undefined, false, undefined, this)
219656
+ }, undefined, false, undefined, this),
219657
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219658
+ marginTop: 1,
219659
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219660
+ fg: "gray",
219661
+ children: [
219662
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219663
+ fg: "green",
219664
+ children: "[ENTER]"
219665
+ }, undefined, false, undefined, this),
219666
+ " ",
219667
+ hasLowBalance || billingUrl ? "Open billing" : "Done",
219668
+ " ·",
219669
+ " ",
219670
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219671
+ fg: "red",
219672
+ children: "[D]"
219673
+ }, undefined, false, undefined, this),
219674
+ " Disconnect ·",
219675
+ " ",
219676
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219677
+ fg: "green",
219678
+ children: "[ESC]"
219679
+ }, undefined, false, undefined, this),
219680
+ " Back"
219681
+ ]
219682
+ }, undefined, true, undefined, this)
219683
+ }, undefined, false, undefined, this)
219684
+ ]
219685
+ }, undefined, true, undefined, this),
219686
+ step === "error" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219687
+ flexDirection: "column",
219688
+ gap: 1,
219689
+ children: [
219690
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219691
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219692
+ fg: "red",
219693
+ children: error
219694
+ }, undefined, false, undefined, this)
219695
+ }, undefined, false, undefined, this),
219696
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219697
+ marginTop: 1,
219698
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219699
+ fg: "gray",
219700
+ children: [
219701
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219702
+ fg: "green",
219703
+ children: "[ENTER]"
219704
+ }, undefined, false, undefined, this),
219705
+ " Try again ·",
219706
+ " ",
219707
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219708
+ fg: "green",
219709
+ children: "[ESC]"
219710
+ }, undefined, false, undefined, this),
219711
+ " Cancel"
219712
+ ]
219713
+ }, undefined, true, undefined, this)
219714
+ }, undefined, false, undefined, this)
219715
+ ]
219716
+ }, undefined, true, undefined, this)
219717
+ ]
219718
+ }, undefined, true, undefined, this);
219719
+ }
219720
+
219721
+ // src/tui/components/commands/credits-flow.tsx
219722
+ var import_react62 = __toESM(require_react(), 1);
219723
+ init_tokenRefresh();
219724
+ function CreditsFlow() {
219725
+ const route = useRoute();
219726
+ const appConfig = useConfig();
219727
+ const [step, setStep] = import_react62.useState("loading");
219728
+ const [credits, setCredits] = import_react62.useState(null);
219729
+ const [error, setError] = import_react62.useState(null);
219730
+ const creditsUrl = `${getPensarConsoleUrl()}/credits`;
219731
+ const goHome = () => {
219732
+ route.navigate({ type: "base", path: "home" });
219733
+ };
219734
+ const openBrowser = () => {
219735
+ const url = creditsUrl;
219736
+ try {
219737
+ const platform = process.platform;
219738
+ if (platform === "darwin") {
219739
+ Bun.spawn(["open", url]);
219740
+ } else if (platform === "win32") {
219741
+ Bun.spawn(["cmd", "/c", "start", url]);
219742
+ } else {
219743
+ Bun.spawn(["xdg-open", url]);
219744
+ }
219745
+ } catch {}
219746
+ setStep("browser-opened");
219747
+ };
219748
+ const fetchBalance = async () => {
219749
+ const tokenResult = await ensureValidToken({
219750
+ accessToken: appConfig.data.accessToken,
219751
+ refreshToken: appConfig.data.refreshToken,
219752
+ pensarAPIKey: appConfig.data.pensarAPIKey,
219753
+ pensarApiUrl: appConfig.data.pensarApiUrl
219754
+ });
219755
+ if (!tokenResult) {
219756
+ setStep("no-auth");
219757
+ return;
219758
+ }
219759
+ setStep("loading");
219760
+ setError(null);
219761
+ try {
219762
+ const apiUrl = getPensarApiUrl(appConfig.data);
219763
+ const headers = {
219764
+ Authorization: `Bearer ${tokenResult.token}`
219765
+ };
219766
+ if (tokenResult.type === "workos" && appConfig.data.workspaceId) {
219767
+ headers["X-Workspace-Id"] = appConfig.data.workspaceId;
219768
+ }
219769
+ const response = await fetch(`${apiUrl}/bedrock/validate`, {
219770
+ method: "GET",
219771
+ headers
219772
+ });
219773
+ if (!response.ok) {
219774
+ throw new Error("Failed to fetch balance");
219775
+ }
219776
+ const result = await response.json();
219777
+ setCredits({
219778
+ balance: result.credits.balance,
219779
+ workspace: result.workspace.name
219780
+ });
219781
+ setStep("display");
219782
+ } catch (err) {
219783
+ setError(err instanceof Error ? err.message : "Failed to fetch balance");
219784
+ setStep("display");
219785
+ }
219786
+ };
219787
+ import_react62.useEffect(() => {
219788
+ fetchBalance();
219789
+ }, []);
219790
+ useKeyboard((key) => {
219791
+ if (key.name === "escape") {
219792
+ goHome();
219793
+ return;
219794
+ }
219795
+ if (step === "no-auth") {
219796
+ if (key.name === "return") {
219797
+ route.navigate({ type: "base", path: "auth" });
219798
+ }
219799
+ }
219800
+ if (step === "display") {
219801
+ if (key.name === "return") {
219802
+ openBrowser();
219803
+ }
219804
+ if (key.raw === "r" || key.raw === "R") {
219805
+ fetchBalance();
219806
+ }
219807
+ }
219808
+ if (step === "browser-opened") {
219809
+ if (key.name === "return") {
219810
+ fetchBalance();
219811
+ }
219812
+ }
219813
+ });
219814
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219815
+ flexDirection: "column",
219816
+ width: "100%",
219817
+ maxWidth: 80,
219818
+ alignItems: "flex-start",
219819
+ padding: 1,
219820
+ children: [
219821
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219822
+ marginBottom: 1,
219823
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219824
+ fg: "green",
219825
+ children: "Credits"
219826
+ }, undefined, false, undefined, this)
219827
+ }, undefined, false, undefined, this),
219828
+ step === "loading" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219829
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219830
+ fg: "yellow",
219831
+ children: "Fetching balance..."
219832
+ }, undefined, false, undefined, this)
219833
+ }, undefined, false, undefined, this),
219834
+ step === "no-auth" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219835
+ flexDirection: "column",
219836
+ gap: 1,
219837
+ children: [
219838
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219839
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219840
+ fg: "yellow",
219841
+ children: "Not connected to Pensar Console."
219842
+ }, undefined, false, undefined, this)
219843
+ }, undefined, false, undefined, this),
219844
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219845
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219846
+ fg: "gray",
219847
+ children: [
219848
+ "Run ",
219849
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219850
+ fg: "green",
219851
+ children: "/auth"
219852
+ }, undefined, false, undefined, this),
219853
+ " first to connect your account."
219854
+ ]
219855
+ }, undefined, true, undefined, this)
219856
+ }, undefined, false, undefined, this),
219857
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219858
+ marginTop: 1,
219859
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219860
+ fg: "gray",
219861
+ children: [
219862
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219863
+ fg: "green",
219864
+ children: "[ENTER]"
219865
+ }, undefined, false, undefined, this),
219866
+ " Run /auth ·",
219867
+ " ",
219868
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219869
+ fg: "green",
219870
+ children: "[ESC]"
219871
+ }, undefined, false, undefined, this),
219872
+ " Back"
219873
+ ]
219874
+ }, undefined, true, undefined, this)
219875
+ }, undefined, false, undefined, this)
219876
+ ]
219877
+ }, undefined, true, undefined, this),
219878
+ step === "display" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219879
+ flexDirection: "column",
219880
+ gap: 1,
219881
+ children: [
219882
+ error ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219883
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219884
+ fg: "red",
219885
+ children: [
219886
+ "Error: ",
219887
+ error
219888
+ ]
219889
+ }, undefined, true, undefined, this)
219890
+ }, undefined, false, undefined, this) : credits ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
219891
+ children: [
219892
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219893
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219894
+ fg: "white",
219895
+ children: [
219896
+ "Workspace: ",
219897
+ credits.workspace
219898
+ ]
219899
+ }, undefined, true, undefined, this)
219900
+ }, undefined, false, undefined, this),
219901
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219902
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219903
+ fg: "white",
219904
+ children: [
219905
+ "Balance:",
219906
+ " ",
219907
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219908
+ fg: credits.balance < 5 ? "yellow" : "green",
219909
+ children: [
219910
+ "$",
219911
+ credits.balance.toFixed(2)
219912
+ ]
219913
+ }, undefined, true, undefined, this)
219914
+ ]
219915
+ }, undefined, true, undefined, this)
219916
+ }, undefined, false, undefined, this),
219917
+ credits.balance < 5 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219918
+ marginTop: 1,
219919
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219920
+ fg: "yellow",
219921
+ children: "Low balance. We recommend at least $30 for uninterrupted pentest runs."
219922
+ }, undefined, false, undefined, this)
219923
+ }, undefined, false, undefined, this)
219924
+ ]
219925
+ }, undefined, true, undefined, this) : null,
219926
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219927
+ marginTop: 1,
219928
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219929
+ fg: "gray",
219930
+ children: [
219931
+ "Press ",
219932
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219933
+ fg: "green",
219934
+ children: "[ENTER]"
219935
+ }, undefined, false, undefined, this),
219936
+ " to buy credits in your browser."
219937
+ ]
219938
+ }, undefined, true, undefined, this)
219939
+ }, undefined, false, undefined, this),
219940
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219941
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219942
+ fg: "gray",
219943
+ children: [
219944
+ "Or visit: ",
219945
+ creditsUrl
219946
+ ]
219947
+ }, undefined, true, undefined, this)
219948
+ }, undefined, false, undefined, this),
219949
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219950
+ marginTop: 1,
219951
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219952
+ fg: "gray",
219953
+ children: [
219954
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219955
+ fg: "green",
219956
+ children: "[ENTER]"
219957
+ }, undefined, false, undefined, this),
219958
+ " Open browser ·",
219959
+ " ",
219960
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219961
+ fg: "green",
219962
+ children: "[R]"
219963
+ }, undefined, false, undefined, this),
219964
+ " Refresh ·",
219965
+ " ",
219966
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219967
+ fg: "green",
219968
+ children: "[ESC]"
219969
+ }, undefined, false, undefined, this),
219970
+ " Back"
219971
+ ]
219972
+ }, undefined, true, undefined, this)
219973
+ }, undefined, false, undefined, this)
219974
+ ]
219975
+ }, undefined, true, undefined, this),
219976
+ step === "browser-opened" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219977
+ flexDirection: "column",
219978
+ gap: 1,
219979
+ children: [
219980
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219981
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219982
+ fg: "green",
219983
+ children: "Browser opened. Purchase credits on the Pensar Console."
219984
+ }, undefined, false, undefined, this)
219985
+ }, undefined, false, undefined, this),
219986
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219987
+ marginTop: 1,
219988
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219989
+ fg: "gray",
219990
+ children: [
219991
+ "Press ",
219992
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219993
+ fg: "green",
219994
+ children: "[ENTER]"
219995
+ }, undefined, false, undefined, this),
219996
+ " to refresh your balance after purchasing."
219997
+ ]
219998
+ }, undefined, true, undefined, this)
219999
+ }, undefined, false, undefined, this),
220000
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
220001
+ marginTop: 1,
220002
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
220003
+ fg: "gray",
220004
+ children: [
220005
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
220006
+ fg: "green",
220007
+ children: "[ENTER]"
220008
+ }, undefined, false, undefined, this),
220009
+ " Refresh balance ·",
220010
+ " ",
220011
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
220012
+ fg: "green",
220013
+ children: "[ESC]"
220014
+ }, undefined, false, undefined, this),
220015
+ " Back"
220016
+ ]
220017
+ }, undefined, true, undefined, this)
220018
+ }, undefined, false, undefined, this)
220019
+ ]
220020
+ }, undefined, true, undefined, this)
220021
+ ]
220022
+ }, undefined, true, undefined, this);
220023
+ }
220024
+
217585
220025
  // src/tui/context/keybinding.tsx
217586
- var import_react64 = __toESM(require_react(), 1);
220026
+ var import_react68 = __toESM(require_react(), 1);
217587
220027
 
217588
220028
  // src/tui/keybindings/keybind.tsx
217589
- var import_react60 = __toESM(require_react(), 1);
220029
+ var import_react64 = __toESM(require_react(), 1);
217590
220030
 
217591
220031
  // src/tui/keybindings/actions.ts
217592
220032
  var movementActions = [
@@ -217838,7 +220278,7 @@ var allActions = [
217838
220278
  var actionsByKey = new Map(allActions.map((action) => [action.key, action]));
217839
220279
  var actionsById = new Map(allActions.map((action) => [action.id, action]));
217840
220280
  // src/tui/keybindings/keybind.tsx
217841
- var LeaderKeyContext = import_react60.createContext(null);
220281
+ var LeaderKeyContext = import_react64.createContext(null);
217842
220282
  // src/tui/keybindings/registry.ts
217843
220283
  function createKeybindings(deps) {
217844
220284
  const {
@@ -218016,7 +220456,7 @@ function matchesKeybind(pressed, combo) {
218016
220456
  }
218017
220457
 
218018
220458
  // src/tui/context/keybinding.tsx
218019
- var KeybindingContext = import_react64.createContext(undefined);
220459
+ var KeybindingContext = import_react68.createContext(undefined);
218020
220460
  function KeybindingProvider({
218021
220461
  children,
218022
220462
  deps
@@ -218055,7 +220495,7 @@ function KeybindingProvider({
218055
220495
  }
218056
220496
 
218057
220497
  // src/tui/components/pentest/pentest.tsx
218058
- var import_react73 = __toESM(require_react(), 1);
220498
+ var import_react77 = __toESM(require_react(), 1);
218059
220499
  import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
218060
220500
  import { join as join19 } from "path";
218061
220501
  import { exec as exec3 } from "child_process";
@@ -218070,7 +220510,7 @@ import { join as join18 } from "path";
218070
220510
  // src/core/workflows/whiteboxAttackSurface.ts
218071
220511
  init_zod();
218072
220512
  init_agent4();
218073
- init_types4();
220513
+ init_types5();
218074
220514
  var DEFAULT_CONCURRENCY3 = 5;
218075
220515
  var WHITEBOX_CODE_AGENT_SYSTEM_PROMPT = `You are an expert source-code analyst with direct filesystem access. You will be given a specific objective — focus exclusively on completing it.
218076
220516
 
@@ -218608,8 +221048,11 @@ Found ${findings.length} vulnerabilities`);
218608
221048
  return { findings, findingsPath, pocsPath, reportPath };
218609
221049
  }
218610
221050
 
221051
+ // src/tui/components/pentest/pentest.tsx
221052
+ init_utils2();
221053
+
218611
221054
  // src/tui/components/agent-display.tsx
218612
- var import_react71 = __toESM(require_react(), 1);
221055
+ var import_react75 = __toESM(require_react(), 1);
218613
221056
 
218614
221057
  // node_modules/marked/lib/marked.esm.js
218615
221058
  function L2() {
@@ -220375,14 +222818,14 @@ function getResultSummary(result, toolName) {
220375
222818
  return null;
220376
222819
  }
220377
222820
  // src/tui/components/shared/ascii-spinner.tsx
220378
- var import_react65 = __toESM(require_react(), 1);
222821
+ var import_react69 = __toESM(require_react(), 1);
220379
222822
  var SPINNER_FRAMES = ["/", "-", "\\", "|"];
220380
222823
  var SPINNER_INTERVAL = 100;
220381
222824
  function AsciiSpinner({ label, fg: fg2 }) {
220382
222825
  const { colors: colors2 } = useTheme();
220383
222826
  const spinnerColor = fg2 ?? colors2.info;
220384
- const [frame, setFrame] = import_react65.useState(0);
220385
- import_react65.useEffect(() => {
222827
+ const [frame, setFrame] = import_react69.useState(0);
222828
+ import_react69.useEffect(() => {
220386
222829
  const interval = setInterval(() => {
220387
222830
  setFrame((f3) => (f3 + 1) % SPINNER_FRAMES.length);
220388
222831
  }, SPINNER_INTERVAL);
@@ -220394,14 +222837,14 @@ function AsciiSpinner({ label, fg: fg2 }) {
220394
222837
  }, undefined, false, undefined, this);
220395
222838
  }
220396
222839
  // src/tui/components/shared/tool-renderer.tsx
220397
- var import_react66 = __toESM(require_react(), 1);
220398
- var ToolRenderer = import_react66.memo(function ToolRenderer2({
222840
+ var import_react70 = __toESM(require_react(), 1);
222841
+ var ToolRenderer = import_react70.memo(function ToolRenderer2({
220399
222842
  message,
220400
222843
  verbose = false,
220401
222844
  expandedLogs = false
220402
222845
  }) {
220403
222846
  const { colors: colors2 } = useTheme();
220404
- const [showOutput, setShowOutput] = import_react66.useState(false);
222847
+ const [showOutput, setShowOutput] = import_react70.useState(false);
220405
222848
  if (!isToolMessage(message)) {
220406
222849
  return null;
220407
222850
  }
@@ -220498,8 +222941,8 @@ var ToolRenderer = import_react66.memo(function ToolRenderer2({
220498
222941
  }, undefined, true, undefined, this);
220499
222942
  });
220500
222943
  // src/tui/components/shared/message-renderer.tsx
220501
- var import_react67 = __toESM(require_react(), 1);
220502
- var MessageRenderer = import_react67.memo(function MessageRenderer2({
222944
+ var import_react71 = __toESM(require_react(), 1);
222945
+ var MessageRenderer = import_react71.memo(function MessageRenderer2({
220503
222946
  message,
220504
222947
  isStreaming = false,
220505
222948
  verbose = false,
@@ -220509,7 +222952,7 @@ var MessageRenderer = import_react67.memo(function MessageRenderer2({
220509
222952
  }) {
220510
222953
  const { colors: colors2 } = useTheme();
220511
222954
  const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
220512
- const displayContent = import_react67.useMemo(() => message.role === "assistant" ? markdownToStyledText(content, colors2) : content, [content, message.role, colors2]);
222955
+ const displayContent = import_react71.useMemo(() => message.role === "assistant" ? markdownToStyledText(content, colors2) : content, [content, message.role, colors2]);
220513
222956
  if (isToolMessage(message)) {
220514
222957
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ToolRenderer, {
220515
222958
  message,
@@ -220621,9 +223064,9 @@ var MessageRenderer = import_react67.memo(function MessageRenderer2({
220621
223064
  }, undefined, false, undefined, this);
220622
223065
  });
220623
223066
  // src/tui/components/shared/approval-prompt.tsx
220624
- var import_react68 = __toESM(require_react(), 1);
223067
+ var import_react72 = __toESM(require_react(), 1);
220625
223068
  // src/tui/components/shared/message-reducer.ts
220626
- var import_react70 = __toESM(require_react(), 1);
223069
+ var import_react74 = __toESM(require_react(), 1);
220627
223070
  // src/tui/components/agent-display.tsx
220628
223071
  function getStableKey(item, contextId = "root") {
220629
223072
  if ("messages" in item) {
@@ -220699,11 +223142,11 @@ function AgentDisplay({
220699
223142
  ]
220700
223143
  }, undefined, true, undefined, this);
220701
223144
  }
220702
- var SubAgentDisplay = import_react71.memo(function SubAgentDisplay2({
223145
+ var SubAgentDisplay = import_react75.memo(function SubAgentDisplay2({
220703
223146
  subagent
220704
223147
  }) {
220705
223148
  const { colors: colors2 } = useTheme();
220706
- const [open, setOpen] = import_react71.useState(false);
223149
+ const [open, setOpen] = import_react75.useState(false);
220707
223150
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
220708
223151
  height: open ? 40 : "auto",
220709
223152
  onMouseDown: () => setOpen(!open),
@@ -220758,7 +223201,7 @@ var SubAgentDisplay = import_react71.memo(function SubAgentDisplay2({
220758
223201
  ]
220759
223202
  }, undefined, true, undefined, this);
220760
223203
  });
220761
- var AgentMessage = import_react71.memo(function AgentMessage2({
223204
+ var AgentMessage = import_react75.memo(function AgentMessage2({
220762
223205
  message
220763
223206
  }) {
220764
223207
  const { colors: colors2 } = useTheme();
@@ -220822,9 +223265,9 @@ var AgentMessage = import_react71.memo(function AgentMessage2({
220822
223265
  flexDirection: "column",
220823
223266
  marginTop: 0,
220824
223267
  paddingLeft: 2,
220825
- children: streamingLogs.slice(-3).map((log, idx) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
223268
+ children: streamingLogs.slice(-3).map((log2, idx) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
220826
223269
  fg: colors2.textMuted,
220827
- content: log.length > 100 ? log.slice(0, 100) + "…" : log
223270
+ content: log2.length > 100 ? log2.slice(0, 100) + "…" : log2
220828
223271
  }, idx, false, undefined, this))
220829
223272
  }, undefined, false, undefined, this)
220830
223273
  ]
@@ -220871,8 +223314,8 @@ var AgentMessage = import_react71.memo(function AgentMessage2({
220871
223314
  });
220872
223315
  function ToolDetails({ message }) {
220873
223316
  const { colors: colors2 } = useTheme();
220874
- const [showArgs, setShowArgs] = import_react71.useState(false);
220875
- const [showResult, setShowResult] = import_react71.useState(false);
223317
+ const [showArgs, setShowArgs] = import_react75.useState(false);
223318
+ const [showResult, setShowResult] = import_react75.useState(false);
220876
223319
  if (message.role !== "tool") {
220877
223320
  return null;
220878
223321
  }
@@ -220950,24 +223393,24 @@ function Pentest({ sessionId }) {
220950
223393
  const config3 = useConfig();
220951
223394
  const { model, setThinking, setIsExecuting, isExecuting } = useAgent();
220952
223395
  const { stack, externalDialogOpen } = useDialog();
220953
- const [session, setSession] = import_react73.useState(null);
220954
- const [error40, setError] = import_react73.useState(null);
220955
- const [phase, setPhase] = import_react73.useState("loading");
220956
- const [abortController, setAbortController] = import_react73.useState(null);
220957
- const [panelMessages, setPanelMessages] = import_react73.useState([]);
220958
- const panelTextRef = import_react73.useRef("");
220959
- const panelSourceRef = import_react73.useRef(null);
220960
- const [pentestAgents, setPentestAgents] = import_react73.useState({});
220961
- const pentestTextRefs = import_react73.useRef({});
220962
- const [assets, setAssets] = import_react73.useState([]);
220963
- const [viewMode, setViewMode] = import_react73.useState("overview");
220964
- const [selectedAgentId, setSelectedAgentId] = import_react73.useState(null);
220965
- const [focusedIndex, setFocusedIndex] = import_react73.useState(0);
220966
- const [showOrchestratorPanel, setShowOrchestratorPanel] = import_react73.useState(false);
220967
- const [startTime, setStartTime] = import_react73.useState(null);
220968
- const pentestAgentList = import_react73.useMemo(() => Object.values(pentestAgents).sort((a, b3) => a.createdAt.getTime() - b3.createdAt.getTime()), [pentestAgents]);
220969
- const selectedAgent = import_react73.useMemo(() => selectedAgentId ? pentestAgents[selectedAgentId] ?? null : null, [pentestAgents, selectedAgentId]);
220970
- import_react73.useEffect(() => {
223396
+ const [session, setSession] = import_react77.useState(null);
223397
+ const [error40, setError] = import_react77.useState(null);
223398
+ const [phase, setPhase] = import_react77.useState("loading");
223399
+ const [abortController, setAbortController] = import_react77.useState(null);
223400
+ const [panelMessages, setPanelMessages] = import_react77.useState([]);
223401
+ const panelTextRef = import_react77.useRef("");
223402
+ const panelSourceRef = import_react77.useRef(null);
223403
+ const [pentestAgents, setPentestAgents] = import_react77.useState({});
223404
+ const pentestTextRefs = import_react77.useRef({});
223405
+ const [assets, setAssets] = import_react77.useState([]);
223406
+ const [viewMode, setViewMode] = import_react77.useState("overview");
223407
+ const [selectedAgentId, setSelectedAgentId] = import_react77.useState(null);
223408
+ const [focusedIndex, setFocusedIndex] = import_react77.useState(0);
223409
+ const [showOrchestratorPanel, setShowOrchestratorPanel] = import_react77.useState(false);
223410
+ const [startTime, setStartTime] = import_react77.useState(null);
223411
+ const pentestAgentList = import_react77.useMemo(() => Object.values(pentestAgents).sort((a, b3) => a.createdAt.getTime() - b3.createdAt.getTime()), [pentestAgents]);
223412
+ const selectedAgent = import_react77.useMemo(() => selectedAgentId ? pentestAgents[selectedAgentId] ?? null : null, [pentestAgents, selectedAgentId]);
223413
+ import_react77.useEffect(() => {
220971
223414
  async function load() {
220972
223415
  try {
220973
223416
  const s2 = await sessions.get(sessionId);
@@ -220984,7 +223427,7 @@ function Pentest({ sessionId }) {
220984
223427
  }
220985
223428
  load();
220986
223429
  }, [sessionId]);
220987
- import_react73.useEffect(() => {
223430
+ import_react77.useEffect(() => {
220988
223431
  if (!session)
220989
223432
  return;
220990
223433
  const assetsPath = join19(session.rootPath, "assets");
@@ -221007,12 +223450,12 @@ function Pentest({ sessionId }) {
221007
223450
  const interval = setInterval(readAssets, 2000);
221008
223451
  return () => clearInterval(interval);
221009
223452
  }, [session]);
221010
- import_react73.useEffect(() => {
223453
+ import_react77.useEffect(() => {
221011
223454
  return () => {
221012
223455
  abortController?.abort();
221013
223456
  };
221014
223457
  }, [abortController]);
221015
- const ensurePentestAgent = import_react73.useCallback((subagentId) => {
223458
+ const ensurePentestAgent = import_react77.useCallback((subagentId) => {
221016
223459
  setPentestAgents((prev) => {
221017
223460
  if (prev[subagentId])
221018
223461
  return prev;
@@ -221029,7 +223472,7 @@ function Pentest({ sessionId }) {
221029
223472
  };
221030
223473
  });
221031
223474
  }, []);
221032
- const handleSubagentSpawn = import_react73.useCallback(({
223475
+ const handleSubagentSpawn = import_react77.useCallback(({
221033
223476
  subagentId,
221034
223477
  input
221035
223478
  }) => {
@@ -221049,7 +223492,7 @@ function Pentest({ sessionId }) {
221049
223492
  }
221050
223493
  }));
221051
223494
  }, []);
221052
- const handleSubagentComplete = import_react73.useCallback(({ subagentId, status }) => {
223495
+ const handleSubagentComplete = import_react77.useCallback(({ subagentId, status }) => {
221053
223496
  if (!subagentId.startsWith("pentest-agent-"))
221054
223497
  return;
221055
223498
  setPentestAgents((prev) => {
@@ -221062,7 +223505,7 @@ function Pentest({ sessionId }) {
221062
223505
  };
221063
223506
  });
221064
223507
  }, []);
221065
- const appendPanelText = import_react73.useCallback((source, text2) => {
223508
+ const appendPanelText = import_react77.useCallback((source, text2) => {
221066
223509
  if (panelSourceRef.current !== source) {
221067
223510
  panelTextRef.current = "";
221068
223511
  panelSourceRef.current = source;
@@ -221087,7 +223530,7 @@ function Pentest({ sessionId }) {
221087
223530
  ];
221088
223531
  });
221089
223532
  }, []);
221090
- const appendPentestText = import_react73.useCallback((subagentId, text2) => {
223533
+ const appendPentestText = import_react77.useCallback((subagentId, text2) => {
221091
223534
  ensurePentestAgent(subagentId);
221092
223535
  if (!pentestTextRefs.current[subagentId]) {
221093
223536
  pentestTextRefs.current[subagentId] = "";
@@ -221120,7 +223563,7 @@ function Pentest({ sessionId }) {
221120
223563
  };
221121
223564
  });
221122
223565
  }, [ensurePentestAgent]);
221123
- const addPanelToolCall = import_react73.useCallback((toolCallId, toolName, args) => {
223566
+ const addPanelToolCall = import_react77.useCallback((toolCallId, toolName, args) => {
221124
223567
  panelTextRef.current = "";
221125
223568
  panelSourceRef.current = null;
221126
223569
  const description = typeof args?.toolCallDescription === "string" ? args.toolCallDescription : toolName;
@@ -221139,7 +223582,7 @@ function Pentest({ sessionId }) {
221139
223582
  return [...prev, msg];
221140
223583
  });
221141
223584
  }, []);
221142
- const addPentestToolCall = import_react73.useCallback((subagentId, toolCallId, toolName, args) => {
223585
+ const addPentestToolCall = import_react77.useCallback((subagentId, toolCallId, toolName, args) => {
221143
223586
  pentestTextRefs.current[subagentId] = "";
221144
223587
  ensurePentestAgent(subagentId);
221145
223588
  const description = typeof args?.toolCallDescription === "string" ? args.toolCallDescription : toolName;
@@ -221184,12 +223627,12 @@ function Pentest({ sessionId }) {
221184
223627
  }
221185
223628
  ];
221186
223629
  };
221187
- const updatePanelToolResult = import_react73.useCallback((toolCallId, toolName, result) => {
223630
+ const updatePanelToolResult = import_react77.useCallback((toolCallId, toolName, result) => {
221188
223631
  panelTextRef.current = "";
221189
223632
  panelSourceRef.current = null;
221190
223633
  setPanelMessages((prev) => toolResultUpdater(prev, toolCallId, toolName, result));
221191
223634
  }, []);
221192
- const updatePentestToolResult = import_react73.useCallback((subagentId, toolCallId, toolName, result) => {
223635
+ const updatePentestToolResult = import_react77.useCallback((subagentId, toolCallId, toolName, result) => {
221193
223636
  pentestTextRefs.current[subagentId] = "";
221194
223637
  setPentestAgents((prev) => {
221195
223638
  const agent = prev[subagentId];
@@ -221204,7 +223647,7 @@ function Pentest({ sessionId }) {
221204
223647
  };
221205
223648
  });
221206
223649
  }, []);
221207
- const startPentest = import_react73.useCallback(async (s2) => {
223650
+ const startPentest = import_react77.useCallback(async (s2) => {
221208
223651
  setPhase("discovery");
221209
223652
  setStartTime(new Date);
221210
223653
  setIsExecuting(true);
@@ -221218,12 +223661,7 @@ function Pentest({ sessionId }) {
221218
223661
  session: s2,
221219
223662
  model: model.id,
221220
223663
  abortSignal: controller.signal,
221221
- authConfig: {
221222
- anthropicAPIKey: config3.data.anthropicAPIKey ?? undefined,
221223
- openAiAPIKey: config3.data.openAiAPIKey ?? undefined,
221224
- openRouterAPIKey: config3.data.openRouterAPIKey ?? undefined,
221225
- local: config3.data.localModelUrl ? { baseURL: config3.data.localModelUrl } : undefined
221226
- },
223664
+ authConfig: buildAuthConfig(config3.data),
221227
223665
  callbacks: {
221228
223666
  onTextDelta: (d3) => {
221229
223667
  setThinking(false);
@@ -221306,7 +223744,7 @@ function Pentest({ sessionId }) {
221306
223744
  handleSubagentSpawn,
221307
223745
  handleSubagentComplete
221308
223746
  ]);
221309
- import_react73.useEffect(() => {
223747
+ import_react77.useEffect(() => {
221310
223748
  if (session && phase === "loading") {
221311
223749
  startPentest(session);
221312
223750
  }
@@ -221368,7 +223806,7 @@ function Pentest({ sessionId }) {
221368
223806
  }
221369
223807
  }
221370
223808
  });
221371
- const openReport = import_react73.useCallback(() => {
223809
+ const openReport = import_react77.useCallback(() => {
221372
223810
  if (!session)
221373
223811
  return;
221374
223812
  const reportPath = join19(session.rootPath, "pentest-report.md");
@@ -221560,7 +223998,7 @@ function OrchestratorPanel({
221560
223998
  }) {
221561
223999
  const { colors: colors2 } = useTheme();
221562
224000
  const isRunning = phase !== "loading" && phase !== "completed" && phase !== "error";
221563
- const assetSummary = import_react73.useMemo(() => {
224001
+ const assetSummary = import_react77.useMemo(() => {
221564
224002
  const byType = {};
221565
224003
  for (const a of assets) {
221566
224004
  const key = a.assetType;
@@ -221809,7 +224247,7 @@ function AgentCardGrid({
221809
224247
  onSelectAgent
221810
224248
  }) {
221811
224249
  const { colors: colors2 } = useTheme();
221812
- const rows = import_react73.useMemo(() => {
224250
+ const rows = import_react77.useMemo(() => {
221813
224251
  const result = [];
221814
224252
  for (let i2 = 0;i2 < agents.length; i2 += 2) {
221815
224253
  result.push(agents.slice(i2, i2 + 2));
@@ -221857,14 +224295,14 @@ function AgentCard({
221857
224295
  completed: colors2.primary,
221858
224296
  failed: colors2.error
221859
224297
  }[agent.status];
221860
- const lastActivity = import_react73.useMemo(() => {
224298
+ const lastActivity = import_react77.useMemo(() => {
221861
224299
  const last = agent.messages[agent.messages.length - 1];
221862
224300
  if (!last)
221863
224301
  return "Starting...";
221864
224302
  const text2 = typeof last.content === "string" ? last.content.replace(/\n/g, " ").trim() : "Working...";
221865
224303
  return text2.length > 50 ? text2.substring(0, 47) + "..." : text2;
221866
224304
  }, [agent.messages]);
221867
- const toolCalls = import_react73.useMemo(() => agent.messages.filter((m4) => m4.role === "tool").length, [agent.messages]);
224305
+ const toolCalls = import_react77.useMemo(() => agent.messages.filter((m4) => m4.role === "tool").length, [agent.messages]);
221868
224306
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
221869
224307
  flexGrow: 1,
221870
224308
  flexBasis: 0,
@@ -222051,8 +224489,8 @@ function MetricsBar({
222051
224489
  isExecuting
222052
224490
  }) {
222053
224491
  const { colors: colors2 } = useTheme();
222054
- const [now2, setNow] = import_react73.useState(Date.now());
222055
- import_react73.useEffect(() => {
224492
+ const [now2, setNow] = import_react77.useState(Date.now());
224493
+ import_react77.useEffect(() => {
222056
224494
  if (!isExecuting)
222057
224495
  return;
222058
224496
  const interval = setInterval(() => setNow(Date.now()), 1000);
@@ -222195,7 +224633,7 @@ function MetricsBar({
222195
224633
  }
222196
224634
 
222197
224635
  // src/tui/components/operator-dashboard/index.tsx
222198
- var import_react78 = __toESM(require_react(), 1);
224636
+ var import_react82 = __toESM(require_react(), 1);
222199
224637
 
222200
224638
  // src/core/api/offesecAgent.ts
222201
224639
  init_offensiveSecurityAgent();
@@ -222210,6 +224648,9 @@ async function runOffensiveSecurityAgent(input) {
222210
224648
  });
222211
224649
  }
222212
224650
 
224651
+ // src/tui/components/operator-dashboard/index.tsx
224652
+ init_utils2();
224653
+
222213
224654
  // src/core/agents/offSecAgent/index.ts
222214
224655
  init_offensiveSecurityAgent();
222215
224656
  init_tools();
@@ -222292,7 +224733,7 @@ function InlineApprovalPrompt2({ approval }) {
222292
224733
  }
222293
224734
 
222294
224735
  // src/tui/components/chat/loading-indicator.tsx
222295
- var import_react75 = __toESM(require_react(), 1);
224736
+ var import_react79 = __toESM(require_react(), 1);
222296
224737
  var SPINNER_FRAMES2 = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
222297
224738
  var SPINNER_INTERVAL2 = 80;
222298
224739
  var DOTS_FRAMES = ["", ".", "..", "..."];
@@ -222303,15 +224744,15 @@ function LoadingIndicator({
222303
224744
  toolName
222304
224745
  }) {
222305
224746
  const { colors: colors2 } = useTheme();
222306
- const [spinnerFrame, setSpinnerFrame] = import_react75.useState(0);
222307
- const [dotsFrame, setDotsFrame] = import_react75.useState(0);
222308
- import_react75.useEffect(() => {
224747
+ const [spinnerFrame, setSpinnerFrame] = import_react79.useState(0);
224748
+ const [dotsFrame, setDotsFrame] = import_react79.useState(0);
224749
+ import_react79.useEffect(() => {
222309
224750
  const interval = setInterval(() => {
222310
224751
  setSpinnerFrame((f3) => (f3 + 1) % SPINNER_FRAMES2.length);
222311
224752
  }, SPINNER_INTERVAL2);
222312
224753
  return () => clearInterval(interval);
222313
224754
  }, []);
222314
- import_react75.useEffect(() => {
224755
+ import_react79.useEffect(() => {
222315
224756
  const interval = setInterval(() => {
222316
224757
  setDotsFrame((f3) => (f3 + 1) % DOTS_FRAMES.length);
222317
224758
  }, DOTS_INTERVAL);
@@ -222581,7 +225022,7 @@ function MessageList({
222581
225022
  }
222582
225023
 
222583
225024
  // src/tui/components/chat/input-area.tsx
222584
- var import_react76 = __toESM(require_react(), 1);
225025
+ var import_react80 = __toESM(require_react(), 1);
222585
225026
  function NormalInputAreaInner({
222586
225027
  value,
222587
225028
  onChange,
@@ -222596,17 +225037,17 @@ function NormalInputAreaInner({
222596
225037
  }) {
222597
225038
  const { colors: colors2 } = useTheme();
222598
225039
  const { inputValue, setInputValue } = useInput();
222599
- const promptRef = import_react76.useRef(null);
222600
- const isExternalUpdate = import_react76.useRef(false);
225040
+ const promptRef = import_react80.useRef(null);
225041
+ const isExternalUpdate = import_react80.useRef(false);
222601
225042
  const isDisabled = status === "running";
222602
- import_react76.useEffect(() => {
225043
+ import_react80.useEffect(() => {
222603
225044
  if (value !== inputValue) {
222604
225045
  isExternalUpdate.current = true;
222605
225046
  setInputValue(value);
222606
225047
  promptRef.current?.setValue(value);
222607
225048
  }
222608
225049
  }, [value]);
222609
- import_react76.useEffect(() => {
225050
+ import_react80.useEffect(() => {
222610
225051
  if (isExternalUpdate.current) {
222611
225052
  isExternalUpdate.current = false;
222612
225053
  return;
@@ -222756,7 +225197,7 @@ function ApprovalInputArea2({
222756
225197
  lastDeclineNote
222757
225198
  }) {
222758
225199
  const { colors: colors2 } = useTheme();
222759
- const [focusedElement, setFocusedElement] = import_react76.useState(0);
225200
+ const [focusedElement, setFocusedElement] = import_react80.useState(0);
222760
225201
  const tierColor = getTierColor(colors2, approval.tier);
222761
225202
  useKeyboard((key) => {
222762
225203
  if (key.name === "up") {
@@ -222878,20 +225319,20 @@ function OperatorDashboard({
222878
225319
  const route = useRoute();
222879
225320
  const config3 = useConfig();
222880
225321
  const { model, setThinking, setIsExecuting } = useAgent();
222881
- const [session, setSession] = import_react78.useState(null);
222882
- const [loading, setLoading] = import_react78.useState(true);
222883
- const [error40, setError] = import_react78.useState(null);
222884
- const [status, setStatus] = import_react78.useState("idle");
222885
- const abortControllerRef = import_react78.useRef(null);
222886
- const [messages, setMessages] = import_react78.useState([]);
222887
- const textRef = import_react78.useRef("");
222888
- const [inputValue, setInputValue] = import_react78.useState("");
222889
- const [operatorState, setOperatorState] = import_react78.useState(() => createInitialOperatorState("manual", 2));
222890
- const [pendingApprovals] = import_react78.useState([]);
222891
- const [lastApprovedAction] = import_react78.useState(null);
222892
- const [verboseMode, setVerboseMode] = import_react78.useState(false);
222893
- const [expandedLogs, setExpandedLogs] = import_react78.useState(false);
222894
- import_react78.useEffect(() => {
225322
+ const [session, setSession] = import_react82.useState(null);
225323
+ const [loading, setLoading] = import_react82.useState(true);
225324
+ const [error40, setError] = import_react82.useState(null);
225325
+ const [status, setStatus] = import_react82.useState("idle");
225326
+ const abortControllerRef = import_react82.useRef(null);
225327
+ const [messages, setMessages] = import_react82.useState([]);
225328
+ const textRef = import_react82.useRef("");
225329
+ const [inputValue, setInputValue] = import_react82.useState("");
225330
+ const [operatorState, setOperatorState] = import_react82.useState(() => createInitialOperatorState("manual", 2));
225331
+ const [pendingApprovals] = import_react82.useState([]);
225332
+ const [lastApprovedAction] = import_react82.useState(null);
225333
+ const [verboseMode, setVerboseMode] = import_react82.useState(false);
225334
+ const [expandedLogs, setExpandedLogs] = import_react82.useState(false);
225335
+ import_react82.useEffect(() => {
222895
225336
  async function loadSession() {
222896
225337
  try {
222897
225338
  const s2 = await sessions.get(sessionId);
@@ -222923,7 +225364,7 @@ function OperatorDashboard({
222923
225364
  }
222924
225365
  loadSession();
222925
225366
  }, [sessionId, isResume]);
222926
- const appendText = import_react78.useCallback((text2) => {
225367
+ const appendText = import_react82.useCallback((text2) => {
222927
225368
  textRef.current += text2;
222928
225369
  const accumulated = textRef.current;
222929
225370
  setMessages((prev) => {
@@ -222939,7 +225380,7 @@ function OperatorDashboard({
222939
225380
  ];
222940
225381
  });
222941
225382
  }, []);
222942
- const addToolCall = import_react78.useCallback((toolCallId, toolName, args) => {
225383
+ const addToolCall = import_react82.useCallback((toolCallId, toolName, args) => {
222943
225384
  textRef.current = "";
222944
225385
  setMessages((prev) => [
222945
225386
  ...prev,
@@ -222954,7 +225395,7 @@ function OperatorDashboard({
222954
225395
  }
222955
225396
  ]);
222956
225397
  }, []);
222957
- const updateToolResult = import_react78.useCallback((toolCallId, _toolName, result) => {
225398
+ const updateToolResult = import_react82.useCallback((toolCallId, _toolName, result) => {
222958
225399
  textRef.current = "";
222959
225400
  setMessages((prev) => {
222960
225401
  const idx = prev.findIndex((m4) => isToolMessage(m4) && m4.toolCallId === toolCallId);
@@ -222965,7 +225406,7 @@ function OperatorDashboard({
222965
225406
  return updated;
222966
225407
  });
222967
225408
  }, []);
222968
- const runAgent = import_react78.useCallback(async (prompt) => {
225409
+ const runAgent = import_react82.useCallback(async (prompt) => {
222969
225410
  if (!session)
222970
225411
  return;
222971
225412
  setStatus("running");
@@ -222989,12 +225430,7 @@ function OperatorDashboard({
222989
225430
  target: session.targets[0],
222990
225431
  activeTools: [...ALL_TOOL_NAMES],
222991
225432
  abortSignal: controller.signal,
222992
- authConfig: {
222993
- anthropicAPIKey: config3.data.anthropicAPIKey ?? undefined,
222994
- openAiAPIKey: config3.data.openAiAPIKey ?? undefined,
222995
- openRouterAPIKey: config3.data.openRouterAPIKey ?? undefined,
222996
- local: config3.data.localModelUrl ? { baseURL: config3.data.localModelUrl } : undefined
222997
- },
225433
+ authConfig: buildAuthConfig(config3.data),
222998
225434
  callbacks: {
222999
225435
  onTextDelta: (d3) => {
223000
225436
  setThinking(false);
@@ -223044,13 +225480,13 @@ function OperatorDashboard({
223044
225480
  setThinking,
223045
225481
  setIsExecuting
223046
225482
  ]);
223047
- const handleSubmit = import_react78.useCallback((value) => {
225483
+ const handleSubmit = import_react82.useCallback((value) => {
223048
225484
  if (!value.trim() || status === "running")
223049
225485
  return;
223050
225486
  setInputValue("");
223051
225487
  runAgent(value.trim());
223052
225488
  }, [status, runAgent]);
223053
- const handleAbort = import_react78.useCallback(() => {
225489
+ const handleAbort = import_react82.useCallback(() => {
223054
225490
  if (abortControllerRef.current) {
223055
225491
  abortControllerRef.current.abort();
223056
225492
  abortControllerRef.current = null;
@@ -223261,7 +225697,8 @@ Session paths:
223261
225697
  }
223262
225698
 
223263
225699
  // src/tui/components/commands/theme-picker.tsx
223264
- var import_react80 = __toESM(require_react(), 1);
225700
+ var import_react84 = __toESM(require_react(), 1);
225701
+ init_config2();
223265
225702
  function ThemePicker() {
223266
225703
  const dimensions = useTerminalDimensions();
223267
225704
  const route = useRoute();
@@ -223274,15 +225711,15 @@ function ThemePicker() {
223274
225711
  toggleMode,
223275
225712
  setMode
223276
225713
  } = useTheme();
223277
- const [selectedIndex, setSelectedIndex] = import_react80.useState(() => Math.max(0, availableThemes.indexOf(theme.name)));
223278
- const originalThemeRef = import_react80.useRef(theme.name);
223279
- const originalModeRef = import_react80.useRef(mode);
223280
- const handleClose = import_react80.useCallback(() => {
225714
+ const [selectedIndex, setSelectedIndex] = import_react84.useState(() => Math.max(0, availableThemes.indexOf(theme.name)));
225715
+ const originalThemeRef = import_react84.useRef(theme.name);
225716
+ const originalModeRef = import_react84.useRef(mode);
225717
+ const handleClose = import_react84.useCallback(() => {
223281
225718
  setTheme(originalThemeRef.current);
223282
225719
  setMode(originalModeRef.current);
223283
225720
  route.navigate({ type: "base", path: "home" });
223284
225721
  }, [setTheme, setMode, route]);
223285
- const handleConfirm = import_react80.useCallback(async () => {
225722
+ const handleConfirm = import_react84.useCallback(async () => {
223286
225723
  const currentThemeName = availableThemes[selectedIndex];
223287
225724
  if (currentThemeName) {
223288
225725
  await config.update({ theme: currentThemeName });
@@ -225810,13 +228247,13 @@ async function detectTerminalMode(timeoutMs = 1000) {
225810
228247
 
225811
228248
  // src/tui/index.tsx
225812
228249
  function App({ appConfig }) {
225813
- const [focusIndex, setFocusIndex] = import_react83.useState(0);
225814
- const [cwd, setCwd] = import_react83.useState(process.cwd());
225815
- const [ctrlCPressTime, setCtrlCPressTime] = import_react83.useState(null);
225816
- const [showExitWarning, setShowExitWarning] = import_react83.useState(false);
225817
- const [inputKey, setInputKey] = import_react83.useState(0);
225818
- const [showSessionsDialog, setShowSessionsDialog] = import_react83.useState(false);
225819
- const [showShortcutsDialog, setShowShortcutsDialog] = import_react83.useState(false);
228250
+ const [focusIndex, setFocusIndex] = import_react87.useState(0);
228251
+ const [cwd, setCwd] = import_react87.useState(process.cwd());
228252
+ const [ctrlCPressTime, setCtrlCPressTime] = import_react87.useState(null);
228253
+ const [showExitWarning, setShowExitWarning] = import_react87.useState(false);
228254
+ const [inputKey, setInputKey] = import_react87.useState(0);
228255
+ const [showSessionsDialog, setShowSessionsDialog] = import_react87.useState(false);
228256
+ const [showShortcutsDialog, setShowShortcutsDialog] = import_react87.useState(false);
225820
228257
  const navigableItems = ["command-input"];
225821
228258
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ConfigProvider, {
225822
228259
  config: appConfig,
@@ -225880,14 +228317,14 @@ function AppContent({
225880
228317
  const { toast } = useToast();
225881
228318
  const { refocusPrompt } = useFocus();
225882
228319
  const { setExternalDialogOpen } = useDialog();
225883
- import_react83.useEffect(() => {
228320
+ import_react87.useEffect(() => {
225884
228321
  checkForUpdate().then(({ updateAvailable, currentVersion, latestVersion }) => {
225885
228322
  if (!updateAvailable)
225886
228323
  return;
225887
228324
  toast(`Update available: v${currentVersion} → v${latestVersion}. Run: pensar upgrade`, "warn", 8000);
225888
228325
  });
225889
228326
  }, []);
225890
- import_react83.useEffect(() => {
228327
+ import_react87.useEffect(() => {
225891
228328
  if (route.data.type !== "base")
225892
228329
  return;
225893
228330
  if (!config3.data.responsibleUseAccepted && route.data.path !== "disclosure") {
@@ -225896,7 +228333,7 @@ function AppContent({
225896
228333
  route.navigate({ type: "base", path: "providers" });
225897
228334
  }
225898
228335
  }, [config3.data.responsibleUseAccepted, route.data]);
225899
- import_react83.useEffect(() => {
228336
+ import_react87.useEffect(() => {
225900
228337
  if (showExitWarning) {
225901
228338
  const timer = setTimeout(() => {
225902
228339
  setShowExitWarning(false);
@@ -226042,6 +228479,14 @@ function CommandDisplay({
226042
228479
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
226043
228480
  when: "models",
226044
228481
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelsDisplay, {}, undefined, false, undefined, this)
228482
+ }, undefined, false, undefined, this),
228483
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
228484
+ when: "auth",
228485
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AuthFlow, {}, undefined, false, undefined, this)
228486
+ }, undefined, false, undefined, this),
228487
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
228488
+ when: "credits",
228489
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(CreditsFlow, {}, undefined, false, undefined, this)
226045
228490
  }, undefined, false, undefined, this)
226046
228491
  ]
226047
228492
  }, undefined, true, undefined, this)