@pensar/apex 0.0.77 → 0.0.78-canary.27f4f443

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 +2802 -370
  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.27f4f443",
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) {
@@ -96795,6 +97545,852 @@ var init_httpRequest = __esm(() => {
96795
97545
  });
96796
97546
  });
96797
97547
 
97548
+ // src/lib/cvss/types.ts
97549
+ function getSeverityFromScore(score) {
97550
+ if (score === 0)
97551
+ return "NONE";
97552
+ if (score <= 3.9)
97553
+ return "LOW";
97554
+ if (score <= 6.9)
97555
+ return "MEDIUM";
97556
+ if (score <= 8.9)
97557
+ return "HIGH";
97558
+ return "CRITICAL";
97559
+ }
97560
+ var init_types4 = () => {};
97561
+
97562
+ // src/lib/cvss/macrovector-scores.ts
97563
+ var MACROVECTOR_LOOKUP, METRIC_LEVELS, MAX_SEVERITY, STEP = 0.1, EPSILON;
97564
+ var init_macrovector_scores = __esm(() => {
97565
+ MACROVECTOR_LOOKUP = {
97566
+ "000000": 10,
97567
+ "000001": 9.9,
97568
+ "000010": 9.8,
97569
+ "000011": 9.5,
97570
+ "000020": 9.5,
97571
+ "000021": 9.2,
97572
+ "000100": 10,
97573
+ "000101": 9.6,
97574
+ "000110": 9.3,
97575
+ "000111": 8.7,
97576
+ "000120": 9.1,
97577
+ "000121": 8.1,
97578
+ "000200": 9.3,
97579
+ "000201": 9,
97580
+ "000210": 8.9,
97581
+ "000211": 8,
97582
+ "000220": 8.1,
97583
+ "000221": 6.8,
97584
+ "001000": 9.8,
97585
+ "001001": 9.5,
97586
+ "001010": 9.5,
97587
+ "001011": 9.2,
97588
+ "001020": 9,
97589
+ "001021": 8.4,
97590
+ "001100": 9.3,
97591
+ "001101": 9.2,
97592
+ "001110": 8.9,
97593
+ "001111": 8.1,
97594
+ "001120": 8.1,
97595
+ "001121": 6.5,
97596
+ "001200": 8.8,
97597
+ "001201": 8,
97598
+ "001210": 7.8,
97599
+ "001211": 7,
97600
+ "001220": 6.9,
97601
+ "001221": 4.8,
97602
+ "002001": 9.2,
97603
+ "002011": 8.2,
97604
+ "002021": 7.2,
97605
+ "002101": 7.9,
97606
+ "002111": 6.9,
97607
+ "002121": 5,
97608
+ "002201": 6.9,
97609
+ "002211": 5.5,
97610
+ "002221": 2.7,
97611
+ "010000": 9.9,
97612
+ "010001": 9.7,
97613
+ "010010": 9.5,
97614
+ "010011": 9.2,
97615
+ "010020": 9.2,
97616
+ "010021": 8.5,
97617
+ "010100": 9.5,
97618
+ "010101": 9.1,
97619
+ "010110": 9,
97620
+ "010111": 8.3,
97621
+ "010120": 8.4,
97622
+ "010121": 7.1,
97623
+ "010200": 9.2,
97624
+ "010201": 8.1,
97625
+ "010210": 8.2,
97626
+ "010211": 7.1,
97627
+ "010220": 7.2,
97628
+ "010221": 5.3,
97629
+ "011000": 9.5,
97630
+ "011001": 9.3,
97631
+ "011010": 9.2,
97632
+ "011011": 8.5,
97633
+ "011020": 8.5,
97634
+ "011021": 7.3,
97635
+ "011100": 9.2,
97636
+ "011101": 8.2,
97637
+ "011110": 8,
97638
+ "011111": 7.2,
97639
+ "011120": 7,
97640
+ "011121": 5.9,
97641
+ "011200": 8.4,
97642
+ "011201": 7,
97643
+ "011210": 7.1,
97644
+ "011211": 5.2,
97645
+ "011220": 5,
97646
+ "011221": 3,
97647
+ "012001": 8.6,
97648
+ "012011": 7.5,
97649
+ "012021": 5.2,
97650
+ "012101": 7.1,
97651
+ "012111": 5.2,
97652
+ "012121": 2.9,
97653
+ "012201": 6.3,
97654
+ "012211": 2.9,
97655
+ "012221": 1.7,
97656
+ "100000": 9.8,
97657
+ "100001": 9.5,
97658
+ "100010": 9.4,
97659
+ "100011": 8.7,
97660
+ "100020": 9.1,
97661
+ "100021": 8.1,
97662
+ "100100": 9.4,
97663
+ "100101": 8.9,
97664
+ "100110": 8.6,
97665
+ "100111": 7.4,
97666
+ "100120": 7.7,
97667
+ "100121": 6.4,
97668
+ "100200": 8.7,
97669
+ "100201": 7.5,
97670
+ "100210": 7.4,
97671
+ "100211": 6.3,
97672
+ "100220": 6.3,
97673
+ "100221": 4.9,
97674
+ "101000": 9.4,
97675
+ "101001": 8.9,
97676
+ "101010": 8.8,
97677
+ "101011": 7.7,
97678
+ "101020": 7.6,
97679
+ "101021": 6.7,
97680
+ "101100": 8.6,
97681
+ "101101": 7.6,
97682
+ "101110": 7.4,
97683
+ "101111": 5.8,
97684
+ "101120": 5.9,
97685
+ "101121": 5,
97686
+ "101200": 7.2,
97687
+ "101201": 5.7,
97688
+ "101210": 5.7,
97689
+ "101211": 5.2,
97690
+ "101220": 5.2,
97691
+ "101221": 2.5,
97692
+ "102001": 8.3,
97693
+ "102011": 7,
97694
+ "102021": 5.4,
97695
+ "102101": 6.5,
97696
+ "102111": 5.8,
97697
+ "102121": 2.6,
97698
+ "102201": 5.3,
97699
+ "102211": 2.1,
97700
+ "102221": 1.3,
97701
+ "110000": 9.5,
97702
+ "110001": 9,
97703
+ "110010": 8.8,
97704
+ "110011": 7.6,
97705
+ "110020": 7.6,
97706
+ "110021": 7,
97707
+ "110100": 9,
97708
+ "110101": 7.7,
97709
+ "110110": 7.5,
97710
+ "110111": 6.2,
97711
+ "110120": 6.1,
97712
+ "110121": 5.3,
97713
+ "110200": 7.7,
97714
+ "110201": 6.6,
97715
+ "110210": 6.8,
97716
+ "110211": 5.9,
97717
+ "110220": 5.2,
97718
+ "110221": 3,
97719
+ "111000": 8.9,
97720
+ "111001": 7.8,
97721
+ "111010": 7.6,
97722
+ "111011": 6.7,
97723
+ "111020": 6.2,
97724
+ "111021": 5.8,
97725
+ "111100": 7.4,
97726
+ "111101": 5.9,
97727
+ "111110": 5.7,
97728
+ "111111": 5.7,
97729
+ "111120": 4.7,
97730
+ "111121": 2.3,
97731
+ "111200": 6.1,
97732
+ "111201": 5.2,
97733
+ "111210": 5.7,
97734
+ "111211": 2.9,
97735
+ "111220": 2.4,
97736
+ "111221": 1.6,
97737
+ "112001": 7.1,
97738
+ "112011": 5.9,
97739
+ "112021": 3,
97740
+ "112101": 5.8,
97741
+ "112111": 2.6,
97742
+ "112121": 1.5,
97743
+ "112201": 2.3,
97744
+ "112211": 1.3,
97745
+ "112221": 0.6,
97746
+ "200000": 9.3,
97747
+ "200001": 8.7,
97748
+ "200010": 8.6,
97749
+ "200011": 7.2,
97750
+ "200020": 7.5,
97751
+ "200021": 5.8,
97752
+ "200100": 8.6,
97753
+ "200101": 7.4,
97754
+ "200110": 7.4,
97755
+ "200111": 6.1,
97756
+ "200120": 5.6,
97757
+ "200121": 3.4,
97758
+ "200200": 7,
97759
+ "200201": 5.4,
97760
+ "200210": 5.2,
97761
+ "200211": 4,
97762
+ "200220": 4,
97763
+ "200221": 2.2,
97764
+ "201000": 8.5,
97765
+ "201001": 7.5,
97766
+ "201010": 7.4,
97767
+ "201011": 5.5,
97768
+ "201020": 6.2,
97769
+ "201021": 5.1,
97770
+ "201100": 7.2,
97771
+ "201101": 5.7,
97772
+ "201110": 5.5,
97773
+ "201111": 4.1,
97774
+ "201120": 4.6,
97775
+ "201121": 1.9,
97776
+ "201200": 5.3,
97777
+ "201201": 3.6,
97778
+ "201210": 3.4,
97779
+ "201211": 1.9,
97780
+ "201220": 1.9,
97781
+ "201221": 0.8,
97782
+ "202001": 6.4,
97783
+ "202011": 5.1,
97784
+ "202021": 2,
97785
+ "202101": 4.7,
97786
+ "202111": 2.1,
97787
+ "202121": 1.1,
97788
+ "202201": 2.4,
97789
+ "202211": 0.9,
97790
+ "202221": 0.4,
97791
+ "210000": 8.8,
97792
+ "210001": 7.5,
97793
+ "210010": 7.3,
97794
+ "210011": 5.3,
97795
+ "210020": 6,
97796
+ "210021": 5,
97797
+ "210100": 7.3,
97798
+ "210101": 5.5,
97799
+ "210110": 5.9,
97800
+ "210111": 4,
97801
+ "210120": 4.1,
97802
+ "210121": 2,
97803
+ "210200": 5.4,
97804
+ "210201": 4.3,
97805
+ "210210": 4.5,
97806
+ "210211": 2.2,
97807
+ "210220": 2,
97808
+ "210221": 1.1,
97809
+ "211000": 7.5,
97810
+ "211001": 5.5,
97811
+ "211010": 5.8,
97812
+ "211011": 4.5,
97813
+ "211020": 4,
97814
+ "211021": 2.1,
97815
+ "211100": 6.1,
97816
+ "211101": 5.1,
97817
+ "211110": 4.8,
97818
+ "211111": 1.8,
97819
+ "211120": 2,
97820
+ "211121": 0.9,
97821
+ "211200": 4.6,
97822
+ "211201": 1.8,
97823
+ "211210": 1.7,
97824
+ "211211": 0.7,
97825
+ "211220": 0.8,
97826
+ "211221": 0.2,
97827
+ "212001": 5.3,
97828
+ "212011": 2.4,
97829
+ "212021": 1.4,
97830
+ "212101": 2.4,
97831
+ "212111": 1.2,
97832
+ "212121": 0.5,
97833
+ "212201": 1,
97834
+ "212211": 0.3,
97835
+ "212221": 0.1
97836
+ };
97837
+ METRIC_LEVELS = {
97838
+ AV: { N: 0, A: 0.1, L: 0.2, P: 0.3 },
97839
+ PR: { N: 0, L: 0.1, H: 0.2 },
97840
+ UI: { N: 0, P: 0.1, A: 0.2 },
97841
+ AC: { L: 0, H: 0.1 },
97842
+ AT: { N: 0, P: 0.1 },
97843
+ VC: { H: 0, L: 0.1, N: 0.2 },
97844
+ VI: { H: 0, L: 0.1, N: 0.2 },
97845
+ VA: { H: 0, L: 0.1, N: 0.2 },
97846
+ SC: { H: 0.1, L: 0.2, N: 0.3 },
97847
+ SI: { S: 0, H: 0.1, L: 0.2, N: 0.3 },
97848
+ SA: { S: 0, H: 0.1, L: 0.2, N: 0.3 },
97849
+ CR: { H: 0, M: 0.1, L: 0.2 },
97850
+ IR: { H: 0, M: 0.1, L: 0.2 },
97851
+ AR: { H: 0, M: 0.1, L: 0.2 },
97852
+ E: { A: 0, P: 0.1, U: 0.2 }
97853
+ };
97854
+ MAX_SEVERITY = {
97855
+ eq1: { 0: 1, 1: 4, 2: 5 },
97856
+ eq2: { 0: 1, 1: 2 },
97857
+ eq3eq6: {
97858
+ 0: { 0: 7, 1: 6 },
97859
+ 1: { 0: 8, 1: 8 },
97860
+ 2: { 1: 10 }
97861
+ },
97862
+ eq4: { 0: 6, 1: 5, 2: 4 },
97863
+ eq5: { 0: 1, 1: 1, 2: 1 }
97864
+ };
97865
+ EPSILON = Math.pow(10, -6);
97866
+ });
97867
+
97868
+ // src/lib/cvss/calculator.ts
97869
+ function buildVectorString(metrics) {
97870
+ const parts = ["CVSS:4.0"];
97871
+ for (const metric of BASE_METRICS) {
97872
+ const value = metrics[metric];
97873
+ if (value !== undefined) {
97874
+ parts.push(`${metric}:${value}`);
97875
+ }
97876
+ }
97877
+ for (const metric of THREAT_METRICS) {
97878
+ const value = metrics[metric];
97879
+ if (value !== undefined && value !== "X") {
97880
+ parts.push(`${metric}:${value}`);
97881
+ }
97882
+ }
97883
+ for (const metric of ENVIRONMENTAL_METRICS) {
97884
+ const value = metrics[metric];
97885
+ if (value !== undefined && value !== "X") {
97886
+ parts.push(`${metric}:${value}`);
97887
+ }
97888
+ }
97889
+ for (const metric of SUPPLEMENTAL_METRICS) {
97890
+ const value = metrics[metric];
97891
+ if (value !== undefined && value !== "X") {
97892
+ parts.push(`${metric}:${value}`);
97893
+ }
97894
+ }
97895
+ return parts.join("/");
97896
+ }
97897
+ function getEffectiveValue(metrics, baseMetric, modifiedMetric) {
97898
+ if (modifiedMetric) {
97899
+ const modValue = metrics[modifiedMetric];
97900
+ if (modValue && modValue !== "X") {
97901
+ return modValue;
97902
+ }
97903
+ }
97904
+ return metrics[baseMetric] ?? "";
97905
+ }
97906
+ function computeEQ1(metrics) {
97907
+ const av = getEffectiveValue(metrics, "AV", "MAV");
97908
+ const pr = getEffectiveValue(metrics, "PR", "MPR");
97909
+ const ui = getEffectiveValue(metrics, "UI", "MUI");
97910
+ if (av === "N" && pr === "N" && ui === "N") {
97911
+ return 0;
97912
+ }
97913
+ if (!(av === "P" || pr === "H" || ui === "A")) {
97914
+ return 1;
97915
+ }
97916
+ return 2;
97917
+ }
97918
+ function computeEQ2(metrics) {
97919
+ const ac = getEffectiveValue(metrics, "AC", "MAC");
97920
+ const at = getEffectiveValue(metrics, "AT", "MAT");
97921
+ if (ac === "L" && at === "N") {
97922
+ return 0;
97923
+ }
97924
+ return 1;
97925
+ }
97926
+ function computeEQ3(metrics) {
97927
+ const vc = getEffectiveValue(metrics, "VC", "MVC");
97928
+ const vi = getEffectiveValue(metrics, "VI", "MVI");
97929
+ const va = getEffectiveValue(metrics, "VA", "MVA");
97930
+ if (vc === "H" && vi === "H") {
97931
+ return 0;
97932
+ }
97933
+ if (vc === "H" || vi === "H" || va === "H") {
97934
+ return 1;
97935
+ }
97936
+ return 2;
97937
+ }
97938
+ function computeEQ4(metrics) {
97939
+ const msi = metrics.MSI || "X";
97940
+ const msa = metrics.MSA || "X";
97941
+ const sc = getEffectiveValue(metrics, "SC", "MSC");
97942
+ const si = msi !== "X" ? msi : metrics.SI;
97943
+ const sa = msa !== "X" ? msa : metrics.SA;
97944
+ if (msi === "S" || msa === "S") {
97945
+ return 0;
97946
+ }
97947
+ if (sc === "H" || si === "H" || sa === "H") {
97948
+ return 1;
97949
+ }
97950
+ return 2;
97951
+ }
97952
+ function computeEQ5(metrics) {
97953
+ const e = metrics.E || "A";
97954
+ if (e === "A" || e === "X") {
97955
+ return 0;
97956
+ }
97957
+ if (e === "P") {
97958
+ return 1;
97959
+ }
97960
+ return 2;
97961
+ }
97962
+ function computeEQ6(metrics) {
97963
+ const cr = metrics.CR || "H";
97964
+ const ir = metrics.IR || "H";
97965
+ const ar = metrics.AR || "H";
97966
+ const vc = getEffectiveValue(metrics, "VC", "MVC");
97967
+ const vi = getEffectiveValue(metrics, "VI", "MVI");
97968
+ const va = getEffectiveValue(metrics, "VA", "MVA");
97969
+ if (cr === "H" && vc === "H" || ir === "H" && vi === "H" || ar === "H" && va === "H") {
97970
+ return 0;
97971
+ }
97972
+ return 1;
97973
+ }
97974
+ function computeMacroVector(metrics) {
97975
+ const eq1 = computeEQ1(metrics);
97976
+ const eq2 = computeEQ2(metrics);
97977
+ const eq3 = computeEQ3(metrics);
97978
+ const eq4 = computeEQ4(metrics);
97979
+ const eq5 = computeEQ5(metrics);
97980
+ const eq6 = computeEQ6(metrics);
97981
+ return `${eq1}${eq2}${eq3}${eq4}${eq5}${eq6}`;
97982
+ }
97983
+ function hasNoImpact(metrics) {
97984
+ const vc = getEffectiveValue(metrics, "VC", "MVC");
97985
+ const vi = getEffectiveValue(metrics, "VI", "MVI");
97986
+ const va = getEffectiveValue(metrics, "VA", "MVA");
97987
+ const sc = getEffectiveValue(metrics, "SC", "MSC");
97988
+ const si = metrics.MSI !== "X" && metrics.MSI ? metrics.MSI : metrics.SI;
97989
+ const sa = metrics.MSA !== "X" && metrics.MSA ? metrics.MSA : metrics.SA;
97990
+ return vc === "N" && vi === "N" && va === "N" && sc === "N" && si === "N" && sa === "N";
97991
+ }
97992
+ function getMetricDistance(metrics, metric) {
97993
+ const levels = METRIC_LEVELS[metric];
97994
+ if (!levels)
97995
+ return 0;
97996
+ let value;
97997
+ const modifiedMetric = "M" + metric;
97998
+ const metricsRecord = metrics;
97999
+ if (metricsRecord[modifiedMetric] && metricsRecord[modifiedMetric] !== "X") {
98000
+ value = metricsRecord[modifiedMetric] ?? "";
98001
+ } else {
98002
+ value = metricsRecord[metric] ?? "";
98003
+ }
98004
+ if (metric === "E" && (!value || value === "X")) {
98005
+ value = "A";
98006
+ }
98007
+ if ((metric === "CR" || metric === "IR" || metric === "AR") && (!value || value === "X")) {
98008
+ value = "H";
98009
+ }
98010
+ return levels[value] || 0;
98011
+ }
98012
+ function getNextLowerScore(macroVector, position) {
98013
+ const digits = macroVector.split("").map(Number);
98014
+ digits[position]++;
98015
+ const nextMacroVector = digits.join("");
98016
+ return MACROVECTOR_LOOKUP[nextMacroVector] ?? null;
98017
+ }
98018
+ function interpolateScore(metrics, macroVector, baseScore) {
98019
+ const eq1 = parseInt(macroVector[0]);
98020
+ const eq2 = parseInt(macroVector[1]);
98021
+ const eq3 = parseInt(macroVector[2]);
98022
+ const eq4 = parseInt(macroVector[3]);
98023
+ const eq5 = parseInt(macroVector[4]);
98024
+ const eq6 = parseInt(macroVector[5]);
98025
+ let meanDistance = 0;
98026
+ let eqCount = 0;
98027
+ const eq1NextLower = getNextLowerScore(macroVector, 0);
98028
+ if (eq1NextLower !== null) {
98029
+ const msd = baseScore - eq1NextLower;
98030
+ const maxDepth = MAX_SEVERITY.eq1[eq1] || 1;
98031
+ const avDist = getMetricDistance(metrics, "AV");
98032
+ const prDist = getMetricDistance(metrics, "PR");
98033
+ const uiDist = getMetricDistance(metrics, "UI");
98034
+ const severityDist = avDist + prDist + uiDist;
98035
+ const normalizedDist = severityDist / (maxDepth * STEP);
98036
+ meanDistance += msd * normalizedDist;
98037
+ eqCount++;
98038
+ }
98039
+ const eq2NextLower = getNextLowerScore(macroVector, 1);
98040
+ if (eq2NextLower !== null) {
98041
+ const msd = baseScore - eq2NextLower;
98042
+ const maxDepth = MAX_SEVERITY.eq2[eq2] || 1;
98043
+ const acDist = getMetricDistance(metrics, "AC");
98044
+ const atDist = getMetricDistance(metrics, "AT");
98045
+ const severityDist = acDist + atDist;
98046
+ const normalizedDist = severityDist / (maxDepth * STEP);
98047
+ meanDistance += msd * normalizedDist;
98048
+ eqCount++;
98049
+ }
98050
+ const eq3eq6MaxSeverity = MAX_SEVERITY.eq3eq6;
98051
+ if (eq3eq6MaxSeverity[eq3] && eq3eq6MaxSeverity[eq3][eq6] !== undefined) {
98052
+ const eq3NextLower = getNextLowerScore(macroVector, 2);
98053
+ if (eq3NextLower !== null) {
98054
+ const msd = baseScore - eq3NextLower;
98055
+ const maxDepth = eq3eq6MaxSeverity[eq3][eq6] || 1;
98056
+ const vcDist = getMetricDistance(metrics, "VC");
98057
+ const viDist = getMetricDistance(metrics, "VI");
98058
+ const vaDist = getMetricDistance(metrics, "VA");
98059
+ const crDist = getMetricDistance(metrics, "CR");
98060
+ const irDist = getMetricDistance(metrics, "IR");
98061
+ const arDist = getMetricDistance(metrics, "AR");
98062
+ const severityDist = vcDist + viDist + vaDist + crDist + irDist + arDist;
98063
+ const normalizedDist = severityDist / (maxDepth * STEP);
98064
+ meanDistance += msd * normalizedDist;
98065
+ eqCount++;
98066
+ }
98067
+ }
98068
+ const eq4NextLower = getNextLowerScore(macroVector, 3);
98069
+ if (eq4NextLower !== null) {
98070
+ const msd = baseScore - eq4NextLower;
98071
+ const maxDepth = MAX_SEVERITY.eq4[eq4] || 1;
98072
+ const scDist = getMetricDistance(metrics, "SC");
98073
+ const siDist = getMetricDistance(metrics, "SI");
98074
+ const saDist = getMetricDistance(metrics, "SA");
98075
+ const severityDist = scDist + siDist + saDist;
98076
+ const normalizedDist = severityDist / (maxDepth * STEP);
98077
+ meanDistance += msd * normalizedDist;
98078
+ eqCount++;
98079
+ }
98080
+ const eq5NextLower = getNextLowerScore(macroVector, 4);
98081
+ if (eq5NextLower !== null) {
98082
+ const msd = baseScore - eq5NextLower;
98083
+ const maxDepth = MAX_SEVERITY.eq5[eq5] || 1;
98084
+ const eDist = getMetricDistance(metrics, "E");
98085
+ const normalizedDist = eDist / (maxDepth * STEP);
98086
+ meanDistance += msd * normalizedDist;
98087
+ eqCount++;
98088
+ }
98089
+ const avgDistance = eqCount > 0 ? meanDistance / eqCount : 0;
98090
+ let finalScore = baseScore - avgDistance;
98091
+ finalScore = Math.max(0, Math.min(10, finalScore));
98092
+ finalScore = Math.round(finalScore * 10) / 10;
98093
+ return finalScore;
98094
+ }
98095
+ function calculateCVSS4Score(metrics) {
98096
+ if (hasNoImpact(metrics)) {
98097
+ return {
98098
+ score: 0,
98099
+ severity: "NONE",
98100
+ vectorString: buildVectorString(metrics),
98101
+ metrics,
98102
+ scoreType: getScoreType(metrics)
98103
+ };
98104
+ }
98105
+ const macroVector = computeMacroVector(metrics);
98106
+ const baseScore = MACROVECTOR_LOOKUP[macroVector];
98107
+ if (baseScore === undefined) {
98108
+ throw new Error(`Invalid MacroVector: ${macroVector}`);
98109
+ }
98110
+ const score = interpolateScore(metrics, macroVector, baseScore);
98111
+ const severity = getSeverityFromScore(score);
98112
+ const scoreType = getScoreType(metrics);
98113
+ return {
98114
+ score,
98115
+ severity,
98116
+ vectorString: buildVectorString(metrics),
98117
+ metrics,
98118
+ scoreType
98119
+ };
98120
+ }
98121
+ function getScoreType(metrics) {
98122
+ const hasThreat = metrics.E !== undefined && metrics.E !== "X";
98123
+ 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";
98124
+ if (hasThreat && hasEnvironmental)
98125
+ return "CVSS-BTE";
98126
+ if (hasEnvironmental)
98127
+ return "CVSS-BE";
98128
+ if (hasThreat)
98129
+ return "CVSS-BT";
98130
+ return "CVSS-B";
98131
+ }
98132
+ var BASE_METRICS, THREAT_METRICS, ENVIRONMENTAL_METRICS, SUPPLEMENTAL_METRICS;
98133
+ var init_calculator = __esm(() => {
98134
+ init_types4();
98135
+ init_macrovector_scores();
98136
+ BASE_METRICS = [
98137
+ "AV",
98138
+ "AC",
98139
+ "AT",
98140
+ "PR",
98141
+ "UI",
98142
+ "VC",
98143
+ "VI",
98144
+ "VA",
98145
+ "SC",
98146
+ "SI",
98147
+ "SA"
98148
+ ];
98149
+ THREAT_METRICS = ["E"];
98150
+ ENVIRONMENTAL_METRICS = [
98151
+ "CR",
98152
+ "IR",
98153
+ "AR",
98154
+ "MAV",
98155
+ "MAC",
98156
+ "MAT",
98157
+ "MPR",
98158
+ "MUI",
98159
+ "MVC",
98160
+ "MVI",
98161
+ "MVA",
98162
+ "MSC",
98163
+ "MSI",
98164
+ "MSA"
98165
+ ];
98166
+ SUPPLEMENTAL_METRICS = ["S", "AU", "R", "V", "RE", "U"];
98167
+ });
98168
+
98169
+ // src/lib/cvss/index.ts
98170
+ var init_cvss = __esm(() => {
98171
+ init_types4();
98172
+ init_calculator();
98173
+ init_macrovector_scores();
98174
+ });
98175
+
98176
+ // src/core/agents/specialized/cvssScorer/index.ts
98177
+ async function scoreFindingWithCVSS(input, model, authConfig) {
98178
+ const prompt = buildScoringPrompt(input);
98179
+ const assessment = await generateObjectResponse({
98180
+ model,
98181
+ schema: CVSSMetricsOutputSchema,
98182
+ prompt,
98183
+ system: CVSS_SCORER_SYSTEM_PROMPT,
98184
+ authConfig
98185
+ });
98186
+ const cvssResult = calculateCVSS4Score({
98187
+ ...assessment.metrics
98188
+ });
98189
+ return {
98190
+ score: cvssResult.score,
98191
+ severity: cvssResult.severity,
98192
+ vectorString: cvssResult.vectorString,
98193
+ metrics: cvssResult.metrics,
98194
+ scoreType: cvssResult.scoreType,
98195
+ reasoning: assessment.reasoning
98196
+ };
98197
+ }
98198
+ function buildScoringPrompt(input) {
98199
+ const { finding, agentMessages } = input;
98200
+ let prompt = `# Vulnerability Finding to Score
98201
+
98202
+ ## Finding Details
98203
+
98204
+ **Title:** ${finding.title}
98205
+ **Vulnerability Class:** ${finding.vulnerabilityClass || "Unknown"}
98206
+ **Endpoint:** ${finding.endpoint}
98207
+
98208
+ ### Description
98209
+ ${finding.description}
98210
+
98211
+ ### Impact Assessment
98212
+ ${finding.impact}
98213
+
98214
+ ### Evidence (POC Output)
98215
+ \`\`\`
98216
+ ${finding.evidence}
98217
+ \`\`\`
98218
+
98219
+ `;
98220
+ if (agentMessages && agentMessages.length > 0) {
98221
+ prompt += `## Discovery Context
98222
+
98223
+ The following is a summary of how this vulnerability was discovered (from the testing agent's conversation):
98224
+
98225
+ `;
98226
+ const contextSummary = extractContextSummary(agentMessages);
98227
+ prompt += contextSummary;
98228
+ }
98229
+ prompt += `
98230
+ ## Task
98231
+
98232
+ Analyze this vulnerability finding and determine the appropriate CVSS 4.0 metrics.
98233
+
98234
+ Consider:
98235
+ 1. How the vulnerability is exploited (attack vector, complexity, requirements)
98236
+ 2. What privileges/authentication were needed
98237
+ 3. Whether user interaction is required
98238
+ 4. The actual impact demonstrated in the evidence
98239
+ 5. Potential for lateral movement or subsequent system compromise
98240
+
98241
+ Provide your metrics assessment and brief reasoning.
98242
+ `;
98243
+ return prompt;
98244
+ }
98245
+ function extractContextSummary(messages) {
98246
+ const contextParts = [];
98247
+ let foundToolCalls = 0;
98248
+ const maxToolCalls = 5;
98249
+ for (const message of messages) {
98250
+ if (foundToolCalls >= maxToolCalls)
98251
+ break;
98252
+ if (message.role === "assistant" && typeof message.content === "string") {
98253
+ const hypothesisMatch = message.content.match(/HYPOTHESIS:[\s\S]*?(?=VALIDATION:|$)/);
98254
+ const validationMatch = message.content.match(/VALIDATION:[\s\S]*?(?=HYPOTHESIS:|$)/);
98255
+ if (hypothesisMatch) {
98256
+ contextParts.push(`- ${hypothesisMatch[0].substring(0, 300)}...`);
98257
+ }
98258
+ if (validationMatch) {
98259
+ contextParts.push(`- ${validationMatch[0].substring(0, 300)}...`);
98260
+ }
98261
+ }
98262
+ if (message.role === "assistant" && Array.isArray(message.content)) {
98263
+ for (const rawPart of message.content) {
98264
+ const part = rawPart;
98265
+ if (part.type === "tool-call" && part.toolName) {
98266
+ const input = part.input;
98267
+ const desc = input?.toolCallDescription || `Used ${part.toolName}`;
98268
+ contextParts.push(`- Tool: ${String(desc)}`);
98269
+ foundToolCalls++;
98270
+ }
98271
+ }
98272
+ }
98273
+ }
98274
+ if (contextParts.length === 0) {
98275
+ return `No additional context available from testing conversation.
98276
+ `;
98277
+ }
98278
+ return contextParts.join(`
98279
+ `) + `
98280
+ `;
98281
+ }
98282
+ 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.
98283
+
98284
+ ## CVSS 4.0 Metrics Guide
98285
+
98286
+ ### Attack Vector (AV)
98287
+ - **N (Network)**: Remotely exploitable over the internet (web app vulns, network services)
98288
+ - **A (Adjacent)**: Requires shared physical or logical network (same WiFi, VLAN)
98289
+ - **L (Local)**: Requires local access or user interaction to deliver payload
98290
+ - **P (Physical)**: Requires physical hardware access
98291
+
98292
+ ### Attack Complexity (AC)
98293
+ - **L (Low)**: No special preparation needed, works reliably
98294
+ - **H (High)**: Requires race conditions, bypassing defenses, or specific configurations
98295
+
98296
+ ### Attack Requirements (AT)
98297
+ - **N (None)**: Works under normal conditions
98298
+ - **P (Present)**: Requires specific deployment conditions (race window, man-in-the-middle position)
98299
+
98300
+ ### Privileges Required (PR)
98301
+ - **N (None)**: Unauthenticated attack
98302
+ - **L (Low)**: Requires basic user-level privileges
98303
+ - **H (High)**: Requires administrative/root privileges
98304
+
98305
+ ### User Interaction (UI)
98306
+ - **N (None)**: No user action required
98307
+ - **P (Passive)**: User visits a page, opens a file, or is on a vulnerable session
98308
+ - **A (Active)**: User must click a link, dismiss warnings, or actively interact
98309
+
98310
+ ### Confidentiality Impact (VC - Vulnerable System, SC - Subsequent Systems)
98311
+ - **H (High)**: Complete loss of confidentiality (full data access, credential theft)
98312
+ - **L (Low)**: Limited data exposure (some info leak but not critical)
98313
+ - **N (None)**: No confidentiality impact
98314
+
98315
+ ### Integrity Impact (VI - Vulnerable System, SI - Subsequent Systems)
98316
+ - **H (High)**: Complete loss of integrity (arbitrary modification, code execution)
98317
+ - **L (Low)**: Limited modification capability
98318
+ - **N (None)**: No integrity impact
98319
+
98320
+ ### Availability Impact (VA - Vulnerable System, SA - Subsequent Systems)
98321
+ - **H (High)**: Complete denial of service
98322
+ - **L (Low)**: Reduced performance or intermittent availability
98323
+ - **N (None)**: No availability impact
98324
+
98325
+ ### Exploit Maturity (E)
98326
+ - **A (Attacked)**: Working exploit exists (POC confirmed vulnerability)
98327
+ - **P (POC)**: Proof-of-concept code exists but may not be weaponized
98328
+ - **U (Unreported)**: No known public exploit
98329
+
98330
+ ## Vulnerability Class Guidelines
98331
+
98332
+ ### SQL Injection (sqli)
98333
+ - Typically: AV:N, AC:L, AT:N, PR varies, UI:N
98334
+ - VC:H (data access), VI:H (data modification), VA:L-H (depending on impact)
98335
+ - SC/SI/SA: Usually N unless database is shared
98336
+
98337
+ ### Cross-Site Scripting (xss)
98338
+ - Reflected: AV:N, AC:L, AT:N, PR:N, UI:A (user must click)
98339
+ - Stored: AV:N, AC:L, AT:N, PR varies, UI:P (user visits page)
98340
+ - VC:L (session theft), VI:L (DOM modification), VA:N
98341
+ - SC/SI/SA: Usually N (client-side only)
98342
+
98343
+ ### Command Injection / RCE
98344
+ - Typically: AV:N, AC:L, AT:N, PR varies, UI:N
98345
+ - VC:H, VI:H, VA:H (complete system compromise)
98346
+ - SC/SI/SA: Potentially H if can pivot
98347
+
98348
+ ### IDOR / Access Control
98349
+ - Typically: AV:N, AC:L, AT:N, PR:L (needs some access), UI:N
98350
+ - Impact varies based on what data is accessed
98351
+
98352
+ ### SSRF
98353
+ - Typically: AV:N, AC:L, AT:N, PR varies, UI:N
98354
+ - VC on vulnerable: L-N, SC on subsequent: H (internal network access)
98355
+
98356
+ ### Path Traversal / LFI
98357
+ - Typically: AV:N, AC:L, AT:N, PR varies, UI:N
98358
+ - VC:H (file read), VI:N (unless write), VA:N
98359
+
98360
+ ## Analysis Instructions
98361
+
98362
+ 1. Read the finding description and evidence carefully
98363
+ 2. Consider the attack vector based on how the vulnerability was exploited
98364
+ 3. Assess complexity based on whether special conditions were needed
98365
+ 4. Determine privileges based on authentication requirements
98366
+ 5. Evaluate user interaction based on exploit mechanics
98367
+ 6. Assess impact on both the vulnerable system AND potential subsequent systems
98368
+ 7. Since a POC exists and confirmed the vulnerability, E should typically be 'A'
98369
+
98370
+ Always provide brief reasoning explaining your key decisions.`;
98371
+ var init_cvssScorer = __esm(() => {
98372
+ init_zod();
98373
+ init_ai2();
98374
+ init_cvss();
98375
+ CVSSMetricsOutputSchema = exports_external.object({
98376
+ metrics: exports_external.object({
98377
+ 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"),
98378
+ AC: exports_external.enum(["L", "H"]).describe("Attack Complexity: L=Low (no special conditions), H=High (requires specific conditions/bypassing)"),
98379
+ AT: exports_external.enum(["N", "P"]).describe("Attack Requirements: N=None (works in most configs), P=Present (requires race conditions/specific setup)"),
98380
+ PR: exports_external.enum(["N", "L", "H"]).describe("Privileges Required: N=None (unauthenticated), L=Low (basic user), H=High (admin)"),
98381
+ UI: exports_external.enum(["N", "P", "A"]).describe("User Interaction: N=None, P=Passive (user visits page), A=Active (user must click/interact)"),
98382
+ VC: exports_external.enum(["H", "L", "N"]).describe("Confidentiality Impact on Vulnerable System: H=High (total loss), L=Low (partial), N=None"),
98383
+ VI: exports_external.enum(["H", "L", "N"]).describe("Integrity Impact on Vulnerable System: H=High (total loss), L=Low (partial), N=None"),
98384
+ VA: exports_external.enum(["H", "L", "N"]).describe("Availability Impact on Vulnerable System: H=High (total loss), L=Low (partial), N=None"),
98385
+ SC: exports_external.enum(["H", "L", "N"]).describe("Confidentiality Impact on Subsequent Systems: H=High, L=Low, N=None (no pivoting)"),
98386
+ SI: exports_external.enum(["H", "L", "N"]).describe("Integrity Impact on Subsequent Systems: H=High, L=Low, N=None"),
98387
+ SA: exports_external.enum(["H", "L", "N"]).describe("Availability Impact on Subsequent Systems: H=High, L=Low, N=None"),
98388
+ E: exports_external.enum(["A", "P", "U"]).describe("Exploit Maturity: A=Attacked (working exploit exists), P=POC available, U=Unreported")
98389
+ }),
98390
+ reasoning: exports_external.string().describe("Brief explanation (2-3 sentences) of the key factors that influenced the metric choices")
98391
+ });
98392
+ });
98393
+
96798
98394
  // src/core/agents/offSecAgent/tools/documentFinding.ts
96799
98395
  import { join as join5 } from "path";
96800
98396
  import { writeFileSync as writeFileSync4, appendFileSync as appendFileSync2 } from "fs";
@@ -96840,11 +98436,37 @@ FINDING STRUCTURE:
96840
98436
  }
96841
98437
  }
96842
98438
  const timestamp = new Date().toISOString();
98439
+ let cvssResult;
98440
+ try {
98441
+ cvssResult = await scoreFindingWithCVSS({
98442
+ finding: {
98443
+ title: finding.title,
98444
+ description: finding.description,
98445
+ impact: finding.impact,
98446
+ evidence: finding.evidence,
98447
+ endpoint: finding.endpoint,
98448
+ remediation: finding.remediation
98449
+ },
98450
+ agentMessages: []
98451
+ }, ctx4.model, ctx4.authConfig);
98452
+ } catch (err) {
98453
+ console.warn("CVSS scoring failed, proceeding without score:", err instanceof Error ? err.message : err);
98454
+ }
96843
98455
  const findingWithMeta = {
96844
98456
  ...finding,
96845
98457
  timestamp,
96846
98458
  sessionId: session.id,
96847
- target: session.targets[0]
98459
+ target: session.targets[0],
98460
+ ...cvssResult && {
98461
+ cvss: {
98462
+ score: cvssResult.score,
98463
+ severity: cvssResult.severity,
98464
+ vectorString: cvssResult.vectorString,
98465
+ metrics: cvssResult.metrics,
98466
+ scoreType: cvssResult.scoreType,
98467
+ reasoning: cvssResult.reasoning
98468
+ }
98469
+ }
96848
98470
  };
96849
98471
  const safeTitle = finding.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").substring(0, 50);
96850
98472
  const findingId = `${timestamp.split("T")[0]}-${safeTitle}`;
@@ -96854,9 +98476,21 @@ FINDING STRUCTURE:
96854
98476
  const mdPath = join5(session.findingsPath, mdFilename);
96855
98477
  try {
96856
98478
  writeFileSync4(jsonPath, JSON.stringify(findingWithMeta, null, 2));
98479
+ const cvssLine = cvssResult ? `
98480
+ **CVSS 4.0 Score:** ${cvssResult.score} (${cvssResult.severity})
98481
+ **Vector:** \`${cvssResult.vectorString}\` ` : "";
98482
+ const cvssSection = cvssResult ? `
98483
+ ## CVSS 4.0 Assessment
98484
+
98485
+ **Score:** ${cvssResult.score} / 10.0 (${cvssResult.severity})
98486
+ **Vector:** \`${cvssResult.vectorString}\`
98487
+ **Score Type:** ${cvssResult.scoreType}
98488
+
98489
+ **Reasoning:** ${cvssResult.reasoning}
98490
+ ` : "";
96857
98491
  const markdown = `# ${finding.title}
96858
98492
 
96859
- **Severity:** ${finding.severity}
98493
+ **Severity:** ${finding.severity} ${cvssLine}
96860
98494
  **Target:** ${session.targets[0]}
96861
98495
  **Endpoint:** ${finding.endpoint}
96862
98496
  **Date:** ${timestamp}
@@ -96869,7 +98503,7 @@ ${finding.description}
96869
98503
  ## Impact
96870
98504
 
96871
98505
  ${finding.impact}
96872
-
98506
+ ${cvssSection}
96873
98507
  ## Evidence
96874
98508
 
96875
98509
  \`\`\`
@@ -96894,7 +98528,8 @@ ${finding.references}` : ""}
96894
98528
  `;
96895
98529
  writeFileSync4(mdPath, markdown);
96896
98530
  const summaryPath = join5(session.rootPath, "findings-summary.md");
96897
- const summaryEntry = `- [${finding.severity}] ${finding.title} - \`findings/${mdFilename}\`
98531
+ const cvssTag = cvssResult ? ` (CVSS ${cvssResult.score})` : "";
98532
+ const summaryEntry = `- [${finding.severity}]${cvssTag} ${finding.title} - \`findings/${mdFilename}\`
96898
98533
  `;
96899
98534
  try {
96900
98535
  appendFileSync2(summaryPath, summaryEntry);
@@ -96936,6 +98571,7 @@ var documentVulnerabilityInputSchema;
96936
98571
  var init_documentFinding = __esm(() => {
96937
98572
  init_dist5();
96938
98573
  init_zod();
98574
+ init_cvssScorer();
96939
98575
  documentVulnerabilityInputSchema = exports_external.object({
96940
98576
  title: exports_external.string().describe("Finding title"),
96941
98577
  severity: exports_external.enum(["CRITICAL", "HIGH", "MEDIUM", "LOW"]),
@@ -99103,7 +100739,7 @@ For each app you identified, spawn a coding agent with a detailed objective. The
99103
100739
 
99104
100740
  // src/core/agents/specialized/whiteboxAttackSurface/types.ts
99105
100741
  var EndpointSchema, AppSchema, WhiteboxAttackSurfaceResultSchema;
99106
- var init_types4 = __esm(() => {
100742
+ var init_types5 = __esm(() => {
99107
100743
  init_zod();
99108
100744
  EndpointSchema = exports_external.object({
99109
100745
  method: exports_external.string().describe("HTTP method (GET, POST, PUT, DELETE, etc.) or 'PAGE' for web pages"),
@@ -99165,7 +100801,7 @@ var init_agent2 = __esm(() => {
99165
100801
  init_dist5();
99166
100802
  init_dist5();
99167
100803
  init_offensiveSecurityAgent();
99168
- init_types4();
100804
+ init_types5();
99169
100805
  WhiteboxAttackSurfaceAgent = class WhiteboxAttackSurfaceAgent extends OffensiveSecurityAgent {
99170
100806
  constructor(opts) {
99171
100807
  const {
@@ -99718,6 +101354,13 @@ var PentestResponseSchema, TargetedPentestAgent, PENTEST_SYSTEM_PROMPT = `You ar
99718
101354
 
99719
101355
  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
101356
 
101357
+ CRITICAL — Source Code Prohibition:
101358
+ - You are performing a BLACKBOX penetration test. You must NEVER read, view, access, or analyze source code under any circumstances.
101359
+ - 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).
101360
+ - 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.
101361
+ - 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.
101362
+ - Your testing must rely exclusively on external interaction: sending requests, observing responses, and analyzing observable behavior.
101363
+
99721
101364
  Your methodology:
99722
101365
  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
101366
  2. VERIFY — Confirm the target endpoint exists and is reachable. Understand its basic behavior (response format, parameters, auth requirements).
@@ -138829,7 +140472,7 @@ var require_node3 = __commonJS((exports, module2) => {
138829
140472
  var tty = __require("tty");
138830
140473
  var util4 = __require("util");
138831
140474
  exports.init = init2;
138832
- exports.log = log;
140475
+ exports.log = log2;
138833
140476
  exports.formatArgs = formatArgs;
138834
140477
  exports.save = save;
138835
140478
  exports.load = load;
@@ -138961,7 +140604,7 @@ var require_node3 = __commonJS((exports, module2) => {
138961
140604
  }
138962
140605
  return new Date().toISOString() + " ";
138963
140606
  }
138964
- function log(...args) {
140607
+ function log2(...args) {
138965
140608
  return process.stderr.write(util4.formatWithOptions(exports.inspectOpts, ...args) + `
138966
140609
  `);
138967
140610
  }
@@ -147926,7 +149569,7 @@ var require_logging_utils = __commonJS((exports) => {
147926
149569
  exports.getDebugBackend = getDebugBackend;
147927
149570
  exports.getStructuredBackend = getStructuredBackend;
147928
149571
  exports.setBackend = setBackend;
147929
- exports.log = log;
149572
+ exports.log = log2;
147930
149573
  var events_1 = __require("events");
147931
149574
  var process3 = __importStar(__require("process"));
147932
149575
  var util4 = __importStar(__require("util"));
@@ -147953,7 +149596,7 @@ var require_logging_utils = __commonJS((exports) => {
147953
149596
  this.func.info = (...args) => this.invokeSeverity(LogSeverity.INFO, ...args);
147954
149597
  this.func.warn = (...args) => this.invokeSeverity(LogSeverity.WARNING, ...args);
147955
149598
  this.func.error = (...args) => this.invokeSeverity(LogSeverity.ERROR, ...args);
147956
- this.func.sublog = (namespace2) => log(namespace2, this.func);
149599
+ this.func.sublog = (namespace2) => log2(namespace2, this.func);
147957
149600
  }
147958
149601
  invoke(fields, ...args) {
147959
149602
  if (this.upstream) {
@@ -148114,7 +149757,7 @@ var require_logging_utils = __commonJS((exports) => {
148114
149757
  cachedBackend = backend;
148115
149758
  loggerCache.clear();
148116
149759
  }
148117
- function log(namespace, parent) {
149760
+ function log2(namespace, parent) {
148118
149761
  if (!cachedBackend) {
148119
149762
  const enablesFlag = process3.env[exports.env.nodeEnables];
148120
149763
  if (!enablesFlag) {
@@ -148254,7 +149897,7 @@ var require_src6 = __commonJS((exports) => {
148254
149897
  exports.HEADER_NAME = "Metadata-Flavor";
148255
149898
  exports.HEADER_VALUE = "Google";
148256
149899
  exports.HEADERS = Object.freeze({ [exports.HEADER_NAME]: exports.HEADER_VALUE });
148257
- var log = logger.log("gcp-metadata");
149900
+ var log2 = logger.log("gcp-metadata");
148258
149901
  exports.METADATA_SERVER_DETECTION = Object.freeze({
148259
149902
  "assume-present": "don't try to ping the metadata server, but assume it's present",
148260
149903
  none: "don't try to ping the metadata server, but don't try to use it either",
@@ -148317,9 +149960,9 @@ var require_src6 = __commonJS((exports) => {
148317
149960
  responseType: "text",
148318
149961
  timeout: requestTimeout()
148319
149962
  };
148320
- log.info("instance request %j", req);
149963
+ log2.info("instance request %j", req);
148321
149964
  const res = await requestMethod(req);
148322
- log.info("instance metadata is %s", res.data);
149965
+ log2.info("instance metadata is %s", res.data);
148323
149966
  const metadataFlavor = res.headers.get(exports.HEADER_NAME);
148324
149967
  if (metadataFlavor !== exports.HEADER_VALUE) {
148325
149968
  throw new RangeError(`Invalid response from metadata service: incorrect ${exports.HEADER_NAME} header. Expected '${exports.HEADER_VALUE}', got ${metadataFlavor ? `'${metadataFlavor}'` : "no header"}`);
@@ -156447,7 +158090,7 @@ var require_http2 = __commonJS((exports) => {
156447
158090
  var process3 = __require("process");
156448
158091
  var util_1 = require_util7();
156449
158092
  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;
158093
+ var DEBUG2 = !!process3.env.HTTP2_DEBUG;
156451
158094
  exports.sessions = {};
156452
158095
  async function request(config3) {
156453
158096
  const opts = extend3(true, {}, config3);
@@ -156561,7 +158204,7 @@ var require_http2 = __commonJS((exports) => {
156561
158204
  }
156562
158205
  function _getClient(host) {
156563
158206
  if (!exports.sessions[host]) {
156564
- if (DEBUG) {
158207
+ if (DEBUG2) {
156565
158208
  console.log(`Creating client for ${host}`);
156566
158209
  }
156567
158210
  const session = http22.connect(`https://${host}`);
@@ -156574,7 +158217,7 @@ var require_http2 = __commonJS((exports) => {
156574
158217
  });
156575
158218
  exports.sessions[host] = { session };
156576
158219
  } else {
156577
- if (DEBUG) {
158220
+ if (DEBUG2) {
156578
158221
  console.log(`Used cached client for ${host}`);
156579
158222
  }
156580
158223
  }
@@ -156587,17 +158230,17 @@ var require_http2 = __commonJS((exports) => {
156587
158230
  }
156588
158231
  const { session } = sessionData;
156589
158232
  delete exports.sessions[url2.host];
156590
- if (DEBUG) {
158233
+ if (DEBUG2) {
156591
158234
  console.error(`Closing ${url2.host}`);
156592
158235
  }
156593
158236
  session.close(() => {
156594
- if (DEBUG) {
158237
+ if (DEBUG2) {
156595
158238
  console.error(`Closed ${url2.host}`);
156596
158239
  }
156597
158240
  });
156598
158241
  setTimeout(() => {
156599
158242
  if (session && !session.destroyed) {
156600
- if (DEBUG) {
158243
+ if (DEBUG2) {
156601
158244
  console.log(`Forcing close ${url2.host}`);
156602
158245
  }
156603
158246
  if (session) {
@@ -166108,11 +167751,11 @@ var require_tools = __commonJS((exports, module2) => {
166108
167751
  }
166109
167752
  }
166110
167753
  }
166111
- function buildFormatters(level, bindings, log) {
167754
+ function buildFormatters(level, bindings, log2) {
166112
167755
  return {
166113
167756
  level,
166114
167757
  bindings,
166115
- log
167758
+ log: log2
166116
167759
  };
166117
167760
  }
166118
167761
  function normalizeDestFileDescriptor(destination) {
@@ -166457,8 +168100,8 @@ var require_proto = __commonJS((exports, module2) => {
166457
168100
  } else
166458
168101
  instance[serializersSym] = serializers;
166459
168102
  if (options.hasOwnProperty("formatters")) {
166460
- const { level, bindings: chindings, log } = options.formatters;
166461
- instance[formattersSym] = buildFormatters(level || formatters.level, chindings || resetChildingsFormatter, log || formatters.log);
168103
+ const { level, bindings: chindings, log: log2 } = options.formatters;
168104
+ instance[formattersSym] = buildFormatters(level || formatters.level, chindings || resetChildingsFormatter, log2 || formatters.log);
166462
168105
  } else {
166463
168106
  instance[formattersSym] = buildFormatters(formatters.level, resetChildingsFormatter, formatters.log);
166464
168107
  }
@@ -209469,219 +211112,16 @@ var useTerminalDimensions = () => {
209469
211112
  };
209470
211113
 
209471
211114
  // src/tui/index.tsx
209472
- var import_react83 = __toESM(require_react(), 1);
211115
+ var import_react87 = __toESM(require_react(), 1);
209473
211116
 
209474
211117
  // src/tui/components/footer.tsx
209475
211118
  import os5 from "os";
209476
211119
 
209477
211120
  // src/tui/context/agent.tsx
209478
211121
  init_models();
211122
+ init_config();
209479
211123
  var import_react11 = __toESM(require_react(), 1);
209480
211124
 
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
211125
  // src/core/providers/utils.ts
209686
211126
  init_models();
209687
211127
 
@@ -209716,6 +211156,12 @@ var AVAILABLE_PROVIDERS = [
209716
211156
  name: "Local LLM",
209717
211157
  description: "OpenAI-compatible local model (vLLM, LM Studio, Ollama)",
209718
211158
  requiresAPIKey: false
211159
+ },
211160
+ {
211161
+ id: "pensar",
211162
+ name: "Pensar",
211163
+ description: "Managed inference via Pensar Console (usage-based billing)",
211164
+ requiresAPIKey: true
209719
211165
  }
209720
211166
  ];
209721
211167
 
@@ -209742,12 +211188,14 @@ function isProviderConfigured(providerId, config) {
209742
211188
  return !!config.bedrockAPIKey;
209743
211189
  case "local":
209744
211190
  return !!(config.localModelUrl || config.localModelName || process.env.LOCAL_MODEL_URL);
211191
+ case "pensar":
211192
+ return !!(config.pensarAPIKey || config.accessToken);
209745
211193
  default:
209746
211194
  return false;
209747
211195
  }
209748
211196
  }
209749
211197
  function hasAnyProviderConfigured(config) {
209750
- return !!config.anthropicAPIKey || !!config.openAiAPIKey || !!config.openRouterAPIKey || !!config.bedrockAPIKey || !!config.localModelUrl || !!config.localModelName || !!process.env.LOCAL_MODEL_URL;
211198
+ return !!config.anthropicAPIKey || !!config.openAiAPIKey || !!config.openRouterAPIKey || !!config.bedrockAPIKey || !!config.localModelUrl || !!config.localModelName || !!process.env.LOCAL_MODEL_URL || !!config.pensarAPIKey || !!config.accessToken;
209751
211199
  }
209752
211200
  function getAvailableModels(config) {
209753
211201
  const models = AVAILABLE_MODELS.filter((model) => {
@@ -209768,10 +211216,17 @@ var import_jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
209768
211216
 
209769
211217
  // src/tui/context/agent.tsx
209770
211218
  var PREFERRED_DEFAULTS = {
211219
+ pensar: "pensar:anthropic.claude-haiku-4-5-20251001-v1:0",
209771
211220
  anthropic: "claude-haiku-4-5",
209772
211221
  openai: "gpt-4o-mini"
209773
211222
  };
209774
- var PROVIDER_PREFERENCE = ["anthropic", "openai", "openrouter", "bedrock"];
211223
+ var PROVIDER_PREFERENCE = [
211224
+ "pensar",
211225
+ "anthropic",
211226
+ "openai",
211227
+ "openrouter",
211228
+ "bedrock"
211229
+ ];
209775
211230
  var AgentContext = import_react11.createContext(null);
209776
211231
  function useAgent() {
209777
211232
  const context = import_react11.useContext(AgentContext);
@@ -210091,6 +211546,9 @@ function create(prefix, descending2, timestamp) {
210091
211546
  return prefixes[prefix] + "_" + timeBytes.toString("hex") + randomBase62(LENGTH - 12);
210092
211547
  }
210093
211548
 
211549
+ // src/core/session/index.ts
211550
+ init_installation();
211551
+
210094
211552
  // src/core/storage/index.ts
210095
211553
  init_zod();
210096
211554
  import os3 from "os";
@@ -210509,8 +211967,6 @@ var SessionConfigObject = zod_default.object({
210509
211967
  authenticationInstructions: zod_default.string().optional(),
210510
211968
  requestsPerSecond: zod_default.number().optional(),
210511
211969
  operatorSettings: OperatorSettingsObject.optional(),
210512
- enableCvssScoring: zod_default.boolean().optional(),
210513
- cvssModel: zod_default.string().optional(),
210514
211970
  toolsetState: ToolsetStateSchema.optional(),
210515
211971
  enumerateSubdomains: zod_default.boolean().optional(),
210516
211972
  cwd: zod_default.string().optional(),
@@ -211356,14 +212812,8 @@ async function createSwarmSessionFromFlags(flags) {
211356
212812
  return session;
211357
212813
  }
211358
212814
 
211359
- // src/core/config/index.ts
211360
- var config = {
211361
- get,
211362
- init,
211363
- update
211364
- };
211365
-
211366
212815
  // src/tui/command-registry.ts
212816
+ init_config2();
211367
212817
  var commands = [
211368
212818
  {
211369
212819
  name: "pentest",
@@ -211648,6 +213098,29 @@ var commands = [
211648
213098
  handler: async () => {
211649
213099
  process.kill(process.pid, "SIGINT");
211650
213100
  }
213101
+ },
213102
+ {
213103
+ name: "auth",
213104
+ description: "Connect to Pensar Console for managed inference",
213105
+ category: "General",
213106
+ handler: async (args, ctx3) => {
213107
+ ctx3.navigate({
213108
+ type: "base",
213109
+ path: "auth"
213110
+ });
213111
+ }
213112
+ },
213113
+ {
213114
+ name: "credits",
213115
+ aliases: ["buy"],
213116
+ description: "Buy credits / check balance",
213117
+ category: "General",
213118
+ handler: async (args, ctx3) => {
213119
+ ctx3.navigate({
213120
+ type: "base",
213121
+ path: "credits"
213122
+ });
213123
+ }
211651
213124
  }
211652
213125
  ];
211653
213126
  var commandRegistry = commands.map((config2) => (ctx3) => ({
@@ -212325,6 +213798,7 @@ function AlertDialog({
212325
213798
  }
212326
213799
 
212327
213800
  // src/tui/components/commands/config-dialog.tsx
213801
+ init_config2();
212328
213802
  function ConfigDialog() {
212329
213803
  const route = useRoute();
212330
213804
  const [open, setOpen] = import_react25.useState(false);
@@ -212408,6 +213882,7 @@ function ConfigForm({ appConfig }) {
212408
213882
  var import_react37 = __toESM(require_react(), 1);
212409
213883
 
212410
213884
  // src/tui/context/config.tsx
213885
+ init_config2();
212411
213886
  var import_react26 = __toESM(require_react(), 1);
212412
213887
  var ctx3 = import_react26.createContext(null);
212413
213888
  function ConfigProvider({ children, config: config2 }) {
@@ -213006,9 +214481,17 @@ var providerNames = {
213006
214481
  openai: "OpenAI",
213007
214482
  openrouter: "OpenRouter",
213008
214483
  bedrock: "Bedrock",
214484
+ pensar: "Pensar",
213009
214485
  local: "Local LLM"
213010
214486
  };
213011
- var providerOrder = ["anthropic", "openai", "openrouter", "bedrock", "local"];
214487
+ var providerOrder = [
214488
+ "pensar",
214489
+ "anthropic",
214490
+ "openai",
214491
+ "openrouter",
214492
+ "bedrock",
214493
+ "local"
214494
+ ];
213012
214495
  function ModelPicker({
213013
214496
  config: config2,
213014
214497
  selectedModel,
@@ -216338,6 +217821,7 @@ function SessionsBrowser() {
216338
217821
 
216339
217822
  // src/tui/components/commands/provider-manager.tsx
216340
217823
  var import_react50 = __toESM(require_react(), 1);
217824
+ init_config2();
216341
217825
  // src/tui/components/commands/provider-selection.tsx
216342
217826
  var import_react47 = __toESM(require_react(), 1);
216343
217827
  function ProviderSelection({
@@ -216525,6 +218009,8 @@ function APIKeyInput({
216525
218009
  return "Get your API key from openrouter.ai/keys";
216526
218010
  case "bedrock":
216527
218011
  return "Enter your AWS Access Key ID (configure region separately) or AWS Bedrock API Key";
218012
+ case "pensar":
218013
+ return "Get your API key from console.pensar.dev/connect (or run /auth)";
216528
218014
  default:
216529
218015
  return "Enter your API key";
216530
218016
  }
@@ -216678,6 +218164,9 @@ function ProviderManager() {
216678
218164
  }, undefined, true, undefined, this);
216679
218165
  }
216680
218166
 
218167
+ // src/tui/index.tsx
218168
+ init_config2();
218169
+
216681
218170
  // src/tui/components/switch.tsx
216682
218171
  var import_react51 = __toESM(require_react(), 1);
216683
218172
  var CaseSymbol = Symbol("Switch.Case");
@@ -217015,6 +218504,9 @@ function ErrorBoundary2({ children }) {
217015
218504
  return import_react55.default.createElement(ErrorBoundaryInner, { onError: handleError }, children);
217016
218505
  }
217017
218506
 
218507
+ // src/tui/index.tsx
218508
+ init_installation();
218509
+
217018
218510
  // src/tui/keybindings-registry.ts
217019
218511
  var keybindings = [
217020
218512
  {
@@ -217582,11 +219074,946 @@ function ModelsDisplay() {
217582
219074
  }, undefined, true, undefined, this);
217583
219075
  }
217584
219076
 
219077
+ // src/tui/components/commands/auth-flow.tsx
219078
+ var import_react60 = __toESM(require_react(), 1);
219079
+ init_config2();
219080
+ function AuthFlow() {
219081
+ const route = useRoute();
219082
+ const appConfig = useConfig();
219083
+ const isConnected = !!(appConfig.data.accessToken || appConfig.data.pensarAPIKey);
219084
+ const [step, setStep] = import_react60.useState(isConnected ? "success" : "start");
219085
+ const [error, setError] = import_react60.useState(null);
219086
+ const [authMode, setAuthMode] = import_react60.useState(null);
219087
+ const [deviceInfo, setDeviceInfo] = import_react60.useState(null);
219088
+ const [legacyDeviceInfo, setLegacyDeviceInfo] = import_react60.useState(null);
219089
+ const [workspaces, setWorkspaces] = import_react60.useState([]);
219090
+ const [selectedWorkspace, setSelectedWorkspace] = import_react60.useState(null);
219091
+ const [selectedIndex, setSelectedIndex] = import_react60.useState(0);
219092
+ const [billingUrl, setBillingUrl] = import_react60.useState(null);
219093
+ const [balance, setBalance] = import_react60.useState(null);
219094
+ const pollingRef = import_react60.useRef(null);
219095
+ const cancelledRef = import_react60.useRef(false);
219096
+ const connectedWorkspace = appConfig.data.workspaceSlug ? { name: appConfig.data.workspaceSlug, slug: appConfig.data.workspaceSlug } : null;
219097
+ const goHome = () => {
219098
+ route.navigate({ type: "base", path: "home" });
219099
+ };
219100
+ const cleanup = () => {
219101
+ cancelledRef.current = true;
219102
+ if (pollingRef.current) {
219103
+ clearTimeout(pollingRef.current);
219104
+ pollingRef.current = null;
219105
+ }
219106
+ };
219107
+ import_react60.useEffect(() => {
219108
+ return cleanup;
219109
+ }, []);
219110
+ const openUrl = (url) => {
219111
+ try {
219112
+ const platform = process.platform;
219113
+ if (platform === "darwin") {
219114
+ Bun.spawn(["open", url]);
219115
+ } else if (platform === "win32") {
219116
+ Bun.spawn(["cmd", "/c", "start", url]);
219117
+ } else {
219118
+ Bun.spawn(["xdg-open", url]);
219119
+ }
219120
+ } catch {}
219121
+ };
219122
+ const startDeviceFlow = async () => {
219123
+ setStep("requesting");
219124
+ setError(null);
219125
+ cancelledRef.current = false;
219126
+ const apiUrl = getPensarApiUrl(appConfig.data);
219127
+ try {
219128
+ const configResponse = await fetch(`${apiUrl}/api/cli/config`);
219129
+ if (configResponse.ok) {
219130
+ const cliConfig = await configResponse.json();
219131
+ const response = await fetch("https://api.workos.com/user_management/authorize/device", {
219132
+ method: "POST",
219133
+ headers: { "Content-Type": "application/json" },
219134
+ body: JSON.stringify({ client_id: cliConfig.workosClientId })
219135
+ });
219136
+ if (response.ok) {
219137
+ const data = await response.json();
219138
+ setAuthMode("workos");
219139
+ setDeviceInfo(data);
219140
+ openUrl(data.verification_uri_complete);
219141
+ setStep("polling");
219142
+ pollForToken(apiUrl, cliConfig.workosClientId, data.device_code, data.interval, data.expires_in);
219143
+ return;
219144
+ }
219145
+ }
219146
+ } catch {}
219147
+ try {
219148
+ const response = await fetch(`${apiUrl}/auth/device/code`, {
219149
+ method: "POST",
219150
+ headers: { "Content-Type": "application/json" }
219151
+ });
219152
+ if (!response.ok) {
219153
+ throw new Error("Failed to start device authorization");
219154
+ }
219155
+ const data = await response.json();
219156
+ setAuthMode("legacy");
219157
+ setLegacyDeviceInfo(data);
219158
+ openUrl(data.verificationUriComplete);
219159
+ setStep("polling");
219160
+ pollForLegacyToken(apiUrl, data.deviceCode, data.interval, data.expiresIn);
219161
+ } catch (err) {
219162
+ setError(err instanceof Error ? err.message : "Failed to start authorization");
219163
+ setStep("error");
219164
+ }
219165
+ };
219166
+ const pollForToken = (apiUrl, clientId, deviceCode, interval, expiresIn) => {
219167
+ const deadline = Date.now() + expiresIn * 1000;
219168
+ const poll = async () => {
219169
+ if (cancelledRef.current)
219170
+ return;
219171
+ if (Date.now() > deadline) {
219172
+ setError("Authorization timed out. Please try again.");
219173
+ setStep("error");
219174
+ return;
219175
+ }
219176
+ try {
219177
+ const response = await fetch("https://api.workos.com/user_management/authenticate", {
219178
+ method: "POST",
219179
+ headers: { "Content-Type": "application/json" },
219180
+ body: JSON.stringify({
219181
+ client_id: clientId,
219182
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
219183
+ device_code: deviceCode
219184
+ })
219185
+ });
219186
+ if (cancelledRef.current)
219187
+ return;
219188
+ if (response.status === 400) {
219189
+ pollingRef.current = setTimeout(poll, interval * 1000);
219190
+ return;
219191
+ }
219192
+ if (!response.ok) {
219193
+ throw new Error("Authentication failed");
219194
+ }
219195
+ const data = await response.json();
219196
+ await config.update({
219197
+ accessToken: data.access_token,
219198
+ refreshToken: data.refresh_token
219199
+ });
219200
+ await appConfig.reload();
219201
+ await fetchWorkspaces(apiUrl, data.access_token);
219202
+ } catch (err) {
219203
+ if (cancelledRef.current)
219204
+ return;
219205
+ pollingRef.current = setTimeout(poll, interval * 1000);
219206
+ }
219207
+ };
219208
+ pollingRef.current = setTimeout(poll, interval * 1000);
219209
+ };
219210
+ const pollForLegacyToken = (apiUrl, deviceCode, interval, expiresIn) => {
219211
+ const deadline = Date.now() + expiresIn * 1000;
219212
+ const poll = async () => {
219213
+ if (cancelledRef.current)
219214
+ return;
219215
+ if (Date.now() > deadline) {
219216
+ setError("Authorization timed out. Please try again.");
219217
+ setStep("error");
219218
+ return;
219219
+ }
219220
+ try {
219221
+ const response = await fetch(`${apiUrl}/auth/device/token`, {
219222
+ method: "POST",
219223
+ headers: { "Content-Type": "application/json" },
219224
+ body: JSON.stringify({ deviceCode })
219225
+ });
219226
+ if (!response.ok) {
219227
+ throw new Error("Failed to check authorization status");
219228
+ }
219229
+ const data = await response.json();
219230
+ if (cancelledRef.current)
219231
+ return;
219232
+ if (data.status === "complete" && data.apiKey) {
219233
+ await config.update({ pensarAPIKey: data.apiKey });
219234
+ if (data.workspace) {
219235
+ await config.update({
219236
+ workspaceId: data.workspace.id,
219237
+ workspaceSlug: data.workspace.slug
219238
+ });
219239
+ }
219240
+ appConfig.reload();
219241
+ setSelectedWorkspace(data.workspace ? {
219242
+ ...data.workspace,
219243
+ balance: data.credits?.balance ?? 0,
219244
+ hasPaymentMethod: true
219245
+ } : null);
219246
+ setBalance(data.credits?.balance ?? null);
219247
+ setStep("success");
219248
+ return;
219249
+ }
219250
+ if (data.status === "expired") {
219251
+ setError("Authorization expired. Please try again.");
219252
+ setStep("error");
219253
+ return;
219254
+ }
219255
+ if (data.status === "not_found") {
219256
+ setError("Invalid authorization session. Please try again.");
219257
+ setStep("error");
219258
+ return;
219259
+ }
219260
+ pollingRef.current = setTimeout(poll, interval * 1000);
219261
+ } catch (err) {
219262
+ if (cancelledRef.current)
219263
+ return;
219264
+ pollingRef.current = setTimeout(poll, interval * 1000);
219265
+ }
219266
+ };
219267
+ pollingRef.current = setTimeout(poll, interval * 1000);
219268
+ };
219269
+ const fetchWorkspaces = async (apiUrl, accessToken) => {
219270
+ try {
219271
+ const response = await fetch(`${apiUrl}/api/cli/workspaces`, {
219272
+ headers: { Authorization: `Bearer ${accessToken}` }
219273
+ });
219274
+ if (!response.ok) {
219275
+ throw new Error("Failed to fetch workspaces");
219276
+ }
219277
+ const data = await response.json();
219278
+ if (data.workspaces.length === 0) {
219279
+ setError("No workspaces found. Create one at console.pensar.dev");
219280
+ setStep("error");
219281
+ return;
219282
+ }
219283
+ if (data.workspaces.length === 1) {
219284
+ await selectWorkspace(apiUrl, accessToken, data.workspaces[0]);
219285
+ return;
219286
+ }
219287
+ setWorkspaces(data.workspaces);
219288
+ setSelectedIndex(0);
219289
+ setStep("select-workspace");
219290
+ } catch (err) {
219291
+ setError(err instanceof Error ? err.message : "Failed to fetch workspaces");
219292
+ setStep("error");
219293
+ }
219294
+ };
219295
+ const selectWorkspace = async (apiUrl, accessToken, workspace) => {
219296
+ setSelectedWorkspace(workspace);
219297
+ setStep("checking-billing");
219298
+ try {
219299
+ const response = await fetch(`${apiUrl}/api/cli/select-workspace`, {
219300
+ method: "POST",
219301
+ headers: {
219302
+ "Content-Type": "application/json",
219303
+ Authorization: `Bearer ${accessToken}`
219304
+ },
219305
+ body: JSON.stringify({ workspaceId: workspace.id })
219306
+ });
219307
+ if (!response.ok) {
219308
+ throw new Error("Failed to select workspace");
219309
+ }
219310
+ const data = await response.json();
219311
+ await config.update({
219312
+ workspaceId: workspace.id,
219313
+ workspaceSlug: workspace.slug
219314
+ });
219315
+ appConfig.reload();
219316
+ setBalance(data.billing.balance);
219317
+ if (!data.confirmed && data.billingUrl) {
219318
+ setBillingUrl(data.billingUrl);
219319
+ }
219320
+ setStep("success");
219321
+ } catch (err) {
219322
+ setError(err instanceof Error ? err.message : "Failed to select workspace");
219323
+ setStep("error");
219324
+ }
219325
+ };
219326
+ const handleDisconnect = async () => {
219327
+ await config.update({
219328
+ pensarAPIKey: null,
219329
+ accessToken: null,
219330
+ refreshToken: null,
219331
+ workspaceId: null,
219332
+ workspaceSlug: null
219333
+ });
219334
+ appConfig.reload();
219335
+ setAuthMode(null);
219336
+ setSelectedWorkspace(null);
219337
+ setBalance(null);
219338
+ setBillingUrl(null);
219339
+ setDeviceInfo(null);
219340
+ setLegacyDeviceInfo(null);
219341
+ setStep("start");
219342
+ };
219343
+ const hasLowBalance = balance !== null && balance < 1;
219344
+ const effectiveBillingUrl = billingUrl || (selectedWorkspace?.slug ? `${getPensarConsoleUrl()}/${selectedWorkspace.slug}/settings/billing` : connectedWorkspace?.slug ? `${getPensarConsoleUrl()}/${connectedWorkspace.slug}/settings/billing` : `${getPensarConsoleUrl()}/credits`);
219345
+ const openBillingPage = () => {
219346
+ openUrl(effectiveBillingUrl);
219347
+ goHome();
219348
+ };
219349
+ useKeyboard((key) => {
219350
+ if (key.name === "escape") {
219351
+ cleanup();
219352
+ goHome();
219353
+ return;
219354
+ }
219355
+ if (step === "start") {
219356
+ if (key.name === "return") {
219357
+ startDeviceFlow();
219358
+ }
219359
+ }
219360
+ if (step === "select-workspace") {
219361
+ if (key.name === "up" && selectedIndex > 0) {
219362
+ setSelectedIndex((i) => i - 1);
219363
+ }
219364
+ if (key.name === "down" && selectedIndex < workspaces.length - 1) {
219365
+ setSelectedIndex((i) => i + 1);
219366
+ }
219367
+ if (key.name === "return" && workspaces[selectedIndex]) {
219368
+ const currentConfig = appConfig.data;
219369
+ const apiUrl = getPensarApiUrl(currentConfig);
219370
+ const accessToken = currentConfig.accessToken;
219371
+ selectWorkspace(apiUrl, accessToken, workspaces[selectedIndex]);
219372
+ }
219373
+ }
219374
+ if (step === "error") {
219375
+ if (key.name === "return") {
219376
+ startDeviceFlow();
219377
+ }
219378
+ }
219379
+ if (step === "success") {
219380
+ if (key.name === "return") {
219381
+ if (hasLowBalance || billingUrl) {
219382
+ openBillingPage();
219383
+ } else {
219384
+ goHome();
219385
+ }
219386
+ }
219387
+ if (key.raw === "d" || key.raw === "D") {
219388
+ handleDisconnect();
219389
+ }
219390
+ }
219391
+ });
219392
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219393
+ flexDirection: "column",
219394
+ width: "100%",
219395
+ maxWidth: 80,
219396
+ alignItems: "flex-start",
219397
+ padding: 1,
219398
+ children: [
219399
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219400
+ marginBottom: 1,
219401
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219402
+ fg: "green",
219403
+ children: "Pensar Console — Managed Inference"
219404
+ }, undefined, false, undefined, this)
219405
+ }, undefined, false, undefined, this),
219406
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219407
+ marginBottom: 1,
219408
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219409
+ fg: "gray",
219410
+ children: [
219411
+ "Connect to Pensar Console for usage-based AI inference.",
219412
+ `
219413
+ `,
219414
+ "No API keys needed — just a Pensar account with credits."
219415
+ ]
219416
+ }, undefined, true, undefined, this)
219417
+ }, undefined, false, undefined, this),
219418
+ step === "start" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219419
+ flexDirection: "column",
219420
+ gap: 1,
219421
+ children: [
219422
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219423
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219424
+ fg: "white",
219425
+ children: [
219426
+ "Press ",
219427
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219428
+ fg: "green",
219429
+ children: "[ENTER]"
219430
+ }, undefined, false, undefined, this),
219431
+ " to authorize via your browser."
219432
+ ]
219433
+ }, undefined, true, undefined, this)
219434
+ }, undefined, false, undefined, this),
219435
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219436
+ marginTop: 1,
219437
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219438
+ fg: "gray",
219439
+ children: [
219440
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219441
+ fg: "green",
219442
+ children: "[ENTER]"
219443
+ }, undefined, false, undefined, this),
219444
+ " Connect ·",
219445
+ " ",
219446
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219447
+ fg: "green",
219448
+ children: "[ESC]"
219449
+ }, undefined, false, undefined, this),
219450
+ " Cancel"
219451
+ ]
219452
+ }, undefined, true, undefined, this)
219453
+ }, undefined, false, undefined, this)
219454
+ ]
219455
+ }, undefined, true, undefined, this),
219456
+ step === "requesting" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219457
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219458
+ fg: "yellow",
219459
+ children: "Starting authorization..."
219460
+ }, undefined, false, undefined, this)
219461
+ }, undefined, false, undefined, this),
219462
+ step === "polling" && (deviceInfo || legacyDeviceInfo) && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219463
+ flexDirection: "column",
219464
+ gap: 1,
219465
+ children: [
219466
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219467
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219468
+ fg: "yellow",
219469
+ children: "Waiting for browser authorization..."
219470
+ }, undefined, false, undefined, this)
219471
+ }, undefined, false, undefined, this),
219472
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219473
+ marginTop: 1,
219474
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219475
+ fg: "white",
219476
+ children: [
219477
+ "Your code:",
219478
+ " ",
219479
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219480
+ fg: "green",
219481
+ children: deviceInfo?.user_code || legacyDeviceInfo?.userCode
219482
+ }, undefined, false, undefined, this)
219483
+ ]
219484
+ }, undefined, true, undefined, this)
219485
+ }, undefined, false, undefined, this),
219486
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219487
+ marginTop: 1,
219488
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219489
+ fg: "gray",
219490
+ children: [
219491
+ "If the browser didn't open, visit:",
219492
+ `
219493
+ `,
219494
+ deviceInfo?.verification_uri_complete || legacyDeviceInfo?.verificationUriComplete
219495
+ ]
219496
+ }, undefined, true, undefined, this)
219497
+ }, undefined, false, undefined, this),
219498
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219499
+ marginTop: 1,
219500
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219501
+ fg: "gray",
219502
+ children: [
219503
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219504
+ fg: "green",
219505
+ children: "[ESC]"
219506
+ }, undefined, false, undefined, this),
219507
+ " Cancel"
219508
+ ]
219509
+ }, undefined, true, undefined, this)
219510
+ }, undefined, false, undefined, this)
219511
+ ]
219512
+ }, undefined, true, undefined, this),
219513
+ step === "select-workspace" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219514
+ flexDirection: "column",
219515
+ gap: 1,
219516
+ children: [
219517
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219518
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219519
+ fg: "white",
219520
+ children: "Select a workspace:"
219521
+ }, undefined, false, undefined, this)
219522
+ }, undefined, false, undefined, this),
219523
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219524
+ flexDirection: "column",
219525
+ marginTop: 1,
219526
+ children: workspaces.map((ws, i) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219527
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219528
+ fg: i === selectedIndex ? "green" : "gray",
219529
+ children: [
219530
+ i === selectedIndex ? "▸ " : " ",
219531
+ ws.name,
219532
+ " ",
219533
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219534
+ fg: "gray",
219535
+ children: [
219536
+ "(",
219537
+ ws.slug,
219538
+ ") — $",
219539
+ ws.balance.toFixed(2)
219540
+ ]
219541
+ }, undefined, true, undefined, this)
219542
+ ]
219543
+ }, undefined, true, undefined, this)
219544
+ }, ws.id, false, undefined, this))
219545
+ }, undefined, false, undefined, this),
219546
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219547
+ marginTop: 1,
219548
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219549
+ fg: "gray",
219550
+ children: [
219551
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219552
+ fg: "green",
219553
+ children: "[↑/↓]"
219554
+ }, undefined, false, undefined, this),
219555
+ " Navigate ·",
219556
+ " ",
219557
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219558
+ fg: "green",
219559
+ children: "[ENTER]"
219560
+ }, undefined, false, undefined, this),
219561
+ " Select ·",
219562
+ " ",
219563
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219564
+ fg: "green",
219565
+ children: "[ESC]"
219566
+ }, undefined, false, undefined, this),
219567
+ " Cancel"
219568
+ ]
219569
+ }, undefined, true, undefined, this)
219570
+ }, undefined, false, undefined, this)
219571
+ ]
219572
+ }, undefined, true, undefined, this),
219573
+ step === "checking-billing" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219574
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219575
+ fg: "yellow",
219576
+ children: [
219577
+ "Checking billing for ",
219578
+ selectedWorkspace?.name,
219579
+ "..."
219580
+ ]
219581
+ }, undefined, true, undefined, this)
219582
+ }, undefined, false, undefined, this),
219583
+ step === "success" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219584
+ flexDirection: "column",
219585
+ gap: 1,
219586
+ children: [
219587
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219588
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219589
+ fg: "green",
219590
+ children: "Connected to Pensar Console"
219591
+ }, undefined, false, undefined, this)
219592
+ }, undefined, false, undefined, this),
219593
+ (selectedWorkspace || connectedWorkspace) && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219594
+ flexDirection: "column",
219595
+ children: [
219596
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219597
+ fg: "white",
219598
+ children: [
219599
+ "Workspace: ",
219600
+ selectedWorkspace?.name || connectedWorkspace?.name
219601
+ ]
219602
+ }, undefined, true, undefined, this),
219603
+ balance !== null && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219604
+ fg: "white",
219605
+ children: [
219606
+ "Credits:",
219607
+ " ",
219608
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219609
+ fg: hasLowBalance ? "yellow" : "white",
219610
+ children: [
219611
+ "$",
219612
+ balance.toFixed(2)
219613
+ ]
219614
+ }, undefined, true, undefined, this)
219615
+ ]
219616
+ }, undefined, true, undefined, this)
219617
+ ]
219618
+ }, undefined, true, undefined, this),
219619
+ (hasLowBalance || billingUrl) && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219620
+ marginTop: 1,
219621
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219622
+ fg: "yellow",
219623
+ children: [
219624
+ billingUrl ? "Your workspace needs credits to use Apex CLI." : "Your credit balance is very low. We recommend at least $30 to run",
219625
+ `
219626
+ `,
219627
+ billingUrl ? "Press ENTER to open billing and add credits." : "pentests without interruptions. Press ENTER to open billing."
219628
+ ]
219629
+ }, undefined, true, undefined, this)
219630
+ }, undefined, false, undefined, this),
219631
+ !selectedWorkspace && !connectedWorkspace && appConfig.data.pensarAPIKey && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219632
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219633
+ fg: "gray",
219634
+ children: "Already connected (legacy key saved in config)"
219635
+ }, undefined, false, undefined, this)
219636
+ }, undefined, false, undefined, this),
219637
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219638
+ marginTop: 1,
219639
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219640
+ fg: "gray",
219641
+ children: "Pensar models are now available in the model selector."
219642
+ }, undefined, false, undefined, this)
219643
+ }, undefined, false, undefined, this),
219644
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219645
+ marginTop: 1,
219646
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219647
+ fg: "gray",
219648
+ children: [
219649
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219650
+ fg: "green",
219651
+ children: "[ENTER]"
219652
+ }, undefined, false, undefined, this),
219653
+ " ",
219654
+ hasLowBalance || billingUrl ? "Open billing" : "Done",
219655
+ " ·",
219656
+ " ",
219657
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219658
+ fg: "red",
219659
+ children: "[D]"
219660
+ }, undefined, false, undefined, this),
219661
+ " Disconnect ·",
219662
+ " ",
219663
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219664
+ fg: "green",
219665
+ children: "[ESC]"
219666
+ }, undefined, false, undefined, this),
219667
+ " Back"
219668
+ ]
219669
+ }, undefined, true, undefined, this)
219670
+ }, undefined, false, undefined, this)
219671
+ ]
219672
+ }, undefined, true, undefined, this),
219673
+ step === "error" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219674
+ flexDirection: "column",
219675
+ gap: 1,
219676
+ children: [
219677
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219678
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219679
+ fg: "red",
219680
+ children: error
219681
+ }, undefined, false, undefined, this)
219682
+ }, undefined, false, undefined, this),
219683
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219684
+ marginTop: 1,
219685
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219686
+ fg: "gray",
219687
+ children: [
219688
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219689
+ fg: "green",
219690
+ children: "[ENTER]"
219691
+ }, undefined, false, undefined, this),
219692
+ " Try again ·",
219693
+ " ",
219694
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219695
+ fg: "green",
219696
+ children: "[ESC]"
219697
+ }, undefined, false, undefined, this),
219698
+ " Cancel"
219699
+ ]
219700
+ }, undefined, true, undefined, this)
219701
+ }, undefined, false, undefined, this)
219702
+ ]
219703
+ }, undefined, true, undefined, this)
219704
+ ]
219705
+ }, undefined, true, undefined, this);
219706
+ }
219707
+
219708
+ // src/tui/components/commands/credits-flow.tsx
219709
+ var import_react62 = __toESM(require_react(), 1);
219710
+ init_tokenRefresh();
219711
+ function CreditsFlow() {
219712
+ const route = useRoute();
219713
+ const appConfig = useConfig();
219714
+ const [step, setStep] = import_react62.useState("loading");
219715
+ const [credits, setCredits] = import_react62.useState(null);
219716
+ const [error, setError] = import_react62.useState(null);
219717
+ const creditsUrl = `${getPensarConsoleUrl()}/credits`;
219718
+ const goHome = () => {
219719
+ route.navigate({ type: "base", path: "home" });
219720
+ };
219721
+ const openBrowser = () => {
219722
+ const url = creditsUrl;
219723
+ try {
219724
+ const platform = process.platform;
219725
+ if (platform === "darwin") {
219726
+ Bun.spawn(["open", url]);
219727
+ } else if (platform === "win32") {
219728
+ Bun.spawn(["cmd", "/c", "start", url]);
219729
+ } else {
219730
+ Bun.spawn(["xdg-open", url]);
219731
+ }
219732
+ } catch {}
219733
+ setStep("browser-opened");
219734
+ };
219735
+ const fetchBalance = async () => {
219736
+ const tokenResult = await ensureValidToken({
219737
+ accessToken: appConfig.data.accessToken,
219738
+ refreshToken: appConfig.data.refreshToken,
219739
+ pensarAPIKey: appConfig.data.pensarAPIKey,
219740
+ pensarApiUrl: appConfig.data.pensarApiUrl
219741
+ });
219742
+ if (!tokenResult) {
219743
+ setStep("no-auth");
219744
+ return;
219745
+ }
219746
+ setStep("loading");
219747
+ setError(null);
219748
+ try {
219749
+ const apiUrl = getPensarApiUrl(appConfig.data);
219750
+ const headers = {
219751
+ Authorization: `Bearer ${tokenResult.token}`
219752
+ };
219753
+ if (tokenResult.type === "workos" && appConfig.data.workspaceId) {
219754
+ headers["X-Workspace-Id"] = appConfig.data.workspaceId;
219755
+ }
219756
+ const response = await fetch(`${apiUrl}/bedrock/validate`, {
219757
+ method: "GET",
219758
+ headers
219759
+ });
219760
+ if (!response.ok) {
219761
+ throw new Error("Failed to fetch balance");
219762
+ }
219763
+ const result = await response.json();
219764
+ setCredits({
219765
+ balance: result.credits.balance,
219766
+ workspace: result.workspace.name
219767
+ });
219768
+ setStep("display");
219769
+ } catch (err) {
219770
+ setError(err instanceof Error ? err.message : "Failed to fetch balance");
219771
+ setStep("display");
219772
+ }
219773
+ };
219774
+ import_react62.useEffect(() => {
219775
+ fetchBalance();
219776
+ }, []);
219777
+ useKeyboard((key) => {
219778
+ if (key.name === "escape") {
219779
+ goHome();
219780
+ return;
219781
+ }
219782
+ if (step === "no-auth") {
219783
+ if (key.name === "return") {
219784
+ route.navigate({ type: "base", path: "auth" });
219785
+ }
219786
+ }
219787
+ if (step === "display") {
219788
+ if (key.name === "return") {
219789
+ openBrowser();
219790
+ }
219791
+ if (key.raw === "r" || key.raw === "R") {
219792
+ fetchBalance();
219793
+ }
219794
+ }
219795
+ if (step === "browser-opened") {
219796
+ if (key.name === "return") {
219797
+ fetchBalance();
219798
+ }
219799
+ }
219800
+ });
219801
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219802
+ flexDirection: "column",
219803
+ width: "100%",
219804
+ maxWidth: 80,
219805
+ alignItems: "flex-start",
219806
+ padding: 1,
219807
+ children: [
219808
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219809
+ marginBottom: 1,
219810
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219811
+ fg: "green",
219812
+ children: "Credits"
219813
+ }, undefined, false, undefined, this)
219814
+ }, undefined, false, undefined, this),
219815
+ step === "loading" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219816
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219817
+ fg: "yellow",
219818
+ children: "Fetching balance..."
219819
+ }, undefined, false, undefined, this)
219820
+ }, undefined, false, undefined, this),
219821
+ step === "no-auth" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219822
+ flexDirection: "column",
219823
+ gap: 1,
219824
+ children: [
219825
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219826
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219827
+ fg: "yellow",
219828
+ children: "Not connected to Pensar Console."
219829
+ }, undefined, false, undefined, this)
219830
+ }, undefined, false, undefined, this),
219831
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219832
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219833
+ fg: "gray",
219834
+ children: [
219835
+ "Run ",
219836
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219837
+ fg: "green",
219838
+ children: "/auth"
219839
+ }, undefined, false, undefined, this),
219840
+ " first to connect your account."
219841
+ ]
219842
+ }, undefined, true, undefined, this)
219843
+ }, undefined, false, undefined, this),
219844
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219845
+ marginTop: 1,
219846
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219847
+ fg: "gray",
219848
+ children: [
219849
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219850
+ fg: "green",
219851
+ children: "[ENTER]"
219852
+ }, undefined, false, undefined, this),
219853
+ " Run /auth ·",
219854
+ " ",
219855
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219856
+ fg: "green",
219857
+ children: "[ESC]"
219858
+ }, undefined, false, undefined, this),
219859
+ " Back"
219860
+ ]
219861
+ }, undefined, true, undefined, this)
219862
+ }, undefined, false, undefined, this)
219863
+ ]
219864
+ }, undefined, true, undefined, this),
219865
+ step === "display" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219866
+ flexDirection: "column",
219867
+ gap: 1,
219868
+ children: [
219869
+ error ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219870
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219871
+ fg: "red",
219872
+ children: [
219873
+ "Error: ",
219874
+ error
219875
+ ]
219876
+ }, undefined, true, undefined, this)
219877
+ }, undefined, false, undefined, this) : credits ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
219878
+ children: [
219879
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219880
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219881
+ fg: "white",
219882
+ children: [
219883
+ "Workspace: ",
219884
+ credits.workspace
219885
+ ]
219886
+ }, undefined, true, undefined, this)
219887
+ }, undefined, false, undefined, this),
219888
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219889
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219890
+ fg: "white",
219891
+ children: [
219892
+ "Balance:",
219893
+ " ",
219894
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219895
+ fg: credits.balance < 5 ? "yellow" : "green",
219896
+ children: [
219897
+ "$",
219898
+ credits.balance.toFixed(2)
219899
+ ]
219900
+ }, undefined, true, undefined, this)
219901
+ ]
219902
+ }, undefined, true, undefined, this)
219903
+ }, undefined, false, undefined, this),
219904
+ credits.balance < 5 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219905
+ marginTop: 1,
219906
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219907
+ fg: "yellow",
219908
+ children: "Low balance. We recommend at least $30 for uninterrupted pentest runs."
219909
+ }, undefined, false, undefined, this)
219910
+ }, undefined, false, undefined, this)
219911
+ ]
219912
+ }, undefined, true, undefined, this) : null,
219913
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219914
+ marginTop: 1,
219915
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219916
+ fg: "gray",
219917
+ children: [
219918
+ "Press ",
219919
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219920
+ fg: "green",
219921
+ children: "[ENTER]"
219922
+ }, undefined, false, undefined, this),
219923
+ " to buy credits in your browser."
219924
+ ]
219925
+ }, undefined, true, undefined, this)
219926
+ }, undefined, false, undefined, this),
219927
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219928
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219929
+ fg: "gray",
219930
+ children: [
219931
+ "Or visit: ",
219932
+ creditsUrl
219933
+ ]
219934
+ }, undefined, true, undefined, this)
219935
+ }, undefined, false, undefined, this),
219936
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219937
+ marginTop: 1,
219938
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219939
+ fg: "gray",
219940
+ children: [
219941
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219942
+ fg: "green",
219943
+ children: "[ENTER]"
219944
+ }, undefined, false, undefined, this),
219945
+ " Open browser ·",
219946
+ " ",
219947
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219948
+ fg: "green",
219949
+ children: "[R]"
219950
+ }, undefined, false, undefined, this),
219951
+ " Refresh ·",
219952
+ " ",
219953
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219954
+ fg: "green",
219955
+ children: "[ESC]"
219956
+ }, undefined, false, undefined, this),
219957
+ " Back"
219958
+ ]
219959
+ }, undefined, true, undefined, this)
219960
+ }, undefined, false, undefined, this)
219961
+ ]
219962
+ }, undefined, true, undefined, this),
219963
+ step === "browser-opened" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219964
+ flexDirection: "column",
219965
+ gap: 1,
219966
+ children: [
219967
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219968
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219969
+ fg: "green",
219970
+ children: "Browser opened. Purchase credits on the Pensar Console."
219971
+ }, undefined, false, undefined, this)
219972
+ }, undefined, false, undefined, this),
219973
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219974
+ marginTop: 1,
219975
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219976
+ fg: "gray",
219977
+ children: [
219978
+ "Press ",
219979
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219980
+ fg: "green",
219981
+ children: "[ENTER]"
219982
+ }, undefined, false, undefined, this),
219983
+ " to refresh your balance after purchasing."
219984
+ ]
219985
+ }, undefined, true, undefined, this)
219986
+ }, undefined, false, undefined, this),
219987
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
219988
+ marginTop: 1,
219989
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
219990
+ fg: "gray",
219991
+ children: [
219992
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219993
+ fg: "green",
219994
+ children: "[ENTER]"
219995
+ }, undefined, false, undefined, this),
219996
+ " Refresh balance ·",
219997
+ " ",
219998
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
219999
+ fg: "green",
220000
+ children: "[ESC]"
220001
+ }, undefined, false, undefined, this),
220002
+ " Back"
220003
+ ]
220004
+ }, undefined, true, undefined, this)
220005
+ }, undefined, false, undefined, this)
220006
+ ]
220007
+ }, undefined, true, undefined, this)
220008
+ ]
220009
+ }, undefined, true, undefined, this);
220010
+ }
220011
+
217585
220012
  // src/tui/context/keybinding.tsx
217586
- var import_react64 = __toESM(require_react(), 1);
220013
+ var import_react68 = __toESM(require_react(), 1);
217587
220014
 
217588
220015
  // src/tui/keybindings/keybind.tsx
217589
- var import_react60 = __toESM(require_react(), 1);
220016
+ var import_react64 = __toESM(require_react(), 1);
217590
220017
 
217591
220018
  // src/tui/keybindings/actions.ts
217592
220019
  var movementActions = [
@@ -217838,7 +220265,7 @@ var allActions = [
217838
220265
  var actionsByKey = new Map(allActions.map((action) => [action.key, action]));
217839
220266
  var actionsById = new Map(allActions.map((action) => [action.id, action]));
217840
220267
  // src/tui/keybindings/keybind.tsx
217841
- var LeaderKeyContext = import_react60.createContext(null);
220268
+ var LeaderKeyContext = import_react64.createContext(null);
217842
220269
  // src/tui/keybindings/registry.ts
217843
220270
  function createKeybindings(deps) {
217844
220271
  const {
@@ -218016,7 +220443,7 @@ function matchesKeybind(pressed, combo) {
218016
220443
  }
218017
220444
 
218018
220445
  // src/tui/context/keybinding.tsx
218019
- var KeybindingContext = import_react64.createContext(undefined);
220446
+ var KeybindingContext = import_react68.createContext(undefined);
218020
220447
  function KeybindingProvider({
218021
220448
  children,
218022
220449
  deps
@@ -218055,7 +220482,7 @@ function KeybindingProvider({
218055
220482
  }
218056
220483
 
218057
220484
  // src/tui/components/pentest/pentest.tsx
218058
- var import_react73 = __toESM(require_react(), 1);
220485
+ var import_react77 = __toESM(require_react(), 1);
218059
220486
  import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
218060
220487
  import { join as join19 } from "path";
218061
220488
  import { exec as exec3 } from "child_process";
@@ -218070,7 +220497,7 @@ import { join as join18 } from "path";
218070
220497
  // src/core/workflows/whiteboxAttackSurface.ts
218071
220498
  init_zod();
218072
220499
  init_agent4();
218073
- init_types4();
220500
+ init_types5();
218074
220501
  var DEFAULT_CONCURRENCY3 = 5;
218075
220502
  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
220503
 
@@ -218608,8 +221035,11 @@ Found ${findings.length} vulnerabilities`);
218608
221035
  return { findings, findingsPath, pocsPath, reportPath };
218609
221036
  }
218610
221037
 
221038
+ // src/tui/components/pentest/pentest.tsx
221039
+ init_utils2();
221040
+
218611
221041
  // src/tui/components/agent-display.tsx
218612
- var import_react71 = __toESM(require_react(), 1);
221042
+ var import_react75 = __toESM(require_react(), 1);
218613
221043
 
218614
221044
  // node_modules/marked/lib/marked.esm.js
218615
221045
  function L2() {
@@ -220375,14 +222805,14 @@ function getResultSummary(result, toolName) {
220375
222805
  return null;
220376
222806
  }
220377
222807
  // src/tui/components/shared/ascii-spinner.tsx
220378
- var import_react65 = __toESM(require_react(), 1);
222808
+ var import_react69 = __toESM(require_react(), 1);
220379
222809
  var SPINNER_FRAMES = ["/", "-", "\\", "|"];
220380
222810
  var SPINNER_INTERVAL = 100;
220381
222811
  function AsciiSpinner({ label, fg: fg2 }) {
220382
222812
  const { colors: colors2 } = useTheme();
220383
222813
  const spinnerColor = fg2 ?? colors2.info;
220384
- const [frame, setFrame] = import_react65.useState(0);
220385
- import_react65.useEffect(() => {
222814
+ const [frame, setFrame] = import_react69.useState(0);
222815
+ import_react69.useEffect(() => {
220386
222816
  const interval = setInterval(() => {
220387
222817
  setFrame((f3) => (f3 + 1) % SPINNER_FRAMES.length);
220388
222818
  }, SPINNER_INTERVAL);
@@ -220394,14 +222824,14 @@ function AsciiSpinner({ label, fg: fg2 }) {
220394
222824
  }, undefined, false, undefined, this);
220395
222825
  }
220396
222826
  // src/tui/components/shared/tool-renderer.tsx
220397
- var import_react66 = __toESM(require_react(), 1);
220398
- var ToolRenderer = import_react66.memo(function ToolRenderer2({
222827
+ var import_react70 = __toESM(require_react(), 1);
222828
+ var ToolRenderer = import_react70.memo(function ToolRenderer2({
220399
222829
  message,
220400
222830
  verbose = false,
220401
222831
  expandedLogs = false
220402
222832
  }) {
220403
222833
  const { colors: colors2 } = useTheme();
220404
- const [showOutput, setShowOutput] = import_react66.useState(false);
222834
+ const [showOutput, setShowOutput] = import_react70.useState(false);
220405
222835
  if (!isToolMessage(message)) {
220406
222836
  return null;
220407
222837
  }
@@ -220498,8 +222928,8 @@ var ToolRenderer = import_react66.memo(function ToolRenderer2({
220498
222928
  }, undefined, true, undefined, this);
220499
222929
  });
220500
222930
  // src/tui/components/shared/message-renderer.tsx
220501
- var import_react67 = __toESM(require_react(), 1);
220502
- var MessageRenderer = import_react67.memo(function MessageRenderer2({
222931
+ var import_react71 = __toESM(require_react(), 1);
222932
+ var MessageRenderer = import_react71.memo(function MessageRenderer2({
220503
222933
  message,
220504
222934
  isStreaming = false,
220505
222935
  verbose = false,
@@ -220509,7 +222939,7 @@ var MessageRenderer = import_react67.memo(function MessageRenderer2({
220509
222939
  }) {
220510
222940
  const { colors: colors2 } = useTheme();
220511
222941
  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]);
222942
+ const displayContent = import_react71.useMemo(() => message.role === "assistant" ? markdownToStyledText(content, colors2) : content, [content, message.role, colors2]);
220513
222943
  if (isToolMessage(message)) {
220514
222944
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ToolRenderer, {
220515
222945
  message,
@@ -220621,9 +223051,9 @@ var MessageRenderer = import_react67.memo(function MessageRenderer2({
220621
223051
  }, undefined, false, undefined, this);
220622
223052
  });
220623
223053
  // src/tui/components/shared/approval-prompt.tsx
220624
- var import_react68 = __toESM(require_react(), 1);
223054
+ var import_react72 = __toESM(require_react(), 1);
220625
223055
  // src/tui/components/shared/message-reducer.ts
220626
- var import_react70 = __toESM(require_react(), 1);
223056
+ var import_react74 = __toESM(require_react(), 1);
220627
223057
  // src/tui/components/agent-display.tsx
220628
223058
  function getStableKey(item, contextId = "root") {
220629
223059
  if ("messages" in item) {
@@ -220699,11 +223129,11 @@ function AgentDisplay({
220699
223129
  ]
220700
223130
  }, undefined, true, undefined, this);
220701
223131
  }
220702
- var SubAgentDisplay = import_react71.memo(function SubAgentDisplay2({
223132
+ var SubAgentDisplay = import_react75.memo(function SubAgentDisplay2({
220703
223133
  subagent
220704
223134
  }) {
220705
223135
  const { colors: colors2 } = useTheme();
220706
- const [open, setOpen] = import_react71.useState(false);
223136
+ const [open, setOpen] = import_react75.useState(false);
220707
223137
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
220708
223138
  height: open ? 40 : "auto",
220709
223139
  onMouseDown: () => setOpen(!open),
@@ -220758,7 +223188,7 @@ var SubAgentDisplay = import_react71.memo(function SubAgentDisplay2({
220758
223188
  ]
220759
223189
  }, undefined, true, undefined, this);
220760
223190
  });
220761
- var AgentMessage = import_react71.memo(function AgentMessage2({
223191
+ var AgentMessage = import_react75.memo(function AgentMessage2({
220762
223192
  message
220763
223193
  }) {
220764
223194
  const { colors: colors2 } = useTheme();
@@ -220822,9 +223252,9 @@ var AgentMessage = import_react71.memo(function AgentMessage2({
220822
223252
  flexDirection: "column",
220823
223253
  marginTop: 0,
220824
223254
  paddingLeft: 2,
220825
- children: streamingLogs.slice(-3).map((log, idx) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
223255
+ children: streamingLogs.slice(-3).map((log2, idx) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
220826
223256
  fg: colors2.textMuted,
220827
- content: log.length > 100 ? log.slice(0, 100) + "…" : log
223257
+ content: log2.length > 100 ? log2.slice(0, 100) + "…" : log2
220828
223258
  }, idx, false, undefined, this))
220829
223259
  }, undefined, false, undefined, this)
220830
223260
  ]
@@ -220871,8 +223301,8 @@ var AgentMessage = import_react71.memo(function AgentMessage2({
220871
223301
  });
220872
223302
  function ToolDetails({ message }) {
220873
223303
  const { colors: colors2 } = useTheme();
220874
- const [showArgs, setShowArgs] = import_react71.useState(false);
220875
- const [showResult, setShowResult] = import_react71.useState(false);
223304
+ const [showArgs, setShowArgs] = import_react75.useState(false);
223305
+ const [showResult, setShowResult] = import_react75.useState(false);
220876
223306
  if (message.role !== "tool") {
220877
223307
  return null;
220878
223308
  }
@@ -220950,24 +223380,24 @@ function Pentest({ sessionId }) {
220950
223380
  const config3 = useConfig();
220951
223381
  const { model, setThinking, setIsExecuting, isExecuting } = useAgent();
220952
223382
  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(() => {
223383
+ const [session, setSession] = import_react77.useState(null);
223384
+ const [error40, setError] = import_react77.useState(null);
223385
+ const [phase, setPhase] = import_react77.useState("loading");
223386
+ const [abortController, setAbortController] = import_react77.useState(null);
223387
+ const [panelMessages, setPanelMessages] = import_react77.useState([]);
223388
+ const panelTextRef = import_react77.useRef("");
223389
+ const panelSourceRef = import_react77.useRef(null);
223390
+ const [pentestAgents, setPentestAgents] = import_react77.useState({});
223391
+ const pentestTextRefs = import_react77.useRef({});
223392
+ const [assets, setAssets] = import_react77.useState([]);
223393
+ const [viewMode, setViewMode] = import_react77.useState("overview");
223394
+ const [selectedAgentId, setSelectedAgentId] = import_react77.useState(null);
223395
+ const [focusedIndex, setFocusedIndex] = import_react77.useState(0);
223396
+ const [showOrchestratorPanel, setShowOrchestratorPanel] = import_react77.useState(false);
223397
+ const [startTime, setStartTime] = import_react77.useState(null);
223398
+ const pentestAgentList = import_react77.useMemo(() => Object.values(pentestAgents).sort((a, b3) => a.createdAt.getTime() - b3.createdAt.getTime()), [pentestAgents]);
223399
+ const selectedAgent = import_react77.useMemo(() => selectedAgentId ? pentestAgents[selectedAgentId] ?? null : null, [pentestAgents, selectedAgentId]);
223400
+ import_react77.useEffect(() => {
220971
223401
  async function load() {
220972
223402
  try {
220973
223403
  const s2 = await sessions.get(sessionId);
@@ -220984,7 +223414,7 @@ function Pentest({ sessionId }) {
220984
223414
  }
220985
223415
  load();
220986
223416
  }, [sessionId]);
220987
- import_react73.useEffect(() => {
223417
+ import_react77.useEffect(() => {
220988
223418
  if (!session)
220989
223419
  return;
220990
223420
  const assetsPath = join19(session.rootPath, "assets");
@@ -221007,12 +223437,12 @@ function Pentest({ sessionId }) {
221007
223437
  const interval = setInterval(readAssets, 2000);
221008
223438
  return () => clearInterval(interval);
221009
223439
  }, [session]);
221010
- import_react73.useEffect(() => {
223440
+ import_react77.useEffect(() => {
221011
223441
  return () => {
221012
223442
  abortController?.abort();
221013
223443
  };
221014
223444
  }, [abortController]);
221015
- const ensurePentestAgent = import_react73.useCallback((subagentId) => {
223445
+ const ensurePentestAgent = import_react77.useCallback((subagentId) => {
221016
223446
  setPentestAgents((prev) => {
221017
223447
  if (prev[subagentId])
221018
223448
  return prev;
@@ -221029,7 +223459,7 @@ function Pentest({ sessionId }) {
221029
223459
  };
221030
223460
  });
221031
223461
  }, []);
221032
- const handleSubagentSpawn = import_react73.useCallback(({
223462
+ const handleSubagentSpawn = import_react77.useCallback(({
221033
223463
  subagentId,
221034
223464
  input
221035
223465
  }) => {
@@ -221049,7 +223479,7 @@ function Pentest({ sessionId }) {
221049
223479
  }
221050
223480
  }));
221051
223481
  }, []);
221052
- const handleSubagentComplete = import_react73.useCallback(({ subagentId, status }) => {
223482
+ const handleSubagentComplete = import_react77.useCallback(({ subagentId, status }) => {
221053
223483
  if (!subagentId.startsWith("pentest-agent-"))
221054
223484
  return;
221055
223485
  setPentestAgents((prev) => {
@@ -221062,7 +223492,7 @@ function Pentest({ sessionId }) {
221062
223492
  };
221063
223493
  });
221064
223494
  }, []);
221065
- const appendPanelText = import_react73.useCallback((source, text2) => {
223495
+ const appendPanelText = import_react77.useCallback((source, text2) => {
221066
223496
  if (panelSourceRef.current !== source) {
221067
223497
  panelTextRef.current = "";
221068
223498
  panelSourceRef.current = source;
@@ -221087,7 +223517,7 @@ function Pentest({ sessionId }) {
221087
223517
  ];
221088
223518
  });
221089
223519
  }, []);
221090
- const appendPentestText = import_react73.useCallback((subagentId, text2) => {
223520
+ const appendPentestText = import_react77.useCallback((subagentId, text2) => {
221091
223521
  ensurePentestAgent(subagentId);
221092
223522
  if (!pentestTextRefs.current[subagentId]) {
221093
223523
  pentestTextRefs.current[subagentId] = "";
@@ -221120,7 +223550,7 @@ function Pentest({ sessionId }) {
221120
223550
  };
221121
223551
  });
221122
223552
  }, [ensurePentestAgent]);
221123
- const addPanelToolCall = import_react73.useCallback((toolCallId, toolName, args) => {
223553
+ const addPanelToolCall = import_react77.useCallback((toolCallId, toolName, args) => {
221124
223554
  panelTextRef.current = "";
221125
223555
  panelSourceRef.current = null;
221126
223556
  const description = typeof args?.toolCallDescription === "string" ? args.toolCallDescription : toolName;
@@ -221139,7 +223569,7 @@ function Pentest({ sessionId }) {
221139
223569
  return [...prev, msg];
221140
223570
  });
221141
223571
  }, []);
221142
- const addPentestToolCall = import_react73.useCallback((subagentId, toolCallId, toolName, args) => {
223572
+ const addPentestToolCall = import_react77.useCallback((subagentId, toolCallId, toolName, args) => {
221143
223573
  pentestTextRefs.current[subagentId] = "";
221144
223574
  ensurePentestAgent(subagentId);
221145
223575
  const description = typeof args?.toolCallDescription === "string" ? args.toolCallDescription : toolName;
@@ -221184,12 +223614,12 @@ function Pentest({ sessionId }) {
221184
223614
  }
221185
223615
  ];
221186
223616
  };
221187
- const updatePanelToolResult = import_react73.useCallback((toolCallId, toolName, result) => {
223617
+ const updatePanelToolResult = import_react77.useCallback((toolCallId, toolName, result) => {
221188
223618
  panelTextRef.current = "";
221189
223619
  panelSourceRef.current = null;
221190
223620
  setPanelMessages((prev) => toolResultUpdater(prev, toolCallId, toolName, result));
221191
223621
  }, []);
221192
- const updatePentestToolResult = import_react73.useCallback((subagentId, toolCallId, toolName, result) => {
223622
+ const updatePentestToolResult = import_react77.useCallback((subagentId, toolCallId, toolName, result) => {
221193
223623
  pentestTextRefs.current[subagentId] = "";
221194
223624
  setPentestAgents((prev) => {
221195
223625
  const agent = prev[subagentId];
@@ -221204,7 +223634,7 @@ function Pentest({ sessionId }) {
221204
223634
  };
221205
223635
  });
221206
223636
  }, []);
221207
- const startPentest = import_react73.useCallback(async (s2) => {
223637
+ const startPentest = import_react77.useCallback(async (s2) => {
221208
223638
  setPhase("discovery");
221209
223639
  setStartTime(new Date);
221210
223640
  setIsExecuting(true);
@@ -221218,12 +223648,7 @@ function Pentest({ sessionId }) {
221218
223648
  session: s2,
221219
223649
  model: model.id,
221220
223650
  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
- },
223651
+ authConfig: buildAuthConfig(config3.data),
221227
223652
  callbacks: {
221228
223653
  onTextDelta: (d3) => {
221229
223654
  setThinking(false);
@@ -221306,7 +223731,7 @@ function Pentest({ sessionId }) {
221306
223731
  handleSubagentSpawn,
221307
223732
  handleSubagentComplete
221308
223733
  ]);
221309
- import_react73.useEffect(() => {
223734
+ import_react77.useEffect(() => {
221310
223735
  if (session && phase === "loading") {
221311
223736
  startPentest(session);
221312
223737
  }
@@ -221368,7 +223793,7 @@ function Pentest({ sessionId }) {
221368
223793
  }
221369
223794
  }
221370
223795
  });
221371
- const openReport = import_react73.useCallback(() => {
223796
+ const openReport = import_react77.useCallback(() => {
221372
223797
  if (!session)
221373
223798
  return;
221374
223799
  const reportPath = join19(session.rootPath, "pentest-report.md");
@@ -221560,7 +223985,7 @@ function OrchestratorPanel({
221560
223985
  }) {
221561
223986
  const { colors: colors2 } = useTheme();
221562
223987
  const isRunning = phase !== "loading" && phase !== "completed" && phase !== "error";
221563
- const assetSummary = import_react73.useMemo(() => {
223988
+ const assetSummary = import_react77.useMemo(() => {
221564
223989
  const byType = {};
221565
223990
  for (const a of assets) {
221566
223991
  const key = a.assetType;
@@ -221809,7 +224234,7 @@ function AgentCardGrid({
221809
224234
  onSelectAgent
221810
224235
  }) {
221811
224236
  const { colors: colors2 } = useTheme();
221812
- const rows = import_react73.useMemo(() => {
224237
+ const rows = import_react77.useMemo(() => {
221813
224238
  const result = [];
221814
224239
  for (let i2 = 0;i2 < agents.length; i2 += 2) {
221815
224240
  result.push(agents.slice(i2, i2 + 2));
@@ -221857,14 +224282,14 @@ function AgentCard({
221857
224282
  completed: colors2.primary,
221858
224283
  failed: colors2.error
221859
224284
  }[agent.status];
221860
- const lastActivity = import_react73.useMemo(() => {
224285
+ const lastActivity = import_react77.useMemo(() => {
221861
224286
  const last = agent.messages[agent.messages.length - 1];
221862
224287
  if (!last)
221863
224288
  return "Starting...";
221864
224289
  const text2 = typeof last.content === "string" ? last.content.replace(/\n/g, " ").trim() : "Working...";
221865
224290
  return text2.length > 50 ? text2.substring(0, 47) + "..." : text2;
221866
224291
  }, [agent.messages]);
221867
- const toolCalls = import_react73.useMemo(() => agent.messages.filter((m4) => m4.role === "tool").length, [agent.messages]);
224292
+ const toolCalls = import_react77.useMemo(() => agent.messages.filter((m4) => m4.role === "tool").length, [agent.messages]);
221868
224293
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
221869
224294
  flexGrow: 1,
221870
224295
  flexBasis: 0,
@@ -222051,8 +224476,8 @@ function MetricsBar({
222051
224476
  isExecuting
222052
224477
  }) {
222053
224478
  const { colors: colors2 } = useTheme();
222054
- const [now2, setNow] = import_react73.useState(Date.now());
222055
- import_react73.useEffect(() => {
224479
+ const [now2, setNow] = import_react77.useState(Date.now());
224480
+ import_react77.useEffect(() => {
222056
224481
  if (!isExecuting)
222057
224482
  return;
222058
224483
  const interval = setInterval(() => setNow(Date.now()), 1000);
@@ -222195,7 +224620,7 @@ function MetricsBar({
222195
224620
  }
222196
224621
 
222197
224622
  // src/tui/components/operator-dashboard/index.tsx
222198
- var import_react78 = __toESM(require_react(), 1);
224623
+ var import_react82 = __toESM(require_react(), 1);
222199
224624
 
222200
224625
  // src/core/api/offesecAgent.ts
222201
224626
  init_offensiveSecurityAgent();
@@ -222210,6 +224635,9 @@ async function runOffensiveSecurityAgent(input) {
222210
224635
  });
222211
224636
  }
222212
224637
 
224638
+ // src/tui/components/operator-dashboard/index.tsx
224639
+ init_utils2();
224640
+
222213
224641
  // src/core/agents/offSecAgent/index.ts
222214
224642
  init_offensiveSecurityAgent();
222215
224643
  init_tools();
@@ -222292,7 +224720,7 @@ function InlineApprovalPrompt2({ approval }) {
222292
224720
  }
222293
224721
 
222294
224722
  // src/tui/components/chat/loading-indicator.tsx
222295
- var import_react75 = __toESM(require_react(), 1);
224723
+ var import_react79 = __toESM(require_react(), 1);
222296
224724
  var SPINNER_FRAMES2 = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
222297
224725
  var SPINNER_INTERVAL2 = 80;
222298
224726
  var DOTS_FRAMES = ["", ".", "..", "..."];
@@ -222303,15 +224731,15 @@ function LoadingIndicator({
222303
224731
  toolName
222304
224732
  }) {
222305
224733
  const { colors: colors2 } = useTheme();
222306
- const [spinnerFrame, setSpinnerFrame] = import_react75.useState(0);
222307
- const [dotsFrame, setDotsFrame] = import_react75.useState(0);
222308
- import_react75.useEffect(() => {
224734
+ const [spinnerFrame, setSpinnerFrame] = import_react79.useState(0);
224735
+ const [dotsFrame, setDotsFrame] = import_react79.useState(0);
224736
+ import_react79.useEffect(() => {
222309
224737
  const interval = setInterval(() => {
222310
224738
  setSpinnerFrame((f3) => (f3 + 1) % SPINNER_FRAMES2.length);
222311
224739
  }, SPINNER_INTERVAL2);
222312
224740
  return () => clearInterval(interval);
222313
224741
  }, []);
222314
- import_react75.useEffect(() => {
224742
+ import_react79.useEffect(() => {
222315
224743
  const interval = setInterval(() => {
222316
224744
  setDotsFrame((f3) => (f3 + 1) % DOTS_FRAMES.length);
222317
224745
  }, DOTS_INTERVAL);
@@ -222581,7 +225009,7 @@ function MessageList({
222581
225009
  }
222582
225010
 
222583
225011
  // src/tui/components/chat/input-area.tsx
222584
- var import_react76 = __toESM(require_react(), 1);
225012
+ var import_react80 = __toESM(require_react(), 1);
222585
225013
  function NormalInputAreaInner({
222586
225014
  value,
222587
225015
  onChange,
@@ -222596,17 +225024,17 @@ function NormalInputAreaInner({
222596
225024
  }) {
222597
225025
  const { colors: colors2 } = useTheme();
222598
225026
  const { inputValue, setInputValue } = useInput();
222599
- const promptRef = import_react76.useRef(null);
222600
- const isExternalUpdate = import_react76.useRef(false);
225027
+ const promptRef = import_react80.useRef(null);
225028
+ const isExternalUpdate = import_react80.useRef(false);
222601
225029
  const isDisabled = status === "running";
222602
- import_react76.useEffect(() => {
225030
+ import_react80.useEffect(() => {
222603
225031
  if (value !== inputValue) {
222604
225032
  isExternalUpdate.current = true;
222605
225033
  setInputValue(value);
222606
225034
  promptRef.current?.setValue(value);
222607
225035
  }
222608
225036
  }, [value]);
222609
- import_react76.useEffect(() => {
225037
+ import_react80.useEffect(() => {
222610
225038
  if (isExternalUpdate.current) {
222611
225039
  isExternalUpdate.current = false;
222612
225040
  return;
@@ -222756,7 +225184,7 @@ function ApprovalInputArea2({
222756
225184
  lastDeclineNote
222757
225185
  }) {
222758
225186
  const { colors: colors2 } = useTheme();
222759
- const [focusedElement, setFocusedElement] = import_react76.useState(0);
225187
+ const [focusedElement, setFocusedElement] = import_react80.useState(0);
222760
225188
  const tierColor = getTierColor(colors2, approval.tier);
222761
225189
  useKeyboard((key) => {
222762
225190
  if (key.name === "up") {
@@ -222878,20 +225306,20 @@ function OperatorDashboard({
222878
225306
  const route = useRoute();
222879
225307
  const config3 = useConfig();
222880
225308
  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(() => {
225309
+ const [session, setSession] = import_react82.useState(null);
225310
+ const [loading, setLoading] = import_react82.useState(true);
225311
+ const [error40, setError] = import_react82.useState(null);
225312
+ const [status, setStatus] = import_react82.useState("idle");
225313
+ const abortControllerRef = import_react82.useRef(null);
225314
+ const [messages, setMessages] = import_react82.useState([]);
225315
+ const textRef = import_react82.useRef("");
225316
+ const [inputValue, setInputValue] = import_react82.useState("");
225317
+ const [operatorState, setOperatorState] = import_react82.useState(() => createInitialOperatorState("manual", 2));
225318
+ const [pendingApprovals] = import_react82.useState([]);
225319
+ const [lastApprovedAction] = import_react82.useState(null);
225320
+ const [verboseMode, setVerboseMode] = import_react82.useState(false);
225321
+ const [expandedLogs, setExpandedLogs] = import_react82.useState(false);
225322
+ import_react82.useEffect(() => {
222895
225323
  async function loadSession() {
222896
225324
  try {
222897
225325
  const s2 = await sessions.get(sessionId);
@@ -222923,7 +225351,7 @@ function OperatorDashboard({
222923
225351
  }
222924
225352
  loadSession();
222925
225353
  }, [sessionId, isResume]);
222926
- const appendText = import_react78.useCallback((text2) => {
225354
+ const appendText = import_react82.useCallback((text2) => {
222927
225355
  textRef.current += text2;
222928
225356
  const accumulated = textRef.current;
222929
225357
  setMessages((prev) => {
@@ -222939,7 +225367,7 @@ function OperatorDashboard({
222939
225367
  ];
222940
225368
  });
222941
225369
  }, []);
222942
- const addToolCall = import_react78.useCallback((toolCallId, toolName, args) => {
225370
+ const addToolCall = import_react82.useCallback((toolCallId, toolName, args) => {
222943
225371
  textRef.current = "";
222944
225372
  setMessages((prev) => [
222945
225373
  ...prev,
@@ -222954,7 +225382,7 @@ function OperatorDashboard({
222954
225382
  }
222955
225383
  ]);
222956
225384
  }, []);
222957
- const updateToolResult = import_react78.useCallback((toolCallId, _toolName, result) => {
225385
+ const updateToolResult = import_react82.useCallback((toolCallId, _toolName, result) => {
222958
225386
  textRef.current = "";
222959
225387
  setMessages((prev) => {
222960
225388
  const idx = prev.findIndex((m4) => isToolMessage(m4) && m4.toolCallId === toolCallId);
@@ -222965,7 +225393,7 @@ function OperatorDashboard({
222965
225393
  return updated;
222966
225394
  });
222967
225395
  }, []);
222968
- const runAgent = import_react78.useCallback(async (prompt) => {
225396
+ const runAgent = import_react82.useCallback(async (prompt) => {
222969
225397
  if (!session)
222970
225398
  return;
222971
225399
  setStatus("running");
@@ -222989,12 +225417,7 @@ function OperatorDashboard({
222989
225417
  target: session.targets[0],
222990
225418
  activeTools: [...ALL_TOOL_NAMES],
222991
225419
  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
- },
225420
+ authConfig: buildAuthConfig(config3.data),
222998
225421
  callbacks: {
222999
225422
  onTextDelta: (d3) => {
223000
225423
  setThinking(false);
@@ -223044,13 +225467,13 @@ function OperatorDashboard({
223044
225467
  setThinking,
223045
225468
  setIsExecuting
223046
225469
  ]);
223047
- const handleSubmit = import_react78.useCallback((value) => {
225470
+ const handleSubmit = import_react82.useCallback((value) => {
223048
225471
  if (!value.trim() || status === "running")
223049
225472
  return;
223050
225473
  setInputValue("");
223051
225474
  runAgent(value.trim());
223052
225475
  }, [status, runAgent]);
223053
- const handleAbort = import_react78.useCallback(() => {
225476
+ const handleAbort = import_react82.useCallback(() => {
223054
225477
  if (abortControllerRef.current) {
223055
225478
  abortControllerRef.current.abort();
223056
225479
  abortControllerRef.current = null;
@@ -223261,7 +225684,8 @@ Session paths:
223261
225684
  }
223262
225685
 
223263
225686
  // src/tui/components/commands/theme-picker.tsx
223264
- var import_react80 = __toESM(require_react(), 1);
225687
+ var import_react84 = __toESM(require_react(), 1);
225688
+ init_config2();
223265
225689
  function ThemePicker() {
223266
225690
  const dimensions = useTerminalDimensions();
223267
225691
  const route = useRoute();
@@ -223274,15 +225698,15 @@ function ThemePicker() {
223274
225698
  toggleMode,
223275
225699
  setMode
223276
225700
  } = 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(() => {
225701
+ const [selectedIndex, setSelectedIndex] = import_react84.useState(() => Math.max(0, availableThemes.indexOf(theme.name)));
225702
+ const originalThemeRef = import_react84.useRef(theme.name);
225703
+ const originalModeRef = import_react84.useRef(mode);
225704
+ const handleClose = import_react84.useCallback(() => {
223281
225705
  setTheme(originalThemeRef.current);
223282
225706
  setMode(originalModeRef.current);
223283
225707
  route.navigate({ type: "base", path: "home" });
223284
225708
  }, [setTheme, setMode, route]);
223285
- const handleConfirm = import_react80.useCallback(async () => {
225709
+ const handleConfirm = import_react84.useCallback(async () => {
223286
225710
  const currentThemeName = availableThemes[selectedIndex];
223287
225711
  if (currentThemeName) {
223288
225712
  await config.update({ theme: currentThemeName });
@@ -225810,13 +228234,13 @@ async function detectTerminalMode(timeoutMs = 1000) {
225810
228234
 
225811
228235
  // src/tui/index.tsx
225812
228236
  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);
228237
+ const [focusIndex, setFocusIndex] = import_react87.useState(0);
228238
+ const [cwd, setCwd] = import_react87.useState(process.cwd());
228239
+ const [ctrlCPressTime, setCtrlCPressTime] = import_react87.useState(null);
228240
+ const [showExitWarning, setShowExitWarning] = import_react87.useState(false);
228241
+ const [inputKey, setInputKey] = import_react87.useState(0);
228242
+ const [showSessionsDialog, setShowSessionsDialog] = import_react87.useState(false);
228243
+ const [showShortcutsDialog, setShowShortcutsDialog] = import_react87.useState(false);
225820
228244
  const navigableItems = ["command-input"];
225821
228245
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ConfigProvider, {
225822
228246
  config: appConfig,
@@ -225880,14 +228304,14 @@ function AppContent({
225880
228304
  const { toast } = useToast();
225881
228305
  const { refocusPrompt } = useFocus();
225882
228306
  const { setExternalDialogOpen } = useDialog();
225883
- import_react83.useEffect(() => {
228307
+ import_react87.useEffect(() => {
225884
228308
  checkForUpdate().then(({ updateAvailable, currentVersion, latestVersion }) => {
225885
228309
  if (!updateAvailable)
225886
228310
  return;
225887
228311
  toast(`Update available: v${currentVersion} → v${latestVersion}. Run: pensar upgrade`, "warn", 8000);
225888
228312
  });
225889
228313
  }, []);
225890
- import_react83.useEffect(() => {
228314
+ import_react87.useEffect(() => {
225891
228315
  if (route.data.type !== "base")
225892
228316
  return;
225893
228317
  if (!config3.data.responsibleUseAccepted && route.data.path !== "disclosure") {
@@ -225896,7 +228320,7 @@ function AppContent({
225896
228320
  route.navigate({ type: "base", path: "providers" });
225897
228321
  }
225898
228322
  }, [config3.data.responsibleUseAccepted, route.data]);
225899
- import_react83.useEffect(() => {
228323
+ import_react87.useEffect(() => {
225900
228324
  if (showExitWarning) {
225901
228325
  const timer = setTimeout(() => {
225902
228326
  setShowExitWarning(false);
@@ -226042,6 +228466,14 @@ function CommandDisplay({
226042
228466
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
226043
228467
  when: "models",
226044
228468
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelsDisplay, {}, undefined, false, undefined, this)
228469
+ }, undefined, false, undefined, this),
228470
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
228471
+ when: "auth",
228472
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AuthFlow, {}, undefined, false, undefined, this)
228473
+ }, undefined, false, undefined, this),
228474
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
228475
+ when: "credits",
228476
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(CreditsFlow, {}, undefined, false, undefined, this)
226045
228477
  }, undefined, false, undefined, this)
226046
228478
  ]
226047
228479
  }, undefined, true, undefined, this)