@pensar/apex 0.0.59-canary.f54bbf92 → 0.0.60-canary.2ebd004e

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 +237 -33
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -135232,6 +135232,12 @@ var AVAILABLE_PROVIDERS = [
135232
135232
  name: "AWS Bedrock",
135233
135233
  description: "Amazon Bedrock AI models",
135234
135234
  requiresAPIKey: true
135235
+ },
135236
+ {
135237
+ id: "local",
135238
+ name: "Local LLM",
135239
+ description: "OpenAI-compatible local model (vLLM, LM Studio, Ollama)",
135240
+ requiresAPIKey: false
135235
135241
  }
135236
135242
  ];
135237
135243
 
@@ -135256,17 +135262,27 @@ function isProviderConfigured(providerId, config) {
135256
135262
  return !!config.openRouterAPIKey;
135257
135263
  case "bedrock":
135258
135264
  return !!config.bedrockAPIKey;
135265
+ case "local":
135266
+ return !!(config.localModelUrl || config.localModelName || process.env.LOCAL_MODEL_URL);
135259
135267
  default:
135260
135268
  return false;
135261
135269
  }
135262
135270
  }
135263
135271
  function hasAnyProviderConfigured(config) {
135264
- return !!config.anthropicAPIKey || !!config.openAiAPIKey || !!config.openRouterAPIKey || !!config.bedrockAPIKey;
135272
+ return !!config.anthropicAPIKey || !!config.openAiAPIKey || !!config.openRouterAPIKey || !!config.bedrockAPIKey || !!config.localModelUrl || !!config.localModelName || !!process.env.LOCAL_MODEL_URL;
135265
135273
  }
135266
135274
  function getAvailableModels(config) {
135267
- return AVAILABLE_MODELS.filter((model) => {
135275
+ const models = AVAILABLE_MODELS.filter((model) => {
135268
135276
  return isProviderConfigured(model.provider, config);
135269
135277
  });
135278
+ if (isProviderConfigured("local", config) && config.localModelName) {
135279
+ models.push({
135280
+ id: config.localModelName,
135281
+ name: config.localModelName,
135282
+ provider: "local"
135283
+ });
135284
+ }
135285
+ return models;
135270
135286
  }
135271
135287
 
135272
135288
  // node_modules/@opentui/react/jsx-dev-runtime.js
@@ -136902,28 +136918,6 @@ var config = {
136902
136918
 
136903
136919
  // src/tui/command-registry.ts
136904
136920
  var commands = [
136905
- {
136906
- name: "help",
136907
- description: "Show help dialog",
136908
- category: "General",
136909
- handler: async (args, ctx3) => {
136910
- ctx3.navigate({
136911
- type: "base",
136912
- path: "help"
136913
- });
136914
- }
136915
- },
136916
- {
136917
- name: "config",
136918
- description: "Show config dialog",
136919
- category: "General",
136920
- handler: async (args, ctx3) => {
136921
- ctx3.navigate({
136922
- type: "base",
136923
- path: "config"
136924
- });
136925
- }
136926
- },
136927
136921
  {
136928
136922
  name: "pentest",
136929
136923
  aliases: ["p", "web", "w"],
@@ -137077,6 +137071,28 @@ var commands = [
137077
137071
  });
137078
137072
  }
137079
137073
  },
137074
+ {
137075
+ name: "help",
137076
+ description: "Show help dialog",
137077
+ category: "General",
137078
+ handler: async (args, ctx3) => {
137079
+ ctx3.navigate({
137080
+ type: "base",
137081
+ path: "help"
137082
+ });
137083
+ }
137084
+ },
137085
+ {
137086
+ name: "config",
137087
+ description: "Show config dialog",
137088
+ category: "General",
137089
+ handler: async (args, ctx3) => {
137090
+ ctx3.navigate({
137091
+ type: "base",
137092
+ path: "config"
137093
+ });
137094
+ }
137095
+ },
137080
137096
  {
137081
137097
  name: "models",
137082
137098
  description: "Show available AI models",
@@ -138415,10 +138431,41 @@ function HomeView({ onNavigate, onStartSession }) {
138415
138431
  }, undefined, false, undefined, this)
138416
138432
  ]
138417
138433
  }, undefined, true, undefined, this),
138434
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138435
+ flexDirection: "column",
138436
+ marginTop: 2,
138437
+ children: [
138438
+ { cmd: "/pentest", desc: "autonomous pentest" },
138439
+ { cmd: "/operator", desc: "interactive operator" },
138440
+ { cmd: "/models", desc: "select AI model" },
138441
+ { cmd: "/providers", desc: "manage API keys" }
138442
+ ].map(({ cmd, desc }) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138443
+ flexDirection: "row",
138444
+ children: [
138445
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138446
+ width: 14,
138447
+ justifyContent: "flex-end",
138448
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138449
+ fg: colors2.primary,
138450
+ children: cmd
138451
+ }, undefined, false, undefined, this)
138452
+ }, undefined, false, undefined, this),
138453
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138454
+ width: 4
138455
+ }, undefined, false, undefined, this),
138456
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138457
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138458
+ fg: colors2.textMuted,
138459
+ children: desc
138460
+ }, undefined, false, undefined, this)
138461
+ }, undefined, false, undefined, this)
138462
+ ]
138463
+ }, cmd, true, undefined, this))
138464
+ }, undefined, false, undefined, this),
138418
138465
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138419
138466
  flexDirection: "column",
138420
138467
  width: inputWidth,
138421
- marginTop: 8,
138468
+ marginTop: 2,
138422
138469
  padding: 1,
138423
138470
  border: ["left", "right"],
138424
138471
  borderColor: colors2.primary,
@@ -138511,14 +138558,22 @@ var providerNames = {
138511
138558
  anthropic: "Claude",
138512
138559
  openai: "OpenAI",
138513
138560
  openrouter: "OpenRouter",
138514
- bedrock: "Bedrock"
138561
+ bedrock: "Bedrock",
138562
+ local: "Local LLM"
138515
138563
  };
138516
- var providerOrder = ["anthropic", "openai", "openrouter", "bedrock"];
138564
+ var providerOrder = [
138565
+ "anthropic",
138566
+ "openai",
138567
+ "openrouter",
138568
+ "bedrock",
138569
+ "local"
138570
+ ];
138517
138571
  function ModelPicker({
138518
138572
  config: config2,
138519
138573
  selectedModel,
138520
138574
  onSelectModel,
138521
138575
  onConfirm,
138576
+ onConfigUpdate,
138522
138577
  focused = true,
138523
138578
  isModelUserSelected = false
138524
138579
  }) {
@@ -138527,6 +138582,13 @@ function ModelPicker({
138527
138582
  const [searchQuery, setSearchQuery] = import_react33.useState("");
138528
138583
  const [expandedProviders, setExpandedProviders] = import_react33.useState(new Set(["anthropic"]));
138529
138584
  const [focusedIndex, setFocusedIndex] = import_react33.useState(0);
138585
+ const [localUrl, setLocalUrl] = import_react33.useState(config2?.localModelUrl ?? "");
138586
+ const [localModelName, setLocalModelName] = import_react33.useState(config2?.localModelName ?? "");
138587
+ const [editingLocalField, setEditingLocalField] = import_react33.useState(null);
138588
+ import_react33.useEffect(() => {
138589
+ setLocalUrl(config2?.localModelUrl ?? "");
138590
+ setLocalModelName(config2?.localModelName ?? "");
138591
+ }, [config2?.localModelUrl, config2?.localModelName]);
138530
138592
  import_react33.useEffect(() => {
138531
138593
  if (config2) {
138532
138594
  const models = getAvailableModels(config2);
@@ -138556,6 +138618,20 @@ function ModelPicker({
138556
138618
  const navigationItems = import_react33.useMemo(() => {
138557
138619
  const items = [];
138558
138620
  for (const provider of providerOrder) {
138621
+ if (provider === "local") {
138622
+ items.push({ type: "provider", provider: "local" });
138623
+ if (expandedProviders.has("local")) {
138624
+ items.push({ type: "local-input", field: "url" });
138625
+ items.push({ type: "local-input", field: "model" });
138626
+ const localModels = groupedModels["local"];
138627
+ if (localModels) {
138628
+ for (const m2 of localModels) {
138629
+ items.push({ type: "model", model: m2 });
138630
+ }
138631
+ }
138632
+ }
138633
+ continue;
138634
+ }
138559
138635
  const models = groupedModels[provider];
138560
138636
  if (!models || models.length === 0)
138561
138637
  continue;
@@ -138577,9 +138653,29 @@ function ModelPicker({
138577
138653
  import_react33.useEffect(() => {
138578
138654
  setFocusedIndex((prev) => Math.min(prev, Math.max(0, navigationItems.length - 1)));
138579
138655
  }, [navigationItems.length]);
138656
+ const commitLocalConfig = import_react33.useCallback((url, modelName) => {
138657
+ if (!onConfigUpdate)
138658
+ return;
138659
+ const update3 = {
138660
+ localModelUrl: url || null,
138661
+ localModelName: modelName || null
138662
+ };
138663
+ onConfigUpdate(update3);
138664
+ }, [onConfigUpdate]);
138665
+ const finishEditing = import_react33.useCallback(() => {
138666
+ setEditingLocalField(null);
138667
+ commitLocalConfig(localUrl, localModelName);
138668
+ }, [localUrl, localModelName, commitLocalConfig]);
138580
138669
  const handleKeyboard = import_react33.useCallback((key) => {
138581
138670
  if (!focused)
138582
138671
  return false;
138672
+ if (editingLocalField) {
138673
+ if (key.name === "escape") {
138674
+ finishEditing();
138675
+ return true;
138676
+ }
138677
+ return false;
138678
+ }
138583
138679
  if (navigationItems.length === 0)
138584
138680
  return false;
138585
138681
  if (key.name === "up" || key.name === "down") {
@@ -138603,6 +138699,10 @@ function ModelPicker({
138603
138699
  const currentItem = navigationItems[focusedIndex];
138604
138700
  if (!currentItem)
138605
138701
  return false;
138702
+ if (currentItem.type === "local-input") {
138703
+ setEditingLocalField(currentItem.field);
138704
+ return true;
138705
+ }
138606
138706
  if (currentItem.type === "provider") {
138607
138707
  const targetProvider = currentItem.provider;
138608
138708
  setExpandedProviders((prev) => {
@@ -138623,7 +138723,7 @@ function ModelPicker({
138623
138723
  const currentItem = navigationItems[focusedIndex];
138624
138724
  if (!currentItem)
138625
138725
  return false;
138626
- const targetProvider = currentItem.type === "provider" ? currentItem.provider : currentItem.model.provider;
138726
+ const targetProvider = currentItem.type === "provider" ? currentItem.provider : currentItem.type === "model" ? currentItem.model.provider : "local";
138627
138727
  if (key.name === "left") {
138628
138728
  setExpandedProviders((prev) => {
138629
138729
  const next = new Set(prev);
@@ -138649,6 +138749,8 @@ function ModelPicker({
138649
138749
  return false;
138650
138750
  }, [
138651
138751
  focused,
138752
+ editingLocalField,
138753
+ finishEditing,
138652
138754
  navigationItems,
138653
138755
  focusedIndex,
138654
138756
  onSelectModel,
@@ -138674,12 +138776,112 @@ function ModelPicker({
138674
138776
  children: "Type to search models..."
138675
138777
  }, undefined, false, undefined, this),
138676
138778
  providerOrder.map((provider) => {
138677
- const models = groupedModels[provider];
138678
- if (!models || models.length === 0)
138679
- return null;
138680
138779
  const isExpanded = expandedProviders.has(provider);
138681
138780
  const providerName = providerNames[provider] || provider;
138682
138781
  const isProviderFocused = navigationItems[focusedIndex]?.type === "provider" && navigationItems[focusedIndex].provider === provider;
138782
+ if (provider === "local") {
138783
+ const localModels = groupedModels["local"];
138784
+ const modelCount = localModels?.length ?? 0;
138785
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138786
+ flexDirection: "column",
138787
+ gap: 0,
138788
+ children: [
138789
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138790
+ fg: isProviderFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
138791
+ children: [
138792
+ isProviderFocused ? "❯" : isExpanded ? "▾" : "▸",
138793
+ " ",
138794
+ providerName,
138795
+ modelCount > 0 ? ` (${modelCount})` : ""
138796
+ ]
138797
+ }, undefined, true, undefined, this),
138798
+ isExpanded && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138799
+ flexDirection: "column",
138800
+ gap: 0,
138801
+ paddingLeft: 2,
138802
+ children: [
138803
+ (() => {
138804
+ const isUrlFocused = navigationItems[focusedIndex]?.type === "local-input" && navigationItems[focusedIndex].field === "url";
138805
+ const isUrlEditing = editingLocalField === "url";
138806
+ if (isUrlEditing) {
138807
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138808
+ flexDirection: "row",
138809
+ children: [
138810
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138811
+ fg: colors2.primary,
138812
+ children: " URL: "
138813
+ }, undefined, false, undefined, this),
138814
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
138815
+ focused: true,
138816
+ value: localUrl,
138817
+ backgroundColor: "transparent",
138818
+ cursorColor: colors2.textMuted,
138819
+ onInput: (v2) => setLocalUrl(typeof v2 === "string" ? v2 : ""),
138820
+ onPaste: (event) => {
138821
+ const cleaned = String(event.text).replace(/\r?\n/g, "");
138822
+ setLocalUrl((prev) => `${prev}${cleaned}`);
138823
+ },
138824
+ onSubmit: finishEditing
138825
+ }, undefined, false, undefined, this)
138826
+ ]
138827
+ }, undefined, true, undefined, this);
138828
+ }
138829
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138830
+ fg: isUrlFocused ? colors2.primary : colors2.textMuted,
138831
+ children: ` URL: ${localUrl || "(press Enter to set)"}`
138832
+ }, undefined, false, undefined, this);
138833
+ })(),
138834
+ (() => {
138835
+ const isModelFocused = navigationItems[focusedIndex]?.type === "local-input" && navigationItems[focusedIndex].field === "model";
138836
+ const isModelEditing = editingLocalField === "model";
138837
+ if (isModelEditing) {
138838
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138839
+ flexDirection: "row",
138840
+ children: [
138841
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138842
+ fg: colors2.primary,
138843
+ children: " Model: "
138844
+ }, undefined, false, undefined, this),
138845
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
138846
+ focused: true,
138847
+ value: localModelName,
138848
+ backgroundColor: "transparent",
138849
+ cursorColor: colors2.textMuted,
138850
+ onInput: (v2) => setLocalModelName(typeof v2 === "string" ? v2 : ""),
138851
+ onPaste: (event) => {
138852
+ const cleaned = String(event.text).replace(/\r?\n/g, "");
138853
+ setLocalModelName((prev) => `${prev}${cleaned}`);
138854
+ },
138855
+ onSubmit: finishEditing
138856
+ }, undefined, false, undefined, this)
138857
+ ]
138858
+ }, undefined, true, undefined, this);
138859
+ }
138860
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138861
+ fg: isModelFocused ? colors2.primary : colors2.textMuted,
138862
+ children: ` Model: ${localModelName || "(press Enter to set)"}`
138863
+ }, undefined, false, undefined, this);
138864
+ })(),
138865
+ localModels?.map((m2) => {
138866
+ const isSelected = m2.id === selectedModel.id;
138867
+ const isFocused = navigationItems[focusedIndex]?.type === "model" && navigationItems[focusedIndex].model.id === m2.id;
138868
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138869
+ fg: isFocused ? colors2.primary : colors2.textMuted,
138870
+ children: [
138871
+ isSelected ? "●" : "○",
138872
+ " ",
138873
+ m2.name
138874
+ ]
138875
+ }, m2.id, true, undefined, this);
138876
+ })
138877
+ ]
138878
+ }, undefined, true, undefined, this)
138879
+ ]
138880
+ }, "local", true, undefined, this);
138881
+ }
138882
+ const models = groupedModels[provider];
138883
+ if (!models || models.length === 0)
138884
+ return null;
138683
138885
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
138684
138886
  flexDirection: "column",
138685
138887
  gap: 0,
@@ -138720,7 +138922,7 @@ function ModelPicker({
138720
138922
  }),
138721
138923
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
138722
138924
  fg: colors2.textMuted,
138723
- children: "↑/↓ navigate | ←/→ collapse/expand | Type to search"
138925
+ children: editingLocalField ? "Type or paste | Enter/Esc to confirm" : "↑/↓ navigate | ←/→ collapse/expand | Type to search"
138724
138926
  }, undefined, false, undefined, this)
138725
138927
  ]
138726
138928
  }, undefined, true, undefined, this);
@@ -142662,6 +142864,7 @@ function ModelsDisplay() {
142662
142864
  selectedModel: model,
142663
142865
  onSelectModel: setModel,
142664
142866
  onConfirm: goHome,
142867
+ onConfigUpdate: config2.update,
142665
142868
  focused: true,
142666
142869
  isModelUserSelected
142667
142870
  }, undefined, false, undefined, this)
@@ -148161,7 +148364,8 @@ function OperatorDashboard({
148161
148364
  authConfig: {
148162
148365
  anthropicAPIKey: config4.data.anthropicAPIKey ?? undefined,
148163
148366
  openAiAPIKey: config4.data.openAiAPIKey ?? undefined,
148164
- openRouterAPIKey: config4.data.openRouterAPIKey ?? undefined
148367
+ openRouterAPIKey: config4.data.openRouterAPIKey ?? undefined,
148368
+ local: config4.data.localModelUrl ? { baseURL: config4.data.localModelUrl } : undefined
148165
148369
  },
148166
148370
  callbacks: {
148167
148371
  onTextDelta: (d3) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pensar/apex",
3
- "version": "0.0.59-canary.f54bbf92",
3
+ "version": "0.0.60-canary.2ebd004e",
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",