@pensar/apex 0.0.109 → 0.0.110-canary.a34d3e3d

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,6 +1,10 @@
1
1
  <h1 align="center">Pensar Apex</h1>
2
2
 
3
- <p align="center">AI-powered penetration testing tool that enables you to use an AI agent to perform comprehensive blackbox and whitebox pentesting - directly in your terminal.
3
+ <p align="center">AI-powered penetration testing using an AI agent to perform comprehensive blackbox and whitebox pentesting - directly in your terminal.
4
+ </p>
5
+
6
+ <p align="center">
7
+ Want to run from the cloud or integrate it with your CI/CD? See <a href="https://docs.pensar.dev/console">Pensar Console</a>.
4
8
  </p>
5
9
 
6
10
  <p align="center">
@@ -12,9 +16,57 @@
12
16
  <a href="https://discord.gg/pensar"><img src="https://img.shields.io/badge/Discord-Join%20Us-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
13
17
  </p>
14
18
 
15
- <p align="center">
19
+ <!-- <p align="center">
16
20
  <img src="screenshot.png" alt="Pensar Apex Screenshot" width="800">
17
- </p>
21
+ </p> -->
22
+
23
+
24
+ ## Use Cases
25
+
26
+ Apex enables both developers and security professionals to run autonomous and assisted penetration testing directly from the terminal.
27
+
28
+
29
+ ### Developers: Run a Pentest in Minutes
30
+
31
+ Apex makes it easy for developers to run a real penetration test without needing deep offensive security expertise.
32
+
33
+ Using the autonomous `/pentest` mode, Apex will perform reconnaissance, attack surface discovery, vulnerability testing, and exploitation attempts automatically.
34
+
35
+ This allows teams to quickly identify security issues before they reach production.
36
+
37
+ ```bash
38
+ /pentest
39
+ ```
40
+
41
+ Examples:
42
+ - Test a staging environment before deploying
43
+ - Scan a newly launched domain or API
44
+ - Run quick security checks during development
45
+ - Identify exposed services or misconfigurations
46
+
47
+ This is the **fastest way to get real pentesting coverage without becoming a security expert.**
48
+
49
+ ---
50
+
51
+ ### Security Engineers: Advanced Operator Workflows
52
+
53
+ Security professionals can use Apex as an **agentic offensive security harness** that orchestrates tools and reasoning workflows.
54
+
55
+ The `/operator` mode allows engineers to work interactively with the Offensive Security Agent, guiding investigations and chaining tools dynamically.
56
+
57
+
58
+ ```bash
59
+ /operator
60
+ ```
61
+
62
+ Examples:
63
+ - Deep investigation of suspicious endpoints
64
+ - Manual exploitation of discovered vulnerabilities
65
+ - Tool orchestration across recon and exploitation phases
66
+ - Validation and reproduction of vulnerabilities
67
+ - Open-source security research / testing
68
+
69
+ This turns Apex into a **terminal-native AI pentesting partner** rather than just a scanner.
18
70
 
19
71
  ## Installation
20
72
 
@@ -45,17 +97,13 @@ npm install -g @pensar/apex
45
97
 
46
98
  ## Usage
47
99
 
48
- Run Apex:
100
+ Open the Apex TUI:
49
101
 
50
102
  ```bash
51
103
  pensar
52
104
  ```
53
105
 
54
- ## AI Provider Support
55
-
56
- Apex supports **OpenAI**, **Anthropic**, **AWS Bedrock**, and **vLLM** (local models). **Anthropic models provide the best performance** and are recommended for optimal results.
57
-
58
- ## Kali Linux Container (Recommended)
106
+ ## Kali Linux Container (Optional)
59
107
 
60
108
  For **best performance**, run Apex in the included Kali Linux container with preconfigured pentest tools:
61
109
 
@@ -72,20 +120,6 @@ Inside the container, run:
72
120
  pensar
73
121
  ```
74
122
 
75
- **Note:** On Linux hosts, consider using `network_mode: host` in `docker-compose.yml` for comprehensive network scanning.
76
-
77
- ## vLLM Local Model Support
78
-
79
- To use a local vLLM server:
80
-
81
- 1. Set the vLLM endpoint:
82
-
83
- ```bash
84
- export LOCAL_MODEL_URL="http://localhost:8000/v1"
85
- ```
86
-
87
- 2. In the Apex Models screen, enter your model name in the "Custom local model (vLLM)" input.
88
-
89
123
  ---
90
124
 
91
125
  ### ⚠️ Responsible Use
package/build/auth.js CHANGED
@@ -8,7 +8,7 @@ import fs from "fs/promises";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "@pensar/apex",
11
- version: "0.0.109",
11
+ version: "0.0.110-canary.a34d3e3d",
12
12
  description: "AI-powered penetration testing CLI tool with terminal UI",
13
13
  module: "src/tui/index.tsx",
14
14
  main: "build/index.js",
package/build/index.js CHANGED
@@ -31892,12 +31892,6 @@ var init_pensar = __esm(() => {
31892
31892
  provider: "pensar",
31893
31893
  contextLength: 200000
31894
31894
  },
31895
- {
31896
- id: "pensar:anthropic.claude-opus-4-1-20250805-v1:0",
31897
- name: "Claude Opus 4.1 (Pensar)",
31898
- provider: "pensar",
31899
- contextLength: 200000
31900
- },
31901
31895
  {
31902
31896
  id: "pensar:anthropic.claude-haiku-4-5-20251001-v1:0",
31903
31897
  name: "Claude Haiku 4.5 (Pensar)",
@@ -31977,7 +31971,7 @@ var package_default2;
31977
31971
  var init_package = __esm(() => {
31978
31972
  package_default2 = {
31979
31973
  name: "@pensar/apex",
31980
- version: "0.0.109",
31974
+ version: "0.0.110-canary.a34d3e3d",
31981
31975
  description: "AI-powered penetration testing CLI tool with terminal UI",
31982
31976
  module: "src/tui/index.tsx",
31983
31977
  main: "build/index.js",
@@ -273281,7 +273275,7 @@ var PROVIDER_PREFERENCE_ORDER = [
273281
273275
  "bedrock"
273282
273276
  ];
273283
273277
  var PREFERRED_MODEL_BY_PROVIDER = {
273284
- pensar: "pensar:anthropic.claude-opus-4-1-20250805-v1:0",
273278
+ pensar: "pensar:anthropic.claude-opus-4-6-v1",
273285
273279
  anthropic: "claude-opus-4-6",
273286
273280
  openai: "gpt-5.2-pro",
273287
273281
  google: "gemini-3.1-pro-preview",
@@ -273800,7 +273794,9 @@ function Footer({
273800
273794
  const { colors: colors2 } = useTheme();
273801
273795
  const { model, isExecuting, sessionCwd } = useAgent();
273802
273796
  const effectiveCwd = sessionCwd || cwd;
273803
- const displayCwd = "~" + effectiveCwd.split(os6.homedir()).pop() || "";
273797
+ const relativeCwd = effectiveCwd.split(os6.homedir()).pop() || "";
273798
+ const segments = relativeCwd.split("/").filter(Boolean);
273799
+ const displayCwd = segments.length <= 2 ? "~/" + segments.join("/") : "…/" + segments.slice(-2).join("/");
273804
273800
  const session = useSession();
273805
273801
  const route = useRoute();
273806
273802
  const { isInputEmpty } = useInput();
@@ -273813,11 +273809,15 @@ function Footer({
273813
273809
  justifyContent: "space-between",
273814
273810
  width: "100%",
273815
273811
  maxWidth: "100%",
273812
+ height: 1,
273816
273813
  flexShrink: 0,
273814
+ overflow: "hidden",
273817
273815
  children: [
273818
273816
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
273819
273817
  flexDirection: "row",
273820
273818
  gap: 1,
273819
+ flexShrink: 1,
273820
+ overflow: "hidden",
273821
273821
  children: [
273822
273822
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
273823
273823
  fg: colors2.textMuted,
@@ -273850,6 +273850,7 @@ function Footer({
273850
273850
  showExitWarning ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
273851
273851
  flexDirection: "row",
273852
273852
  gap: 1,
273853
+ flexShrink: 0,
273853
273854
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
273854
273855
  fg: colors2.warning,
273855
273856
  children: "⚠ Press Ctrl+C again to exit"
@@ -273857,6 +273858,7 @@ function Footer({
273857
273858
  }, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
273858
273859
  flexDirection: "row",
273859
273860
  gap: 2,
273861
+ flexShrink: 0,
273860
273862
  children: hotkeys.map((hotkey, index) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
273861
273863
  flexDirection: "row",
273862
273864
  gap: 1,
@@ -279246,7 +279248,9 @@ function AuthFlow({ onClose }) {
279246
279248
  const { colors: colors2 } = useTheme();
279247
279249
  const appConfig = useConfig();
279248
279250
  const alreadyConnected = isConnected(appConfig.data);
279249
- const [step, setStep] = import_react48.useState(alreadyConnected ? "success" : "start");
279251
+ const hasWorkspace = !!appConfig.data.workspaceId;
279252
+ const needsWorkspace = alreadyConnected && !hasWorkspace && !!appConfig.data.accessToken;
279253
+ const [step, setStep] = import_react48.useState(needsWorkspace ? "requesting" : alreadyConnected ? "success" : "start");
279250
279254
  const [error40, setError] = import_react48.useState(null);
279251
279255
  const [flowInfo, setFlowInfo] = import_react48.useState(null);
279252
279256
  const [workspaces, setWorkspaces] = import_react48.useState([]);
@@ -279457,6 +279461,14 @@ function AuthFlow({ onClose }) {
279457
279461
  setStep("error");
279458
279462
  }
279459
279463
  };
279464
+ import_react48.useEffect(() => {
279465
+ if (!needsWorkspace)
279466
+ return;
279467
+ const ac = new AbortController;
279468
+ abortRef.current = ac;
279469
+ const apiUrl = getPensarApiUrl();
279470
+ handleFetchWorkspaces(apiUrl, appConfig.data.accessToken, ac);
279471
+ }, []);
279460
279472
  const handleDisconnect = async () => {
279461
279473
  await disconnect();
279462
279474
  appConfig.reload();
@@ -279948,7 +279960,11 @@ function ProviderManager() {
279948
279960
  });
279949
279961
  };
279950
279962
  const handleAuthClose = () => {
279951
- route.navigate({ type: "base", path: "home" });
279963
+ if (isOnboarding) {
279964
+ setFlowState("choosing");
279965
+ } else {
279966
+ route.navigate({ type: "base", path: "home" });
279967
+ }
279952
279968
  };
279953
279969
  const otherProviders = AVAILABLE_PROVIDERS.filter((p) => p.id !== "pensar");
279954
279970
  const selectedProviderInfo = AVAILABLE_PROVIDERS.find((p) => p.id === selectedProvider);
@@ -286757,7 +286773,23 @@ function MessageList({
286757
286773
  }, undefined, false, undefined, this),
286758
286774
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286759
286775
  fg: colors2.textMuted,
286760
- children: " - Switch between Plan or Default mode"
286776
+ children: [
286777
+ " ",
286778
+ "- Switch between Plan or Default mode"
286779
+ ]
286780
+ }, undefined, true, undefined, this)
286781
+ ]
286782
+ }, undefined, true, undefined, this),
286783
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
286784
+ flexDirection: "row",
286785
+ children: [
286786
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286787
+ fg: colors2.primary,
286788
+ children: "Option+Shift+Tab"
286789
+ }, undefined, false, undefined, this),
286790
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286791
+ fg: colors2.textMuted,
286792
+ children: " - Toggle approval on/off"
286761
286793
  }, undefined, false, undefined, this)
286762
286794
  ]
286763
286795
  }, undefined, true, undefined, this)
@@ -286864,8 +286896,6 @@ function NormalInputAreaInner({
286864
286896
  status,
286865
286897
  mode = "operator",
286866
286898
  operatorMode,
286867
- verboseMode = false,
286868
- expandedLogs = false,
286869
286899
  enableAutocomplete = false,
286870
286900
  autocompleteOptions = [],
286871
286901
  enableCommands = false,
@@ -286944,30 +286974,25 @@ function NormalInputAreaInner({
286944
286974
  gap: 2,
286945
286975
  marginTop: 1,
286946
286976
  backgroundColor: "transparent",
286977
+ alignItems: "center",
286978
+ overflow: "hidden",
286947
286979
  children: [
286948
- operatorMode === "plan" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286949
- fg: colors2.warning,
286950
- children: "PLAN"
286951
- }, undefined, false, undefined, this),
286952
- operatorMode === "auto" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286953
- fg: colors2.primary,
286954
- children: "AUTO"
286955
- }, undefined, false, undefined, this),
286956
- operatorMode === "manual" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286957
- fg: colors2.textMuted,
286958
- children: "MANUAL"
286980
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286981
+ fg: "#000000",
286982
+ bg: operatorMode === "plan" ? colors2.warning : colors2.success,
286983
+ children: ` ${operatorMode === "plan" ? "PLAN" : "DEFAULT"} `
286959
286984
  }, undefined, false, undefined, this),
286960
286985
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286961
286986
  fg: colors2.textMuted,
286962
286987
  children: "/ commands"
286963
286988
  }, undefined, false, undefined, this),
286964
286989
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286965
- fg: verboseMode ? colors2.primary : colors2.textMuted,
286966
- children: verboseMode ? "verbose:on" : "verbose"
286990
+ fg: colors2.textMuted,
286991
+ children: "⇧Tab mode"
286967
286992
  }, undefined, false, undefined, this),
286968
286993
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286969
- fg: expandedLogs ? colors2.primary : colors2.textMuted,
286970
- children: expandedLogs ? "logs:full" : "logs"
286994
+ fg: colors2.textMuted,
286995
+ children: "⌥⇧Tab approval"
286971
286996
  }, undefined, false, undefined, this),
286972
286997
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
286973
286998
  fg: colors2.textMuted,
@@ -288417,7 +288442,7 @@ function OperatorDashboard({
288417
288442
  children: agentMode === "plan" ? "PLAN" : "DEFAULT"
288418
288443
  }, undefined, false, undefined, this),
288419
288444
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
288420
- fg: operatorState.requireApproval ? colors2.warning : colors2.primary,
288445
+ fg: operatorState.requireApproval ? colors2.success : colors2.warning,
288421
288446
  children: operatorState.requireApproval ? "APPROVAL ON" : "APPROVAL OFF"
288422
288447
  }, undefined, false, undefined, this),
288423
288448
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
@@ -288459,9 +288484,7 @@ function OperatorDashboard({
288459
288484
  focused: status === "running" ? selectedQueueIndex < 0 : resolveInputFocused(status, stack.length, externalDialogOpen),
288460
288485
  status: status === "waiting" ? "running" : status,
288461
288486
  mode: "operator",
288462
- operatorMode: agentMode === "plan" ? "plan" : operatorState.mode,
288463
- verboseMode,
288464
- expandedLogs,
288487
+ operatorMode: agentMode,
288465
288488
  pendingApproval: currentPending,
288466
288489
  onApprove: handleApprove,
288467
288490
  onAutoApprove: handleAutoApprove,
@@ -291750,8 +291773,8 @@ function AppContent({
291750
291773
  return;
291751
291774
  if (!config3.data.responsibleUseAccepted && route.data.path !== "disclosure") {
291752
291775
  route.navigate({ type: "base", path: "disclosure" });
291753
- } else if (config3.data.responsibleUseAccepted && !hasAnyProviderConfigured(config3.data) && route.data.path !== "providers" && route.data.path !== "disclosure") {
291754
- route.navigate({ type: "base", path: "providers" });
291776
+ } else if (config3.data.responsibleUseAccepted && !hasAnyProviderConfigured(config3.data) && route.data.path !== "auth" && route.data.path !== "providers" && route.data.path !== "disclosure") {
291777
+ route.navigate({ type: "base", path: "auth" });
291755
291778
  }
291756
291779
  }, [config3.data.responsibleUseAccepted, route.data]);
291757
291780
  import_react90.useEffect(() => {
@@ -291879,7 +291902,7 @@ function CommandDisplay({
291879
291902
  await config3.update({ responsibleUseAccepted: true });
291880
291903
  route.navigate({
291881
291904
  type: "base",
291882
- path: "providers"
291905
+ path: "auth"
291883
291906
  });
291884
291907
  };
291885
291908
  if (route.data.type === "base") {
@@ -291925,6 +291948,18 @@ function CommandDisplay({
291925
291948
  when: "models",
291926
291949
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelsDisplay, {}, undefined, false, undefined, this)
291927
291950
  }, undefined, false, undefined, this),
291951
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
291952
+ when: "auth",
291953
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AuthFlow, {
291954
+ onClose: () => {
291955
+ if (hasAnyProviderConfigured(config3.data)) {
291956
+ route.navigate({ type: "base", path: "home" });
291957
+ } else {
291958
+ route.navigate({ type: "base", path: "providers" });
291959
+ }
291960
+ }
291961
+ }, undefined, false, undefined, this)
291962
+ }, undefined, false, undefined, this),
291928
291963
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
291929
291964
  when: "providers",
291930
291965
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ProviderManager, {}, undefined, false, undefined, this)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pensar/apex",
3
- "version": "0.0.109",
3
+ "version": "0.0.110-canary.a34d3e3d",
4
4
  "description": "AI-powered penetration testing CLI tool with terminal UI",
5
5
  "module": "src/tui/index.tsx",
6
6
  "main": "build/index.js",