@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
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
2
|
import { useBotlit as t } from "../providers/BotlitProvider.js";
|
|
3
3
|
import { useUpdateBot as n } from "../hooks/useBots.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
4
|
+
import { BOTMANOR_PERMISSIONS as r } from "../permissions/botmanorPermissions.js";
|
|
5
|
+
import { BotmanorPermissionGate as i } from "../permissions/useBotmanorPermissions.js";
|
|
6
|
+
import { Button as a } from "../components/Button.js";
|
|
7
|
+
import "../permissions/index.js";
|
|
8
|
+
import { Card as o, CardContent as s, CardDescription as c, CardHeader as l, CardTitle as u } from "../components/Card.js";
|
|
9
|
+
import { Badge as d, StatusBadge as f } from "../components/Badge.js";
|
|
10
|
+
import { EmptyState as p } from "../components/EmptyState.js";
|
|
11
|
+
import { useBotWorkflowDefinition as m, useCreateBotWorkflow as h } from "../hooks/useBotWorkflow.js";
|
|
12
|
+
import { useCallback as g, useState as _ } from "react";
|
|
13
|
+
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
14
|
+
import { ExternalLink as b, Sparkles as x, Unlink as S, Workflow as C } from "lucide-react";
|
|
15
|
+
import { toast as w } from "sonner";
|
|
13
16
|
//#region src/botlit/pages/BotWorkflowTab.tsx
|
|
14
|
-
var
|
|
17
|
+
var T = "v1", E = "internal:api", D = "1.0.0", O = "onManualTrigger", k = "Trigger Manually", A = "Runs the flow when you click 'Test workflow', or when this bot is invoked.", j = [
|
|
15
18
|
"trigger",
|
|
16
19
|
"manual",
|
|
17
20
|
"entry"
|
|
18
21
|
];
|
|
19
|
-
function
|
|
22
|
+
function M() {
|
|
20
23
|
let e = globalThis.crypto;
|
|
21
24
|
return e && typeof e.randomUUID == "function" ? e.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
22
25
|
}
|
|
23
|
-
function
|
|
26
|
+
function N(e, t) {
|
|
24
27
|
return {
|
|
25
28
|
key: e,
|
|
26
29
|
name: t,
|
|
@@ -29,38 +32,38 @@ function j(e, t) {
|
|
|
29
32
|
data: {
|
|
30
33
|
id: e,
|
|
31
34
|
workflowKey: e,
|
|
32
|
-
version:
|
|
35
|
+
version: T,
|
|
33
36
|
name: t,
|
|
34
37
|
nodes: [{
|
|
35
|
-
id:
|
|
36
|
-
type:
|
|
38
|
+
id: M(),
|
|
39
|
+
type: E,
|
|
37
40
|
position: {
|
|
38
41
|
x: 160,
|
|
39
42
|
y: 160
|
|
40
43
|
},
|
|
41
|
-
kind:
|
|
44
|
+
kind: j,
|
|
42
45
|
data: {
|
|
43
|
-
operation_name:
|
|
46
|
+
operation_name: O,
|
|
44
47
|
operation_type: "trigger",
|
|
45
|
-
version:
|
|
48
|
+
version: D,
|
|
46
49
|
nodeData: {
|
|
47
|
-
nodeType:
|
|
48
|
-
name:
|
|
49
|
-
description:
|
|
50
|
-
version:
|
|
51
|
-
kind:
|
|
50
|
+
nodeType: E,
|
|
51
|
+
name: k,
|
|
52
|
+
description: A,
|
|
53
|
+
version: D,
|
|
54
|
+
kind: j,
|
|
52
55
|
operations: [{
|
|
53
|
-
name:
|
|
56
|
+
name: O,
|
|
54
57
|
type: "trigger",
|
|
55
58
|
role: "trigger",
|
|
56
|
-
description:
|
|
59
|
+
description: A
|
|
57
60
|
}]
|
|
58
61
|
},
|
|
59
62
|
args: {
|
|
60
63
|
context: { node: {
|
|
61
|
-
id:
|
|
62
|
-
type:
|
|
63
|
-
kind:
|
|
64
|
+
id: E,
|
|
65
|
+
type: E,
|
|
66
|
+
kind: j
|
|
64
67
|
} },
|
|
65
68
|
state: {},
|
|
66
69
|
credentials: []
|
|
@@ -76,151 +79,157 @@ function j(e, t) {
|
|
|
76
79
|
},
|
|
77
80
|
meta: {
|
|
78
81
|
creator: "botlit_bot_design",
|
|
79
|
-
version:
|
|
82
|
+
version: T
|
|
80
83
|
},
|
|
81
84
|
tags: []
|
|
82
85
|
};
|
|
83
86
|
}
|
|
84
|
-
var
|
|
85
|
-
let
|
|
86
|
-
|
|
87
|
-
}, [
|
|
88
|
-
|
|
87
|
+
var P = ({ botId: E, botName: D, workflowId: O, onWorkflowChanged: k }) => {
|
|
88
|
+
let A = e(), { navigateTo: j } = t(), { createWorkflow: P } = h(), [F] = n(), [I, L] = _(!1), { workflow: R, loading: z } = m(void 0, O ?? void 0), B = g((e, t) => {
|
|
89
|
+
j(`workflows/workflow/${e}/${t || T}`);
|
|
90
|
+
}, [j]), V = g(async () => {
|
|
91
|
+
L(!0);
|
|
89
92
|
try {
|
|
90
|
-
let e =
|
|
93
|
+
let e = M(), t = `${D} workflow`, n = await P({
|
|
91
94
|
workflowKey: e,
|
|
92
|
-
version:
|
|
95
|
+
version: T,
|
|
93
96
|
name: t,
|
|
94
|
-
description:
|
|
95
|
-
definitionJson:
|
|
97
|
+
description: A("botlit.botDetail.workflow.createdDescription", "Behaviour designed for this BotManor bot."),
|
|
98
|
+
definitionJson: N(e, t)
|
|
96
99
|
});
|
|
97
|
-
if (!n) throw Error(
|
|
98
|
-
await
|
|
99
|
-
botId:
|
|
100
|
+
if (!n) throw Error(A("botlit.botDetail.workflow.createError", "Failed to create the workflow"));
|
|
101
|
+
await F({
|
|
102
|
+
botId: E,
|
|
100
103
|
input: { workflowId: n.id }
|
|
101
|
-
}), await
|
|
104
|
+
}), await k(), w.success(A("botlit.botDetail.workflow.createSuccess", "Workflow created and bound to this bot")), B(n.workflowKey, n.version);
|
|
102
105
|
} catch (e) {
|
|
103
|
-
let t = e instanceof Error ? e.message :
|
|
104
|
-
|
|
106
|
+
let t = e instanceof Error ? e.message : A("botlit.botDetail.workflow.createError", "Failed to create the workflow");
|
|
107
|
+
w.error(t);
|
|
105
108
|
} finally {
|
|
106
|
-
|
|
109
|
+
L(!1);
|
|
107
110
|
}
|
|
108
111
|
}, [
|
|
109
|
-
|
|
110
|
-
T,
|
|
111
|
-
M,
|
|
112
|
+
E,
|
|
112
113
|
D,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
P,
|
|
115
|
+
k,
|
|
116
|
+
B,
|
|
117
|
+
A,
|
|
118
|
+
F
|
|
119
|
+
]), H = g(async () => {
|
|
120
|
+
if (confirm(A("botlit.botDetail.workflow.unbindConfirm", "Unbind this workflow from the bot? The workflow itself is not deleted."))) {
|
|
121
|
+
L(!0);
|
|
119
122
|
try {
|
|
120
|
-
await
|
|
121
|
-
botId:
|
|
123
|
+
await F({
|
|
124
|
+
botId: E,
|
|
122
125
|
input: { workflowId: null }
|
|
123
|
-
}), await
|
|
126
|
+
}), await k(), w.success(A("botlit.botDetail.workflow.unbindSuccess", "Workflow unbound"));
|
|
124
127
|
} catch (e) {
|
|
125
|
-
let t = e instanceof Error ? e.message :
|
|
126
|
-
|
|
128
|
+
let t = e instanceof Error ? e.message : A("botlit.botDetail.workflow.unbindError", "Failed to unbind the workflow");
|
|
129
|
+
w.error(t);
|
|
127
130
|
} finally {
|
|
128
|
-
|
|
131
|
+
L(!1);
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
134
|
}, [
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
E,
|
|
136
|
+
k,
|
|
137
|
+
A,
|
|
138
|
+
F
|
|
136
139
|
]);
|
|
137
|
-
return
|
|
140
|
+
return O ? /* @__PURE__ */ v("div", {
|
|
138
141
|
className: "space-y-6",
|
|
139
|
-
children: /* @__PURE__ */
|
|
142
|
+
children: /* @__PURE__ */ y(o, { children: [/* @__PURE__ */ y(l, { children: [/* @__PURE__ */ y(u, {
|
|
140
143
|
className: "flex items-center gap-2",
|
|
141
|
-
children: [/* @__PURE__ */
|
|
142
|
-
}), /* @__PURE__ */
|
|
144
|
+
children: [/* @__PURE__ */ v(C, { size: 18 }), A("botlit.botDetail.workflow.bound.title", "Bound workflow")]
|
|
145
|
+
}), /* @__PURE__ */ v(c, { children: A("botlit.botDetail.workflow.bound.description", "This workflow defines what the bot does when it is invoked.") })] }), /* @__PURE__ */ y(s, {
|
|
143
146
|
className: "space-y-6",
|
|
144
|
-
children: [
|
|
147
|
+
children: [z ? /* @__PURE__ */ v("div", { className: "h-24 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer" }) : R ? /* @__PURE__ */ y("div", {
|
|
145
148
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
146
149
|
children: [
|
|
147
|
-
/* @__PURE__ */
|
|
150
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
148
151
|
className: "text-sm text-text-secondary",
|
|
149
|
-
children:
|
|
150
|
-
}), /* @__PURE__ */
|
|
152
|
+
children: A("botlit.botDetail.workflow.fields.name", "Name")
|
|
153
|
+
}), /* @__PURE__ */ v("p", {
|
|
151
154
|
className: "font-medium text-text-primary",
|
|
152
|
-
children:
|
|
155
|
+
children: R.name
|
|
153
156
|
})] }),
|
|
154
|
-
/* @__PURE__ */
|
|
157
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
155
158
|
className: "text-sm text-text-secondary",
|
|
156
|
-
children:
|
|
157
|
-
}), /* @__PURE__ */
|
|
159
|
+
children: A("botlit.botDetail.workflow.fields.key", "Key")
|
|
160
|
+
}), /* @__PURE__ */ v("p", {
|
|
158
161
|
className: "mt-1 break-all font-mono text-sm text-text-primary",
|
|
159
|
-
children:
|
|
162
|
+
children: R.workflowKey
|
|
160
163
|
})] }),
|
|
161
|
-
/* @__PURE__ */
|
|
164
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
162
165
|
className: "text-sm text-text-secondary",
|
|
163
|
-
children:
|
|
164
|
-
}), /* @__PURE__ */
|
|
166
|
+
children: A("botlit.botDetail.workflow.fields.version", "Version")
|
|
167
|
+
}), /* @__PURE__ */ v(d, {
|
|
165
168
|
variant: "outline",
|
|
166
169
|
className: "mt-1",
|
|
167
|
-
children:
|
|
170
|
+
children: R.version
|
|
168
171
|
})] }),
|
|
169
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
170
173
|
className: "text-sm text-text-secondary",
|
|
171
|
-
children:
|
|
172
|
-
}), /* @__PURE__ */
|
|
174
|
+
children: A("botlit.botDetail.workflow.fields.status", "Status")
|
|
175
|
+
}), /* @__PURE__ */ v("div", {
|
|
173
176
|
className: "mt-1",
|
|
174
|
-
children: /* @__PURE__ */
|
|
177
|
+
children: /* @__PURE__ */ v(f, { status: R.status })
|
|
175
178
|
})] })
|
|
176
179
|
]
|
|
177
|
-
}) : /* @__PURE__ */
|
|
178
|
-
/* @__PURE__ */
|
|
180
|
+
}) : /* @__PURE__ */ y("div", { children: [
|
|
181
|
+
/* @__PURE__ */ v("p", {
|
|
179
182
|
className: "text-sm text-text-secondary",
|
|
180
|
-
children:
|
|
183
|
+
children: A("botlit.botDetail.workflow.fields.workflowId", "Workflow ID")
|
|
181
184
|
}),
|
|
182
|
-
/* @__PURE__ */
|
|
185
|
+
/* @__PURE__ */ v("p", {
|
|
183
186
|
className: "mt-1 break-all font-mono text-sm text-text-primary",
|
|
184
|
-
children:
|
|
187
|
+
children: O
|
|
185
188
|
}),
|
|
186
|
-
/* @__PURE__ */
|
|
189
|
+
/* @__PURE__ */ v("p", {
|
|
187
190
|
className: "mt-2 text-sm text-text-secondary",
|
|
188
|
-
children:
|
|
191
|
+
children: A("botlit.botDetail.workflow.unresolved", "This workflow could not be loaded — it may have been deleted. Unbind it and design a new one.")
|
|
189
192
|
})
|
|
190
|
-
] }), /* @__PURE__ */
|
|
193
|
+
] }), /* @__PURE__ */ y("div", {
|
|
191
194
|
className: "flex flex-wrap gap-2",
|
|
192
|
-
children: [
|
|
193
|
-
leftIcon: /* @__PURE__ */
|
|
194
|
-
disabled:
|
|
195
|
-
onClick: () => R
|
|
196
|
-
children:
|
|
197
|
-
}), /* @__PURE__ */
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
195
|
+
children: [R && /* @__PURE__ */ v(a, {
|
|
196
|
+
leftIcon: /* @__PURE__ */ v(b, { size: 16 }),
|
|
197
|
+
disabled: I,
|
|
198
|
+
onClick: () => B(R.workflowKey, R.version),
|
|
199
|
+
children: A("botlit.botDetail.workflow.actions.openBuilder", "Open in builder")
|
|
200
|
+
}), /* @__PURE__ */ v(i, {
|
|
201
|
+
permission: r.APP_UPDATE,
|
|
202
|
+
children: /* @__PURE__ */ v(a, {
|
|
203
|
+
variant: "outline",
|
|
204
|
+
leftIcon: /* @__PURE__ */ v(S, { size: 16 }),
|
|
205
|
+
isLoading: I,
|
|
206
|
+
disabled: I,
|
|
207
|
+
onClick: () => void H(),
|
|
208
|
+
children: A("botlit.botDetail.workflow.actions.unbind", "Unbind")
|
|
209
|
+
})
|
|
204
210
|
})]
|
|
205
211
|
})]
|
|
206
212
|
})] })
|
|
207
|
-
}) : /* @__PURE__ */
|
|
213
|
+
}) : /* @__PURE__ */ v(o, { children: /* @__PURE__ */ v(s, {
|
|
208
214
|
className: "pt-6",
|
|
209
|
-
children: /* @__PURE__ */
|
|
215
|
+
children: /* @__PURE__ */ v(p, {
|
|
210
216
|
illustration: "empty-generic",
|
|
211
|
-
title:
|
|
212
|
-
description:
|
|
213
|
-
action: /* @__PURE__ */
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
217
|
+
title: A("botlit.botDetail.workflow.empty.title", "Design this bot as a workflow"),
|
|
218
|
+
description: A("botlit.botDetail.workflow.empty.description", "A bot's behaviour is designed as a FluidGrids workflow — the visual canvas where you wire triggers, agents, tools and conditions together. Start one and we will create it, bind it to this bot and open the builder for you."),
|
|
219
|
+
action: /* @__PURE__ */ v(i, {
|
|
220
|
+
permission: r.APP_UPDATE,
|
|
221
|
+
children: /* @__PURE__ */ v(a, {
|
|
222
|
+
leftIcon: /* @__PURE__ */ v(x, { size: 16 }),
|
|
223
|
+
isLoading: I,
|
|
224
|
+
disabled: I,
|
|
225
|
+
onClick: () => void V(),
|
|
226
|
+
children: A("botlit.botDetail.workflow.empty.action", "Design in workflow builder")
|
|
227
|
+
})
|
|
219
228
|
})
|
|
220
229
|
})
|
|
221
230
|
}) });
|
|
222
231
|
};
|
|
223
232
|
//#endregion
|
|
224
|
-
export {
|
|
233
|
+
export { P as BotWorkflowTab };
|
|
225
234
|
|
|
226
235
|
//# sourceMappingURL=BotWorkflowTab.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BotWorkflowTab.js","names":[],"sources":["../../../src/botlit/pages/BotWorkflowTab.tsx"],"sourcesContent":["/**\n * BotWorkflowTab\n * @module botlit/pages\n *\n * Shown inside BotDetailPage → \"Workflow\" tab.\n *\n * A bot's behaviour is designed as a FluidGrids workflow. `BotApp.workflowId`\n * holds the binding (validated server-side by `assertWorkflowBindingsResolvable`\n * on wspace-botlit-svc), and the FluidGrids builder is already mounted INSIDE\n * BotManor at `workflows/workflow/:id/:version` — so this tab never leaves the\n * product.\n *\n * Flow:\n * 1. Bound → resolve the definition by id (`useWorkflowDefinition`), show\n * name / key / version / status, \"Open in builder\", \"Unbind\".\n * A binding that no longer resolves falls back to the raw id so\n * the user can still clear it.\n * 2. Not bound → zero state whose primary action creates a seeded workflow\n * definition, binds it via `updateBotApp(workflowId)` and drops\n * the user straight into the embedded builder.\n *\n * IMPORTANT — seed graph shape. `BotlitFluidGridsBridge.extractTriggerNodeIds`\n * reads nodes from `definitionJson.data.nodes` (NOT the top level) and treats a\n * node as a trigger when its `type` is `trigger`/`trigger:*`/`*_trigger`, OR its\n * `kind[]` / `data.nodeData.kind[]` contains `trigger`. The seed below uses the\n * workflow service's own BUILTIN manual trigger (`internal:api`, \"Trigger\n * Manually\") — the node the executor resolves as the entry point — and carries\n * `kind: ['trigger', ...]` in both places, so a freshly designed bot is\n * immediately runnable instead of failing with `WORKFLOW_NO_TRIGGER_NODE`.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useState } from 'react';\nimport { ExternalLink, Sparkles, Unlink, Workflow } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useCreateBotWorkflow as useCreateWorkflow,\n useBotWorkflowDefinition as useWorkflowDefinition,\n} from '../hooks/useBotWorkflow';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Badge, StatusBadge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { useUpdateBot } from '../hooks/useBots';\nimport { useBotlit } from '../providers/BotlitProvider';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Seed graph\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** First version minted for a bot's workflow — matches the builder's own default. */\nconst INITIAL_VERSION = 'v1';\n\n/**\n * BUILTIN \"Trigger Manually\" node (`MANUAL_TRIGGER_MANIFEST` in\n * wspace-workflow-svc). The executor resolves this type as a workflow's entry\n * node, and the builder renders it from the seeded node registry.\n */\nconst MANUAL_TRIGGER_NODE_TYPE = 'internal:api';\nconst MANUAL_TRIGGER_NODE_VERSION = '1.0.0';\nconst MANUAL_TRIGGER_OPERATION = 'onManualTrigger';\nconst MANUAL_TRIGGER_NAME = 'Trigger Manually';\nconst MANUAL_TRIGGER_DESCRIPTION =\n \"Runs the flow when you click 'Test workflow', or when this bot is invoked.\";\n\n/** `kind[]` the BotManor bridge scans for when classifying a node as a trigger. */\nconst MANUAL_TRIGGER_KIND = ['trigger', 'manual', 'entry'];\n\n/** Node shape written into `definitionJson.data.nodes`. */\ntype SeedNode = {\n id: string;\n type: string;\n position: { x: number; y: number };\n /** Top-level kind — the bridge's second trigger-detection path. */\n kind: string[];\n data: {\n operation_name: string;\n operation_type: string;\n version: string;\n nodeData: {\n nodeType: string;\n name: string;\n description: string;\n version: string;\n /** The bridge's third trigger-detection path. */\n kind: string[];\n operations: { name: string; type: string; role: string; description: string }[];\n };\n args: {\n context: { node: { id: string; type: string; kind: string[] } };\n state: Record<string, never>;\n credentials: never[];\n };\n };\n};\n\n/**\n * `definitionJson` document. Mirrors what the builder itself persists\n * (`useSaveWorkflow`) so the graph round-trips through the builder unchanged.\n */\ntype SeedDefinition = {\n key: string;\n name: string;\n description: string;\n workflow_type: string;\n data: {\n id: string;\n workflowKey: string;\n version: string;\n name: string;\n nodes: SeedNode[];\n edges: never[];\n sidebar: { active: string; panels: { nodeProperties: { selectedNode: null } } };\n stickyNotes: never[];\n };\n meta: { creator: string; version: string };\n tags: string[];\n};\n\n/**\n * Generate an opaque id. `crypto.randomUUID` is unavailable on insecure origins\n * and in some test environments, so fall back to a time+random token rather than\n * throwing mid-create.\n */\nfunction generateId(): string {\n const webCrypto = globalThis.crypto as Crypto | undefined;\n if (webCrypto && typeof webCrypto.randomUUID === 'function') {\n return webCrypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Build the starter graph: one manual trigger, nothing else. */\nfunction buildSeedDefinition(workflowKey: string, name: string): SeedDefinition {\n const triggerNode: SeedNode = {\n id: generateId(),\n type: MANUAL_TRIGGER_NODE_TYPE,\n position: { x: 160, y: 160 },\n kind: MANUAL_TRIGGER_KIND,\n data: {\n operation_name: MANUAL_TRIGGER_OPERATION,\n operation_type: 'trigger',\n version: MANUAL_TRIGGER_NODE_VERSION,\n nodeData: {\n nodeType: MANUAL_TRIGGER_NODE_TYPE,\n name: MANUAL_TRIGGER_NAME,\n description: MANUAL_TRIGGER_DESCRIPTION,\n version: MANUAL_TRIGGER_NODE_VERSION,\n kind: MANUAL_TRIGGER_KIND,\n operations: [\n {\n name: MANUAL_TRIGGER_OPERATION,\n type: 'trigger',\n role: 'trigger',\n description: MANUAL_TRIGGER_DESCRIPTION,\n },\n ],\n },\n args: {\n context: {\n node: {\n id: MANUAL_TRIGGER_NODE_TYPE,\n type: MANUAL_TRIGGER_NODE_TYPE,\n kind: MANUAL_TRIGGER_KIND,\n },\n },\n state: {},\n credentials: [],\n },\n },\n };\n\n return {\n key: workflowKey,\n name,\n description: name,\n workflow_type: 'query-conditional',\n data: {\n id: workflowKey,\n workflowKey,\n version: INITIAL_VERSION,\n name,\n nodes: [triggerNode],\n edges: [],\n sidebar: { active: 'store-browse', panels: { nodeProperties: { selectedNode: null } } },\n stickyNotes: [],\n },\n meta: { creator: 'botlit_bot_design', version: INITIAL_VERSION },\n tags: [],\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface BotWorkflowTabProps {\n botId: string;\n botName: string;\n /** `BotApp.workflowId` — the bound workflow definition id, if any. */\n workflowId: string | null | undefined;\n /** Refetch the bot so the tab reflects the new binding. */\n onWorkflowChanged: () => Promise<void> | void;\n}\n\nexport const BotWorkflowTab: FC<BotWorkflowTabProps> = ({\n botId,\n botName,\n workflowId,\n onWorkflowChanged,\n}) => {\n const t = useTr();\n const { navigateTo } = useBotlit();\n const { createWorkflow } = useCreateWorkflow();\n const [updateBot] = useUpdateBot();\n const [busy, setBusy] = useState(false);\n\n // Resolve by id — `BotApp.workflowId` stores the definition's opaque id, not\n // its workflowKey (which is what the builder route takes). The hook refetches\n // on its own when the bound id changes, so binding/unbinding only has to\n // refresh the bot.\n const { workflow, loading: workflowLoading } = useWorkflowDefinition(\n undefined,\n workflowId ?? undefined\n );\n\n const openBuilder = useCallback(\n (workflowKey: string, version: string) => {\n navigateTo(`workflows/workflow/${workflowKey}/${version || INITIAL_VERSION}`);\n },\n [navigateTo]\n );\n\n const handleDesign = useCallback(async () => {\n setBusy(true);\n try {\n const workflowKey = generateId();\n const name = `${botName} workflow`;\n const created = await createWorkflow({\n workflowKey,\n version: INITIAL_VERSION,\n name,\n description: t(\n 'botlit.botDetail.workflow.createdDescription',\n 'Behaviour designed for this BotManor bot.'\n ),\n definitionJson: buildSeedDefinition(workflowKey, name),\n });\n\n if (!created) {\n throw new Error(\n t('botlit.botDetail.workflow.createError', 'Failed to create the workflow')\n );\n }\n\n await updateBot({ botId, input: { workflowId: created.id } });\n await onWorkflowChanged();\n toast.success(\n t('botlit.botDetail.workflow.createSuccess', 'Workflow created and bound to this bot')\n );\n openBuilder(created.workflowKey, created.version);\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.botDetail.workflow.createError', 'Failed to create the workflow');\n toast.error(message);\n } finally {\n setBusy(false);\n }\n }, [botId, botName, createWorkflow, onWorkflowChanged, openBuilder, t, updateBot]);\n\n const handleUnbind = useCallback(async () => {\n if (\n !confirm(\n t(\n 'botlit.botDetail.workflow.unbindConfirm',\n 'Unbind this workflow from the bot? The workflow itself is not deleted.'\n )\n )\n ) {\n return;\n }\n setBusy(true);\n try {\n await updateBot({ botId, input: { workflowId: null } });\n await onWorkflowChanged();\n toast.success(t('botlit.botDetail.workflow.unbindSuccess', 'Workflow unbound'));\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.botDetail.workflow.unbindError', 'Failed to unbind the workflow');\n toast.error(message);\n } finally {\n setBusy(false);\n }\n }, [botId, onWorkflowChanged, t, updateBot]);\n\n // ── Not bound ─────────────────────────────────────────────────────────────\n if (!workflowId) {\n return (\n <Card>\n <CardContent className=\"pt-6\">\n <EmptyState\n illustration=\"empty-generic\"\n title={t('botlit.botDetail.workflow.empty.title', 'Design this bot as a workflow')}\n description={t(\n 'botlit.botDetail.workflow.empty.description',\n \"A bot's behaviour is designed as a FluidGrids workflow — the visual canvas where you wire triggers, agents, tools and conditions together. Start one and we will create it, bind it to this bot and open the builder for you.\"\n )}\n action={\n <Button\n leftIcon={<Sparkles size={16} />}\n isLoading={busy}\n disabled={busy}\n onClick={() => void handleDesign()}\n >\n {t('botlit.botDetail.workflow.empty.action', 'Design in workflow builder')}\n </Button>\n }\n />\n </CardContent>\n </Card>\n );\n }\n\n // ── Bound ─────────────────────────────────────────────────────────────────\n return (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Workflow size={18} />\n {t('botlit.botDetail.workflow.bound.title', 'Bound workflow')}\n </CardTitle>\n <CardDescription>\n {t(\n 'botlit.botDetail.workflow.bound.description',\n 'This workflow defines what the bot does when it is invoked.'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n {workflowLoading ? (\n <div className=\"h-24 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ) : workflow ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.name', 'Name')}\n </p>\n <p className=\"font-medium text-text-primary\">{workflow.name}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.key', 'Key')}\n </p>\n <p className=\"mt-1 break-all font-mono text-sm text-text-primary\">\n {workflow.workflowKey}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.version', 'Version')}\n </p>\n <Badge variant=\"outline\" className=\"mt-1\">\n {workflow.version}\n </Badge>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.status', 'Status')}\n </p>\n <div className=\"mt-1\">\n <StatusBadge status={workflow.status} />\n </div>\n </div>\n </div>\n ) : (\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.workflowId', 'Workflow ID')}\n </p>\n <p className=\"mt-1 break-all font-mono text-sm text-text-primary\">{workflowId}</p>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {t(\n 'botlit.botDetail.workflow.unresolved',\n 'This workflow could not be loaded — it may have been deleted. Unbind it and design a new one.'\n )}\n </p>\n </div>\n )}\n\n <div className=\"flex flex-wrap gap-2\">\n {workflow && (\n <Button\n leftIcon={<ExternalLink size={16} />}\n disabled={busy}\n onClick={() => openBuilder(workflow.workflowKey, workflow.version)}\n >\n {t('botlit.botDetail.workflow.actions.openBuilder', 'Open in builder')}\n </Button>\n )}\n <Button\n variant=\"outline\"\n leftIcon={<Unlink size={16} />}\n isLoading={busy}\n disabled={busy}\n onClick={() => void handleUnbind()}\n >\n {t('botlit.botDetail.workflow.actions.unbind', 'Unbind')}\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BotWorkflowTab.js","names":[],"sources":["../../../src/botlit/pages/BotWorkflowTab.tsx"],"sourcesContent":["/**\n * BotWorkflowTab\n * @module botlit/pages\n *\n * Shown inside BotDetailPage → \"Workflow\" tab.\n *\n * A bot's behaviour is designed as a FluidGrids workflow. `BotApp.workflowId`\n * holds the binding (validated server-side by `assertWorkflowBindingsResolvable`\n * on wspace-botlit-svc), and the FluidGrids builder is already mounted INSIDE\n * BotManor at `workflows/workflow/:id/:version` — so this tab never leaves the\n * product.\n *\n * Flow:\n * 1. Bound → resolve the definition by id (`useWorkflowDefinition`), show\n * name / key / version / status, \"Open in builder\", \"Unbind\".\n * A binding that no longer resolves falls back to the raw id so\n * the user can still clear it.\n * 2. Not bound → zero state whose primary action creates a seeded workflow\n * definition, binds it via `updateBotApp(workflowId)` and drops\n * the user straight into the embedded builder.\n *\n * IMPORTANT — seed graph shape. `BotlitFluidGridsBridge.extractTriggerNodeIds`\n * reads nodes from `definitionJson.data.nodes` (NOT the top level) and treats a\n * node as a trigger when its `type` is `trigger`/`trigger:*`/`*_trigger`, OR its\n * `kind[]` / `data.nodeData.kind[]` contains `trigger`. The seed below uses the\n * workflow service's own BUILTIN manual trigger (`internal:api`, \"Trigger\n * Manually\") — the node the executor resolves as the entry point — and carries\n * `kind: ['trigger', ...]` in both places, so a freshly designed bot is\n * immediately runnable instead of failing with `WORKFLOW_NO_TRIGGER_NODE`.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useState } from 'react';\nimport { ExternalLink, Sparkles, Unlink, Workflow } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useCreateBotWorkflow as useCreateWorkflow,\n useBotWorkflowDefinition as useWorkflowDefinition,\n} from '../hooks/useBotWorkflow';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Badge, StatusBadge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { useUpdateBot } from '../hooks/useBots';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { BOTMANOR_PERMISSIONS, BotmanorPermissionGate } from '../permissions';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Seed graph\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** First version minted for a bot's workflow — matches the builder's own default. */\nconst INITIAL_VERSION = 'v1';\n\n/**\n * BUILTIN \"Trigger Manually\" node (`MANUAL_TRIGGER_MANIFEST` in\n * wspace-workflow-svc). The executor resolves this type as a workflow's entry\n * node, and the builder renders it from the seeded node registry.\n */\nconst MANUAL_TRIGGER_NODE_TYPE = 'internal:api';\nconst MANUAL_TRIGGER_NODE_VERSION = '1.0.0';\nconst MANUAL_TRIGGER_OPERATION = 'onManualTrigger';\nconst MANUAL_TRIGGER_NAME = 'Trigger Manually';\nconst MANUAL_TRIGGER_DESCRIPTION =\n \"Runs the flow when you click 'Test workflow', or when this bot is invoked.\";\n\n/** `kind[]` the BotManor bridge scans for when classifying a node as a trigger. */\nconst MANUAL_TRIGGER_KIND = ['trigger', 'manual', 'entry'];\n\n/** Node shape written into `definitionJson.data.nodes`. */\ntype SeedNode = {\n id: string;\n type: string;\n position: { x: number; y: number };\n /** Top-level kind — the bridge's second trigger-detection path. */\n kind: string[];\n data: {\n operation_name: string;\n operation_type: string;\n version: string;\n nodeData: {\n nodeType: string;\n name: string;\n description: string;\n version: string;\n /** The bridge's third trigger-detection path. */\n kind: string[];\n operations: { name: string; type: string; role: string; description: string }[];\n };\n args: {\n context: { node: { id: string; type: string; kind: string[] } };\n state: Record<string, never>;\n credentials: never[];\n };\n };\n};\n\n/**\n * `definitionJson` document. Mirrors what the builder itself persists\n * (`useSaveWorkflow`) so the graph round-trips through the builder unchanged.\n */\ntype SeedDefinition = {\n key: string;\n name: string;\n description: string;\n workflow_type: string;\n data: {\n id: string;\n workflowKey: string;\n version: string;\n name: string;\n nodes: SeedNode[];\n edges: never[];\n sidebar: { active: string; panels: { nodeProperties: { selectedNode: null } } };\n stickyNotes: never[];\n };\n meta: { creator: string; version: string };\n tags: string[];\n};\n\n/**\n * Generate an opaque id. `crypto.randomUUID` is unavailable on insecure origins\n * and in some test environments, so fall back to a time+random token rather than\n * throwing mid-create.\n */\nfunction generateId(): string {\n const webCrypto = globalThis.crypto as Crypto | undefined;\n if (webCrypto && typeof webCrypto.randomUUID === 'function') {\n return webCrypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Build the starter graph: one manual trigger, nothing else. */\nfunction buildSeedDefinition(workflowKey: string, name: string): SeedDefinition {\n const triggerNode: SeedNode = {\n id: generateId(),\n type: MANUAL_TRIGGER_NODE_TYPE,\n position: { x: 160, y: 160 },\n kind: MANUAL_TRIGGER_KIND,\n data: {\n operation_name: MANUAL_TRIGGER_OPERATION,\n operation_type: 'trigger',\n version: MANUAL_TRIGGER_NODE_VERSION,\n nodeData: {\n nodeType: MANUAL_TRIGGER_NODE_TYPE,\n name: MANUAL_TRIGGER_NAME,\n description: MANUAL_TRIGGER_DESCRIPTION,\n version: MANUAL_TRIGGER_NODE_VERSION,\n kind: MANUAL_TRIGGER_KIND,\n operations: [\n {\n name: MANUAL_TRIGGER_OPERATION,\n type: 'trigger',\n role: 'trigger',\n description: MANUAL_TRIGGER_DESCRIPTION,\n },\n ],\n },\n args: {\n context: {\n node: {\n id: MANUAL_TRIGGER_NODE_TYPE,\n type: MANUAL_TRIGGER_NODE_TYPE,\n kind: MANUAL_TRIGGER_KIND,\n },\n },\n state: {},\n credentials: [],\n },\n },\n };\n\n return {\n key: workflowKey,\n name,\n description: name,\n workflow_type: 'query-conditional',\n data: {\n id: workflowKey,\n workflowKey,\n version: INITIAL_VERSION,\n name,\n nodes: [triggerNode],\n edges: [],\n sidebar: { active: 'store-browse', panels: { nodeProperties: { selectedNode: null } } },\n stickyNotes: [],\n },\n meta: { creator: 'botlit_bot_design', version: INITIAL_VERSION },\n tags: [],\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface BotWorkflowTabProps {\n botId: string;\n botName: string;\n /** `BotApp.workflowId` — the bound workflow definition id, if any. */\n workflowId: string | null | undefined;\n /** Refetch the bot so the tab reflects the new binding. */\n onWorkflowChanged: () => Promise<void> | void;\n}\n\nexport const BotWorkflowTab: FC<BotWorkflowTabProps> = ({\n botId,\n botName,\n workflowId,\n onWorkflowChanged,\n}) => {\n const t = useTr();\n const { navigateTo } = useBotlit();\n const { createWorkflow } = useCreateWorkflow();\n const [updateBot] = useUpdateBot();\n const [busy, setBusy] = useState(false);\n\n // Resolve by id — `BotApp.workflowId` stores the definition's opaque id, not\n // its workflowKey (which is what the builder route takes). The hook refetches\n // on its own when the bound id changes, so binding/unbinding only has to\n // refresh the bot.\n const { workflow, loading: workflowLoading } = useWorkflowDefinition(\n undefined,\n workflowId ?? undefined\n );\n\n const openBuilder = useCallback(\n (workflowKey: string, version: string) => {\n navigateTo(`workflows/workflow/${workflowKey}/${version || INITIAL_VERSION}`);\n },\n [navigateTo]\n );\n\n const handleDesign = useCallback(async () => {\n setBusy(true);\n try {\n const workflowKey = generateId();\n const name = `${botName} workflow`;\n const created = await createWorkflow({\n workflowKey,\n version: INITIAL_VERSION,\n name,\n description: t(\n 'botlit.botDetail.workflow.createdDescription',\n 'Behaviour designed for this BotManor bot.'\n ),\n definitionJson: buildSeedDefinition(workflowKey, name),\n });\n\n if (!created) {\n throw new Error(\n t('botlit.botDetail.workflow.createError', 'Failed to create the workflow')\n );\n }\n\n await updateBot({ botId, input: { workflowId: created.id } });\n await onWorkflowChanged();\n toast.success(\n t('botlit.botDetail.workflow.createSuccess', 'Workflow created and bound to this bot')\n );\n openBuilder(created.workflowKey, created.version);\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.botDetail.workflow.createError', 'Failed to create the workflow');\n toast.error(message);\n } finally {\n setBusy(false);\n }\n }, [botId, botName, createWorkflow, onWorkflowChanged, openBuilder, t, updateBot]);\n\n const handleUnbind = useCallback(async () => {\n if (\n !confirm(\n t(\n 'botlit.botDetail.workflow.unbindConfirm',\n 'Unbind this workflow from the bot? The workflow itself is not deleted.'\n )\n )\n ) {\n return;\n }\n setBusy(true);\n try {\n await updateBot({ botId, input: { workflowId: null } });\n await onWorkflowChanged();\n toast.success(t('botlit.botDetail.workflow.unbindSuccess', 'Workflow unbound'));\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.botDetail.workflow.unbindError', 'Failed to unbind the workflow');\n toast.error(message);\n } finally {\n setBusy(false);\n }\n }, [botId, onWorkflowChanged, t, updateBot]);\n\n // ── Not bound ─────────────────────────────────────────────────────────────\n if (!workflowId) {\n return (\n <Card>\n <CardContent className=\"pt-6\">\n <EmptyState\n illustration=\"empty-generic\"\n title={t('botlit.botDetail.workflow.empty.title', 'Design this bot as a workflow')}\n description={t(\n 'botlit.botDetail.workflow.empty.description',\n \"A bot's behaviour is designed as a FluidGrids workflow — the visual canvas where you wire triggers, agents, tools and conditions together. Start one and we will create it, bind it to this bot and open the builder for you.\"\n )}\n action={\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.APP_UPDATE}>\n <Button\n leftIcon={<Sparkles size={16} />}\n isLoading={busy}\n disabled={busy}\n onClick={() => void handleDesign()}\n >\n {t('botlit.botDetail.workflow.empty.action', 'Design in workflow builder')}\n </Button>\n </BotmanorPermissionGate>\n }\n />\n </CardContent>\n </Card>\n );\n }\n\n // ── Bound ─────────────────────────────────────────────────────────────────\n return (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Workflow size={18} />\n {t('botlit.botDetail.workflow.bound.title', 'Bound workflow')}\n </CardTitle>\n <CardDescription>\n {t(\n 'botlit.botDetail.workflow.bound.description',\n 'This workflow defines what the bot does when it is invoked.'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n {workflowLoading ? (\n <div className=\"h-24 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ) : workflow ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.name', 'Name')}\n </p>\n <p className=\"font-medium text-text-primary\">{workflow.name}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.key', 'Key')}\n </p>\n <p className=\"mt-1 break-all font-mono text-sm text-text-primary\">\n {workflow.workflowKey}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.version', 'Version')}\n </p>\n <Badge variant=\"outline\" className=\"mt-1\">\n {workflow.version}\n </Badge>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.status', 'Status')}\n </p>\n <div className=\"mt-1\">\n <StatusBadge status={workflow.status} />\n </div>\n </div>\n </div>\n ) : (\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.workflowId', 'Workflow ID')}\n </p>\n <p className=\"mt-1 break-all font-mono text-sm text-text-primary\">{workflowId}</p>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {t(\n 'botlit.botDetail.workflow.unresolved',\n 'This workflow could not be loaded — it may have been deleted. Unbind it and design a new one.'\n )}\n </p>\n </div>\n )}\n\n <div className=\"flex flex-wrap gap-2\">\n {workflow && (\n <Button\n leftIcon={<ExternalLink size={16} />}\n disabled={busy}\n onClick={() => openBuilder(workflow.workflowKey, workflow.version)}\n >\n {t('botlit.botDetail.workflow.actions.openBuilder', 'Open in builder')}\n </Button>\n )}\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.APP_UPDATE}>\n <Button\n variant=\"outline\"\n leftIcon={<Unlink size={16} />}\n isLoading={busy}\n disabled={busy}\n onClick={() => void handleUnbind()}\n >\n {t('botlit.botDetail.workflow.actions.unbind', 'Unbind')}\n </Button>\n </BotmanorPermissionGate>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n};\n"],"mappings":"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"}
|