@burdenoff/microfe-botmanor 2026.1027.3 → 2026.1029.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/components/RetentionStatusPanel.js +95 -0
- package/dist/botlit/components/RetentionStatusPanel.js.map +1 -0
- 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/PoliciesPage.js +100 -98
- package/dist/botlit/pages/PoliciesPage.js.map +1 -1
- package/dist/botlit/pages/PolicyDetailPage.js +333 -301
- package/dist/botlit/pages/PolicyDetailPage.js.map +1 -1
- 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 +4 -4
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { formatRelativeTime as e } from "../utils/formatters.js";
|
|
2
|
+
import { BOTMANOR_PERMISSIONS as t } from "../permissions/botmanorPermissions.js";
|
|
3
|
+
import { BotmanorPermissionGate as n } from "../permissions/useBotmanorPermissions.js";
|
|
4
|
+
import { Button as r } from "./Button.js";
|
|
5
|
+
import "../permissions/index.js";
|
|
6
|
+
import { Card as i, CardContent as a, CardDescription as o, CardHeader as s, CardTitle as c } from "./Card.js";
|
|
7
|
+
import { Badge as l } from "./Badge.js";
|
|
8
|
+
import { useApplyRetention as u, useRetentionStatus as d } from "../hooks/useRetention.js";
|
|
9
|
+
import "../hooks/index.js";
|
|
10
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
11
|
+
import { Archive as m, RefreshCw as h } from "lucide-react";
|
|
12
|
+
import { toast as g } from "sonner";
|
|
13
|
+
//#region src/botlit/components/RetentionStatusPanel.tsx
|
|
14
|
+
function _(e, t) {
|
|
15
|
+
return e == null ? "Kept for the life of the workspace" : e === 0 ? t : `${e} day${e === 1 ? "" : "s"}`;
|
|
16
|
+
}
|
|
17
|
+
var v = () => {
|
|
18
|
+
let { data: v, loading: y, error: b, refetch: x } = d(), { apply: S, loading: C } = u(), w = async () => {
|
|
19
|
+
try {
|
|
20
|
+
let e = await S();
|
|
21
|
+
e.status === "COMPLETED" ? g.success(`Retention applied: ${e.executionsPurged} run payloads and ${e.a2aMessagesPurged} agent messages erased`) : g.error(e.errorMessage ?? "The retention pass failed");
|
|
22
|
+
} catch (e) {
|
|
23
|
+
g.error(e instanceof Error ? e.message : "Failed to apply retention");
|
|
24
|
+
}
|
|
25
|
+
await x();
|
|
26
|
+
};
|
|
27
|
+
if (b) return null;
|
|
28
|
+
let T = v?.recentRuns[0];
|
|
29
|
+
return /* @__PURE__ */ p(i, {
|
|
30
|
+
"data-testid": "retention-status-panel",
|
|
31
|
+
children: [/* @__PURE__ */ f(s, {
|
|
32
|
+
className: "pb-2",
|
|
33
|
+
children: /* @__PURE__ */ p("div", {
|
|
34
|
+
className: "flex flex-wrap items-start justify-between gap-3",
|
|
35
|
+
children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ p(c, {
|
|
36
|
+
className: "flex items-center gap-2 text-base",
|
|
37
|
+
children: [/* @__PURE__ */ f(m, { size: 18 }), "Data retention"]
|
|
38
|
+
}), /* @__PURE__ */ p(o, { children: [
|
|
39
|
+
"Active retention policies are enforced every hour. When a workspace is deleted, its BotManor data is erased after",
|
|
40
|
+
" ",
|
|
41
|
+
v ? `${v.endOfTermErasureGraceDays} days` : "the grace period",
|
|
42
|
+
"."
|
|
43
|
+
] })] }), /* @__PURE__ */ f(n, {
|
|
44
|
+
permission: t.POLICY_UPDATE,
|
|
45
|
+
children: /* @__PURE__ */ f(r, {
|
|
46
|
+
variant: "outline",
|
|
47
|
+
size: "sm",
|
|
48
|
+
leftIcon: /* @__PURE__ */ f(h, { size: 14 }),
|
|
49
|
+
onClick: () => void w(),
|
|
50
|
+
isLoading: C,
|
|
51
|
+
disabled: y || !v || v.activePolicyCount === 0,
|
|
52
|
+
children: "Apply now"
|
|
53
|
+
})
|
|
54
|
+
})]
|
|
55
|
+
})
|
|
56
|
+
}), /* @__PURE__ */ f(a, { children: y && !v ? /* @__PURE__ */ f("div", { className: "h-16 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg" }) : v ? /* @__PURE__ */ p("div", {
|
|
57
|
+
className: "grid grid-cols-1 sm:grid-cols-3 gap-4 text-sm",
|
|
58
|
+
children: [
|
|
59
|
+
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
|
|
60
|
+
className: "text-text-secondary",
|
|
61
|
+
children: "Run inputs and outputs"
|
|
62
|
+
}), /* @__PURE__ */ f("p", {
|
|
63
|
+
className: "font-medium",
|
|
64
|
+
"data-testid": "retention-execution-days",
|
|
65
|
+
children: _(v.executionDataRetentionDays, "Erased when the run ends")
|
|
66
|
+
})] }),
|
|
67
|
+
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
|
|
68
|
+
className: "text-text-secondary",
|
|
69
|
+
children: "Assistant conversations"
|
|
70
|
+
}), /* @__PURE__ */ f("p", {
|
|
71
|
+
className: "font-medium",
|
|
72
|
+
"data-testid": "retention-conversation-days",
|
|
73
|
+
children: _(v.conversationRetentionDays, "Erased when the run ends")
|
|
74
|
+
})] }),
|
|
75
|
+
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
|
|
76
|
+
className: "text-text-secondary",
|
|
77
|
+
children: "Last pass"
|
|
78
|
+
}), T ? /* @__PURE__ */ p("p", {
|
|
79
|
+
className: "font-medium flex flex-wrap items-center gap-2",
|
|
80
|
+
children: [/* @__PURE__ */ f("span", { children: e(T.finishedAt) }), /* @__PURE__ */ f(l, {
|
|
81
|
+
variant: T.status === "COMPLETED" ? "success" : "destructive",
|
|
82
|
+
children: T.status === "COMPLETED" ? `${T.executionsPurged + T.a2aMessagesPurged} erased` : "Failed"
|
|
83
|
+
})]
|
|
84
|
+
}) : /* @__PURE__ */ f("p", {
|
|
85
|
+
className: "font-medium",
|
|
86
|
+
children: v.activePolicyCount === 0 ? "No active retention policy" : "Not run yet"
|
|
87
|
+
})] })
|
|
88
|
+
]
|
|
89
|
+
}) : null })]
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
//#endregion
|
|
93
|
+
export { v as RetentionStatusPanel };
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=RetentionStatusPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RetentionStatusPanel.js","names":[],"sources":["../../../src/botlit/components/RetentionStatusPanel.tsx"],"sourcesContent":["/**\n * Retention status panel (BOFF-9661)\n * @module botlit/components\n *\n * Shows the retention periods in force for the workspace (the shortest of its\n * ACTIVE retention policies), the end-of-term erasure grace period and the\n * most recent retention passes, and lets a policy editor run a pass now.\n */\n\nimport type { FC } from 'react';\nimport { toast } from 'sonner';\nimport { Archive, RefreshCw } from 'lucide-react';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from './Card';\nimport { Button } from './Button';\nimport { Badge } from './Badge';\nimport { useApplyRetention, useRetentionStatus } from '../hooks';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { BOTMANOR_PERMISSIONS, BotmanorPermissionGate } from '../permissions';\n\nfunction describePeriod(days: number | null | undefined, zeroLabel: string): string {\n if (days === null || days === undefined) return 'Kept for the life of the workspace';\n if (days === 0) return zeroLabel;\n return `${days} day${days === 1 ? '' : 's'}`;\n}\n\nexport const RetentionStatusPanel: FC = () => {\n const { data: status, loading, error, refetch } = useRetentionStatus();\n const { apply, loading: applying } = useApplyRetention();\n\n const handleApply = async () => {\n try {\n const run = await apply();\n if (run.status === 'COMPLETED') {\n toast.success(\n `Retention applied: ${run.executionsPurged} run payloads and ${run.a2aMessagesPurged} agent messages erased`\n );\n } else {\n toast.error(run.errorMessage ?? 'The retention pass failed');\n }\n } catch (applyError) {\n toast.error(applyError instanceof Error ? applyError.message : 'Failed to apply retention');\n }\n await refetch();\n };\n\n if (error) {\n return null;\n }\n\n const lastRun = status?.recentRuns[0];\n\n return (\n <Card data-testid=\"retention-status-panel\">\n <CardHeader className=\"pb-2\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Archive size={18} />\n Data retention\n </CardTitle>\n <CardDescription>\n Active retention policies are enforced every hour. When a workspace is deleted, its\n BotManor data is erased after{' '}\n {status ? `${status.endOfTermErasureGraceDays} days` : 'the grace period'}.\n </CardDescription>\n </div>\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.POLICY_UPDATE}>\n <Button\n variant=\"outline\"\n size=\"sm\"\n leftIcon={<RefreshCw size={14} />}\n onClick={() => void handleApply()}\n isLoading={applying}\n disabled={loading || !status || status.activePolicyCount === 0}\n >\n Apply now\n </Button>\n </BotmanorPermissionGate>\n </div>\n </CardHeader>\n <CardContent>\n {loading && !status ? (\n <div className=\"h-16 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n ) : status ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">Run inputs and outputs</p>\n <p className=\"font-medium\" data-testid=\"retention-execution-days\">\n {describePeriod(status.executionDataRetentionDays, 'Erased when the run ends')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Assistant conversations</p>\n <p className=\"font-medium\" data-testid=\"retention-conversation-days\">\n {describePeriod(status.conversationRetentionDays, 'Erased when the run ends')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Last pass</p>\n {lastRun ? (\n <p className=\"font-medium flex flex-wrap items-center gap-2\">\n <span>{formatRelativeTime(lastRun.finishedAt)}</span>\n <Badge variant={lastRun.status === 'COMPLETED' ? 'success' : 'destructive'}>\n {lastRun.status === 'COMPLETED'\n ? `${lastRun.executionsPurged + lastRun.a2aMessagesPurged} erased`\n : 'Failed'}\n </Badge>\n </p>\n ) : (\n <p className=\"font-medium\">\n {status.activePolicyCount === 0 ? 'No active retention policy' : 'Not run yet'}\n </p>\n )}\n </div>\n </div>\n ) : null}\n </CardContent>\n </Card>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAmBA,SAAS,EAAe,GAAiC,GAA2B;AAGlF,QAFI,KAAS,OAAmC,uCAC5C,MAAS,IAAU,IAChB,GAAG,EAAK,MAAM,MAAS,IAAI,KAAK;;AAGzC,IAAa,UAAiC;CAC5C,IAAM,EAAE,MAAM,GAAQ,YAAS,UAAO,eAAY,GAAoB,EAChE,EAAE,UAAO,SAAS,MAAa,GAAmB,EAElD,IAAc,YAAY;AAC9B,MAAI;GACF,IAAM,IAAM,MAAM,GAAO;AACzB,GAAI,EAAI,WAAW,cACjB,EAAM,QACJ,sBAAsB,EAAI,iBAAiB,oBAAoB,EAAI,kBAAkB,wBACtF,GAED,EAAM,MAAM,EAAI,gBAAgB,4BAA4B;WAEvD,GAAY;AACnB,KAAM,MAAM,aAAsB,QAAQ,EAAW,UAAU,4BAA4B;;AAE7F,QAAM,GAAS;;AAGjB,KAAI,EACF,QAAO;CAGT,IAAM,IAAU,GAAQ,WAAW;AAEnC,QACE,kBAAC,GAAD;EAAM,eAAY;YAAlB,CACE,kBAAC,GAAD;GAAY,WAAU;aACpB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAW,WAAU;eAArB,CACE,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA,EAAA,iBAEX;QACZ,kBAAC,GAAD,EAAA,UAAA;KAAiB;KAEe;KAC7B,IAAS,GAAG,EAAO,0BAA0B,SAAS;KAAmB;KAC1D,EAAA,CAAA,CACd,EAAA,CAAA,EACN,kBAAC,GAAD;KAAwB,YAAY,EAAqB;eACvD,kBAAC,GAAD;MACE,SAAQ;MACR,MAAK;MACL,UAAU,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;MACjC,eAAe,KAAK,GAAa;MACjC,WAAW;MACX,UAAU,KAAW,CAAC,KAAU,EAAO,sBAAsB;gBAC9D;MAEQ,CAAA;KACc,CAAA,CACrB;;GACK,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,KAAW,CAAC,IACX,kBAAC,OAAD,EAAK,WAAU,+DAAgE,CAAA,GAC7E,IACF,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eAAsB;KAA0B,CAAA,EAC7D,kBAAC,KAAD;KAAG,WAAU;KAAc,eAAY;eACpC,EAAe,EAAO,4BAA4B,2BAA2B;KAC5E,CAAA,CACA,EAAA,CAAA;IACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eAAsB;KAA2B,CAAA,EAC9D,kBAAC,KAAD;KAAG,WAAU;KAAc,eAAY;eACpC,EAAe,EAAO,2BAA2B,2BAA2B;KAC3E,CAAA,CACA,EAAA,CAAA;IACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eAAsB;KAAa,CAAA,EAC/C,IACC,kBAAC,KAAD;KAAG,WAAU;eAAb,CACE,kBAAC,QAAD,EAAA,UAAO,EAAmB,EAAQ,WAAW,EAAQ,CAAA,EACrD,kBAAC,GAAD;MAAO,SAAS,EAAQ,WAAW,cAAc,YAAY;gBAC1D,EAAQ,WAAW,cAChB,GAAG,EAAQ,mBAAmB,EAAQ,kBAAkB,WACxD;MACE,CAAA,CACN;SAEJ,kBAAC,KAAD;KAAG,WAAU;eACV,EAAO,sBAAsB,IAAI,+BAA+B;KAC/D,CAAA,CAEF,EAAA,CAAA;IACF;OACJ,MACQ,CAAA,CACT"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ApplyBotmanorRetentionDocument as e, BotmanorRetentionStatusDocument as t } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useWorkspaceId as n, useWorkspaceMutation as r, useWorkspaceQuery as i } from "./internal/workspaceGateway.js";
|
|
3
|
+
//#region src/botlit/hooks/useRetention.ts
|
|
4
|
+
var a = {};
|
|
5
|
+
function o() {
|
|
6
|
+
return i({
|
|
7
|
+
document: t,
|
|
8
|
+
variables: a,
|
|
9
|
+
select: (e) => e?.botmanorRetentionStatus ?? null,
|
|
10
|
+
fallback: null,
|
|
11
|
+
enabled: !!n(),
|
|
12
|
+
errorMessage: "Failed to load retention status"
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function s() {
|
|
16
|
+
let [t, n] = r({
|
|
17
|
+
document: e,
|
|
18
|
+
variables: () => ({}),
|
|
19
|
+
select: (e) => e?.applyBotmanorRetention,
|
|
20
|
+
errorMessage: "Failed to apply retention"
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
apply: t,
|
|
24
|
+
loading: n.loading,
|
|
25
|
+
error: n.error
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { s as useApplyRetention, o as useRetentionStatus };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=useRetention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRetention.js","names":[],"sources":["../../../src/botlit/hooks/useRetention.ts"],"sourcesContent":["/**\n * Retention hooks (BOFF-9661)\n * @module botlit/hooks\n *\n * Reads the retention settings in force for the workspace and its recent\n * retention passes, and runs a pass on demand. Retention itself is enforced\n * hourly by wspace-botmanor-svc; `applyBotmanorRetention` only runs it now.\n */\n\nimport {\n ApplyBotmanorRetentionDocument,\n BotmanorRetentionStatusDocument,\n type ApplyBotmanorRetentionMutation,\n type BotmanorRetentionStatusQuery,\n} from '../../generated/wspace-operations';\nimport {\n useWorkspaceId,\n useWorkspaceMutation,\n useWorkspaceQuery,\n type QueryResult,\n} from './internal/workspaceGateway';\n\nconst NO_VARIABLES: Record<string, unknown> = {};\n\nexport interface RetentionRunRecord {\n id: string;\n trigger: string;\n status: string;\n executionDataRetentionDays?: number | null;\n conversationRetentionDays?: number | null;\n executionsPurged: number;\n a2aMessagesPurged: number;\n errorMessage?: string | null;\n startedAt: string;\n finishedAt: string;\n}\n\nexport interface RetentionStatusRecord {\n executionDataRetentionDays?: number | null;\n conversationRetentionDays?: number | null;\n activePolicyCount: number;\n endOfTermErasureGraceDays: number;\n recentRuns: RetentionRunRecord[];\n}\n\nexport function useRetentionStatus(): QueryResult<RetentionStatusRecord | null> {\n const workspaceId = useWorkspaceId();\n return useWorkspaceQuery<BotmanorRetentionStatusQuery, RetentionStatusRecord | null>({\n document: BotmanorRetentionStatusDocument,\n // No arguments: the workspace comes from the gateway-verified token.\n variables: NO_VARIABLES,\n select: (data) => data?.botmanorRetentionStatus ?? null,\n fallback: null,\n enabled: Boolean(workspaceId),\n errorMessage: 'Failed to load retention status',\n });\n}\n\nexport function useApplyRetention() {\n const [apply, state] = useWorkspaceMutation<\n ApplyBotmanorRetentionMutation,\n void,\n RetentionRunRecord\n >({\n document: ApplyBotmanorRetentionDocument,\n variables: () => ({}),\n select: (data) => data?.applyBotmanorRetention,\n errorMessage: 'Failed to apply retention',\n });\n return { apply, loading: state.loading, error: state.error };\n}\n"],"mappings":";;;AAsBA,IAAM,IAAwC,EAAE;AAuBhD,SAAgB,IAAgE;AAE9E,QAAO,EAA8E;EACnF,UAAU;EAEV,WAAW;EACX,SAAS,MAAS,GAAM,2BAA2B;EACnD,UAAU;EACV,SAAS,EAPS,GAAgB;EAQlC,cAAc;EACf,CAAC;;AAGJ,SAAgB,IAAoB;CAClC,IAAM,CAAC,GAAO,KAAS,EAIrB;EACA,UAAU;EACV,kBAAkB,EAAE;EACpB,SAAS,MAAS,GAAM;EACxB,cAAc;EACf,CAAC;AACF,QAAO;EAAE;EAAO,SAAS,EAAM;EAAS,OAAO,EAAM;EAAO"}
|
|
@@ -13,25 +13,26 @@ import { Input as m } from "../components/Input.js";
|
|
|
13
13
|
import { ErrorState as h } from "../components/ErrorState.js";
|
|
14
14
|
import { usePolicies as g } from "../hooks/usePolicies.js";
|
|
15
15
|
import "../hooks/index.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
16
|
+
import { RetentionStatusPanel as _ } from "../components/RetentionStatusPanel.js";
|
|
17
|
+
import { useState as v } from "react";
|
|
18
|
+
import { GlassCard as y, PagePurpose as b } from "@burdenoff/fe-libs/ui";
|
|
19
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
20
|
+
import { Link as C } from "react-router";
|
|
21
|
+
import { AlertTriangle as w, Clock as T, Database as E, DollarSign as D, Filter as O, MessageSquare as k, Plus as A, Search as j, Shield as M, Trash2 as N, Wrench as P } from "lucide-react";
|
|
21
22
|
//#region src/botlit/pages/PoliciesPage.tsx
|
|
22
|
-
var
|
|
23
|
+
var F = ({ type: e, className: t }) => {
|
|
23
24
|
switch (e) {
|
|
24
|
-
case "CONTENT": return /* @__PURE__ */
|
|
25
|
-
case "TOOL": return /* @__PURE__ */
|
|
26
|
-
case "DATA": return /* @__PURE__ */
|
|
27
|
-
case "RESPONSE": return /* @__PURE__ */
|
|
28
|
-
case "BUDGET": return /* @__PURE__ */
|
|
29
|
-
case "RATE_LIMIT": return /* @__PURE__ */
|
|
30
|
-
case "RETENTION": return /* @__PURE__ */
|
|
31
|
-
default: return /* @__PURE__ */
|
|
25
|
+
case "CONTENT": return /* @__PURE__ */ x(w, { className: t });
|
|
26
|
+
case "TOOL": return /* @__PURE__ */ x(P, { className: t });
|
|
27
|
+
case "DATA": return /* @__PURE__ */ x(E, { className: t });
|
|
28
|
+
case "RESPONSE": return /* @__PURE__ */ x(k, { className: t });
|
|
29
|
+
case "BUDGET": return /* @__PURE__ */ x(D, { className: t });
|
|
30
|
+
case "RATE_LIMIT": return /* @__PURE__ */ x(T, { className: t });
|
|
31
|
+
case "RETENTION": return /* @__PURE__ */ x(N, { className: t });
|
|
32
|
+
default: return /* @__PURE__ */ x(M, { className: t });
|
|
32
33
|
}
|
|
33
34
|
};
|
|
34
|
-
function
|
|
35
|
+
function I(e) {
|
|
35
36
|
return {
|
|
36
37
|
CONTENT: e("botlit.policies.typeLabel.CONTENT", "Content Safety"),
|
|
37
38
|
TOOL: e("botlit.policies.typeLabel.TOOL", "Tool Access"),
|
|
@@ -42,35 +43,36 @@ function F(e) {
|
|
|
42
43
|
RETENTION: e("botlit.policies.typeLabel.RETENTION", "Data Retention")
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
|
-
var
|
|
46
|
-
let
|
|
47
|
-
return /* @__PURE__ */
|
|
48
|
-
title:
|
|
49
|
-
description:
|
|
50
|
-
actions: /* @__PURE__ */
|
|
46
|
+
var L = () => {
|
|
47
|
+
let w = e(), { getPath: T } = t(), [E, D] = v(""), [k, N] = v(null), { data: P, loading: L, error: R, refetch: z } = g({ search: E }), B = I(w), V = k ? P.filter((e) => e.type === k) : P;
|
|
48
|
+
return /* @__PURE__ */ x(a, {
|
|
49
|
+
title: w("botlit.policies.title", "Policies & Guardrails"),
|
|
50
|
+
description: w("botlit.policies.description", "Configure safety rules, access controls, and operational limits"),
|
|
51
|
+
actions: /* @__PURE__ */ x(i, {
|
|
51
52
|
permission: r.POLICY_CREATE,
|
|
52
|
-
children: /* @__PURE__ */
|
|
53
|
-
leftIcon: /* @__PURE__ */
|
|
54
|
-
children: /* @__PURE__ */
|
|
55
|
-
to:
|
|
56
|
-
children:
|
|
53
|
+
children: /* @__PURE__ */ x(o, {
|
|
54
|
+
leftIcon: /* @__PURE__ */ x(A, { size: 16 }),
|
|
55
|
+
children: /* @__PURE__ */ x(C, {
|
|
56
|
+
to: T("policies/new"),
|
|
57
|
+
children: w("botlit.policies.actions.createPolicy", "Create Policy")
|
|
57
58
|
})
|
|
58
59
|
})
|
|
59
60
|
}),
|
|
60
|
-
children: /* @__PURE__ */
|
|
61
|
+
children: /* @__PURE__ */ S("div", {
|
|
61
62
|
className: "space-y-6",
|
|
62
63
|
children: [
|
|
63
|
-
/* @__PURE__ */ b
|
|
64
|
-
icon: /* @__PURE__ */
|
|
65
|
-
children:
|
|
64
|
+
/* @__PURE__ */ x(b, {
|
|
65
|
+
icon: /* @__PURE__ */ x(M, { size: 18 }),
|
|
66
|
+
children: w("botlit.policies.purpose", "Policies are the guardrails that keep your agents safe and within budget. Each policy enforces one kind of rule — blocking harmful content or PII, restricting which tools an agent may call, capping token spend, or rate-limiting requests. Define policies here and attach them to agents so every execution stays compliant and predictable.")
|
|
66
67
|
}),
|
|
67
|
-
/* @__PURE__ */ x(
|
|
68
|
+
/* @__PURE__ */ x(_, {}),
|
|
69
|
+
/* @__PURE__ */ S("div", {
|
|
68
70
|
className: "flex flex-wrap gap-2",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
70
|
-
variant:
|
|
71
|
+
children: [/* @__PURE__ */ x(o, {
|
|
72
|
+
variant: k === null ? "default" : "outline",
|
|
71
73
|
size: "sm",
|
|
72
|
-
onClick: () =>
|
|
73
|
-
children:
|
|
74
|
+
onClick: () => N(null),
|
|
75
|
+
children: w("botlit.store.filters.all", "All")
|
|
74
76
|
}), [
|
|
75
77
|
"CONTENT",
|
|
76
78
|
"TOOL",
|
|
@@ -79,34 +81,34 @@ var I = () => {
|
|
|
79
81
|
"BUDGET",
|
|
80
82
|
"RATE_LIMIT",
|
|
81
83
|
"RETENTION"
|
|
82
|
-
].map((e) => /* @__PURE__ */
|
|
83
|
-
variant:
|
|
84
|
+
].map((e) => /* @__PURE__ */ x(o, {
|
|
85
|
+
variant: k === e ? "default" : "outline",
|
|
84
86
|
size: "sm",
|
|
85
|
-
onClick: () =>
|
|
86
|
-
leftIcon: /* @__PURE__ */
|
|
87
|
+
onClick: () => N(e),
|
|
88
|
+
leftIcon: /* @__PURE__ */ x(F, {
|
|
87
89
|
type: e,
|
|
88
90
|
className: "h-4 w-4"
|
|
89
91
|
}),
|
|
90
|
-
children:
|
|
92
|
+
children: B[e]
|
|
91
93
|
}, e))]
|
|
92
94
|
}),
|
|
93
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ S("div", {
|
|
94
96
|
className: "flex items-center gap-4",
|
|
95
|
-
children: [/* @__PURE__ */
|
|
97
|
+
children: [/* @__PURE__ */ S("div", {
|
|
96
98
|
className: "relative flex-1 max-w-md",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
98
|
-
placeholder:
|
|
99
|
-
value:
|
|
100
|
-
onChange: (e) =>
|
|
99
|
+
children: [/* @__PURE__ */ x(j, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary" }), /* @__PURE__ */ x(m, {
|
|
100
|
+
placeholder: w("botlit.policies.searchPlaceholder", "Search policies..."),
|
|
101
|
+
value: E,
|
|
102
|
+
onChange: (e) => D(e.target.value),
|
|
101
103
|
className: "pl-9"
|
|
102
104
|
})]
|
|
103
|
-
}), /* @__PURE__ */
|
|
105
|
+
}), /* @__PURE__ */ x(o, {
|
|
104
106
|
variant: "outline",
|
|
105
|
-
leftIcon: /* @__PURE__ */
|
|
106
|
-
children:
|
|
107
|
+
leftIcon: /* @__PURE__ */ x(O, { size: 16 }),
|
|
108
|
+
children: w("botlit.policies.actions.moreFilters", "More Filters")
|
|
107
109
|
})]
|
|
108
110
|
}),
|
|
109
|
-
|
|
111
|
+
L ? /* @__PURE__ */ x("div", {
|
|
110
112
|
className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",
|
|
111
113
|
children: [
|
|
112
114
|
1,
|
|
@@ -115,47 +117,47 @@ var I = () => {
|
|
|
115
117
|
4,
|
|
116
118
|
5,
|
|
117
119
|
6
|
|
118
|
-
].map((e) => /* @__PURE__ */
|
|
119
|
-
}) :
|
|
120
|
+
].map((e) => /* @__PURE__ */ x("div", { className: "h-40 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg" }, e))
|
|
121
|
+
}) : R ? /* @__PURE__ */ x(h, {
|
|
120
122
|
resource: "policies",
|
|
121
|
-
error:
|
|
122
|
-
onRetry: () => void
|
|
123
|
-
}) :
|
|
123
|
+
error: R,
|
|
124
|
+
onRetry: () => void z()
|
|
125
|
+
}) : V.length > 0 ? /* @__PURE__ */ x("div", {
|
|
124
126
|
className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",
|
|
125
|
-
children:
|
|
126
|
-
to:
|
|
127
|
-
children: /* @__PURE__ */
|
|
127
|
+
children: V.map((e) => /* @__PURE__ */ x(C, {
|
|
128
|
+
to: T(`policies/${e.id}`),
|
|
129
|
+
children: /* @__PURE__ */ S(s, {
|
|
128
130
|
className: "h-full hover:border-primary/50 transition-colors cursor-pointer",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
131
|
+
children: [/* @__PURE__ */ x(l, {
|
|
130
132
|
className: "pb-2",
|
|
131
|
-
children: /* @__PURE__ */
|
|
133
|
+
children: /* @__PURE__ */ S("div", {
|
|
132
134
|
className: "flex items-start justify-between",
|
|
133
|
-
children: [/* @__PURE__ */
|
|
135
|
+
children: [/* @__PURE__ */ S("div", {
|
|
134
136
|
className: "flex items-center gap-3",
|
|
135
|
-
children: [/* @__PURE__ */
|
|
137
|
+
children: [/* @__PURE__ */ x("div", {
|
|
136
138
|
className: `p-2 rounded-lg ${e.type === "CONTENT" ? "bg-status-error-bg-subtle" : e.type === "TOOL" ? "bg-status-info-bg-subtle" : e.type === "BUDGET" ? "bg-status-warning-bg-subtle" : "bg-action-primary-bg/10"}`,
|
|
137
|
-
children: /* @__PURE__ */
|
|
139
|
+
children: /* @__PURE__ */ x(F, {
|
|
138
140
|
type: e.type,
|
|
139
141
|
className: `h-5 w-5 ${e.type === "CONTENT" ? "text-status-error-text" : e.type === "TOOL" ? "text-status-info-text" : e.type === "BUDGET" ? "text-status-warning-text" : "text-primary"}`
|
|
140
142
|
})
|
|
141
|
-
}), /* @__PURE__ */
|
|
143
|
+
}), /* @__PURE__ */ x("div", { children: /* @__PURE__ */ x(u, {
|
|
142
144
|
className: "text-base",
|
|
143
145
|
children: e.name
|
|
144
146
|
}) })]
|
|
145
|
-
}), /* @__PURE__ */
|
|
147
|
+
}), /* @__PURE__ */ x(f, { status: e.status })]
|
|
146
148
|
})
|
|
147
|
-
}), /* @__PURE__ */
|
|
149
|
+
}), /* @__PURE__ */ x(c, { children: /* @__PURE__ */ S("div", {
|
|
148
150
|
className: "space-y-3",
|
|
149
|
-
children: [/* @__PURE__ */
|
|
151
|
+
children: [/* @__PURE__ */ x("p", {
|
|
150
152
|
className: "text-sm text-text-secondary line-clamp-2",
|
|
151
153
|
children: e.description
|
|
152
|
-
}), /* @__PURE__ */
|
|
154
|
+
}), /* @__PURE__ */ S("div", {
|
|
153
155
|
className: "flex items-center justify-between",
|
|
154
|
-
children: [/* @__PURE__ */
|
|
156
|
+
children: [/* @__PURE__ */ x(d, {
|
|
155
157
|
variant: "outline",
|
|
156
158
|
className: "text-xs",
|
|
157
|
-
children:
|
|
158
|
-
}), /* @__PURE__ */
|
|
159
|
+
children: B[e.type]
|
|
160
|
+
}), /* @__PURE__ */ x("span", {
|
|
159
161
|
className: "text-xs text-text-secondary",
|
|
160
162
|
children: n(e.createdAt)
|
|
161
163
|
})]
|
|
@@ -163,65 +165,65 @@ var I = () => {
|
|
|
163
165
|
}) })]
|
|
164
166
|
})
|
|
165
167
|
}, e.id))
|
|
166
|
-
}) : /* @__PURE__ */
|
|
168
|
+
}) : /* @__PURE__ */ x(p, {
|
|
167
169
|
illustration: "empty-generic",
|
|
168
|
-
title:
|
|
169
|
-
description:
|
|
170
|
-
action: /* @__PURE__ */
|
|
170
|
+
title: k ? w("botlit.policies.empty.titleNoType", "No {{type}} policies", { type: B[k] }) : w("botlit.policies.empty.title", "No policies"),
|
|
171
|
+
description: w("botlit.policies.empty.description", "Create policies to enforce safety rules and operational limits across your agents."),
|
|
172
|
+
action: /* @__PURE__ */ x(i, {
|
|
171
173
|
permission: r.POLICY_CREATE,
|
|
172
|
-
children: /* @__PURE__ */
|
|
173
|
-
leftIcon: /* @__PURE__ */
|
|
174
|
-
children: /* @__PURE__ */
|
|
175
|
-
to:
|
|
176
|
-
children:
|
|
174
|
+
children: /* @__PURE__ */ x(o, {
|
|
175
|
+
leftIcon: /* @__PURE__ */ x(A, { size: 16 }),
|
|
176
|
+
children: /* @__PURE__ */ x(C, {
|
|
177
|
+
to: T("policies/new"),
|
|
178
|
+
children: w("botlit.policies.actions.createPolicy", "Create Policy")
|
|
177
179
|
})
|
|
178
180
|
})
|
|
179
181
|
})
|
|
180
182
|
}),
|
|
181
|
-
/* @__PURE__ */
|
|
183
|
+
/* @__PURE__ */ S(y, {
|
|
182
184
|
treatment: "glass",
|
|
183
185
|
glow: !0,
|
|
184
186
|
className: "p-6",
|
|
185
|
-
children: [/* @__PURE__ */
|
|
187
|
+
children: [/* @__PURE__ */ S("div", {
|
|
186
188
|
className: "mb-4",
|
|
187
|
-
children: [/* @__PURE__ */
|
|
189
|
+
children: [/* @__PURE__ */ x("h2", {
|
|
188
190
|
className: "text-lg font-semibold tracking-tight text-text-primary",
|
|
189
|
-
children:
|
|
190
|
-
}), /* @__PURE__ */
|
|
191
|
+
children: w("botlit.policies.overview.title", "Policy Types")
|
|
192
|
+
}), /* @__PURE__ */ x("p", {
|
|
191
193
|
className: "text-sm text-text-secondary",
|
|
192
|
-
children:
|
|
194
|
+
children: w("botlit.policies.overview.description", "Different policy types control different aspects of agent behavior")
|
|
193
195
|
})]
|
|
194
|
-
}), /* @__PURE__ */
|
|
196
|
+
}), /* @__PURE__ */ x("div", {
|
|
195
197
|
className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4",
|
|
196
198
|
children: [
|
|
197
199
|
{
|
|
198
200
|
type: "CONTENT",
|
|
199
|
-
desc:
|
|
201
|
+
desc: w("botlit.policies.overview.contentDescription", "Detect and block harmful content, jailbreaks, and PII")
|
|
200
202
|
},
|
|
201
203
|
{
|
|
202
204
|
type: "TOOL",
|
|
203
|
-
desc:
|
|
205
|
+
desc: w("botlit.policies.overview.toolDescription", "Control which tools agents can access and use")
|
|
204
206
|
},
|
|
205
207
|
{
|
|
206
208
|
type: "BUDGET",
|
|
207
|
-
desc:
|
|
209
|
+
desc: w("botlit.policies.overview.budgetDescription", "Set token limits and cost caps per execution")
|
|
208
210
|
},
|
|
209
211
|
{
|
|
210
212
|
type: "RATE_LIMIT",
|
|
211
|
-
desc:
|
|
213
|
+
desc: w("botlit.policies.overview.rateLimitDescription", "Limit request frequency to prevent abuse")
|
|
212
214
|
}
|
|
213
|
-
].map((e) => /* @__PURE__ */
|
|
215
|
+
].map((e) => /* @__PURE__ */ S("div", {
|
|
214
216
|
className: "p-4 border border-border-subtle rounded-lg",
|
|
215
|
-
children: [/* @__PURE__ */
|
|
217
|
+
children: [/* @__PURE__ */ S("div", {
|
|
216
218
|
className: "flex items-center gap-2 mb-2",
|
|
217
|
-
children: [/* @__PURE__ */
|
|
219
|
+
children: [/* @__PURE__ */ x(F, {
|
|
218
220
|
type: e.type,
|
|
219
221
|
className: "h-5 w-5 text-primary"
|
|
220
|
-
}), /* @__PURE__ */
|
|
222
|
+
}), /* @__PURE__ */ x("span", {
|
|
221
223
|
className: "font-medium",
|
|
222
|
-
children:
|
|
224
|
+
children: B[e.type]
|
|
223
225
|
})]
|
|
224
|
-
}), /* @__PURE__ */
|
|
226
|
+
}), /* @__PURE__ */ x("p", {
|
|
225
227
|
className: "text-sm text-text-secondary",
|
|
226
228
|
children: e.desc
|
|
227
229
|
})]
|
|
@@ -233,6 +235,6 @@ var I = () => {
|
|
|
233
235
|
});
|
|
234
236
|
};
|
|
235
237
|
//#endregion
|
|
236
|
-
export {
|
|
238
|
+
export { L as PoliciesPage };
|
|
237
239
|
|
|
238
240
|
//# sourceMappingURL=PoliciesPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PoliciesPage.js","names":[],"sources":["../../../src/botlit/pages/PoliciesPage.tsx"],"sourcesContent":["/**\n * Policies Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useState } from 'react';\nimport { Link } from 'react-router';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n Plus,\n Search as SearchIcon,\n Filter,\n Shield,\n Wrench,\n Database,\n MessageSquare,\n DollarSign,\n Clock,\n Trash2,\n AlertTriangle,\n} from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardHeader, CardTitle, CardContent } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input } from '../components/Input';\nimport { Badge, StatusBadge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { ErrorState } from '../components/ErrorState';\nimport { usePolicies, type PolicyRecord } from '../hooks';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { PolicyType } from '../types';\nimport { BOTMANOR_PERMISSIONS, BotmanorPermissionGate } from '../permissions';\n\nconst PolicyTypeIcon: FC<{ type: string; className?: string }> = ({ type, className }) => {\n switch (type) {\n case 'CONTENT':\n return <AlertTriangle className={className} />;\n case 'TOOL':\n return <Wrench className={className} />;\n case 'DATA':\n return <Database className={className} />;\n case 'RESPONSE':\n return <MessageSquare className={className} />;\n case 'BUDGET':\n return <DollarSign className={className} />;\n case 'RATE_LIMIT':\n return <Clock className={className} />;\n case 'RETENTION':\n return <Trash2 className={className} />;\n default:\n return <Shield className={className} />;\n }\n};\n\nfunction usePolicyTypeLabel(t: ReturnType<typeof useTr>): Record<string, string> {\n return {\n CONTENT: t('botlit.policies.typeLabel.CONTENT', 'Content Safety'),\n TOOL: t('botlit.policies.typeLabel.TOOL', 'Tool Access'),\n DATA: t('botlit.policies.typeLabel.DATA', 'Data Access'),\n // New key: the platform i18n store already holds a row for the old\n // `typeLabel.RESPONSE` key (\"Response Format\") and that row wins.\n RESPONSE: t('botlit.policies.typeLabel.RESPONSE_MODELS', 'Response & Models'),\n BUDGET: t('botlit.policies.typeLabel.BUDGET', 'Budget Limits'),\n RATE_LIMIT: t('botlit.policies.typeLabel.RATE_LIMIT', 'Rate Limiting'),\n RETENTION: t('botlit.policies.typeLabel.RETENTION', 'Data Retention'),\n };\n}\n\nexport const PoliciesPage: FC = () => {\n const t = useTr();\n const { getPath } = useBotlit();\n const [search, setSearch] = useState('');\n const [selectedType, setSelectedType] = useState<PolicyType | null>(null);\n const { data: policies, loading, error, refetch } = usePolicies({ search });\n const policyTypeLabel = usePolicyTypeLabel(t);\n\n const filteredPolicies = selectedType\n ? policies.filter((policy) => policy.type === selectedType)\n : policies;\n\n const policyTypes: PolicyType[] = [\n 'CONTENT',\n 'TOOL',\n 'DATA',\n 'RESPONSE',\n 'BUDGET',\n 'RATE_LIMIT',\n 'RETENTION',\n ];\n\n return (\n <PageLayout\n title={t('botlit.policies.title', 'Policies & Guardrails')}\n description={t(\n 'botlit.policies.description',\n 'Configure safety rules, access controls, and operational limits'\n )}\n actions={\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.POLICY_CREATE}>\n <Button leftIcon={<Plus size={16} />}>\n <Link to={getPath('policies/new')}>\n {t('botlit.policies.actions.createPolicy', 'Create Policy')}\n </Link>\n </Button>\n </BotmanorPermissionGate>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Shield size={18} />}>\n {t(\n 'botlit.policies.purpose',\n 'Policies are the guardrails that keep your agents safe and within budget. Each policy enforces one kind of rule — blocking harmful content or PII, restricting which tools an agent may call, capping token spend, or rate-limiting requests. Define policies here and attach them to agents so every execution stays compliant and predictable.'\n )}\n </PagePurpose>\n\n {/* Policy Type Filters */}\n <div className=\"flex flex-wrap gap-2\">\n <Button\n variant={selectedType === null ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setSelectedType(null)}\n >\n {t('botlit.store.filters.all', 'All')}\n </Button>\n {policyTypes.map((type) => (\n <Button\n key={type}\n variant={selectedType === type ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setSelectedType(type)}\n leftIcon={<PolicyTypeIcon type={type} className=\"h-4 w-4\" />}\n >\n {policyTypeLabel[type]}\n </Button>\n ))}\n </div>\n\n {/* Search */}\n <div className=\"flex items-center gap-4\">\n <div className=\"relative flex-1 max-w-md\">\n <SearchIcon className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder={t('botlit.policies.searchPlaceholder', 'Search policies...')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <Button variant=\"outline\" leftIcon={<Filter size={16} />}>\n {t('botlit.policies.actions.moreFilters', 'More Filters')}\n </Button>\n </div>\n\n {/* Policies List */}\n {loading ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {[1, 2, 3, 4, 5, 6].map((i) => (\n <div\n key={i}\n className=\"h-40 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\"\n />\n ))}\n </div>\n ) : error ? (\n <ErrorState resource=\"policies\" error={error} onRetry={() => void refetch()} />\n ) : filteredPolicies.length > 0 ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {filteredPolicies.map((policy: PolicyRecord) => (\n <Link key={policy.id} to={getPath(`policies/${policy.id}`)}>\n <Card className=\"h-full hover:border-primary/50 transition-colors cursor-pointer\">\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div\n className={`p-2 rounded-lg ${\n policy.type === 'CONTENT'\n ? 'bg-status-error-bg-subtle'\n : policy.type === 'TOOL'\n ? 'bg-status-info-bg-subtle'\n : policy.type === 'BUDGET'\n ? 'bg-status-warning-bg-subtle'\n : 'bg-action-primary-bg/10'\n }`}\n >\n <PolicyTypeIcon\n type={policy.type}\n className={`h-5 w-5 ${\n policy.type === 'CONTENT'\n ? 'text-status-error-text'\n : policy.type === 'TOOL'\n ? 'text-status-info-text'\n : policy.type === 'BUDGET'\n ? 'text-status-warning-text'\n : 'text-primary'\n }`}\n />\n </div>\n <div>\n <CardTitle className=\"text-base\">{policy.name}</CardTitle>\n </div>\n </div>\n <StatusBadge status={policy.status} />\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary line-clamp-2\">\n {policy.description}\n </p>\n\n {/* Type Badge */}\n <div className=\"flex items-center justify-between\">\n <Badge variant=\"outline\" className=\"text-xs\">\n {policyTypeLabel[policy.type]}\n </Badge>\n <span className=\"text-xs text-text-secondary\">\n {formatRelativeTime(policy.createdAt)}\n </span>\n </div>\n </div>\n </CardContent>\n </Card>\n </Link>\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title={\n selectedType\n ? t('botlit.policies.empty.titleNoType', 'No {{type}} policies', {\n type: policyTypeLabel[selectedType],\n })\n : t('botlit.policies.empty.title', 'No policies')\n }\n description={t(\n 'botlit.policies.empty.description',\n 'Create policies to enforce safety rules and operational limits across your agents.'\n )}\n action={\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.POLICY_CREATE}>\n <Button leftIcon={<Plus size={16} />}>\n <Link to={getPath('policies/new')}>\n {t('botlit.policies.actions.createPolicy', 'Create Policy')}\n </Link>\n </Button>\n </BotmanorPermissionGate>\n }\n />\n )}\n\n {/* Policy Types Guide */}\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <div className=\"mb-4\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary\">\n {t('botlit.policies.overview.title', 'Policy Types')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'botlit.policies.overview.description',\n 'Different policy types control different aspects of agent behavior'\n )}\n </p>\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n type: 'CONTENT' as PolicyType,\n desc: t(\n 'botlit.policies.overview.contentDescription',\n 'Detect and block harmful content, jailbreaks, and PII'\n ),\n },\n {\n type: 'TOOL' as PolicyType,\n desc: t(\n 'botlit.policies.overview.toolDescription',\n 'Control which tools agents can access and use'\n ),\n },\n {\n type: 'BUDGET' as PolicyType,\n desc: t(\n 'botlit.policies.overview.budgetDescription',\n 'Set token limits and cost caps per execution'\n ),\n },\n {\n type: 'RATE_LIMIT' as PolicyType,\n desc: t(\n 'botlit.policies.overview.rateLimitDescription',\n 'Limit request frequency to prevent abuse'\n ),\n },\n ].map((item) => (\n <div key={item.type} className=\"p-4 border border-border-subtle rounded-lg\">\n <div className=\"flex items-center gap-2 mb-2\">\n <PolicyTypeIcon type={item.type} className=\"h-5 w-5 text-primary\" />\n <span className=\"font-medium\">{policyTypeLabel[item.type]}</span>\n </div>\n <p className=\"text-sm text-text-secondary\">{item.desc}</p>\n </div>\n ))}\n </div>\n </GlassCard>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PoliciesPage.js","names":[],"sources":["../../../src/botlit/pages/PoliciesPage.tsx"],"sourcesContent":["/**\n * Policies Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useState } from 'react';\nimport { Link } from 'react-router';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n Plus,\n Search as SearchIcon,\n Filter,\n Shield,\n Wrench,\n Database,\n MessageSquare,\n DollarSign,\n Clock,\n Trash2,\n AlertTriangle,\n} from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardHeader, CardTitle, CardContent } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input } from '../components/Input';\nimport { Badge, StatusBadge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { ErrorState } from '../components/ErrorState';\nimport { usePolicies, type PolicyRecord } from '../hooks';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { PolicyType } from '../types';\nimport { BOTMANOR_PERMISSIONS, BotmanorPermissionGate } from '../permissions';\nimport { RetentionStatusPanel } from '../components/RetentionStatusPanel';\n\nconst PolicyTypeIcon: FC<{ type: string; className?: string }> = ({ type, className }) => {\n switch (type) {\n case 'CONTENT':\n return <AlertTriangle className={className} />;\n case 'TOOL':\n return <Wrench className={className} />;\n case 'DATA':\n return <Database className={className} />;\n case 'RESPONSE':\n return <MessageSquare className={className} />;\n case 'BUDGET':\n return <DollarSign className={className} />;\n case 'RATE_LIMIT':\n return <Clock className={className} />;\n case 'RETENTION':\n return <Trash2 className={className} />;\n default:\n return <Shield className={className} />;\n }\n};\n\nfunction usePolicyTypeLabel(t: ReturnType<typeof useTr>): Record<string, string> {\n return {\n CONTENT: t('botlit.policies.typeLabel.CONTENT', 'Content Safety'),\n TOOL: t('botlit.policies.typeLabel.TOOL', 'Tool Access'),\n DATA: t('botlit.policies.typeLabel.DATA', 'Data Access'),\n // New key: the platform i18n store already holds a row for the old\n // `typeLabel.RESPONSE` key (\"Response Format\") and that row wins.\n RESPONSE: t('botlit.policies.typeLabel.RESPONSE_MODELS', 'Response & Models'),\n BUDGET: t('botlit.policies.typeLabel.BUDGET', 'Budget Limits'),\n RATE_LIMIT: t('botlit.policies.typeLabel.RATE_LIMIT', 'Rate Limiting'),\n RETENTION: t('botlit.policies.typeLabel.RETENTION', 'Data Retention'),\n };\n}\n\nexport const PoliciesPage: FC = () => {\n const t = useTr();\n const { getPath } = useBotlit();\n const [search, setSearch] = useState('');\n const [selectedType, setSelectedType] = useState<PolicyType | null>(null);\n const { data: policies, loading, error, refetch } = usePolicies({ search });\n const policyTypeLabel = usePolicyTypeLabel(t);\n\n const filteredPolicies = selectedType\n ? policies.filter((policy) => policy.type === selectedType)\n : policies;\n\n const policyTypes: PolicyType[] = [\n 'CONTENT',\n 'TOOL',\n 'DATA',\n 'RESPONSE',\n 'BUDGET',\n 'RATE_LIMIT',\n 'RETENTION',\n ];\n\n return (\n <PageLayout\n title={t('botlit.policies.title', 'Policies & Guardrails')}\n description={t(\n 'botlit.policies.description',\n 'Configure safety rules, access controls, and operational limits'\n )}\n actions={\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.POLICY_CREATE}>\n <Button leftIcon={<Plus size={16} />}>\n <Link to={getPath('policies/new')}>\n {t('botlit.policies.actions.createPolicy', 'Create Policy')}\n </Link>\n </Button>\n </BotmanorPermissionGate>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Shield size={18} />}>\n {t(\n 'botlit.policies.purpose',\n 'Policies are the guardrails that keep your agents safe and within budget. Each policy enforces one kind of rule — blocking harmful content or PII, restricting which tools an agent may call, capping token spend, or rate-limiting requests. Define policies here and attach them to agents so every execution stays compliant and predictable.'\n )}\n </PagePurpose>\n\n <RetentionStatusPanel />\n\n {/* Policy Type Filters */}\n <div className=\"flex flex-wrap gap-2\">\n <Button\n variant={selectedType === null ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setSelectedType(null)}\n >\n {t('botlit.store.filters.all', 'All')}\n </Button>\n {policyTypes.map((type) => (\n <Button\n key={type}\n variant={selectedType === type ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setSelectedType(type)}\n leftIcon={<PolicyTypeIcon type={type} className=\"h-4 w-4\" />}\n >\n {policyTypeLabel[type]}\n </Button>\n ))}\n </div>\n\n {/* Search */}\n <div className=\"flex items-center gap-4\">\n <div className=\"relative flex-1 max-w-md\">\n <SearchIcon className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder={t('botlit.policies.searchPlaceholder', 'Search policies...')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <Button variant=\"outline\" leftIcon={<Filter size={16} />}>\n {t('botlit.policies.actions.moreFilters', 'More Filters')}\n </Button>\n </div>\n\n {/* Policies List */}\n {loading ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {[1, 2, 3, 4, 5, 6].map((i) => (\n <div\n key={i}\n className=\"h-40 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\"\n />\n ))}\n </div>\n ) : error ? (\n <ErrorState resource=\"policies\" error={error} onRetry={() => void refetch()} />\n ) : filteredPolicies.length > 0 ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {filteredPolicies.map((policy: PolicyRecord) => (\n <Link key={policy.id} to={getPath(`policies/${policy.id}`)}>\n <Card className=\"h-full hover:border-primary/50 transition-colors cursor-pointer\">\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div\n className={`p-2 rounded-lg ${\n policy.type === 'CONTENT'\n ? 'bg-status-error-bg-subtle'\n : policy.type === 'TOOL'\n ? 'bg-status-info-bg-subtle'\n : policy.type === 'BUDGET'\n ? 'bg-status-warning-bg-subtle'\n : 'bg-action-primary-bg/10'\n }`}\n >\n <PolicyTypeIcon\n type={policy.type}\n className={`h-5 w-5 ${\n policy.type === 'CONTENT'\n ? 'text-status-error-text'\n : policy.type === 'TOOL'\n ? 'text-status-info-text'\n : policy.type === 'BUDGET'\n ? 'text-status-warning-text'\n : 'text-primary'\n }`}\n />\n </div>\n <div>\n <CardTitle className=\"text-base\">{policy.name}</CardTitle>\n </div>\n </div>\n <StatusBadge status={policy.status} />\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary line-clamp-2\">\n {policy.description}\n </p>\n\n {/* Type Badge */}\n <div className=\"flex items-center justify-between\">\n <Badge variant=\"outline\" className=\"text-xs\">\n {policyTypeLabel[policy.type]}\n </Badge>\n <span className=\"text-xs text-text-secondary\">\n {formatRelativeTime(policy.createdAt)}\n </span>\n </div>\n </div>\n </CardContent>\n </Card>\n </Link>\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title={\n selectedType\n ? t('botlit.policies.empty.titleNoType', 'No {{type}} policies', {\n type: policyTypeLabel[selectedType],\n })\n : t('botlit.policies.empty.title', 'No policies')\n }\n description={t(\n 'botlit.policies.empty.description',\n 'Create policies to enforce safety rules and operational limits across your agents.'\n )}\n action={\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.POLICY_CREATE}>\n <Button leftIcon={<Plus size={16} />}>\n <Link to={getPath('policies/new')}>\n {t('botlit.policies.actions.createPolicy', 'Create Policy')}\n </Link>\n </Button>\n </BotmanorPermissionGate>\n }\n />\n )}\n\n {/* Policy Types Guide */}\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <div className=\"mb-4\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary\">\n {t('botlit.policies.overview.title', 'Policy Types')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'botlit.policies.overview.description',\n 'Different policy types control different aspects of agent behavior'\n )}\n </p>\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n type: 'CONTENT' as PolicyType,\n desc: t(\n 'botlit.policies.overview.contentDescription',\n 'Detect and block harmful content, jailbreaks, and PII'\n ),\n },\n {\n type: 'TOOL' as PolicyType,\n desc: t(\n 'botlit.policies.overview.toolDescription',\n 'Control which tools agents can access and use'\n ),\n },\n {\n type: 'BUDGET' as PolicyType,\n desc: t(\n 'botlit.policies.overview.budgetDescription',\n 'Set token limits and cost caps per execution'\n ),\n },\n {\n type: 'RATE_LIMIT' as PolicyType,\n desc: t(\n 'botlit.policies.overview.rateLimitDescription',\n 'Limit request frequency to prevent abuse'\n ),\n },\n ].map((item) => (\n <div key={item.type} className=\"p-4 border border-border-subtle rounded-lg\">\n <div className=\"flex items-center gap-2 mb-2\">\n <PolicyTypeIcon type={item.type} className=\"h-5 w-5 text-primary\" />\n <span className=\"font-medium\">{policyTypeLabel[item.type]}</span>\n </div>\n <p className=\"text-sm text-text-secondary\">{item.desc}</p>\n </div>\n ))}\n </div>\n </GlassCard>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAqCA,IAAM,KAA4D,EAAE,SAAM,mBAAgB;AACxF,SAAQ,GAAR;EACE,KAAK,UACH,QAAO,kBAAC,GAAD,EAA0B,cAAa,CAAA;EAChD,KAAK,OACH,QAAO,kBAAC,GAAD,EAAmB,cAAa,CAAA;EACzC,KAAK,OACH,QAAO,kBAAC,GAAD,EAAqB,cAAa,CAAA;EAC3C,KAAK,WACH,QAAO,kBAAC,GAAD,EAA0B,cAAa,CAAA;EAChD,KAAK,SACH,QAAO,kBAAC,GAAD,EAAuB,cAAa,CAAA;EAC7C,KAAK,aACH,QAAO,kBAAC,GAAD,EAAkB,cAAa,CAAA;EACxC,KAAK,YACH,QAAO,kBAAC,GAAD,EAAmB,cAAa,CAAA;EACzC,QACE,QAAO,kBAAC,GAAD,EAAmB,cAAa,CAAA;;;AAI7C,SAAS,EAAmB,GAAqD;AAC/E,QAAO;EACL,SAAS,EAAE,qCAAqC,iBAAiB;EACjE,MAAM,EAAE,kCAAkC,cAAc;EACxD,MAAM,EAAE,kCAAkC,cAAc;EAGxD,UAAU,EAAE,6CAA6C,oBAAoB;EAC7E,QAAQ,EAAE,oCAAoC,gBAAgB;EAC9D,YAAY,EAAE,wCAAwC,gBAAgB;EACtE,WAAW,EAAE,uCAAuC,iBAAiB;EACtE;;AAGH,IAAa,UAAyB;CACpC,IAAM,IAAI,GAAO,EACX,EAAE,eAAY,GAAW,EACzB,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAc,KAAmB,EAA4B,KAAK,EACnE,EAAE,MAAM,GAAU,YAAS,UAAO,eAAY,EAAY,EAAE,WAAQ,CAAC,EACrE,IAAkB,EAAmB,EAAE,EAEvC,IAAmB,IACrB,EAAS,QAAQ,MAAW,EAAO,SAAS,EAAa,GACzD;AAYJ,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,yBAAyB,wBAAwB;EAC1D,aAAa,EACX,+BACA,kEACD;EACD,SACE,kBAAC,GAAD;GAAwB,YAAY,EAAqB;aACvD,kBAAC,GAAD;IAAQ,UAAU,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;cAClC,kBAAC,GAAD;KAAM,IAAI,EAAQ,eAAe;eAC9B,EAAE,wCAAwC,gBAAgB;KACtD,CAAA;IACA,CAAA;GACc,CAAA;YAG3B,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KAAa,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;eACpC,EACC,2BACA,mVACD;KACW,CAAA;IAEd,kBAAC,GAAD,EAAwB,CAAA;IAGxB,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,SAAS,MAAiB,OAAO,YAAY;MAC7C,MAAK;MACL,eAAe,EAAgB,KAAK;gBAEnC,EAAE,4BAA4B,MAAM;MAC9B,CAAA,EA7CiB;MAChC;MACA;MACA;MACA;MACA;MACA;MACA;MACD,CAsCoB,KAAK,MAChB,kBAAC,GAAD;MAEE,SAAS,MAAiB,IAAO,YAAY;MAC7C,MAAK;MACL,eAAe,EAAgB,EAAK;MACpC,UAAU,kBAAC,GAAD;OAAsB;OAAM,WAAU;OAAY,CAAA;gBAE3D,EAAgB;MACV,EAPF,EAOE,CACT,CACE;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAY,WAAU,wEAAyE,CAAA,EAC/F,kBAAC,GAAD;OACE,aAAa,EAAE,qCAAqC,qBAAqB;OACzE,OAAO;OACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;OAC1C,WAAU;OACV,CAAA,CACE;SACN,kBAAC,GAAD;MAAQ,SAAQ;MAAU,UAAU,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;gBACrD,EAAE,uCAAuC,eAAe;MAClD,CAAA,CACL;;IAGL,IACC,kBAAC,OAAD;KAAK,WAAU;eACZ;MAAC;MAAG;MAAG;MAAG;MAAG;MAAG;MAAE,CAAC,KAAK,MACvB,kBAAC,OAAD,EAEE,WAAU,+DACV,EAFK,EAEL,CACF;KACE,CAAA,GACJ,IACF,kBAAC,GAAD;KAAY,UAAS;KAAkB;KAAO,eAAe,KAAK,GAAS;KAAI,CAAA,GAC7E,EAAiB,SAAS,IAC5B,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAiB,KAAK,MACrB,kBAAC,GAAD;MAAsB,IAAI,EAAQ,YAAY,EAAO,KAAK;gBACxD,kBAAC,GAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,GAAD;QAAY,WAAU;kBACpB,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WACE,WAAW,kBACT,EAAO,SAAS,YACZ,8BACA,EAAO,SAAS,SACd,6BACA,EAAO,SAAS,WACd,gCACA;qBAGV,kBAAC,GAAD;YACE,MAAM,EAAO;YACb,WAAW,WACT,EAAO,SAAS,YACZ,2BACA,EAAO,SAAS,SACd,0BACA,EAAO,SAAS,WACd,6BACA;YAEV,CAAA;WACE,CAAA,EACN,kBAAC,OAAD,EAAA,UACE,kBAAC,GAAD;WAAW,WAAU;qBAAa,EAAO;WAAiB,CAAA,EACtD,CAAA,CACF;aACN,kBAAC,GAAD,EAAa,QAAQ,EAAO,QAAU,CAAA,CAClC;;QACK,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAO;SACN,CAAA,EAGJ,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;UAAU,WAAU;oBAChC,EAAgB,EAAO;UAClB,CAAA,EACR,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAmB,EAAO,UAAU;UAChC,CAAA,CACH;WACF;WACM,CAAA,CACT;;MACF,EAtDI,EAAO,GAsDX,CACP;KACE,CAAA,GAEN,kBAAC,GAAD;KACE,cAAa;KACb,OACE,IACI,EAAE,qCAAqC,wBAAwB,EAC7D,MAAM,EAAgB,IACvB,CAAC,GACF,EAAE,+BAA+B,cAAc;KAErD,aAAa,EACX,qCACA,qFACD;KACD,QACE,kBAAC,GAAD;MAAwB,YAAY,EAAqB;gBACvD,kBAAC,GAAD;OAAQ,UAAU,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;iBAClC,kBAAC,GAAD;QAAM,IAAI,EAAQ,eAAe;kBAC9B,EAAE,wCAAwC,gBAAgB;QACtD,CAAA;OACA,CAAA;MACc,CAAA;KAE3B,CAAA;IAIJ,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;eAA5C,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAE,kCAAkC,eAAe;OACjD,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,wCACA,qEACD;OACC,CAAA,CACA;SACN,kBAAC,OAAD;MAAK,WAAU;gBACZ;OACC;QACE,MAAM;QACN,MAAM,EACJ,+CACA,wDACD;QACF;OACD;QACE,MAAM;QACN,MAAM,EACJ,4CACA,gDACD;QACF;OACD;QACE,MAAM;QACN,MAAM,EACJ,8CACA,+CACD;QACF;OACD;QACE,MAAM;QACN,MAAM,EACJ,iDACA,2CACD;QACF;OACF,CAAC,KAAK,MACL,kBAAC,OAAD;OAAqB,WAAU;iBAA/B,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAgB,MAAM,EAAK;SAAM,WAAU;SAAyB,CAAA,EACpE,kBAAC,QAAD;SAAM,WAAU;mBAAe,EAAgB,EAAK;SAAa,CAAA,CAC7D;WACN,kBAAC,KAAD;QAAG,WAAU;kBAA+B,EAAK;QAAS,CAAA,CACtD;SANI,EAAK,KAMT,CACN;MACE,CAAA,CACI;;IACR;;EACK,CAAA"}
|