@infuro/cms-core 1.0.20 → 1.0.22

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/dist/admin.js CHANGED
@@ -338,8 +338,7 @@ import {
338
338
  ShoppingCart,
339
339
  CreditCard,
340
340
  Receipt,
341
- FolderTree,
342
- Bot
341
+ FolderTree
343
342
  } from "lucide-react";
344
343
 
345
344
  // src/admin/admin-config-context.tsx
@@ -352,7 +351,7 @@ var defaultValue = {
352
351
  var AdminConfigContext = createContext(defaultValue);
353
352
 
354
353
  // src/lib/cms-version.ts
355
- var CMS_VERSION = true ? "1.0.20" : "0.0.0";
354
+ var CMS_VERSION = true ? "1.0.22" : "0.0.0";
356
355
 
357
356
  // src/components/Admin/Sidebar.tsx
358
357
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -497,10 +496,6 @@ function AdminSidebar({ variant = "sidebar" }) {
497
496
  /* @__PURE__ */ jsx4(Shield, { className: `h-4 w-4 mr-2 ${isActive("/admin/roles") ? iconActive : iconInactive}` }),
498
497
  "Roles"
499
498
  ] }) }),
500
- /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs3(Link2, { href: "/admin/llm_agents", className: `${linkCls} ${isActive("/admin/llm_agents") ? linkActive : linkInactive}`, children: [
501
- /* @__PURE__ */ jsx4(Bot, { className: `h-4 w-4 mr-2 ${isActive("/admin/llm_agents") ? iconActive : iconInactive}` }),
502
- "LLM agents"
503
- ] }) }),
504
499
  /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs3(Link2, { href: "/admin/plugins", className: `${linkCls} ${isActive("/admin/plugins") ? linkActive : linkInactive}`, children: [
505
500
  /* @__PURE__ */ jsx4(Puzzle, { className: `h-4 w-4 mr-2 ${isActive("/admin/plugins") ? iconActive : iconInactive}` }),
506
501
  "Plugins"
@@ -9241,6 +9236,7 @@ function PageBuilderPage({ pageId }) {
9241
9236
 
9242
9237
  // src/admin/pages/PluginsPage.tsx
9243
9238
  import { useContext as useContext6, useState as useState30, useEffect as useEffect27, useCallback as useCallback7 } from "react";
9239
+ import { useSearchParams as useSearchParams4 } from "next/navigation";
9244
9240
  import {
9245
9241
  HardDrive,
9246
9242
  Mail,
@@ -9255,7 +9251,7 @@ import {
9255
9251
  X as X18,
9256
9252
  Plus as Plus8,
9257
9253
  Smartphone,
9258
- Bot as Bot2,
9254
+ Bot,
9259
9255
  FileUp,
9260
9256
  Loader2
9261
9257
  } from "lucide-react";
@@ -9482,6 +9478,7 @@ function PluginSettingsPanel({
9482
9478
  const [agentValidationJson, setAgentValidationJson] = useState30("");
9483
9479
  const [agentLoading, setAgentLoading] = useState30(false);
9484
9480
  const [agentSaving, setAgentSaving] = useState30(false);
9481
+ const [agentProvisionError, setAgentProvisionError] = useState30(null);
9485
9482
  const [kbCatalog, setKbCatalog] = useState30([]);
9486
9483
  const [attachedAgentKnowledge, setAttachedAgentKnowledge] = useState30([]);
9487
9484
  const [attachedKbLoading, setAttachedKbLoading] = useState30(false);
@@ -9638,31 +9635,112 @@ function PluginSettingsPanel({
9638
9635
  setAttachedKbLoading(false);
9639
9636
  }
9640
9637
  }, []);
9638
+ const bootstrapLlmAgentForPlugins = useCallback7(async () => {
9639
+ setAgentProvisionError(null);
9640
+ const listRes = await fetch("/api/llm_agents?limit=100&sortField=name&sortOrder=asc");
9641
+ if (!listRes.ok) {
9642
+ const errBody = await listRes.json().catch(() => ({}));
9643
+ const message = errBody.error ?? `Could not load agents (HTTP ${listRes.status}). Ensure your app uses the latest @infuro/cms-core and your admin user can read entity "llm_agents".`;
9644
+ setAgentProvisionError(message);
9645
+ await fetchLlmAgents();
9646
+ return { ok: false, message };
9647
+ }
9648
+ const listJ = await listRes.json();
9649
+ if (listJ.data?.length) {
9650
+ await fetchLlmAgents();
9651
+ return { ok: true };
9652
+ }
9653
+ const defaultName = botName.trim() || "Assistant";
9654
+ const defaultSlug = slugifyAgentKey(defaultName);
9655
+ const createRes = await fetch("/api/llm_agents", {
9656
+ method: "POST",
9657
+ headers: { "Content-Type": "application/json" },
9658
+ body: JSON.stringify({ name: defaultName, slug: defaultSlug, systemInstruction: "", enabled: true })
9659
+ });
9660
+ if (!createRes.ok) {
9661
+ const errBody = await createRes.json().catch(() => ({}));
9662
+ const message = errBody.error ?? `Could not create default agent (HTTP ${createRes.status}). Check create permission for "llm_agents".`;
9663
+ setAgentProvisionError(message);
9664
+ await fetchLlmAgents();
9665
+ return { ok: false, message };
9666
+ }
9667
+ await fetchLlmAgents();
9668
+ return { ok: true };
9669
+ }, [botName, fetchLlmAgents]);
9670
+ const handleRetryBootstrapAgent = async () => {
9671
+ setAgentLoading(true);
9672
+ try {
9673
+ const r = await bootstrapLlmAgentForPlugins();
9674
+ if (r.ok) {
9675
+ toast5.success("Assistant agent ready \u2014 you can upload knowledge files below.");
9676
+ } else {
9677
+ toast5.error(r.message);
9678
+ }
9679
+ } finally {
9680
+ setAgentLoading(false);
9681
+ }
9682
+ };
9683
+ const handleCreateAssistantFromForm = async () => {
9684
+ const name = agentName.trim() || botName.trim();
9685
+ if (!name) {
9686
+ toast5.error("Enter an assistant name");
9687
+ return;
9688
+ }
9689
+ const tempRaw = agentTemp.trim();
9690
+ const maxRaw = agentMaxTokens.trim();
9691
+ const temperature = tempRaw === "" ? null : Number(tempRaw);
9692
+ const maxTokens = maxRaw === "" ? null : parseInt(maxRaw, 10);
9693
+ if (tempRaw !== "" && !Number.isFinite(temperature)) {
9694
+ toast5.error("Temperature must be a number");
9695
+ return;
9696
+ }
9697
+ if (maxRaw !== "" && (!Number.isFinite(maxTokens) || maxTokens < 1)) {
9698
+ toast5.error("Max tokens must be a positive integer");
9699
+ return;
9700
+ }
9701
+ const slug = slugifyAgentKey(name);
9702
+ setAgentSaving(true);
9703
+ try {
9704
+ const res = await fetch("/api/llm_agents", {
9705
+ method: "POST",
9706
+ headers: { "Content-Type": "application/json" },
9707
+ body: JSON.stringify({
9708
+ name,
9709
+ slug,
9710
+ systemInstruction: agentSystem.trim(),
9711
+ model: agentModel.trim() || null,
9712
+ temperature,
9713
+ maxTokens,
9714
+ validationRules: agentValidationJson.trim() || null,
9715
+ enabled: true
9716
+ })
9717
+ });
9718
+ if (!res.ok) {
9719
+ const err = await res.json().catch(() => ({}));
9720
+ toast5.error(err.error || "Failed to create assistant");
9721
+ return;
9722
+ }
9723
+ setAgentProvisionError(null);
9724
+ setAttachedAgentSlug(slug);
9725
+ await fetchLlmAgents();
9726
+ toast5.success("Assistant created \u2014 add knowledge files below.");
9727
+ } catch {
9728
+ toast5.error("Failed to create assistant");
9729
+ } finally {
9730
+ setAgentSaving(false);
9731
+ }
9732
+ };
9641
9733
  useEffect27(() => {
9642
9734
  if (!isLlm || loading || chatMode !== "llm") return;
9643
9735
  setAgentLoading(true);
9644
9736
  void (async () => {
9645
9737
  try {
9646
- await fetchLlmAgents();
9647
- const listRes = await fetch("/api/llm_agents?limit=1");
9648
- const listJ = listRes.ok ? await listRes.json() : { data: [] };
9649
- if (!listJ.data?.length) {
9650
- const defaultName = botName.trim() || "Assistant";
9651
- const defaultSlug = slugifyAgentKey(defaultName);
9652
- const createRes = await fetch("/api/llm_agents", {
9653
- method: "POST",
9654
- headers: { "Content-Type": "application/json" },
9655
- body: JSON.stringify({ name: defaultName, slug: defaultSlug, systemInstruction: "", enabled: true })
9656
- });
9657
- if (createRes.ok) {
9658
- await fetchLlmAgents();
9659
- }
9660
- }
9738
+ await bootstrapLlmAgentForPlugins();
9661
9739
  } finally {
9662
9740
  setAgentLoading(false);
9663
9741
  }
9664
9742
  })();
9665
- }, [isLlm, loading, chatMode]);
9743
+ }, [isLlm, loading, chatMode, bootstrapLlmAgentForPlugins]);
9666
9744
  useEffect27(() => {
9667
9745
  if (!isLlm || loading || chatMode !== "llm") return;
9668
9746
  void fetchKbCatalog();
@@ -9677,6 +9755,12 @@ function PluginSettingsPanel({
9677
9755
  useEffect27(() => {
9678
9756
  setAttachExistingDocId("__none__");
9679
9757
  }, [attachedAgentSlug]);
9758
+ useEffect27(() => {
9759
+ if (!isLlm || loading || chatMode !== "llm" || agentLoading || agentId) return;
9760
+ if (!agentName.trim() && botName.trim()) {
9761
+ setAgentName(botName.trim());
9762
+ }
9763
+ }, [isLlm, loading, chatMode, agentLoading, agentId, agentName, botName]);
9680
9764
  const buildPayload = () => {
9681
9765
  if (isErp) {
9682
9766
  const sortedIds = [...new Set(erpOpportunityFormIds.filter((n) => Number.isInteger(n) && n > 0))].sort(
@@ -9828,7 +9912,16 @@ function PluginSettingsPanel({
9828
9912
  toast5.error(err.error || "Upload failed");
9829
9913
  return;
9830
9914
  }
9831
- toast5.success("Knowledge added and linked");
9915
+ const ingest = await r.json().catch(() => ({}));
9916
+ if (ingest.warning) {
9917
+ toast5.warning(ingest.detail ? `${ingest.warning} (${ingest.detail})` : ingest.warning);
9918
+ } else if (ingest.embeddingAttempted && (ingest.embeddingsWritten ?? 0) === 0 && (ingest.chunkCount ?? 0) > 0) {
9919
+ toast5.warning(
9920
+ "Document saved but no embeddings were written. Set LLM_GATEWAY_URL + LLM_API_KEY, and ensure EMBEDDING_PROVIDER / EMBEDDING_MODEL match knowledge_base_chunks.embedding dimensions (see server logs)."
9921
+ );
9922
+ } else {
9923
+ toast5.success("Knowledge added and linked");
9924
+ }
9832
9925
  setAttachedKbInputKey((k) => k + 1);
9833
9926
  await fetchAttachedKnowledge(slug);
9834
9927
  await fetchKbCatalog();
@@ -9864,7 +9957,16 @@ function PluginSettingsPanel({
9864
9957
  toast5.error(err.error || "Attach failed");
9865
9958
  return;
9866
9959
  }
9867
- toast5.success("Document attached");
9960
+ const ingest = await r.json().catch(() => ({}));
9961
+ if (ingest.warning) {
9962
+ toast5.warning(ingest.detail ? `${ingest.warning} (${ingest.detail})` : ingest.warning);
9963
+ } else if (ingest.embeddingAttempted && (ingest.embeddingsWritten ?? 0) === 0 && ((ingest.chunksQueuedForEmbedding ?? 0) > 0 || (ingest.chunkCount ?? 0) > 0)) {
9964
+ toast5.warning(
9965
+ "Document attached but no embeddings were written. Configure the LLM/embed gateway, then attach again to fill NULL embeddings."
9966
+ );
9967
+ } else {
9968
+ toast5.success("Document attached");
9969
+ }
9868
9970
  setAttachExistingDocId("__none__");
9869
9971
  await fetchAttachedKnowledge(slug);
9870
9972
  } finally {
@@ -10324,23 +10426,27 @@ function PluginSettingsPanel({
10324
10426
  ] }),
10325
10427
  chatMode === "llm" && /* @__PURE__ */ jsxs46(Fragment14, { children: [
10326
10428
  /* @__PURE__ */ jsxs46("div", { className: "rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50/80 dark:bg-gray-800/40 p-3 space-y-3", children: [
10327
- /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white", children: [
10328
- /* @__PURE__ */ jsx56(Bot2, { className: "h-4 w-4" }),
10329
- "Chat assistant agent"
10429
+ /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10430
+ /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white", children: [
10431
+ /* @__PURE__ */ jsx56(Bot, { className: "h-4 w-4" }),
10432
+ "Chat assistant (single agent)"
10433
+ ] }),
10434
+ /* @__PURE__ */ jsx56("p", { className: "text-[11px] text-gray-500 dark:text-gray-400", children: "Configure one assistant for this site here. After it exists, attach knowledge files in the section below." })
10330
10435
  ] }),
10331
10436
  agentLoading ? /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [
10332
10437
  /* @__PURE__ */ jsx56(Loader2, { className: "h-4 w-4 animate-spin" }),
10333
- "Loading agent\u2026"
10334
- ] }) : !agentId ? /* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "No agent found. Save settings to auto-create one." }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
10438
+ "Loading assistant\u2026"
10439
+ ] }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
10440
+ agentProvisionError ? /* @__PURE__ */ jsx56("p", { className: "rounded border border-amber-200 bg-amber-50 p-2 text-xs text-amber-900 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-100", children: agentProvisionError }) : null,
10335
10441
  /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10336
- /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-name", className: "text-sm", children: "Name" }),
10442
+ /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-name", className: "text-sm", children: "Assistant name" }),
10337
10443
  /* @__PURE__ */ jsx56(
10338
10444
  Input,
10339
10445
  {
10340
10446
  id: "agent-name",
10341
10447
  value: agentName,
10342
10448
  onChange: (e) => setAgentName(e.target.value),
10343
- placeholder: "e.g. Sales assistant",
10449
+ placeholder: "e.g. JM Buddy",
10344
10450
  className: "h-8 text-sm"
10345
10451
  }
10346
10452
  )
@@ -10351,12 +10457,12 @@ function PluginSettingsPanel({
10351
10457
  Input,
10352
10458
  {
10353
10459
  id: "agent-slug",
10354
- value: agentSlug,
10460
+ value: agentId ? agentSlug : slugifyAgentKey((agentName || botName).trim() || "assistant"),
10355
10461
  disabled: true,
10356
10462
  className: "h-8 text-sm font-mono bg-gray-100 dark:bg-gray-700"
10357
10463
  }
10358
10464
  ),
10359
- /* @__PURE__ */ jsx56("p", { className: "text-[11px] text-gray-500", children: "Auto-generated. Used in API routes." })
10465
+ /* @__PURE__ */ jsx56("p", { className: "text-[11px] text-gray-500", children: "Derived from the name. Used in API routes." })
10360
10466
  ] }),
10361
10467
  /* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
10362
10468
  /* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-system", className: "text-sm", children: "System instruction" }),
@@ -10427,7 +10533,37 @@ function PluginSettingsPanel({
10427
10533
  }
10428
10534
  )
10429
10535
  ] }),
10430
- /* @__PURE__ */ jsx56(
10536
+ !agentId ? /* @__PURE__ */ jsxs46("div", { className: "flex flex-wrap items-center gap-2", children: [
10537
+ /* @__PURE__ */ jsx56(
10538
+ Button,
10539
+ {
10540
+ type: "button",
10541
+ size: "sm",
10542
+ className: "gap-1",
10543
+ disabled: agentSaving,
10544
+ onClick: () => void handleCreateAssistantFromForm(),
10545
+ children: agentSaving ? /* @__PURE__ */ jsxs46("span", { className: "inline-flex items-center gap-1.5", children: [
10546
+ /* @__PURE__ */ jsx56(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
10547
+ "Creating\u2026"
10548
+ ] }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
10549
+ /* @__PURE__ */ jsx56(Bot, { className: "h-3.5 w-3.5" }),
10550
+ "Create assistant"
10551
+ ] })
10552
+ }
10553
+ ),
10554
+ /* @__PURE__ */ jsx56(
10555
+ Button,
10556
+ {
10557
+ type: "button",
10558
+ size: "sm",
10559
+ variant: "secondary",
10560
+ className: "gap-1",
10561
+ disabled: agentSaving || agentLoading,
10562
+ onClick: () => void handleRetryBootstrapAgent(),
10563
+ children: "Retry auto-setup"
10564
+ }
10565
+ )
10566
+ ] }) : /* @__PURE__ */ jsx56(
10431
10567
  Button,
10432
10568
  {
10433
10569
  type: "button",
@@ -10440,7 +10576,7 @@ function PluginSettingsPanel({
10440
10576
  "Saving\u2026"
10441
10577
  ] }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
10442
10578
  /* @__PURE__ */ jsx56(Save5, { className: "h-3.5 w-3.5" }),
10443
- "Save agent"
10579
+ "Save assistant"
10444
10580
  ] })
10445
10581
  }
10446
10582
  )
@@ -10692,8 +10828,14 @@ function PluginListItem({
10692
10828
  }
10693
10829
  function PluginsPage() {
10694
10830
  const { pluginDescriptors = [] } = useContext6(AdminConfigContext);
10831
+ const searchParams = useSearchParams4();
10695
10832
  const [selectedName, setSelectedName] = useState30(null);
10696
10833
  const [enabledMap, setEnabledMap] = useState30({});
10834
+ useEffect27(() => {
10835
+ if (searchParams.get("plugin") !== "llm") return;
10836
+ const llmDesc = pluginDescriptors.find((p) => p.settingsGroup === "llm");
10837
+ if (llmDesc) setSelectedName(llmDesc.name);
10838
+ }, [searchParams, pluginDescriptors]);
10697
10839
  useEffect27(() => {
10698
10840
  pluginDescriptors.forEach((p) => {
10699
10841
  if (!p.settingsGroup) return;
@@ -12858,39 +13000,6 @@ var CRUD_CONFIGS = {
12858
13000
  { field: "createdAt", displayName: "Created", type: "date" }
12859
13001
  ],
12860
13002
  addEditPageUrl: ""
12861
- },
12862
- llm_agents: {
12863
- title: "LLM agents",
12864
- apiEndpoint: "/api/llm_agents",
12865
- defaultSortField: "name",
12866
- defaultSortOrder: "asc",
12867
- columns: [
12868
- { field: "name", displayName: "Name" },
12869
- { field: "slug", displayName: "Slug" },
12870
- {
12871
- field: "systemInstruction",
12872
- displayName: "System instruction",
12873
- type: "textarea",
12874
- hideInTable: true,
12875
- textareaRows: 10,
12876
- placeholder: "How the model should behave for this agent (sent as system prompt to the LLM)."
12877
- },
12878
- { field: "model", displayName: "Model", placeholder: "Optional gateway model id" },
12879
- { field: "temperature", displayName: "Temperature", type: "number" },
12880
- { field: "maxTokens", displayName: "Max tokens", type: "number" },
12881
- {
12882
- field: "validationRules",
12883
- displayName: "Validation & output guardrails",
12884
- type: "textarea",
12885
- hideInTable: true,
12886
- textareaRows: 8,
12887
- placeholder: "Plain text: rules appended to the system prompt. Or JSON: guardrails, maxUserChars, blockedSubstrings, etc."
12888
- },
12889
- { field: "enabled", displayName: "Enabled", type: "boolean" },
12890
- { field: "createdAt", displayName: "Created", type: "date", hideInForm: true },
12891
- { field: "updatedAt", displayName: "Updated", type: "datetime", hideInForm: true }
12892
- ],
12893
- addEditPageUrl: ""
12894
13003
  }
12895
13004
  };
12896
13005
  function BlogEditorWrapper({ blogId }) {
@@ -12916,6 +13025,9 @@ function AdminPageResolver({ slug }) {
12916
13025
  if (key === "layout-settings") {
12917
13026
  router.replace("/admin/settings?tab=navbar");
12918
13027
  }
13028
+ if (key === "llm_agents") {
13029
+ router.replace("/admin/plugins?plugin=llm");
13030
+ }
12919
13031
  }, [key, router]);
12920
13032
  if (key === "layout-settings") {
12921
13033
  return /* @__PURE__ */ jsxs53("div", { className: "flex justify-center py-8", children: [
@@ -12923,6 +13035,12 @@ function AdminPageResolver({ slug }) {
12923
13035
  /* @__PURE__ */ jsx63("span", { className: "ml-2", children: "Redirecting..." })
12924
13036
  ] });
12925
13037
  }
13038
+ if (key === "llm_agents") {
13039
+ return /* @__PURE__ */ jsxs53("div", { className: "flex justify-center py-8", children: [
13040
+ /* @__PURE__ */ jsx63("div", { className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600" }),
13041
+ /* @__PURE__ */ jsx63("span", { className: "ml-2", children: "Opening Plugins\u2026" })
13042
+ ] });
13043
+ }
12926
13044
  const Page = PAGE_MAP[key];
12927
13045
  if (Page) {
12928
13046
  return /* @__PURE__ */ jsx63(Page, {});