@pensar/apex 0.0.37-canary.0 → 0.0.39-canary.efda0f61

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
  ```
@@ -121801,11 +121801,38 @@ async function summarizeConversation(messages, opts, model) {
121801
121801
  content: `Summarize this conversation to pass to another agent. This was the system prompt: ${opts.system} `
121802
121802
  }
121803
121803
  ];
121804
- const { text: summary } = await generateText({
121804
+ const { text: summary, usage: summaryUsage } = await generateText({
121805
121805
  model,
121806
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.`,
121807
121807
  messages: summarizedMessages
121808
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
+ }
121809
121836
  const originalLength = typeof opts.prompt === "string" ? opts.prompt.length : 0;
121810
121837
  const enhancedPrompt = originalLength > 1e5 ? `Context: The previous conversation contained very long content that was summarized.
121811
121838
 
@@ -121968,6 +121995,7 @@ function streamResponse(opts) {
121968
121995
  } = opts;
121969
121996
  const messagesContainer = { current: messages || [] };
121970
121997
  const providerModel = getProviderModel(model, authConfig);
121998
+ let rateLimitRetryCount = 0;
121971
121999
  try {
121972
122000
  const response = streamText({
121973
122001
  model: providerModel,
@@ -121981,6 +122009,16 @@ function streamResponse(opts) {
121981
122009
  messagesContainer.current = opts2.messages;
121982
122010
  return;
121983
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
+ },
121984
122022
  onStepFinish,
121985
122023
  abortSignal,
121986
122024
  activeTools,
@@ -121999,7 +122037,7 @@ function streamResponse(opts) {
121999
122037
  throw new Error(`Tool ${toolCall.toolName} not found or has no schema`);
122000
122038
  }
122001
122039
  const jsonSchema2 = inputSchema({ toolName: toolCall.toolName });
122002
- const { object: repairedArgs } = await generateObject({
122040
+ const { object: repairedArgs, usage: repairUsage } = await generateObject({
122003
122041
  model: providerModel,
122004
122042
  schema: tool2.inputSchema,
122005
122043
  prompt: [
@@ -122012,6 +122050,33 @@ function streamResponse(opts) {
122012
122050
  ].join(`
122013
122051
  `)
122014
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
+ }
122015
122080
  return { ...toolCall, input: JSON.stringify(repairedArgs) };
122016
122081
  } catch (repairError) {
122017
122082
  if (!silent) {
@@ -122038,9 +122103,9 @@ function streamResponse(opts) {
122038
122103
  }
122039
122104
  }
122040
122105
  async function generateObjectResponse(opts) {
122041
- const { model, schema, prompt, system, maxTokens, temperature, authConfig } = opts;
122106
+ const { model, schema, prompt, system, maxTokens, temperature, authConfig, onTokenUsage } = opts;
122042
122107
  const providerModel = getProviderModel(model, authConfig);
122043
- const { object: object3 } = await generateObject({
122108
+ const { object: object3, usage } = await generateObject({
122044
122109
  model: providerModel,
122045
122110
  schema,
122046
122111
  prompt,
@@ -122048,6 +122113,9 @@ async function generateObjectResponse(opts) {
122048
122113
  maxTokens,
122049
122114
  temperature
122050
122115
  });
122116
+ if (onTokenUsage && usage) {
122117
+ onTokenUsage(usage.inputTokens ?? 0, usage.outputTokens ?? 0);
122118
+ }
122051
122119
  return object3;
122052
122120
  }
122053
122121
  // src/core/agent/benchmark/prompts.ts
@@ -126861,6 +126929,7 @@ function runAgent(opts) {
126861
126929
  objective,
126862
126930
  model,
126863
126931
  onStepFinish,
126932
+ onToolTokenUsage,
126864
126933
  abortSignal,
126865
126934
  silent,
126866
126935
  authConfig,
@@ -126880,7 +126949,7 @@ function runAgent(opts) {
126880
126949
  analyze_scan,
126881
126950
  scratchpad,
126882
126951
  generate_report
126883
- } = createPentestTools(session, undefined, toolOverride);
126952
+ } = createPentestTools(session, undefined, toolOverride, onToolTokenUsage);
126884
126953
  const document_finding = tool({
126885
126954
  name: "document_finding",
126886
126955
  description: `Document a security finding with severity, impact, and remediation guidance.
@@ -128711,7 +128780,7 @@ Example workflow:
128711
128780
  execute: async (params) => recordTestResultCore(session, params)
128712
128781
  });
128713
128782
  }
128714
- async function generateTestStrategy(params, model) {
128783
+ async function generateTestStrategy(params, model, onTokenUsage) {
128715
128784
  const prompt = `You are a penetration testing expert. Generate a concise testing strategy:
128716
128785
 
128717
128786
  Attack Type: ${params.knowledge.name}
@@ -128737,12 +128806,15 @@ Be tactical and specific.`;
128737
128806
  model: providerModel,
128738
128807
  prompt
128739
128808
  });
128809
+ if (onTokenUsage && result.usage) {
128810
+ onTokenUsage(result.usage.inputTokens ?? 0, result.usage.outputTokens ?? 0);
128811
+ }
128740
128812
  return result.text;
128741
128813
  } catch (error46) {
128742
128814
  return params.knowledge.adaptiveStrategy;
128743
128815
  }
128744
128816
  }
128745
- async function generatePayload(params, model) {
128817
+ async function generatePayload(params, model, onTokenUsage) {
128746
128818
  const prompt = `Generate ONE ${params.knowledge.name} payload for testing.
128747
128819
 
128748
128820
  Techniques:
@@ -128766,7 +128838,8 @@ Generate ONE specific payload. Return ONLY JSON:
128766
128838
  const result = await generateObjectResponse({
128767
128839
  model,
128768
128840
  schema: PayloadSchema,
128769
- prompt
128841
+ prompt,
128842
+ onTokenUsage
128770
128843
  });
128771
128844
  return result;
128772
128845
  } catch (error46) {
@@ -128779,7 +128852,7 @@ Generate ONE specific payload. Return ONLY JSON:
128779
128852
  technique: technique.name
128780
128853
  };
128781
128854
  }
128782
- async function analyzeResponse(params, model) {
128855
+ async function analyzeResponse(params, model, onTokenUsage) {
128783
128856
  const prompt = `Analyze this security test response:
128784
128857
 
128785
128858
  Attack: ${params.knowledge.name}
@@ -128807,7 +128880,8 @@ Analyze: Is this vulnerable? Return ONLY JSON:
128807
128880
  const result = await generateObjectResponse({
128808
128881
  model,
128809
128882
  schema: AnalysisSchema,
128810
- prompt
128883
+ prompt,
128884
+ onTokenUsage
128811
128885
  });
128812
128886
  return result;
128813
128887
  } catch (error46) {
@@ -128826,7 +128900,7 @@ Analyze: Is this vulnerable? Return ONLY JSON:
128826
128900
  suggestedNextTest: "Try alternative payload or technique"
128827
128901
  };
128828
128902
  }
128829
- function createSmartTestTool(session, model) {
128903
+ function createSmartTestTool(session, model, onTokenUsage) {
128830
128904
  return tool({
128831
128905
  name: "test_parameter",
128832
128906
  description: `Intelligently test a parameter for a vulnerability using AI-powered adaptive testing.
@@ -128896,7 +128970,7 @@ test_parameter({
128896
128970
  parameter,
128897
128971
  endpoint,
128898
128972
  context
128899
- }, model);
128973
+ }, model, onTokenUsage);
128900
128974
  console.log(`Strategy: ${strategy}`);
128901
128975
  const results = [];
128902
128976
  let vulnerable = false;
@@ -128909,7 +128983,7 @@ test_parameter({
128909
128983
  context: { ...context, parameter, endpoint },
128910
128984
  previousResults: results,
128911
128985
  round
128912
- }, model);
128986
+ }, model, onTokenUsage);
128913
128987
  console.log(` Payload: ${payloadData.payload}`);
128914
128988
  console.log(` Reasoning: ${payloadData.reasoning}`);
128915
128989
  let response;
@@ -128938,7 +129012,7 @@ test_parameter({
128938
129012
  attackType,
128939
129013
  knowledge,
128940
129014
  previousResults: results
128941
- }, model);
129015
+ }, model, onTokenUsage);
128942
129016
  console.log(` Analysis: ${analysis.reasoning}`);
128943
129017
  console.log(` Vulnerable: ${analysis.vulnerable} (confidence: ${analysis.confidence})`);
128944
129018
  results.push({
@@ -129388,7 +129462,7 @@ function wrapCommandWithHeaders(command, headers) {
129388
129462
  }
129389
129463
  return wrapped;
129390
129464
  }
129391
- function createPentestTools(session, model, toolOverride) {
129465
+ function createPentestTools(session, model, toolOverride, onTokenUsage) {
129392
129466
  const offensiveHeaders = getOffensiveHeaders(session);
129393
129467
  const rateLimiter = session._rateLimiter;
129394
129468
  const executeCommand = tool({
@@ -129562,7 +129636,7 @@ COMMON TESTING PATTERNS:
129562
129636
  http_request: httpRequest,
129563
129637
  document_finding: createDocumentFindingTool(session),
129564
129638
  record_test_result: createRecordTestResultTool(session),
129565
- test_parameter: createSmartTestTool(session, model || "claude-sonnet-4-20250514"),
129639
+ test_parameter: createSmartTestTool(session, model || "claude-sonnet-4-20250514", onTokenUsage),
129566
129640
  check_testing_coverage: createCheckTestingCoverageTool(session),
129567
129641
  validate_completeness: createValidateCompletenessTool(session),
129568
129642
  enumerate_endpoints: createEnumerateEndpointsTool(session),
@@ -129578,7 +129652,7 @@ COMMON TESTING PATTERNS:
129578
129652
  import { join as join5 } from "path";
129579
129653
  import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
129580
129654
  function runAgent2(opts) {
129581
- const { target, model, onStepFinish, abortSignal } = opts;
129655
+ const { target, model, onStepFinish, onToolTokenUsage, abortSignal } = opts;
129582
129656
  const session = opts.session || createSession(target);
129583
129657
  const subagentId = `attack-surface-${nanoid3(6)}`;
129584
129658
  console.log(`Created attack surface session: ${session.id}`);
@@ -129587,7 +129661,7 @@ function runAgent2(opts) {
129587
129661
  if (!existsSync7(assetsPath)) {
129588
129662
  mkdirSync5(assetsPath, { recursive: true });
129589
129663
  }
129590
- const { analyze_scan, execute_command, http_request } = createPentestTools(session, model);
129664
+ const { analyze_scan, execute_command, http_request } = createPentestTools(session, model, undefined, onToolTokenUsage);
129591
129665
  const document_asset = tool({
129592
129666
  name: "document_asset",
129593
129667
  description: `Document a discovered asset during attack surface analysis.
@@ -129748,13 +129822,14 @@ function runAgent3(opts) {
129748
129822
  onSubagentSpawn,
129749
129823
  onSubagentMessage,
129750
129824
  onSubagentComplete,
129825
+ onSubagentTokenUsage,
129751
129826
  session: sessionProp
129752
129827
  } = opts;
129753
129828
  const session = sessionProp || createSession(target, undefined, undefined, sessionConfig);
129754
129829
  const logger = new Logger(session);
129755
129830
  logger.log(`Created thorough pentest session: ${session.id}`);
129756
129831
  logger.log(`Session path: ${session.rootPath}`);
129757
- const tools2 = createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, logger);
129832
+ const tools2 = createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, onSubagentTokenUsage, logger);
129758
129833
  const enhancedPrompt = `
129759
129834
  TARGET: ${target}
129760
129835
 
@@ -129790,7 +129865,7 @@ Begin by using the get_attack_surface tool to map the complete attack surface of
129790
129865
  streamResult.session = session;
129791
129866
  return { streamResult, session };
129792
129867
  }
129793
- function createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, logger) {
129868
+ function createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, onSubagentTokenUsage, logger) {
129794
129869
  const getAttackSurface = tool({
129795
129870
  name: "get_attack_surface",
129796
129871
  description: `Run the attack surface analysis agent to discover all assets and identify targets.
@@ -129822,7 +129897,19 @@ Use this as the FIRST step in your thorough penetration test.`,
129822
129897
  target,
129823
129898
  objective,
129824
129899
  model,
129825
- 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
+ }
129826
129913
  });
129827
129914
  const allMessages = [];
129828
129915
  let currentAssistantText = "";
@@ -129997,7 +130084,19 @@ You can spawn multiple agents in parallel - they will run concurrently.`,
129997
130084
  target: targetInfo.target,
129998
130085
  objective: targetInfo.objective,
129999
130086
  model,
130000
- 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
+ }
130001
130100
  });
130002
130101
  const allMessages = [];
130003
130102
  let currentAssistantText = "";