@burdenoff/microfe-botmanor 2026.1027.2 → 2026.1028.1
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/botlit/BotlitRoutes.js +142 -132
- package/dist/botlit/BotlitRoutes.js.map +1 -1
- package/dist/botlit/components/AgentApprovalPanel.js +144 -126
- package/dist/botlit/components/AgentApprovalPanel.js.map +1 -1
- package/dist/botlit/components/ModelRegistrationPanel.js +4 -4
- package/dist/botlit/components/RetentionStatusPanel.js +95 -0
- package/dist/botlit/components/RetentionStatusPanel.js.map +1 -0
- package/dist/botlit/components/VersionHistoryPanel.js +92 -83
- package/dist/botlit/components/VersionHistoryPanel.js.map +1 -1
- package/dist/botlit/hooks/index.js +1 -0
- package/dist/botlit/hooks/useRetention.js +31 -0
- package/dist/botlit/hooks/useRetention.js.map +1 -0
- package/dist/botlit/pages/A2AAgentDetailPage.js +212 -203
- package/dist/botlit/pages/A2AAgentDetailPage.js.map +1 -1
- package/dist/botlit/pages/A2ARegistryPage.js +129 -117
- package/dist/botlit/pages/A2ARegistryPage.js.map +1 -1
- package/dist/botlit/pages/AgentApprovalsPage.js +122 -110
- package/dist/botlit/pages/AgentApprovalsPage.js.map +1 -1
- package/dist/botlit/pages/AgentBuilderPage.js +18 -18
- package/dist/botlit/pages/AgentDetailPage.js +298 -273
- package/dist/botlit/pages/AgentDetailPage.js.map +1 -1
- package/dist/botlit/pages/AgentsListPage.js +145 -121
- package/dist/botlit/pages/AgentsListPage.js.map +1 -1
- package/dist/botlit/pages/BotChannelsTab.js +238 -226
- package/dist/botlit/pages/BotChannelsTab.js.map +1 -1
- package/dist/botlit/pages/BotDetailPage.js +307 -288
- package/dist/botlit/pages/BotDetailPage.js.map +1 -1
- package/dist/botlit/pages/BotWorkflowTab.js +125 -116
- package/dist/botlit/pages/BotWorkflowTab.js.map +1 -1
- package/dist/botlit/pages/BotsListPage.js +159 -144
- package/dist/botlit/pages/BotsListPage.js.map +1 -1
- package/dist/botlit/pages/BucketsPage.js +279 -258
- package/dist/botlit/pages/BucketsPage.js.map +1 -1
- package/dist/botlit/pages/ChannelDetailPage.js +346 -328
- package/dist/botlit/pages/ChannelDetailPage.js.map +1 -1
- package/dist/botlit/pages/ChannelRoutesPage.js +10 -10
- package/dist/botlit/pages/ChannelsPage.js +163 -148
- package/dist/botlit/pages/ChannelsPage.js.map +1 -1
- package/dist/botlit/pages/DashboardPage.js +129 -114
- package/dist/botlit/pages/DashboardPage.js.map +1 -1
- package/dist/botlit/pages/ExecutionDetailPage.js +145 -139
- package/dist/botlit/pages/ExecutionDetailPage.js.map +1 -1
- package/dist/botlit/pages/ExecutionsPage.js +14 -14
- package/dist/botlit/pages/InstalledPackagesPage.js +245 -227
- package/dist/botlit/pages/InstalledPackagesPage.js.map +1 -1
- package/dist/botlit/pages/KnowledgeBaseDetailPage.js +168 -156
- package/dist/botlit/pages/KnowledgeBaseDetailPage.js.map +1 -1
- package/dist/botlit/pages/KnowledgeBasesPage.js +91 -79
- package/dist/botlit/pages/KnowledgeBasesPage.js.map +1 -1
- package/dist/botlit/pages/MCPServerDetailPage.js +200 -188
- package/dist/botlit/pages/MCPServerDetailPage.js.map +1 -1
- package/dist/botlit/pages/MCPServersPage.js +170 -155
- package/dist/botlit/pages/MCPServersPage.js.map +1 -1
- package/dist/botlit/pages/PoliciesPage.js +115 -104
- package/dist/botlit/pages/PoliciesPage.js.map +1 -1
- package/dist/botlit/pages/PolicyDetailPage.js +368 -324
- package/dist/botlit/pages/PolicyDetailPage.js.map +1 -1
- package/dist/botlit/pages/PromptStudioPage.js +106 -97
- package/dist/botlit/pages/PromptStudioPage.js.map +1 -1
- package/dist/botlit/pages/PromptTemplateDetailPage.js +359 -347
- package/dist/botlit/pages/PromptTemplateDetailPage.js.map +1 -1
- package/dist/botlit/pages/PromptTemplatesPage.js +127 -112
- package/dist/botlit/pages/PromptTemplatesPage.js.map +1 -1
- package/dist/botlit/pages/RetrievalProfileDetailPage.js +219 -210
- package/dist/botlit/pages/RetrievalProfileDetailPage.js.map +1 -1
- package/dist/botlit/pages/RetrievalProfilesPage.js +112 -103
- package/dist/botlit/pages/RetrievalProfilesPage.js.map +1 -1
- package/dist/botlit/pages/SettingsPage.js +264 -252
- package/dist/botlit/pages/SettingsPage.js.map +1 -1
- package/dist/botlit/pages/SkillDetailPage.js +235 -226
- package/dist/botlit/pages/SkillDetailPage.js.map +1 -1
- package/dist/botlit/pages/SkillsPage.js +115 -106
- package/dist/botlit/pages/SkillsPage.js.map +1 -1
- package/dist/botlit/pages/StorePackageDetailPage.js +194 -188
- package/dist/botlit/pages/StorePackageDetailPage.js.map +1 -1
- package/dist/botlit/pages/StorePage.js +161 -155
- package/dist/botlit/pages/StorePage.js.map +1 -1
- package/dist/botlit/permissions/PermissionRequired.js +45 -0
- package/dist/botlit/permissions/PermissionRequired.js.map +1 -0
- package/dist/botlit/permissions/botmanorPermissions.js +72 -0
- package/dist/botlit/permissions/botmanorPermissions.js.map +1 -0
- package/dist/botlit/permissions/index.js +3 -0
- package/dist/botlit/permissions/useBotmanorPermissions.js +19 -0
- package/dist/botlit/permissions/useBotmanorPermissions.js.map +1 -0
- package/dist/botlit/utils/policyConfig.js +67 -42
- package/dist/botlit/utils/policyConfig.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/generated/wspace-operations.js +90 -54
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { useBotlit as t } from "../providers/BotlitProvider.js";
|
|
3
|
+
import { BotmanorPermissionGate as n } from "./useBotmanorPermissions.js";
|
|
4
|
+
import { PageLayout as r } from "../components/PageLayout.js";
|
|
5
|
+
import { Button as i } from "../components/Button.js";
|
|
6
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
7
|
+
import { Lock as s } from "lucide-react";
|
|
8
|
+
//#region src/botlit/permissions/PermissionRequired.tsx
|
|
9
|
+
var c = ({ title: n, backTo: c, backLabel: l }) => {
|
|
10
|
+
let u = e(), { navigateTo: d } = t();
|
|
11
|
+
return /* @__PURE__ */ a(r, {
|
|
12
|
+
title: n,
|
|
13
|
+
children: /* @__PURE__ */ o("div", {
|
|
14
|
+
role: "status",
|
|
15
|
+
className: "flex flex-col items-center justify-center gap-3 rounded-lg border border-border-subtle bg-bg-surface px-4 py-12 text-center",
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ a(s, {
|
|
18
|
+
size: 28,
|
|
19
|
+
className: "text-text-secondary"
|
|
20
|
+
}),
|
|
21
|
+
/* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h2", {
|
|
22
|
+
className: "text-lg font-medium text-text-primary",
|
|
23
|
+
children: u("permissions.required.title", "You do not have access to this action")
|
|
24
|
+
}), /* @__PURE__ */ a("p", {
|
|
25
|
+
className: "mt-2 max-w-md text-sm text-text-secondary",
|
|
26
|
+
children: u("permissions.required.description", "Your role in this workspace can view BotManor resources but not change them. Ask a workspace admin for edit access.")
|
|
27
|
+
})] }),
|
|
28
|
+
/* @__PURE__ */ a(i, {
|
|
29
|
+
variant: "outline",
|
|
30
|
+
size: "sm",
|
|
31
|
+
onClick: () => d(c),
|
|
32
|
+
children: l
|
|
33
|
+
})
|
|
34
|
+
]
|
|
35
|
+
})
|
|
36
|
+
});
|
|
37
|
+
}, l = ({ permission: e, children: t, ...r }) => /* @__PURE__ */ a(n, {
|
|
38
|
+
permission: e,
|
|
39
|
+
fallback: /* @__PURE__ */ a(c, { ...r }),
|
|
40
|
+
children: t
|
|
41
|
+
});
|
|
42
|
+
//#endregion
|
|
43
|
+
export { c as PermissionRequiredState, l as RequirePermission };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=PermissionRequired.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionRequired.js","names":[],"sources":["../../../src/botlit/permissions/PermissionRequired.tsx"],"sourcesContent":["/**\n * Permission-required state\n * @module botlit/permissions\n *\n * Shown in place of a create form when a member opens a `/new` route (from a\n * bookmark or a pasted link) without the permission to create that resource.\n * The member still sees where they are and how to get back; they are not shown\n * a form whose submit the gateway would refuse.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { Lock } from 'lucide-react';\n\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { PageLayout } from '../components/PageLayout';\nimport { Button } from '../components/Button';\nimport type { BotmanorPermission } from './botmanorPermissions';\nimport { BotmanorPermissionGate } from './useBotmanorPermissions';\n\nexport interface PermissionRequiredStateProps {\n /** Page title, e.g. \"Create Policy\". */\n title: string;\n /** Where \"Back\" goes, relative to the BotManor mount (e.g. `policies`). */\n backTo: string;\n /** Label for the back link, e.g. \"Back to policies\". */\n backLabel: string;\n}\n\nexport const PermissionRequiredState: FC<PermissionRequiredStateProps> = ({\n title,\n backTo,\n backLabel,\n}) => {\n const t = useTr();\n const { navigateTo } = useBotlit();\n return (\n <PageLayout title={title}>\n <div\n role=\"status\"\n className=\"flex flex-col items-center justify-center gap-3 rounded-lg border border-border-subtle bg-bg-surface px-4 py-12 text-center\"\n >\n <Lock size={28} className=\"text-text-secondary\" />\n <div>\n <h2 className=\"text-lg font-medium text-text-primary\">\n {t('permissions.required.title', 'You do not have access to this action')}\n </h2>\n <p className=\"mt-2 max-w-md text-sm text-text-secondary\">\n {t(\n 'permissions.required.description',\n 'Your role in this workspace can view BotManor resources but not change them. Ask a workspace admin for edit access.'\n )}\n </p>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigateTo(backTo)}>\n {backLabel}\n </Button>\n </div>\n </PageLayout>\n );\n};\n\nexport interface RequirePermissionProps extends PermissionRequiredStateProps {\n permission: BotmanorPermission;\n children: ReactNode;\n}\n\n/**\n * Render a create page only for members who may create; everyone else gets the\n * permission-required state. Nothing renders while permissions load.\n */\nexport const RequirePermission: FC<RequirePermissionProps> = ({\n permission,\n children,\n ...state\n}) => (\n <BotmanorPermissionGate permission={permission} fallback={<PermissionRequiredState {...state} />}>\n {children}\n </BotmanorPermissionGate>\n);\n"],"mappings":";;;;;;;;AA6BA,IAAa,KAA6D,EACxE,UACA,WACA,mBACI;CACJ,IAAM,IAAI,GAAO,EACX,EAAE,kBAAe,GAAW;AAClC,QACE,kBAAC,GAAD;EAAmB;YACjB,kBAAC,OAAD;GACE,MAAK;GACL,WAAU;aAFZ;IAIE,kBAAC,GAAD;KAAM,MAAM;KAAI,WAAU;KAAwB,CAAA;IAClD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,8BAA8B,wCAAwC;KACtE,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,oCACA,sHACD;KACC,CAAA,CACA,EAAA,CAAA;IACN,kBAAC,GAAD;KAAQ,SAAQ;KAAU,MAAK;KAAK,eAAe,EAAW,EAAO;eAClE;KACM,CAAA;IACL;;EACK,CAAA;GAaJ,KAAiD,EAC5D,eACA,aACA,GAAG,QAEH,kBAAC,GAAD;CAAoC;CAAY,UAAU,kBAAC,GAAD,EAAyB,GAAI,GAAS,CAAA;CAC7F;CACsB,CAAA"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
//#region src/botlit/permissions/botmanorPermissions.ts
|
|
2
|
+
var e = {
|
|
3
|
+
APP_CREATE: "botmanor-app:create",
|
|
4
|
+
APP_UPDATE: "botmanor-app:update",
|
|
5
|
+
APP_DELETE: "botmanor-app:delete",
|
|
6
|
+
APP_PUBLISH: "botmanor-app:publish",
|
|
7
|
+
APP_ROLLBACK: "botmanor-app:rollback",
|
|
8
|
+
APP_AGENT_CREATE: "botmanor-app-agent:create",
|
|
9
|
+
APP_AGENT_UPDATE: "botmanor-app-agent:update",
|
|
10
|
+
APP_AGENT_DELETE: "botmanor-app-agent:delete",
|
|
11
|
+
AGENT_CREATE: "botmanor-agent:create",
|
|
12
|
+
AGENT_UPDATE: "botmanor-agent:update",
|
|
13
|
+
AGENT_DELETE: "botmanor-agent:delete",
|
|
14
|
+
AGENT_PUBLISH: "botmanor-agent:publish",
|
|
15
|
+
AGENT_ROLLBACK: "botmanor-agent:rollback",
|
|
16
|
+
AGENT_EXECUTE: "botmanor-agent:execute",
|
|
17
|
+
AGENT_APPROVE: "botmanor-agent:approve",
|
|
18
|
+
KNOWLEDGE_CREATE: "botmanor-knowledge-base:create",
|
|
19
|
+
KNOWLEDGE_UPDATE: "botmanor-knowledge-base:update",
|
|
20
|
+
KNOWLEDGE_DELETE: "botmanor-knowledge-base:delete",
|
|
21
|
+
KNOWLEDGE_MANAGE: "botmanor-knowledge-base:manage",
|
|
22
|
+
RETRIEVAL_PROFILE_CREATE: "botmanor-retrieval-profile:create",
|
|
23
|
+
RETRIEVAL_PROFILE_UPDATE: "botmanor-retrieval-profile:update",
|
|
24
|
+
RETRIEVAL_PROFILE_DELETE: "botmanor-retrieval-profile:delete",
|
|
25
|
+
MCP_CREATE: "botmanor-mcp:create",
|
|
26
|
+
MCP_UPDATE: "botmanor-mcp:update",
|
|
27
|
+
MCP_DELETE: "botmanor-mcp:delete",
|
|
28
|
+
MCP_TEST: "botmanor-mcp:test",
|
|
29
|
+
MCP_EXECUTE: "botmanor-mcp:execute",
|
|
30
|
+
LLM_CREATE: "botmanor-llm:create",
|
|
31
|
+
LLM_UPDATE: "botmanor-llm:update",
|
|
32
|
+
LLM_DELETE: "botmanor-llm:delete",
|
|
33
|
+
LLM_TEST: "botmanor-llm:test",
|
|
34
|
+
A2A_CREATE: "botmanor-a2a:create",
|
|
35
|
+
A2A_UPDATE: "botmanor-a2a:update",
|
|
36
|
+
A2A_DELETE: "botmanor-a2a:delete",
|
|
37
|
+
A2A_SEND: "botmanor-a2a:send",
|
|
38
|
+
A2A_EXECUTE: "botmanor-a2a:execute",
|
|
39
|
+
A2A_REGISTRY_CREATE: "botmanor-a2a-registry:create",
|
|
40
|
+
A2A_REGISTRY_UPDATE: "botmanor-a2a-registry:update",
|
|
41
|
+
A2A_REGISTRY_DELETE: "botmanor-a2a-registry:delete",
|
|
42
|
+
CHANNEL_CREATE: "botmanor-channel:create",
|
|
43
|
+
CHANNEL_UPDATE: "botmanor-channel:update",
|
|
44
|
+
CHANNEL_DELETE: "botmanor-channel:delete",
|
|
45
|
+
CHANNEL_MESSAGE_CREATE: "botmanor-channel-message:create",
|
|
46
|
+
CHANNEL_MESSAGE_EXECUTE: "botmanor-channel-message:execute",
|
|
47
|
+
POLICY_CREATE: "botmanor-policy:create",
|
|
48
|
+
POLICY_UPDATE: "botmanor-policy:update",
|
|
49
|
+
POLICY_DELETE: "botmanor-policy:delete",
|
|
50
|
+
SKILL_CREATE: "botmanor-skill:create",
|
|
51
|
+
SKILL_UPDATE: "botmanor-skill:update",
|
|
52
|
+
SKILL_DELETE: "botmanor-skill:delete",
|
|
53
|
+
PROMPT_TEMPLATE_CREATE: "botmanor-prompt-template:create",
|
|
54
|
+
PROMPT_TEMPLATE_UPDATE: "botmanor-prompt-template:update",
|
|
55
|
+
PROMPT_TEMPLATE_DELETE: "botmanor-prompt-template:delete",
|
|
56
|
+
PROMPT_TEMPLATE_EXECUTE: "botmanor-prompt-template:execute",
|
|
57
|
+
EXECUTION_CREATE: "botmanor-execution:create",
|
|
58
|
+
EXECUTION_UPDATE: "botmanor-execution:update",
|
|
59
|
+
BUCKET_CREATE: "botmanor-bucket:create",
|
|
60
|
+
BUCKET_UPDATE: "botmanor-bucket:update",
|
|
61
|
+
BUCKET_DELETE: "botmanor-bucket:delete",
|
|
62
|
+
STORE_CREATE: "botmanor-store:create",
|
|
63
|
+
STORE_UPDATE: "botmanor-store:update",
|
|
64
|
+
STORE_DELETE: "botmanor-store:delete",
|
|
65
|
+
TEMPLATE_CREATE: "botmanor-template:create",
|
|
66
|
+
TEMPLATE_UPDATE: "botmanor-template:update",
|
|
67
|
+
TEMPLATE_DELETE: "botmanor-template:delete"
|
|
68
|
+
};
|
|
69
|
+
//#endregion
|
|
70
|
+
export { e as BOTMANOR_PERMISSIONS };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=botmanorPermissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"botmanorPermissions.js","names":[],"sources":["../../../src/botlit/permissions/botmanorPermissions.ts"],"sourcesContent":["/**\n * BotManor permission catalogue\n * @module botlit/permissions\n *\n * The `resource:action` pairs the BotManor UI checks before it offers a write\n * control. Every pair mirrors the `@rbac(resource, action)` directive on the\n * wspace-botmanor-svc mutation that the control calls, so the UI hides exactly\n * what the gateway would refuse. The gateway stays the only enforcement point:\n * hiding a button never grants or denies anything, it only stops a member from\n * attempting an action the gateway will answer with FORBIDDEN.\n *\n * Keep this file in step with the service schema. When a mutation's directive\n * changes, change the pair here and the control that calls it.\n */\n\nexport const BOTMANOR_PERMISSIONS = {\n // Bot apps — createBotApp / updateBotApp / deleteBotApp / publishBotAppVersion /\n // rollbackBotAppVersion\n APP_CREATE: 'botmanor-app:create',\n APP_UPDATE: 'botmanor-app:update',\n APP_DELETE: 'botmanor-app:delete',\n APP_PUBLISH: 'botmanor-app:publish',\n APP_ROLLBACK: 'botmanor-app:rollback',\n\n // Agents behind an app — createAppAgent / updateAppAgent / deleteAppAgent\n APP_AGENT_CREATE: 'botmanor-app-agent:create',\n APP_AGENT_UPDATE: 'botmanor-app-agent:update',\n APP_AGENT_DELETE: 'botmanor-app-agent:delete',\n\n // Agents — createBotmanorAgent / updateBotmanorAgent / deleteBotmanorAgent /\n // publishAgentVersion / rollbackAgentVersion / chatWithAgent /\n // approve|rejectBotmanorAgentActivation / updateBotmanorAgentApprovalSettings /\n // configureAssistantRuntime / publishAssistantWebsite / configureAssistantChannel\n AGENT_CREATE: 'botmanor-agent:create',\n AGENT_UPDATE: 'botmanor-agent:update',\n AGENT_DELETE: 'botmanor-agent:delete',\n AGENT_PUBLISH: 'botmanor-agent:publish',\n AGENT_ROLLBACK: 'botmanor-agent:rollback',\n AGENT_EXECUTE: 'botmanor-agent:execute',\n AGENT_APPROVE: 'botmanor-agent:approve',\n\n // Knowledge bases — createKnowledgeBase / addKnowledgeDocument /\n // updateKnowledgeBase / deleteKnowledgeBase / removeKnowledgeDocument /\n // rebuildKnowledgeBaseIndex / queueKnowledgeSource / retry*\n KNOWLEDGE_CREATE: 'botmanor-knowledge-base:create',\n KNOWLEDGE_UPDATE: 'botmanor-knowledge-base:update',\n KNOWLEDGE_DELETE: 'botmanor-knowledge-base:delete',\n KNOWLEDGE_MANAGE: 'botmanor-knowledge-base:manage',\n\n // Retrieval profiles\n RETRIEVAL_PROFILE_CREATE: 'botmanor-retrieval-profile:create',\n RETRIEVAL_PROFILE_UPDATE: 'botmanor-retrieval-profile:update',\n RETRIEVAL_PROFILE_DELETE: 'botmanor-retrieval-profile:delete',\n\n // MCP servers — create/update/deleteMCPConnection, updateMCPTool,\n // testMCPConnection, refreshMCPTools / refreshMCPResources / callMCPTool\n MCP_CREATE: 'botmanor-mcp:create',\n MCP_UPDATE: 'botmanor-mcp:update',\n MCP_DELETE: 'botmanor-mcp:delete',\n MCP_TEST: 'botmanor-mcp:test',\n MCP_EXECUTE: 'botmanor-mcp:execute',\n\n // LLM providers and models — create/update/delete*, testLlmConnection\n LLM_CREATE: 'botmanor-llm:create',\n LLM_UPDATE: 'botmanor-llm:update',\n LLM_DELETE: 'botmanor-llm:delete',\n LLM_TEST: 'botmanor-llm:test',\n\n // A2A — create/update/deleteA2AConnection, sendA2AMessage, delegateA2ATask\n A2A_CREATE: 'botmanor-a2a:create',\n A2A_UPDATE: 'botmanor-a2a:update',\n A2A_DELETE: 'botmanor-a2a:delete',\n A2A_SEND: 'botmanor-a2a:send',\n A2A_EXECUTE: 'botmanor-a2a:execute',\n\n // A2A registry (the external agents the Registry page lists) —\n // create/update/deleteA2ARegistryEntry\n A2A_REGISTRY_CREATE: 'botmanor-a2a-registry:create',\n A2A_REGISTRY_UPDATE: 'botmanor-a2a-registry:update',\n A2A_REGISTRY_DELETE: 'botmanor-a2a-registry:delete',\n\n // Channels — connectors and routes\n CHANNEL_CREATE: 'botmanor-channel:create',\n CHANNEL_UPDATE: 'botmanor-channel:update',\n CHANNEL_DELETE: 'botmanor-channel:delete',\n // sendChannelMessage is :create, handleChannelMessage is :execute\n CHANNEL_MESSAGE_CREATE: 'botmanor-channel-message:create',\n CHANNEL_MESSAGE_EXECUTE: 'botmanor-channel-message:execute',\n\n // Policies\n POLICY_CREATE: 'botmanor-policy:create',\n POLICY_UPDATE: 'botmanor-policy:update',\n POLICY_DELETE: 'botmanor-policy:delete',\n\n // Skills\n SKILL_CREATE: 'botmanor-skill:create',\n SKILL_UPDATE: 'botmanor-skill:update',\n SKILL_DELETE: 'botmanor-skill:delete',\n\n // Prompt templates — create/duplicate share :create; renderPromptTemplate is :execute\n PROMPT_TEMPLATE_CREATE: 'botmanor-prompt-template:create',\n PROMPT_TEMPLATE_UPDATE: 'botmanor-prompt-template:update',\n PROMPT_TEMPLATE_DELETE: 'botmanor-prompt-template:delete',\n PROMPT_TEMPLATE_EXECUTE: 'botmanor-prompt-template:execute',\n\n // Executions — createExecution / triggerBoundWorkflow are :create,\n // runExecution / updateExecutionStatus are :update\n EXECUTION_CREATE: 'botmanor-execution:create',\n EXECUTION_UPDATE: 'botmanor-execution:update',\n\n // Buckets\n BUCKET_CREATE: 'botmanor-bucket:create',\n BUCKET_UPDATE: 'botmanor-bucket:update',\n BUCKET_DELETE: 'botmanor-bucket:delete',\n\n // Store — installPackage / forkInstalledPackage are :create,\n // updateInstalledPackage :update, uninstallPackage :delete\n STORE_CREATE: 'botmanor-store:create',\n STORE_UPDATE: 'botmanor-store:update',\n STORE_DELETE: 'botmanor-store:delete',\n\n // Agent and app templates\n TEMPLATE_CREATE: 'botmanor-template:create',\n TEMPLATE_UPDATE: 'botmanor-template:update',\n TEMPLATE_DELETE: 'botmanor-template:delete',\n} as const;\n\nexport type BotmanorPermission = (typeof BOTMANOR_PERMISSIONS)[keyof typeof BOTMANOR_PERMISSIONS];\n"],"mappings":";AAeA,IAAa,IAAuB;CAGlC,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,aAAa;CACb,cAAc;CAGd,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAMlB,cAAc;CACd,cAAc;CACd,cAAc;CACd,eAAe;CACf,gBAAgB;CAChB,eAAe;CACf,eAAe;CAKf,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAGlB,0BAA0B;CAC1B,0BAA0B;CAC1B,0BAA0B;CAI1B,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,UAAU;CACV,aAAa;CAGb,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,UAAU;CAGV,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,UAAU;CACV,aAAa;CAIb,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CAGrB,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAEhB,wBAAwB;CACxB,yBAAyB;CAGzB,eAAe;CACf,eAAe;CACf,eAAe;CAGf,cAAc;CACd,cAAc;CACd,cAAc;CAGd,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,yBAAyB;CAIzB,kBAAkB;CAClB,kBAAkB;CAGlB,eAAe;CACf,eAAe;CACf,eAAe;CAIf,cAAc;CACd,cAAc;CACd,cAAc;CAGd,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CAClB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useCallback as e } from "react";
|
|
2
|
+
import { Fragment as t, jsx as n } from "react/jsx-runtime";
|
|
3
|
+
import { useSafePermissions as r } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
4
|
+
//#region src/botlit/permissions/useBotmanorPermissions.tsx
|
|
5
|
+
function i(e, t) {
|
|
6
|
+
return e ? e.isLoading ? "loading" : e.hasPermission(t) ? "allowed" : "denied" : "allowed";
|
|
7
|
+
}
|
|
8
|
+
function a() {
|
|
9
|
+
let t = r();
|
|
10
|
+
return e((e) => i(t, e) === "allowed", [t]);
|
|
11
|
+
}
|
|
12
|
+
var o = ({ permission: e, children: a, fallback: o = null, loading: s = null }) => {
|
|
13
|
+
let c = i(r(), e);
|
|
14
|
+
return c === "allowed" ? /* @__PURE__ */ n(t, { children: a }) : c === "loading" ? /* @__PURE__ */ n(t, { children: s }) : /* @__PURE__ */ n(t, { children: o });
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { o as BotmanorPermissionGate, i as resolvePermission, a as useBotmanorCan };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=useBotmanorPermissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBotmanorPermissions.js","names":[],"sources":["../../../src/botlit/permissions/useBotmanorPermissions.tsx"],"sourcesContent":["/**\n * BotManor permission hooks and gate\n * @module botlit/permissions\n *\n * Thin wrappers over the shell's fe-libs `PermissionProvider` that decide\n * whether the UI should offer a write control. They follow the fe-libs\n * `PermissionGate` contract (render children when allowed, `fallback` when\n * denied, `loading` while the permission set is still being fetched) with one\n * difference: they read the context through `useSafePermissions`, so the MFE\n * keeps rendering in a host that has not mounted RBAC (the standalone dev\n * host, Storybook, unit tests). With no provider every control is offered,\n * which is exactly the behaviour before gating existed; the gateway's `@rbac`\n * directive remains the enforcement point either way.\n *\n * While permissions load, checks answer `false` so a viewer never sees a\n * control flash on and then disappear.\n */\n\nimport { useCallback, type FC, type ReactNode } from 'react';\nimport { useSafePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\n\nimport type { BotmanorPermission } from './botmanorPermissions';\n\n/** Answers whether the current member may use the control behind `permission`. */\nexport type BotmanorPermissionCheck = (permission: BotmanorPermission) => boolean;\n\n/** Outcome of resolving a permission against the shell context. */\nexport type PermissionDecision = 'allowed' | 'denied' | 'loading';\n\n/**\n * Resolve one permission against a (possibly absent) permission context.\n * Exported for unit tests; components use the hooks below.\n */\nexport function resolvePermission(\n context: ReturnType<typeof useSafePermissions>,\n permission: BotmanorPermission\n): PermissionDecision {\n if (!context) return 'allowed';\n if (context.isLoading) return 'loading';\n return context.hasPermission(permission) ? 'allowed' : 'denied';\n}\n\n/**\n * Returns a stable checker for any number of permissions on one page.\n *\n * @example\n * const can = useBotmanorCan();\n * {can(BOTMANOR_PERMISSIONS.POLICY_DELETE) && <Button onClick={remove}>Delete</Button>}\n */\nexport function useBotmanorCan(): BotmanorPermissionCheck {\n const context = useSafePermissions();\n return useCallback(\n (permission: BotmanorPermission) => resolvePermission(context, permission) === 'allowed',\n [context]\n );\n}\n\n/** Single-permission form of {@link useBotmanorCan}. */\nexport function useBotmanorPermission(permission: BotmanorPermission): boolean {\n return useBotmanorCan()(permission);\n}\n\nexport interface BotmanorPermissionGateProps {\n /** The `resource:action` pair the wrapped control needs. */\n permission: BotmanorPermission;\n /** Rendered when the member has the permission. */\n children: ReactNode;\n /** Rendered when the member lacks the permission. Nothing by default. */\n fallback?: ReactNode;\n /** Rendered while permissions load. Nothing by default. */\n loading?: ReactNode;\n}\n\n/**\n * Render `children` only for members who hold `permission`.\n *\n * @example\n * <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.POLICY_CREATE}>\n * <Button>Create Policy</Button>\n * </BotmanorPermissionGate>\n */\nexport const BotmanorPermissionGate: FC<BotmanorPermissionGateProps> = ({\n permission,\n children,\n fallback = null,\n loading = null,\n}) => {\n const decision = resolvePermission(useSafePermissions(), permission);\n if (decision === 'allowed') return <>{children}</>;\n if (decision === 'loading') return <>{loading}</>;\n return <>{fallback}</>;\n};\n"],"mappings":";;;;AAiCA,SAAgB,EACd,GACA,GACoB;AAGpB,QAFK,IACD,EAAQ,YAAkB,YACvB,EAAQ,cAAc,EAAW,GAAG,YAAY,WAFlC;;AAYvB,SAAgB,IAA0C;CACxD,IAAM,IAAU,GAAoB;AACpC,QAAO,GACJ,MAAmC,EAAkB,GAAS,EAAW,KAAK,WAC/E,CAAC,EAAQ,CACV;;AA2BH,IAAa,KAA2D,EACtE,eACA,aACA,cAAW,MACX,aAAU,WACN;CACJ,IAAM,IAAW,EAAkB,GAAoB,EAAE,EAAW;AAGpE,QAFI,MAAa,YAAkB,kBAAA,GAAA,EAAG,aAAY,CAAA,GAC9C,MAAa,YAAkB,kBAAA,GAAA,EAAA,UAAG,GAAW,CAAA,GAC1C,kBAAA,GAAA,EAAA,UAAG,GAAY,CAAA"}
|
|
@@ -4,7 +4,8 @@ var e = [
|
|
|
4
4
|
"TOOL",
|
|
5
5
|
"RESPONSE",
|
|
6
6
|
"BUDGET",
|
|
7
|
-
"RATE_LIMIT"
|
|
7
|
+
"RATE_LIMIT",
|
|
8
|
+
"RETENTION"
|
|
8
9
|
];
|
|
9
10
|
function t(t) {
|
|
10
11
|
return e.includes(t);
|
|
@@ -43,44 +44,55 @@ var n = [
|
|
|
43
44
|
response: {
|
|
44
45
|
allowedModels: "",
|
|
45
46
|
allowedProviders: ""
|
|
47
|
+
},
|
|
48
|
+
retention: {
|
|
49
|
+
executionDataRetentionDays: "90",
|
|
50
|
+
conversationRetentionDays: ""
|
|
46
51
|
}
|
|
47
|
-
};
|
|
48
|
-
function
|
|
52
|
+
}, a = 3650;
|
|
53
|
+
function o(e) {
|
|
49
54
|
return e.split(",").map((e) => e.trim()).filter(Boolean);
|
|
50
55
|
}
|
|
51
|
-
function
|
|
56
|
+
function s(e) {
|
|
52
57
|
return e.split("\n").map((e) => e.trim()).filter(Boolean);
|
|
53
58
|
}
|
|
54
|
-
var
|
|
55
|
-
function
|
|
59
|
+
var c = class extends Error {};
|
|
60
|
+
function l(e, t) {
|
|
56
61
|
let n = e.trim();
|
|
57
62
|
if (!n) return;
|
|
58
63
|
let r = Number(n);
|
|
59
|
-
if (!Number.isInteger(r) || r < 1) throw new
|
|
64
|
+
if (!Number.isInteger(r) || r < 1) throw new c(`${t} must be a whole number of at least 1, or left empty.`);
|
|
60
65
|
return r;
|
|
61
66
|
}
|
|
62
|
-
function
|
|
67
|
+
function u(e, t, n) {
|
|
68
|
+
let r = e.trim();
|
|
69
|
+
if (!r) return;
|
|
70
|
+
let i = Number(r);
|
|
71
|
+
if (!Number.isInteger(i) || i < n || i > 3650) throw new c(`${t} must be a whole number of days from ${n} to ${a}, or left empty.`);
|
|
72
|
+
return i;
|
|
73
|
+
}
|
|
74
|
+
function d(e) {
|
|
63
75
|
for (let t of e) try {
|
|
64
76
|
new RegExp(t, "i");
|
|
65
77
|
} catch {
|
|
66
|
-
throw new
|
|
78
|
+
throw new c(`"${t}" is not a valid regular expression.`);
|
|
67
79
|
}
|
|
68
80
|
}
|
|
69
|
-
function
|
|
81
|
+
function f(e) {
|
|
70
82
|
return Object.fromEntries(Object.entries(e).filter(([, e]) => e !== void 0 && e !== ""));
|
|
71
83
|
}
|
|
72
|
-
function
|
|
73
|
-
let t =
|
|
74
|
-
return
|
|
84
|
+
function p(e) {
|
|
85
|
+
let t = o(e.blockedKeywords), n = s(e.blockedPatterns);
|
|
86
|
+
return d(n), {
|
|
75
87
|
piiDetection: e.piiDetection,
|
|
76
88
|
...t.length > 0 ? { blockedKeywords: t } : {},
|
|
77
89
|
...n.length > 0 ? { blockedPatterns: n } : {}
|
|
78
90
|
};
|
|
79
91
|
}
|
|
80
|
-
function
|
|
81
|
-
let t =
|
|
82
|
-
maxCallsPerExecution:
|
|
83
|
-
timeoutSeconds:
|
|
92
|
+
function m(e) {
|
|
93
|
+
let t = o(e.allowedTools), n = o(e.deniedTools), r = f({
|
|
94
|
+
maxCallsPerExecution: l(e.maxCallsPerExecution, "Max tool calls per execution"),
|
|
95
|
+
timeoutSeconds: l(e.timeoutSeconds, "Tool call timeout")
|
|
84
96
|
});
|
|
85
97
|
return {
|
|
86
98
|
...t.length > 0 ? { allowedTools: t } : {},
|
|
@@ -90,60 +102,72 @@ function f(e) {
|
|
|
90
102
|
...Object.keys(r).length > 0 ? { limits: r } : {}
|
|
91
103
|
};
|
|
92
104
|
}
|
|
93
|
-
function
|
|
94
|
-
return
|
|
95
|
-
maxTokensPerDay:
|
|
96
|
-
maxTokensPerExecution:
|
|
105
|
+
function h(e) {
|
|
106
|
+
return f({
|
|
107
|
+
maxTokensPerDay: l(e.maxTokensPerDay, "Max tokens per day"),
|
|
108
|
+
maxTokensPerExecution: l(e.maxTokensPerExecution, "Max output tokens per model call")
|
|
97
109
|
});
|
|
98
110
|
}
|
|
99
|
-
function
|
|
100
|
-
let t =
|
|
101
|
-
requestsPerMinute:
|
|
102
|
-
requestsPerHour:
|
|
103
|
-
tokensPerMinute:
|
|
111
|
+
function g(e) {
|
|
112
|
+
let t = f({
|
|
113
|
+
requestsPerMinute: l(e.requestsPerMinute, "Requests per minute"),
|
|
114
|
+
requestsPerHour: l(e.requestsPerHour, "Requests per hour"),
|
|
115
|
+
tokensPerMinute: l(e.tokensPerMinute, "Tokens per minute")
|
|
104
116
|
});
|
|
105
117
|
return Object.keys(t).length > 0 ? { limits: t } : {};
|
|
106
118
|
}
|
|
107
|
-
function
|
|
108
|
-
let t =
|
|
119
|
+
function _(e) {
|
|
120
|
+
let t = o(e.allowedModels), n = o(e.allowedProviders).map((e) => e.toUpperCase());
|
|
109
121
|
return {
|
|
110
122
|
...t.length > 0 ? { allowedModels: t } : {},
|
|
111
123
|
...n.length > 0 ? { allowedProviders: n } : {}
|
|
112
124
|
};
|
|
113
125
|
}
|
|
114
|
-
function
|
|
126
|
+
function v(e) {
|
|
127
|
+
let t = f({
|
|
128
|
+
executionDataRetentionDays: u(e.executionDataRetentionDays, "Execution data retention", 0),
|
|
129
|
+
conversationRetentionDays: u(e.conversationRetentionDays, "Conversation retention", 1)
|
|
130
|
+
});
|
|
131
|
+
if (Object.keys(t).length === 0) throw new c("Set at least one retention period.");
|
|
132
|
+
return t;
|
|
133
|
+
}
|
|
134
|
+
function y(e, t) {
|
|
115
135
|
try {
|
|
116
136
|
switch (e) {
|
|
117
137
|
case "CONTENT": return {
|
|
118
138
|
ok: !0,
|
|
119
|
-
config:
|
|
139
|
+
config: p(t.content)
|
|
120
140
|
};
|
|
121
141
|
case "TOOL": return {
|
|
122
142
|
ok: !0,
|
|
123
|
-
config:
|
|
143
|
+
config: m(t.tool)
|
|
124
144
|
};
|
|
125
145
|
case "BUDGET": return {
|
|
126
146
|
ok: !0,
|
|
127
|
-
config:
|
|
147
|
+
config: h(t.budget)
|
|
128
148
|
};
|
|
129
149
|
case "RATE_LIMIT": return {
|
|
130
150
|
ok: !0,
|
|
131
|
-
config:
|
|
151
|
+
config: g(t.rateLimit)
|
|
132
152
|
};
|
|
133
153
|
case "RESPONSE": return {
|
|
134
154
|
ok: !0,
|
|
135
|
-
config:
|
|
155
|
+
config: _(t.response)
|
|
156
|
+
};
|
|
157
|
+
case "RETENTION": return {
|
|
158
|
+
ok: !0,
|
|
159
|
+
config: v(t.retention)
|
|
136
160
|
};
|
|
137
161
|
}
|
|
138
162
|
} catch (e) {
|
|
139
|
-
if (e instanceof
|
|
163
|
+
if (e instanceof c) return {
|
|
140
164
|
ok: !1,
|
|
141
165
|
error: e.message
|
|
142
166
|
};
|
|
143
167
|
throw e;
|
|
144
168
|
}
|
|
145
169
|
}
|
|
146
|
-
var
|
|
170
|
+
var b = {
|
|
147
171
|
CONTENT: [
|
|
148
172
|
"piiDetection",
|
|
149
173
|
"blockedKeywords",
|
|
@@ -159,8 +183,9 @@ var _ = {
|
|
|
159
183
|
],
|
|
160
184
|
RESPONSE: ["allowedModels", "allowedProviders"],
|
|
161
185
|
BUDGET: ["maxTokensPerDay", "maxTokensPerExecution"],
|
|
162
|
-
RATE_LIMIT: ["maxRequestsPerMinute", "limits"]
|
|
163
|
-
|
|
186
|
+
RATE_LIMIT: ["maxRequestsPerMinute", "limits"],
|
|
187
|
+
RETENTION: ["executionDataRetentionDays", "conversationRetentionDays"]
|
|
188
|
+
}, x = {
|
|
164
189
|
TOOL: ["maxCallsPerExecution", "timeoutSeconds"],
|
|
165
190
|
RATE_LIMIT: [
|
|
166
191
|
"requestsPerMinute",
|
|
@@ -168,8 +193,8 @@ var _ = {
|
|
|
168
193
|
"tokensPerMinute"
|
|
169
194
|
]
|
|
170
195
|
};
|
|
171
|
-
function
|
|
172
|
-
let n =
|
|
196
|
+
function S(e, t) {
|
|
197
|
+
let n = b[e];
|
|
173
198
|
if (!n) return null;
|
|
174
199
|
if (!t || typeof t != "object" || Array.isArray(t)) return [];
|
|
175
200
|
let r = [];
|
|
@@ -178,12 +203,12 @@ function y(e, t) {
|
|
|
178
203
|
r.push(i);
|
|
179
204
|
continue;
|
|
180
205
|
}
|
|
181
|
-
let t =
|
|
206
|
+
let t = x[e];
|
|
182
207
|
if (i === "limits" && t && a && typeof a == "object" && !Array.isArray(a)) for (let e of Object.keys(a)) t.includes(e) || r.push(`limits.${e}`);
|
|
183
208
|
}
|
|
184
209
|
return r;
|
|
185
210
|
}
|
|
186
211
|
//#endregion
|
|
187
|
-
export { r as A2A_TRUST_LEVELS, e as CREATABLE_POLICY_TYPES, i as EMPTY_POLICY_FORM, n as TOOL_RISK_LEVELS,
|
|
212
|
+
export { r as A2A_TRUST_LEVELS, e as CREATABLE_POLICY_TYPES, i as EMPTY_POLICY_FORM, a as MAX_RETENTION_DAYS, n as TOOL_RISK_LEVELS, y as buildPolicyConfig, t as isCreatablePolicyType, S as unenforcedConfigKeys };
|
|
188
213
|
|
|
189
214
|
//# sourceMappingURL=policyConfig.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policyConfig.js","names":[],"sources":["../../../src/botlit/utils/policyConfig.ts"],"sourcesContent":["/**\n * Policy editor form state <-> the config shape the BotManor runtime enforces.\n *\n * Every key this module writes is read by the backend `PolicyEvaluatorService`\n * (or the tool runtime that applies a resolved TOOL policy). Settings the\n * runtime does not enforce are deliberately absent, so the editor never offers\n * a control that looks binding and is silently ignored (BOFF-9683).\n * @module botlit/utils\n */\n\nimport type { PolicyConfig, PolicyType } from '../types';\n\n/** Policy types the editor can create, i.e. the types with a runtime evaluator. */\nexport const CREATABLE_POLICY_TYPES = [\n 'CONTENT',\n 'TOOL',\n 'RESPONSE',\n 'BUDGET',\n 'RATE_LIMIT',\n] as const;\nexport type CreatablePolicyType = (typeof CREATABLE_POLICY_TYPES)[number];\n\nexport function isCreatablePolicyType(type: PolicyType): type is CreatablePolicyType {\n return (CREATABLE_POLICY_TYPES as readonly string[]).includes(type);\n}\n\nexport const TOOL_RISK_LEVELS = ['LOW', 'MEDIUM', 'HIGH'] as const;\nexport const A2A_TRUST_LEVELS = ['UNVERIFIED', 'VERIFIED_PUBLISHER', 'INTERNAL'] as const;\n\n/**\n * Raw editor state. Numeric fields are strings so a field can be left empty,\n * which means \"not configured\" rather than a default limit.\n */\nexport interface PolicyFormState {\n content: { piiDetection: boolean; blockedKeywords: string; blockedPatterns: string };\n tool: {\n allowedTools: string;\n deniedTools: string;\n maxToolRiskLevel: '' | (typeof TOOL_RISK_LEVELS)[number];\n a2aMinTrustLevel: '' | (typeof A2A_TRUST_LEVELS)[number];\n maxCallsPerExecution: string;\n timeoutSeconds: string;\n };\n budget: { maxTokensPerDay: string; maxTokensPerExecution: string };\n rateLimit: { requestsPerMinute: string; requestsPerHour: string; tokensPerMinute: string };\n response: { allowedModels: string; allowedProviders: string };\n}\n\nexport const EMPTY_POLICY_FORM: PolicyFormState = {\n content: { piiDetection: true, blockedKeywords: '', blockedPatterns: '' },\n tool: {\n allowedTools: '',\n deniedTools: '',\n maxToolRiskLevel: '',\n a2aMinTrustLevel: '',\n maxCallsPerExecution: '',\n timeoutSeconds: '',\n },\n budget: { maxTokensPerDay: '', maxTokensPerExecution: '' },\n rateLimit: { requestsPerMinute: '60', requestsPerHour: '1000', tokensPerMinute: '' },\n response: { allowedModels: '', allowedProviders: '' },\n};\n\nexport type PolicyConfigResult = { ok: true; config: PolicyConfig } | { ok: false; error: string };\n\n/** Split a comma-separated form value into trimmed, non-empty entries. */\nexport function splitList(value: string): string[] {\n return value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\n/** Split a one-entry-per-line form value into trimmed, non-empty entries. */\nexport function splitLines(value: string): string[] {\n return value\n .split('\\n')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\nclass PolicyFormError extends Error {}\n\n/** Parse an optional positive whole number; empty means \"not configured\". */\nfunction optionalPositiveInt(value: string, label: string): number | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const parsed = Number(trimmed);\n if (!Number.isInteger(parsed) || parsed < 1) {\n throw new PolicyFormError(`${label} must be a whole number of at least 1, or left empty.`);\n }\n return parsed;\n}\n\nfunction assertValidPatterns(patterns: string[]): void {\n for (const pattern of patterns) {\n try {\n // Compiled only to validate admin input; the runtime compiles the same source.\n new RegExp(pattern, 'i');\n } catch {\n throw new PolicyFormError(`\"${pattern}\" is not a valid regular expression.`);\n }\n }\n}\n\nfunction withDefined<T extends Record<string, unknown>>(entries: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(entries).filter(([, value]) => value !== undefined && value !== '')\n ) as Partial<T>;\n}\n\nfunction buildContentConfig(state: PolicyFormState['content']): PolicyConfig {\n const blockedKeywords = splitList(state.blockedKeywords);\n const blockedPatterns = splitLines(state.blockedPatterns);\n assertValidPatterns(blockedPatterns);\n return {\n piiDetection: state.piiDetection,\n ...(blockedKeywords.length > 0 ? { blockedKeywords } : {}),\n ...(blockedPatterns.length > 0 ? { blockedPatterns } : {}),\n };\n}\n\nfunction buildToolConfig(state: PolicyFormState['tool']): PolicyConfig {\n const allowedTools = splitList(state.allowedTools);\n const deniedTools = splitList(state.deniedTools);\n const limits = withDefined({\n maxCallsPerExecution: optionalPositiveInt(\n state.maxCallsPerExecution,\n 'Max tool calls per execution'\n ),\n timeoutSeconds: optionalPositiveInt(state.timeoutSeconds, 'Tool call timeout'),\n });\n return {\n ...(allowedTools.length > 0 ? { allowedTools } : {}),\n ...(deniedTools.length > 0 ? { deniedTools } : {}),\n ...(state.maxToolRiskLevel ? { maxToolRiskLevel: state.maxToolRiskLevel } : {}),\n ...(state.a2aMinTrustLevel ? { a2aMinTrustLevel: state.a2aMinTrustLevel } : {}),\n ...(Object.keys(limits).length > 0 ? { limits } : {}),\n };\n}\n\nfunction buildBudgetConfig(state: PolicyFormState['budget']): PolicyConfig {\n return withDefined({\n maxTokensPerDay: optionalPositiveInt(state.maxTokensPerDay, 'Max tokens per day'),\n maxTokensPerExecution: optionalPositiveInt(\n state.maxTokensPerExecution,\n 'Max output tokens per model call'\n ),\n });\n}\n\nfunction buildRateLimitConfig(state: PolicyFormState['rateLimit']): PolicyConfig {\n const limits = withDefined({\n requestsPerMinute: optionalPositiveInt(state.requestsPerMinute, 'Requests per minute'),\n requestsPerHour: optionalPositiveInt(state.requestsPerHour, 'Requests per hour'),\n tokensPerMinute: optionalPositiveInt(state.tokensPerMinute, 'Tokens per minute'),\n });\n return Object.keys(limits).length > 0 ? { limits } : {};\n}\n\nfunction buildResponseConfig(state: PolicyFormState['response']): PolicyConfig {\n const allowedModels = splitList(state.allowedModels);\n const allowedProviders = splitList(state.allowedProviders).map((provider) =>\n provider.toUpperCase()\n );\n return {\n ...(allowedModels.length > 0 ? { allowedModels } : {}),\n ...(allowedProviders.length > 0 ? { allowedProviders } : {}),\n };\n}\n\n/**\n * Build the config for a new policy from the editor state, or explain what is\n * wrong with the input. Only enforced keys are ever written.\n */\nexport function buildPolicyConfig(\n type: CreatablePolicyType,\n state: PolicyFormState\n): PolicyConfigResult {\n try {\n switch (type) {\n case 'CONTENT':\n return { ok: true, config: buildContentConfig(state.content) };\n case 'TOOL':\n return { ok: true, config: buildToolConfig(state.tool) };\n case 'BUDGET':\n return { ok: true, config: buildBudgetConfig(state.budget) };\n case 'RATE_LIMIT':\n return { ok: true, config: buildRateLimitConfig(state.rateLimit) };\n case 'RESPONSE':\n return { ok: true, config: buildResponseConfig(state.response) };\n }\n } catch (error) {\n if (error instanceof PolicyFormError) return { ok: false, error: error.message };\n throw error;\n }\n}\n\n/** Config keys the runtime enforces, per policy type (top level). */\nconst ENFORCED_KEYS: Partial<Record<PolicyType, readonly string[]>> = {\n CONTENT: ['piiDetection', 'blockedKeywords', 'blockedPatterns', 'customPatterns'],\n TOOL: ['allowedTools', 'deniedTools', 'maxToolRiskLevel', 'a2aMinTrustLevel', 'limits'],\n RESPONSE: ['allowedModels', 'allowedProviders'],\n BUDGET: ['maxTokensPerDay', 'maxTokensPerExecution'],\n RATE_LIMIT: ['maxRequestsPerMinute', 'limits'],\n};\n\n/** Enforced keys inside `limits`, for the types that nest limits. */\nconst ENFORCED_LIMIT_KEYS: Partial<Record<PolicyType, readonly string[]>> = {\n TOOL: ['maxCallsPerExecution', 'timeoutSeconds'],\n RATE_LIMIT: ['requestsPerMinute', 'requestsPerHour', 'tokensPerMinute'],\n};\n\n/**\n * Config keys on a stored policy that the runtime does not enforce, e.g.\n * settings written by an earlier version of this editor. `null` when the\n * policy type has no runtime evaluator at all.\n */\nexport function unenforcedConfigKeys(type: PolicyType, config: unknown): string[] | null {\n const enforced = ENFORCED_KEYS[type];\n if (!enforced) return null;\n if (!config || typeof config !== 'object' || Array.isArray(config)) return [];\n\n const ignored: string[] = [];\n for (const [key, value] of Object.entries(config as Record<string, unknown>)) {\n if (!enforced.includes(key)) {\n ignored.push(key);\n continue;\n }\n const limitKeys = ENFORCED_LIMIT_KEYS[type];\n if (\n key === 'limits' &&\n limitKeys &&\n value &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n for (const limitKey of Object.keys(value as Record<string, unknown>)) {\n if (!limitKeys.includes(limitKey)) ignored.push(`limits.${limitKey}`);\n }\n }\n }\n return ignored;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"policyConfig.js","names":[],"sources":["../../../src/botlit/utils/policyConfig.ts"],"sourcesContent":["/**\n * Policy editor form state <-> the config shape the BotManor runtime enforces.\n *\n * Every key this module writes is read by the backend `PolicyEvaluatorService`\n * (or the tool runtime that applies a resolved TOOL policy). Settings the\n * runtime does not enforce are deliberately absent, so the editor never offers\n * a control that looks binding and is silently ignored (BOFF-9683).\n * @module botlit/utils\n */\n\nimport type { PolicyConfig, PolicyType } from '../types';\n\n/** Policy types the editor can create, i.e. the types with a runtime evaluator. */\nexport const CREATABLE_POLICY_TYPES = [\n 'CONTENT',\n 'TOOL',\n 'RESPONSE',\n 'BUDGET',\n 'RATE_LIMIT',\n 'RETENTION',\n] as const;\nexport type CreatablePolicyType = (typeof CREATABLE_POLICY_TYPES)[number];\n\nexport function isCreatablePolicyType(type: PolicyType): type is CreatablePolicyType {\n return (CREATABLE_POLICY_TYPES as readonly string[]).includes(type);\n}\n\nexport const TOOL_RISK_LEVELS = ['LOW', 'MEDIUM', 'HIGH'] as const;\nexport const A2A_TRUST_LEVELS = ['UNVERIFIED', 'VERIFIED_PUBLISHER', 'INTERNAL'] as const;\n\n/**\n * Raw editor state. Numeric fields are strings so a field can be left empty,\n * which means \"not configured\" rather than a default limit.\n */\nexport interface PolicyFormState {\n content: { piiDetection: boolean; blockedKeywords: string; blockedPatterns: string };\n tool: {\n allowedTools: string;\n deniedTools: string;\n maxToolRiskLevel: '' | (typeof TOOL_RISK_LEVELS)[number];\n a2aMinTrustLevel: '' | (typeof A2A_TRUST_LEVELS)[number];\n maxCallsPerExecution: string;\n timeoutSeconds: string;\n };\n budget: { maxTokensPerDay: string; maxTokensPerExecution: string };\n rateLimit: { requestsPerMinute: string; requestsPerHour: string; tokensPerMinute: string };\n response: { allowedModels: string; allowedProviders: string };\n retention: { executionDataRetentionDays: string; conversationRetentionDays: string };\n}\n\nexport const EMPTY_POLICY_FORM: PolicyFormState = {\n content: { piiDetection: true, blockedKeywords: '', blockedPatterns: '' },\n tool: {\n allowedTools: '',\n deniedTools: '',\n maxToolRiskLevel: '',\n a2aMinTrustLevel: '',\n maxCallsPerExecution: '',\n timeoutSeconds: '',\n },\n budget: { maxTokensPerDay: '', maxTokensPerExecution: '' },\n rateLimit: { requestsPerMinute: '60', requestsPerHour: '1000', tokensPerMinute: '' },\n response: { allowedModels: '', allowedProviders: '' },\n retention: { executionDataRetentionDays: '90', conversationRetentionDays: '' },\n};\n\n/** Longest retention period the runtime accepts (10 years). */\nexport const MAX_RETENTION_DAYS = 3650;\n\nexport type PolicyConfigResult = { ok: true; config: PolicyConfig } | { ok: false; error: string };\n\n/** Split a comma-separated form value into trimmed, non-empty entries. */\nexport function splitList(value: string): string[] {\n return value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\n/** Split a one-entry-per-line form value into trimmed, non-empty entries. */\nexport function splitLines(value: string): string[] {\n return value\n .split('\\n')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\nclass PolicyFormError extends Error {}\n\n/** Parse an optional positive whole number; empty means \"not configured\". */\nfunction optionalPositiveInt(value: string, label: string): number | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const parsed = Number(trimmed);\n if (!Number.isInteger(parsed) || parsed < 1) {\n throw new PolicyFormError(`${label} must be a whole number of at least 1, or left empty.`);\n }\n return parsed;\n}\n\n/** Parse an optional whole number of days in `[min, MAX_RETENTION_DAYS]`; empty means \"not set\". */\nfunction optionalDays(value: string, label: string, min: number): number | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const parsed = Number(trimmed);\n if (!Number.isInteger(parsed) || parsed < min || parsed > MAX_RETENTION_DAYS) {\n throw new PolicyFormError(\n `${label} must be a whole number of days from ${min} to ${MAX_RETENTION_DAYS}, or left empty.`\n );\n }\n return parsed;\n}\n\nfunction assertValidPatterns(patterns: string[]): void {\n for (const pattern of patterns) {\n try {\n // Compiled only to validate admin input; the runtime compiles the same source.\n new RegExp(pattern, 'i');\n } catch {\n throw new PolicyFormError(`\"${pattern}\" is not a valid regular expression.`);\n }\n }\n}\n\nfunction withDefined<T extends Record<string, unknown>>(entries: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(entries).filter(([, value]) => value !== undefined && value !== '')\n ) as Partial<T>;\n}\n\nfunction buildContentConfig(state: PolicyFormState['content']): PolicyConfig {\n const blockedKeywords = splitList(state.blockedKeywords);\n const blockedPatterns = splitLines(state.blockedPatterns);\n assertValidPatterns(blockedPatterns);\n return {\n piiDetection: state.piiDetection,\n ...(blockedKeywords.length > 0 ? { blockedKeywords } : {}),\n ...(blockedPatterns.length > 0 ? { blockedPatterns } : {}),\n };\n}\n\nfunction buildToolConfig(state: PolicyFormState['tool']): PolicyConfig {\n const allowedTools = splitList(state.allowedTools);\n const deniedTools = splitList(state.deniedTools);\n const limits = withDefined({\n maxCallsPerExecution: optionalPositiveInt(\n state.maxCallsPerExecution,\n 'Max tool calls per execution'\n ),\n timeoutSeconds: optionalPositiveInt(state.timeoutSeconds, 'Tool call timeout'),\n });\n return {\n ...(allowedTools.length > 0 ? { allowedTools } : {}),\n ...(deniedTools.length > 0 ? { deniedTools } : {}),\n ...(state.maxToolRiskLevel ? { maxToolRiskLevel: state.maxToolRiskLevel } : {}),\n ...(state.a2aMinTrustLevel ? { a2aMinTrustLevel: state.a2aMinTrustLevel } : {}),\n ...(Object.keys(limits).length > 0 ? { limits } : {}),\n };\n}\n\nfunction buildBudgetConfig(state: PolicyFormState['budget']): PolicyConfig {\n return withDefined({\n maxTokensPerDay: optionalPositiveInt(state.maxTokensPerDay, 'Max tokens per day'),\n maxTokensPerExecution: optionalPositiveInt(\n state.maxTokensPerExecution,\n 'Max output tokens per model call'\n ),\n });\n}\n\nfunction buildRateLimitConfig(state: PolicyFormState['rateLimit']): PolicyConfig {\n const limits = withDefined({\n requestsPerMinute: optionalPositiveInt(state.requestsPerMinute, 'Requests per minute'),\n requestsPerHour: optionalPositiveInt(state.requestsPerHour, 'Requests per hour'),\n tokensPerMinute: optionalPositiveInt(state.tokensPerMinute, 'Tokens per minute'),\n });\n return Object.keys(limits).length > 0 ? { limits } : {};\n}\n\nfunction buildResponseConfig(state: PolicyFormState['response']): PolicyConfig {\n const allowedModels = splitList(state.allowedModels);\n const allowedProviders = splitList(state.allowedProviders).map((provider) =>\n provider.toUpperCase()\n );\n return {\n ...(allowedModels.length > 0 ? { allowedModels } : {}),\n ...(allowedProviders.length > 0 ? { allowedProviders } : {}),\n };\n}\n\nfunction buildRetentionConfig(state: PolicyFormState['retention']): PolicyConfig {\n const config = withDefined({\n executionDataRetentionDays: optionalDays(\n state.executionDataRetentionDays,\n 'Execution data retention',\n 0\n ),\n conversationRetentionDays: optionalDays(\n state.conversationRetentionDays,\n 'Conversation retention',\n 1\n ),\n });\n if (Object.keys(config).length === 0) {\n throw new PolicyFormError('Set at least one retention period.');\n }\n return config;\n}\n\n/**\n * Build the config for a new policy from the editor state, or explain what is\n * wrong with the input. Only enforced keys are ever written.\n */\nexport function buildPolicyConfig(\n type: CreatablePolicyType,\n state: PolicyFormState\n): PolicyConfigResult {\n try {\n switch (type) {\n case 'CONTENT':\n return { ok: true, config: buildContentConfig(state.content) };\n case 'TOOL':\n return { ok: true, config: buildToolConfig(state.tool) };\n case 'BUDGET':\n return { ok: true, config: buildBudgetConfig(state.budget) };\n case 'RATE_LIMIT':\n return { ok: true, config: buildRateLimitConfig(state.rateLimit) };\n case 'RESPONSE':\n return { ok: true, config: buildResponseConfig(state.response) };\n case 'RETENTION':\n return { ok: true, config: buildRetentionConfig(state.retention) };\n }\n } catch (error) {\n if (error instanceof PolicyFormError) return { ok: false, error: error.message };\n throw error;\n }\n}\n\n/** Config keys the runtime enforces, per policy type (top level). */\nconst ENFORCED_KEYS: Partial<Record<PolicyType, readonly string[]>> = {\n CONTENT: ['piiDetection', 'blockedKeywords', 'blockedPatterns', 'customPatterns'],\n TOOL: ['allowedTools', 'deniedTools', 'maxToolRiskLevel', 'a2aMinTrustLevel', 'limits'],\n RESPONSE: ['allowedModels', 'allowedProviders'],\n BUDGET: ['maxTokensPerDay', 'maxTokensPerExecution'],\n RATE_LIMIT: ['maxRequestsPerMinute', 'limits'],\n RETENTION: ['executionDataRetentionDays', 'conversationRetentionDays'],\n};\n\n/** Enforced keys inside `limits`, for the types that nest limits. */\nconst ENFORCED_LIMIT_KEYS: Partial<Record<PolicyType, readonly string[]>> = {\n TOOL: ['maxCallsPerExecution', 'timeoutSeconds'],\n RATE_LIMIT: ['requestsPerMinute', 'requestsPerHour', 'tokensPerMinute'],\n};\n\n/**\n * Config keys on a stored policy that the runtime does not enforce, e.g.\n * settings written by an earlier version of this editor. `null` when the\n * policy type has no runtime evaluator at all.\n */\nexport function unenforcedConfigKeys(type: PolicyType, config: unknown): string[] | null {\n const enforced = ENFORCED_KEYS[type];\n if (!enforced) return null;\n if (!config || typeof config !== 'object' || Array.isArray(config)) return [];\n\n const ignored: string[] = [];\n for (const [key, value] of Object.entries(config as Record<string, unknown>)) {\n if (!enforced.includes(key)) {\n ignored.push(key);\n continue;\n }\n const limitKeys = ENFORCED_LIMIT_KEYS[type];\n if (\n key === 'limits' &&\n limitKeys &&\n value &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n for (const limitKey of Object.keys(value as Record<string, unknown>)) {\n if (!limitKeys.includes(limitKey)) ignored.push(`limits.${limitKey}`);\n }\n }\n }\n return ignored;\n}\n"],"mappings":"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"}
|