@pensar/apex 0.0.36-canary.0 → 0.0.39-canary.2f181ec5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,14 @@
1
1
  <h1 align="center">Pensar Apex</h1>
2
2
 
3
3
  <p align="center">
4
- <a href="https://www.npmjs.com/package/@pensar/apex"><img src="https://img.shields.io/npm/v/@pensar/apex" alt="npm version"></a>
4
+ <a href="https://www.npmjs.com/package/@pensar/apex"><img src="https://img.shields.io/npm/v/@pensar/apex?label=latest" alt="npm version"></a>
5
+ <a href="https://www.npmjs.com/package/@pensar/apex"><img src="https://img.shields.io/npm/v/@pensar/apex/canary?label=prerelease&color=yellow" alt="npm prerelease version"></a>
6
+ <!-- <a href="https://www.npmjs.com/package/@pensar/apex"><img src="https://img.shields.io/npm/dm/@pensar/apex" alt="npm downloads"></a> -->
7
+ <a href="https://github.com/pensarai/homebrew-tap"><img src="https://img.shields.io/github/v/release/pensarai/apex?label=homebrew&logo=homebrew&color=orange" alt="Homebrew"></a>
8
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="Apache 2.0 License"></a>
9
+ <a href="https://docs.pensar.dev/apex"><img src="https://img.shields.io/badge/docs-docs.pensar.dev/apex-purple?logo=readthedocs&logoColor=white" alt="Documentation"></a>
10
+ </p>
11
+
5
12
  <p align="center">
6
13
  <img src="screenshot.png" alt="Pensar Apex Screenshot" width="800">
7
14
  </p>
@@ -40,6 +47,27 @@ Download installer from `https://nmap.org/download.html` and ensure `nmap` is on
40
47
 
41
48
  ### Install Apex
42
49
 
50
+ #### macOS / Linux (Quick Install)
51
+
52
+ ```bash
53
+ curl -fsSL https://pensarai.com/install.sh | bash
54
+ ```
55
+
56
+ #### Homebrew
57
+
58
+ ```bash
59
+ brew tap pensarai/tap
60
+ brew install apex
61
+ ```
62
+
63
+ #### Windows (PowerShell)
64
+
65
+ ```powershell
66
+ irm https://pensarai.com/apex.ps1 | iex
67
+ ```
68
+
69
+ #### npm
70
+
43
71
  ```bash
44
72
  npm install -g @pensar/apex
45
73
  ```
@@ -84615,7 +84615,7 @@ async function installApex(sandbox, branch) {
84615
84615
  const prefix = branch ? `[${branch}] ` : "";
84616
84616
  console.log(`${prefix}\uD83D\uDCE6 Installing Apex globally via bun...`);
84617
84617
  try {
84618
- const installResult = await sandbox.process.executeCommand('export BUN_INSTALL="$HOME/.bun" && export PATH="$BUN_INSTALL/bin:$PATH" && bun install -g @pensar/apex');
84618
+ const installResult = await sandbox.process.executeCommand('export BUN_INSTALL="$HOME/.bun" && export PATH="$BUN_INSTALL/bin:$PATH" && bun install -g @pensar/apex@canary');
84619
84619
  if (installResult.exitCode !== 0) {
84620
84620
  throw new Error(`Bun install failed with exit code ${installResult.exitCode}`);
84621
84621
  }
@@ -121194,6 +121194,12 @@ var OPENROUTER_MODELS = [
121194
121194
  provider: "openrouter",
121195
121195
  contextLength: 64000
121196
121196
  },
121197
+ {
121198
+ id: "mistralai/mistral-large-2512",
121199
+ name: "Mistral Large 3 2512",
121200
+ provider: "openrouter",
121201
+ contextLength: 262144
121202
+ },
121197
121203
  {
121198
121204
  id: "moonshotai/kimi-k2-thinking",
121199
121205
  name: "Kimi K2 Thinking",
@@ -121795,11 +121801,38 @@ async function summarizeConversation(messages, opts, model) {
121795
121801
  content: `Summarize this conversation to pass to another agent. This was the system prompt: ${opts.system} `
121796
121802
  }
121797
121803
  ];
121798
- const { text: summary } = await generateText({
121804
+ const { text: summary, usage: summaryUsage } = await generateText({
121799
121805
  model,
121800
121806
  system: `You are a helpful assistant that summarizes conversations to pass to another agent. Review the conversation and system prompt at the end provided by the user.`,
121801
121807
  messages: summarizedMessages
121802
121808
  });
121809
+ if (opts.onStepFinish && summaryUsage) {
121810
+ opts.onStepFinish({
121811
+ text: "",
121812
+ reasoning: undefined,
121813
+ reasoningDetails: [],
121814
+ files: [],
121815
+ sources: [],
121816
+ toolCalls: [],
121817
+ toolResults: [],
121818
+ finishReason: "stop",
121819
+ usage: {
121820
+ inputTokens: summaryUsage.inputTokens ?? 0,
121821
+ outputTokens: summaryUsage.outputTokens ?? 0,
121822
+ totalTokens: summaryUsage.totalTokens ?? 0
121823
+ },
121824
+ warnings: [],
121825
+ request: {},
121826
+ response: {
121827
+ id: "summarization",
121828
+ timestamp: new Date,
121829
+ modelId: ""
121830
+ },
121831
+ providerMetadata: undefined,
121832
+ stepType: "initial",
121833
+ isContinued: false
121834
+ });
121835
+ }
121803
121836
  const originalLength = typeof opts.prompt === "string" ? opts.prompt.length : 0;
121804
121837
  const enhancedPrompt = originalLength > 1e5 ? `Context: The previous conversation contained very long content that was summarized.
121805
121838
 
@@ -121962,6 +121995,7 @@ function streamResponse(opts) {
121962
121995
  } = opts;
121963
121996
  const messagesContainer = { current: messages || [] };
121964
121997
  const providerModel = getProviderModel(model, authConfig);
121998
+ let rateLimitRetryCount = 0;
121965
121999
  try {
121966
122000
  const response = streamText({
121967
122001
  model: providerModel,
@@ -121975,6 +122009,16 @@ function streamResponse(opts) {
121975
122009
  messagesContainer.current = opts2.messages;
121976
122010
  return;
121977
122011
  },
122012
+ onError: async ({ error: error46 }) => {
122013
+ if (error46.message.toLowerCase().includes("too many tokens") || error46.message.toLowerCase().includes("overloaded")) {
122014
+ rateLimitRetryCount++;
122015
+ await new Promise((resolve2) => setTimeout(resolve2, 1000 * rateLimitRetryCount));
122016
+ if (rateLimitRetryCount < 20) {
122017
+ return;
122018
+ }
122019
+ }
122020
+ throw error46;
122021
+ },
121978
122022
  onStepFinish,
121979
122023
  abortSignal,
121980
122024
  activeTools,
@@ -121993,7 +122037,7 @@ function streamResponse(opts) {
121993
122037
  throw new Error(`Tool ${toolCall.toolName} not found or has no schema`);
121994
122038
  }
121995
122039
  const jsonSchema2 = inputSchema({ toolName: toolCall.toolName });
121996
- const { object: repairedArgs } = await generateObject({
122040
+ const { object: repairedArgs, usage: repairUsage } = await generateObject({
121997
122041
  model: providerModel,
121998
122042
  schema: tool2.inputSchema,
121999
122043
  prompt: [
@@ -122006,6 +122050,33 @@ function streamResponse(opts) {
122006
122050
  ].join(`
122007
122051
  `)
122008
122052
  });
122053
+ if (onStepFinish && repairUsage) {
122054
+ onStepFinish({
122055
+ text: "",
122056
+ reasoning: undefined,
122057
+ reasoningDetails: [],
122058
+ files: [],
122059
+ sources: [],
122060
+ toolCalls: [],
122061
+ toolResults: [],
122062
+ finishReason: "stop",
122063
+ usage: {
122064
+ inputTokens: repairUsage.inputTokens ?? 0,
122065
+ outputTokens: repairUsage.outputTokens ?? 0,
122066
+ totalTokens: repairUsage.totalTokens ?? 0
122067
+ },
122068
+ warnings: [],
122069
+ request: {},
122070
+ response: {
122071
+ id: "tool-repair",
122072
+ timestamp: new Date,
122073
+ modelId: ""
122074
+ },
122075
+ providerMetadata: undefined,
122076
+ stepType: "initial",
122077
+ isContinued: false
122078
+ });
122079
+ }
122009
122080
  return { ...toolCall, input: JSON.stringify(repairedArgs) };
122010
122081
  } catch (repairError) {
122011
122082
  if (!silent) {
@@ -122032,9 +122103,9 @@ function streamResponse(opts) {
122032
122103
  }
122033
122104
  }
122034
122105
  async function generateObjectResponse(opts) {
122035
- const { model, schema, prompt, system, maxTokens, temperature, authConfig } = opts;
122106
+ const { model, schema, prompt, system, maxTokens, temperature, authConfig, onTokenUsage } = opts;
122036
122107
  const providerModel = getProviderModel(model, authConfig);
122037
- const { object: object3 } = await generateObject({
122108
+ const { object: object3, usage } = await generateObject({
122038
122109
  model: providerModel,
122039
122110
  schema,
122040
122111
  prompt,
@@ -122042,6 +122113,9 @@ async function generateObjectResponse(opts) {
122042
122113
  maxTokens,
122043
122114
  temperature
122044
122115
  });
122116
+ if (onTokenUsage && usage) {
122117
+ onTokenUsage(usage.inputTokens ?? 0, usage.outputTokens ?? 0);
122118
+ }
122045
122119
  return object3;
122046
122120
  }
122047
122121
  // src/core/agent/benchmark/prompts.ts
@@ -126855,6 +126929,7 @@ function runAgent(opts) {
126855
126929
  objective,
126856
126930
  model,
126857
126931
  onStepFinish,
126932
+ onToolTokenUsage,
126858
126933
  abortSignal,
126859
126934
  silent,
126860
126935
  authConfig,
@@ -126874,7 +126949,7 @@ function runAgent(opts) {
126874
126949
  analyze_scan,
126875
126950
  scratchpad,
126876
126951
  generate_report
126877
- } = createPentestTools(session, undefined, toolOverride);
126952
+ } = createPentestTools(session, undefined, toolOverride, onToolTokenUsage);
126878
126953
  const document_finding = tool({
126879
126954
  name: "document_finding",
126880
126955
  description: `Document a security finding with severity, impact, and remediation guidance.
@@ -128705,7 +128780,7 @@ Example workflow:
128705
128780
  execute: async (params) => recordTestResultCore(session, params)
128706
128781
  });
128707
128782
  }
128708
- async function generateTestStrategy(params, model) {
128783
+ async function generateTestStrategy(params, model, onTokenUsage) {
128709
128784
  const prompt = `You are a penetration testing expert. Generate a concise testing strategy:
128710
128785
 
128711
128786
  Attack Type: ${params.knowledge.name}
@@ -128731,12 +128806,15 @@ Be tactical and specific.`;
128731
128806
  model: providerModel,
128732
128807
  prompt
128733
128808
  });
128809
+ if (onTokenUsage && result.usage) {
128810
+ onTokenUsage(result.usage.inputTokens ?? 0, result.usage.outputTokens ?? 0);
128811
+ }
128734
128812
  return result.text;
128735
128813
  } catch (error46) {
128736
128814
  return params.knowledge.adaptiveStrategy;
128737
128815
  }
128738
128816
  }
128739
- async function generatePayload(params, model) {
128817
+ async function generatePayload(params, model, onTokenUsage) {
128740
128818
  const prompt = `Generate ONE ${params.knowledge.name} payload for testing.
128741
128819
 
128742
128820
  Techniques:
@@ -128760,7 +128838,8 @@ Generate ONE specific payload. Return ONLY JSON:
128760
128838
  const result = await generateObjectResponse({
128761
128839
  model,
128762
128840
  schema: PayloadSchema,
128763
- prompt
128841
+ prompt,
128842
+ onTokenUsage
128764
128843
  });
128765
128844
  return result;
128766
128845
  } catch (error46) {
@@ -128773,7 +128852,7 @@ Generate ONE specific payload. Return ONLY JSON:
128773
128852
  technique: technique.name
128774
128853
  };
128775
128854
  }
128776
- async function analyzeResponse(params, model) {
128855
+ async function analyzeResponse(params, model, onTokenUsage) {
128777
128856
  const prompt = `Analyze this security test response:
128778
128857
 
128779
128858
  Attack: ${params.knowledge.name}
@@ -128801,7 +128880,8 @@ Analyze: Is this vulnerable? Return ONLY JSON:
128801
128880
  const result = await generateObjectResponse({
128802
128881
  model,
128803
128882
  schema: AnalysisSchema,
128804
- prompt
128883
+ prompt,
128884
+ onTokenUsage
128805
128885
  });
128806
128886
  return result;
128807
128887
  } catch (error46) {
@@ -128820,7 +128900,7 @@ Analyze: Is this vulnerable? Return ONLY JSON:
128820
128900
  suggestedNextTest: "Try alternative payload or technique"
128821
128901
  };
128822
128902
  }
128823
- function createSmartTestTool(session, model) {
128903
+ function createSmartTestTool(session, model, onTokenUsage) {
128824
128904
  return tool({
128825
128905
  name: "test_parameter",
128826
128906
  description: `Intelligently test a parameter for a vulnerability using AI-powered adaptive testing.
@@ -128890,7 +128970,7 @@ test_parameter({
128890
128970
  parameter,
128891
128971
  endpoint,
128892
128972
  context
128893
- }, model);
128973
+ }, model, onTokenUsage);
128894
128974
  console.log(`Strategy: ${strategy}`);
128895
128975
  const results = [];
128896
128976
  let vulnerable = false;
@@ -128903,7 +128983,7 @@ test_parameter({
128903
128983
  context: { ...context, parameter, endpoint },
128904
128984
  previousResults: results,
128905
128985
  round
128906
- }, model);
128986
+ }, model, onTokenUsage);
128907
128987
  console.log(` Payload: ${payloadData.payload}`);
128908
128988
  console.log(` Reasoning: ${payloadData.reasoning}`);
128909
128989
  let response;
@@ -128932,7 +129012,7 @@ test_parameter({
128932
129012
  attackType,
128933
129013
  knowledge,
128934
129014
  previousResults: results
128935
- }, model);
129015
+ }, model, onTokenUsage);
128936
129016
  console.log(` Analysis: ${analysis.reasoning}`);
128937
129017
  console.log(` Vulnerable: ${analysis.vulnerable} (confidence: ${analysis.confidence})`);
128938
129018
  results.push({
@@ -129382,7 +129462,7 @@ function wrapCommandWithHeaders(command, headers) {
129382
129462
  }
129383
129463
  return wrapped;
129384
129464
  }
129385
- function createPentestTools(session, model, toolOverride) {
129465
+ function createPentestTools(session, model, toolOverride, onTokenUsage) {
129386
129466
  const offensiveHeaders = getOffensiveHeaders(session);
129387
129467
  const rateLimiter = session._rateLimiter;
129388
129468
  const executeCommand = tool({
@@ -129556,7 +129636,7 @@ COMMON TESTING PATTERNS:
129556
129636
  http_request: httpRequest,
129557
129637
  document_finding: createDocumentFindingTool(session),
129558
129638
  record_test_result: createRecordTestResultTool(session),
129559
- test_parameter: createSmartTestTool(session, model || "claude-sonnet-4-20250514"),
129639
+ test_parameter: createSmartTestTool(session, model || "claude-sonnet-4-20250514", onTokenUsage),
129560
129640
  check_testing_coverage: createCheckTestingCoverageTool(session),
129561
129641
  validate_completeness: createValidateCompletenessTool(session),
129562
129642
  enumerate_endpoints: createEnumerateEndpointsTool(session),
@@ -129572,7 +129652,7 @@ COMMON TESTING PATTERNS:
129572
129652
  import { join as join5 } from "path";
129573
129653
  import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
129574
129654
  function runAgent2(opts) {
129575
- const { target, model, onStepFinish, abortSignal } = opts;
129655
+ const { target, model, onStepFinish, onToolTokenUsage, abortSignal } = opts;
129576
129656
  const session = opts.session || createSession(target);
129577
129657
  const subagentId = `attack-surface-${nanoid3(6)}`;
129578
129658
  console.log(`Created attack surface session: ${session.id}`);
@@ -129581,7 +129661,7 @@ function runAgent2(opts) {
129581
129661
  if (!existsSync7(assetsPath)) {
129582
129662
  mkdirSync5(assetsPath, { recursive: true });
129583
129663
  }
129584
- const { analyze_scan, execute_command, http_request } = createPentestTools(session, model);
129664
+ const { analyze_scan, execute_command, http_request } = createPentestTools(session, model, undefined, onToolTokenUsage);
129585
129665
  const document_asset = tool({
129586
129666
  name: "document_asset",
129587
129667
  description: `Document a discovered asset during attack surface analysis.
@@ -129742,13 +129822,14 @@ function runAgent3(opts) {
129742
129822
  onSubagentSpawn,
129743
129823
  onSubagentMessage,
129744
129824
  onSubagentComplete,
129825
+ onSubagentTokenUsage,
129745
129826
  session: sessionProp
129746
129827
  } = opts;
129747
129828
  const session = sessionProp || createSession(target, undefined, undefined, sessionConfig);
129748
129829
  const logger = new Logger(session);
129749
129830
  logger.log(`Created thorough pentest session: ${session.id}`);
129750
129831
  logger.log(`Session path: ${session.rootPath}`);
129751
- const tools2 = createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, logger);
129832
+ const tools2 = createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, onSubagentTokenUsage, logger);
129752
129833
  const enhancedPrompt = `
129753
129834
  TARGET: ${target}
129754
129835
 
@@ -129784,7 +129865,7 @@ Begin by using the get_attack_surface tool to map the complete attack surface of
129784
129865
  streamResult.session = session;
129785
129866
  return { streamResult, session };
129786
129867
  }
129787
- function createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, logger) {
129868
+ function createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, onSubagentTokenUsage, logger) {
129788
129869
  const getAttackSurface = tool({
129789
129870
  name: "get_attack_surface",
129790
129871
  description: `Run the attack surface analysis agent to discover all assets and identify targets.
@@ -129816,7 +129897,19 @@ Use this as the FIRST step in your thorough penetration test.`,
129816
129897
  target,
129817
129898
  objective,
129818
129899
  model,
129819
- abortSignal
129900
+ abortSignal,
129901
+ onStepFinish: ({ usage }) => {
129902
+ if (onSubagentTokenUsage) {
129903
+ const inputTokens = usage.inputTokens ?? 0;
129904
+ const outputTokens = usage.outputTokens ?? 0;
129905
+ onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
129906
+ }
129907
+ },
129908
+ onToolTokenUsage: (inputTokens, outputTokens) => {
129909
+ if (onSubagentTokenUsage) {
129910
+ onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
129911
+ }
129912
+ }
129820
129913
  });
129821
129914
  const allMessages = [];
129822
129915
  let currentAssistantText = "";
@@ -129991,7 +130084,19 @@ You can spawn multiple agents in parallel - they will run concurrently.`,
129991
130084
  target: targetInfo.target,
129992
130085
  objective: targetInfo.objective,
129993
130086
  model,
129994
- abortSignal
130087
+ abortSignal,
130088
+ onStepFinish: ({ usage }) => {
130089
+ if (onSubagentTokenUsage) {
130090
+ const inputTokens = usage.inputTokens ?? 0;
130091
+ const outputTokens = usage.outputTokens ?? 0;
130092
+ onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
130093
+ }
130094
+ },
130095
+ onToolTokenUsage: (inputTokens, outputTokens) => {
130096
+ if (onSubagentTokenUsage) {
130097
+ onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
130098
+ }
130099
+ }
129995
130100
  });
129996
130101
  const allMessages = [];
129997
130102
  let currentAssistantText = "";