@workflow-code/cli 0.2.4-20261011008.codex-cli-web → 0.2.4-20261012001
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/{chunk-WN7VAGK6.js → chunk-5DWX3LL6.js} +1 -1
- package/dist/{chunk-MNY562W5.js → chunk-5FPIZZE4.js} +12 -7
- package/dist/{chunk-PGM3IPWA.js → chunk-SGVXPYCT.js} +1 -0
- package/dist/index.js +4 -4
- package/dist/{local-QLYFH4WT.js → local-XHR2O2FP.js} +2 -2
- package/dist/{mcp-LQ2IV6VA.js → mcp-RPT64F7T.js} +2 -2
- package/dist/web/kanban-workspace.css +76 -10
- package/dist/web/kanban-workspace.js +13517 -308
- package/dist/web/related-projects.mjs +528 -0
- package/dist/web/resource-preview.css +2172 -0
- package/dist/web/resource-preview.js +16515 -0
- package/dist/web/worker.mjs +12 -4
- package/dist/web/workflow-ui.css +76 -10
- package/dist/web/workflow-ui.js +13745 -419
- package/dist/web/workflow-workspace.css +76 -10
- package/dist/web/workflow-workspace.js +13791 -468
- package/dist/{web-7M4WBBL7.js → web-ROA37F4H.js} +921 -207
- package/dist/{workspace-V3UXVFK6.js → workspace-OKFR57T6.js} +1 -1
- package/package.json +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
assertLocalCliCoreCompatible
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5DWX3LL6.js";
|
|
5
5
|
import {
|
|
6
6
|
assertKanbanMethodJson,
|
|
7
7
|
consumeKanbanRelatedProjectRevisionStream,
|
|
@@ -21,12 +21,13 @@ import {
|
|
|
21
21
|
sanitizeKanbanValues
|
|
22
22
|
} from "./chunk-HLMW6LAQ.js";
|
|
23
23
|
import "./chunk-J2JMC4MZ.js";
|
|
24
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-5FPIZZE4.js";
|
|
25
25
|
import {
|
|
26
26
|
clearCliAuthConfig,
|
|
27
|
+
createEmptyWorkflowAuthConfig,
|
|
27
28
|
readCliAuthConfig,
|
|
28
29
|
writeCliAuthConfig
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-SGVXPYCT.js";
|
|
30
31
|
import {
|
|
31
32
|
isWorkflowProjectDataStorageLocationAllowed,
|
|
32
33
|
readWorkflowProjectInfo,
|
|
@@ -34,7 +35,9 @@ import {
|
|
|
34
35
|
} from "./chunk-P6K3NB5S.js";
|
|
35
36
|
import {
|
|
36
37
|
getCurrentLocale,
|
|
37
|
-
i18n
|
|
38
|
+
i18n,
|
|
39
|
+
initI18n,
|
|
40
|
+
normalizeLocale
|
|
38
41
|
} from "./chunk-7WEXRL5A.js";
|
|
39
42
|
import "./chunk-NSPRIPOP.js";
|
|
40
43
|
|
|
@@ -42,6 +45,348 @@ import "./chunk-NSPRIPOP.js";
|
|
|
42
45
|
import { spawn as spawn3 } from "child_process";
|
|
43
46
|
import path8 from "path";
|
|
44
47
|
|
|
48
|
+
// src/web/messages.zh-cn.json
|
|
49
|
+
var messages_zh_cn_default = {
|
|
50
|
+
" and others": "\u7B49",
|
|
51
|
+
", ": "\u3001",
|
|
52
|
+
"{0}": "\u5DF2{0}",
|
|
53
|
+
"{0} \xB7 {1}/{2} items": "{0} \xB7 {1}/{2} \u9879",
|
|
54
|
+
"{0} days": "{0} \u5929",
|
|
55
|
+
"{0} failed": "{0}\u5931\u8D25",
|
|
56
|
+
"{0} hours": "{0} \u5C0F\u65F6",
|
|
57
|
+
"{0} min": "{0} \u5206",
|
|
58
|
+
"{0} must be a valid boolean.": "{0} \u9700\u8981\u662F\u6709\u6548\u5E03\u5C14\u503C\u3002",
|
|
59
|
+
"{0} must be a valid number.": "{0} \u9700\u8981\u662F\u6709\u6548\u6570\u5B57\u3002",
|
|
60
|
+
"{0} must be valid JSON.": "{0} \u9700\u8981\u662F\u6709\u6548\u7684 JSON\u3002",
|
|
61
|
+
"{0} selected": "\u5DF2\u9009 {0} \u9879",
|
|
62
|
+
"{0} tools": "{0} \u4E2A\u5DE5\u5177",
|
|
63
|
+
"{0}...": "\u6B63\u5728{0}",
|
|
64
|
+
"{0}{1} \xB7 {2} items": "\u5DF2{0}{1} \xB7 {2} \u9879",
|
|
65
|
+
"{1} in {0}": "{0} \u4E2D {1}",
|
|
66
|
+
"Account balance ${0}": "\u8D26\u6237\u4F59\u989D ${0}",
|
|
67
|
+
"Add attachment": "\u6DFB\u52A0\u9644\u4EF6",
|
|
68
|
+
"Agent run": "Agent \u8FD0\u884C",
|
|
69
|
+
"All chats": "\u5168\u90E8\u804A\u5929",
|
|
70
|
+
"All conversations": "\u5168\u90E8\u5BF9\u8BDD",
|
|
71
|
+
Apply: "\u5E94\u7528",
|
|
72
|
+
"Apply changes": "\u5E94\u7528\u4FEE\u6539",
|
|
73
|
+
"Approve execution": "\u6279\u51C6\u6267\u884C",
|
|
74
|
+
Ask: "\u63D0\u95EE",
|
|
75
|
+
Attachment: "\u9644\u4EF6",
|
|
76
|
+
"Auto-approve": "\u81EA\u52A8\u6279\u51C6",
|
|
77
|
+
"Awaiting confirmation": "\u7B49\u5F85\u786E\u8BA4",
|
|
78
|
+
"Awaiting confirmation: {0}": "\u7B49\u5F85\u786E\u8BA4{0}",
|
|
79
|
+
"Back to {0}": "\u8FD4\u56DE {0}",
|
|
80
|
+
Branch: "\u5206\u652F",
|
|
81
|
+
"Browser sign-in": "\u6D4F\u89C8\u5668\u767B\u5F55",
|
|
82
|
+
"Call tool": "\u8C03\u7528\u5DE5\u5177",
|
|
83
|
+
Cancel: "\u53D6\u6D88",
|
|
84
|
+
"Cancel editing": "\u53D6\u6D88\u7F16\u8F91",
|
|
85
|
+
Card: "\u5361\u7247",
|
|
86
|
+
Chat: "\u804A\u5929",
|
|
87
|
+
"Choose files": "\u9009\u62E9\u6587\u4EF6",
|
|
88
|
+
Close: "\u5173\u95ED",
|
|
89
|
+
"Close account": "\u5173\u95ED\u8D26\u53F7",
|
|
90
|
+
"Close conversation history": "\u5173\u95ED\u5BF9\u8BDD\u5386\u53F2",
|
|
91
|
+
"Close parameters": "\u5173\u95ED\u53C2\u6570",
|
|
92
|
+
"Close search": "\u5173\u95ED\u641C\u7D22",
|
|
93
|
+
Code: "\u4EE3\u7801",
|
|
94
|
+
Command: "\u547D\u4EE4",
|
|
95
|
+
"Compact context": "\u538B\u7F29\u4E0A\u4E0B\u6587",
|
|
96
|
+
Complete: "\u5B8C\u6210",
|
|
97
|
+
Completed: "\u5DF2\u5B8C\u6210",
|
|
98
|
+
"Configuration {0}": "\u914D\u7F6E {0}",
|
|
99
|
+
"Configuration actions": "\u914D\u7F6E\u64CD\u4F5C",
|
|
100
|
+
"Configuration name": "\u914D\u7F6E\u540D\u79F0",
|
|
101
|
+
"Connecting to project": "\u6B63\u5728\u8FDE\u63A5\u9879\u76EE",
|
|
102
|
+
"Connection changed.": "\u8FDE\u63A5\u5DF2\u53D8\u66F4\u3002",
|
|
103
|
+
"Copy timed out.": "\u590D\u5236\u8D85\u65F6\u3002",
|
|
104
|
+
"Host clipboard is unavailable.": "\u5BBF\u4E3B\u526A\u8D34\u677F\u4E0D\u53EF\u7528\u3002",
|
|
105
|
+
"Connection interrupted": "\u8FDE\u63A5\u5DF2\u4E2D\u65AD",
|
|
106
|
+
"Connection interrupted.": "\u8FDE\u63A5\u5DF2\u4E2D\u65AD\u3002",
|
|
107
|
+
"Connection not verified": "\u8FDE\u63A5\u672A\u9A8C\u8BC1",
|
|
108
|
+
Conversation: "\u5BF9\u8BDD",
|
|
109
|
+
"Conversation actions": "\u5BF9\u8BDD\u64CD\u4F5C",
|
|
110
|
+
"Conversation history": "\u5BF9\u8BDD\u5386\u53F2",
|
|
111
|
+
"Conversation messages": "\u5BF9\u8BDD\u6D88\u606F",
|
|
112
|
+
"Conversation name": "\u5BF9\u8BDD\u540D\u79F0",
|
|
113
|
+
Conversations: "\u5BF9\u8BDD\u5217\u8868",
|
|
114
|
+
Copied: "\u5DF2\u590D\u5236",
|
|
115
|
+
Copy: "\u590D\u5236",
|
|
116
|
+
"Copy {0}": "\u590D\u5236{0}",
|
|
117
|
+
"Copy code": "\u590D\u5236\u4EE3\u7801",
|
|
118
|
+
"Copy message": "\u590D\u5236\u6D88\u606F",
|
|
119
|
+
"Current configuration": "\u5F53\u524D\u914D\u7F6E",
|
|
120
|
+
Daily: "\u6BCF\u65E5",
|
|
121
|
+
Default: "\u9ED8\u8BA4",
|
|
122
|
+
Delete: "\u5220\u9664",
|
|
123
|
+
"Delete conversation": "\u5220\u9664\u5BF9\u8BDD",
|
|
124
|
+
"Delete record": "\u5220\u9664\u8BB0\u5F55",
|
|
125
|
+
"Delete this configuration?": "\u5220\u9664\u8FD9\u4E2A\u914D\u7F6E\uFF1F",
|
|
126
|
+
"Delete this conversation?": "\u5220\u9664\u8FD9\u4E2A\u5BF9\u8BDD\uFF1F",
|
|
127
|
+
"Delete this run?": "\u5220\u9664\u8FD9\u6761\u8FD0\u884C\u8BB0\u5F55\uFF1F",
|
|
128
|
+
Deny: "\u62D2\u7EDD",
|
|
129
|
+
Details: "\u8BE6\u60C5",
|
|
130
|
+
"Download file": "\u4E0B\u8F7D\u6587\u4EF6",
|
|
131
|
+
"Duration {0}{1}s": "\u7528\u65F6 {0}{1}s",
|
|
132
|
+
Edit: "\u7F16\u8F91",
|
|
133
|
+
"Edit file": "\u7F16\u8F91\u6587\u4EF6",
|
|
134
|
+
"Edit queued message": "\u7F16\u8F91\u5F85\u53D1\u6D88\u606F",
|
|
135
|
+
Email: "\u90AE\u7BB1",
|
|
136
|
+
Enable: "\u542F\u7528",
|
|
137
|
+
"End run": "\u7ED3\u675F\u8FD0\u884C",
|
|
138
|
+
"Ended: {0}": "\u7ED3\u675F\uFF1A{0}",
|
|
139
|
+
"Enter your email and password.": "\u8BF7\u8F93\u5165\u90AE\u7BB1\u548C\u5BC6\u7801\u3002",
|
|
140
|
+
"Entrypoint parameters": "\u5165\u53E3\u53C2\u6570",
|
|
141
|
+
Entrypoints: "\u8FD0\u884C\u5165\u53E3",
|
|
142
|
+
Error: "\u9519\u8BEF",
|
|
143
|
+
Execute: "\u6267\u884C",
|
|
144
|
+
"Execute plan": "\u6309\u8BA1\u5212\u6267\u884C",
|
|
145
|
+
"Export conversation": "\u5BFC\u51FA\u5BF9\u8BDD",
|
|
146
|
+
"Export record": "\u5BFC\u51FA\u8BB0\u5F55",
|
|
147
|
+
"Export run": "\u5BFC\u51FA\u8FD0\u884C\u8BB0\u5F55",
|
|
148
|
+
Failed: "\u5931\u8D25",
|
|
149
|
+
"Failed to load Kanban": "\u770B\u677F\u52A0\u8F7D\u5931\u8D25",
|
|
150
|
+
File: "\u6587\u4EF6",
|
|
151
|
+
"File exceeds 50 MiB.": "\u6587\u4EF6\u8D85\u8FC7 50 MiB\u3002",
|
|
152
|
+
"Files in {0}": "{0} \u4E2D\u7684\u6587\u4EF6",
|
|
153
|
+
"Fit graph": "\u9002\u914D\u6D41\u7A0B",
|
|
154
|
+
Form: "\u8868\u5355",
|
|
155
|
+
"Full screen": "\u5168\u5C4F",
|
|
156
|
+
"Generated file": "\u751F\u6210\u6587\u4EF6",
|
|
157
|
+
"Go to latest message": "\u56DE\u5230\u6700\u65B0\u6D88\u606F",
|
|
158
|
+
Graph: "\u6D41\u7A0B",
|
|
159
|
+
Height: "\u9AD8\u5EA6",
|
|
160
|
+
"Hide conversations": "\u9690\u85CF\u5BF9\u8BDD\u5217\u8868",
|
|
161
|
+
"Hide Kanban configurations": "\u9690\u85CF\u770B\u677F\u914D\u7F6E",
|
|
162
|
+
"Hide password": "\u9690\u85CF\u5BC6\u7801",
|
|
163
|
+
"Hide runs": "\u9690\u85CF\u8FD0\u884C\u8BB0\u5F55",
|
|
164
|
+
Image: "\u56FE\u7247",
|
|
165
|
+
"Incorrect email or password, or sign-in expired.": "\u90AE\u7BB1\u6216\u5BC6\u7801\u4E0D\u6B63\u786E\uFF0C\u6216\u767B\u5F55\u5DF2\u5931\u6548\u3002",
|
|
166
|
+
Interrupted: "\u5DF2\u4E2D\u65AD",
|
|
167
|
+
"JSON argument array": "JSON \u53C2\u6570\u6570\u7EC4",
|
|
168
|
+
"Just now": "\u521A\u521A",
|
|
169
|
+
Kanban: "\u770B\u677F",
|
|
170
|
+
"Kanban configurations": "\u770B\u677F\u914D\u7F6E",
|
|
171
|
+
List: "\u5217\u51FA",
|
|
172
|
+
"List files": "\u5217\u51FA\u6587\u4EF6",
|
|
173
|
+
"Load more": "\u52A0\u8F7D\u66F4\u591A",
|
|
174
|
+
"Loading Kanban": "\u6B63\u5728\u52A0\u8F7D\u770B\u677F",
|
|
175
|
+
"Loading parameters...": "\u52A0\u8F7D\u53C2\u6570...",
|
|
176
|
+
"Loading...": "\u52A0\u8F7D\u4E2D...",
|
|
177
|
+
"Lock title": "\u9501\u5B9A\u6807\u9898",
|
|
178
|
+
Logs: "\u65E5\u5FD7",
|
|
179
|
+
Loop: "\u5FAA\u73AF",
|
|
180
|
+
"Loop end": "\u5FAA\u73AF\u7ED3\u675F",
|
|
181
|
+
"Managed by server": "\u7531\u670D\u52A1\u5668\u7BA1\u7406",
|
|
182
|
+
"Manage this account through Studio.": "\u8BF7\u5728 Studio \u4E2D\u7BA1\u7406\u6B64\u8D26\u53F7\u3002",
|
|
183
|
+
Merge: "\u6C47\u5408",
|
|
184
|
+
Message: "\u6D88\u606F",
|
|
185
|
+
Monthly: "\u6BCF\u6708",
|
|
186
|
+
"Move up": "\u4E0A\u79FB",
|
|
187
|
+
"New configuration": "\u65B0\u5EFA\u914D\u7F6E",
|
|
188
|
+
"New conversation": "\u65B0\u5BF9\u8BDD",
|
|
189
|
+
"New run": "\u65B0\u5EFA\u8FD0\u884C",
|
|
190
|
+
Next: "\u4E0B\u4E00\u4E2A",
|
|
191
|
+
"Next question": "\u4E0B\u4E00\u95EE",
|
|
192
|
+
No: "\u5426",
|
|
193
|
+
"No active subscriptions": "\u6682\u65E0\u5DF2\u8BA2\u9605\u5957\u9910",
|
|
194
|
+
"No additional details": "\u6682\u65E0\u989D\u5916\u8BE6\u60C5",
|
|
195
|
+
"No available options": "\u6682\u65E0\u53EF\u7528\u9009\u9879",
|
|
196
|
+
"No conversations": "\u6682\u65E0\u5BF9\u8BDD",
|
|
197
|
+
"No logs": "\u6682\u65E0\u65E5\u5FD7",
|
|
198
|
+
"No matching conversations": "\u6CA1\u6709\u5339\u914D\u7684\u5BF9\u8BDD",
|
|
199
|
+
"No options": "\u6682\u65E0\u9009\u9879",
|
|
200
|
+
"No page methods registered": "\u9875\u9762\u6682\u672A\u6CE8\u518C\u65B9\u6CD5",
|
|
201
|
+
"No runs": "\u6682\u65E0\u8FD0\u884C\u8BB0\u5F55",
|
|
202
|
+
"No static workflow structure is available. Showing recorded execution nodes.": "\u672A\u89E3\u6790\u5230\u9759\u6001\u6D41\u7A0B\u7ED3\u6784\uFF0C\u663E\u793A\u5DF2\u6709\u8FD0\u884C\u8282\u70B9\u3002",
|
|
203
|
+
"No tools declared by this project": "\u9879\u76EE\u672A\u58F0\u660E\u5DE5\u5177",
|
|
204
|
+
"Node {0} does not match the static structure.": "\u8282\u70B9 {0} \u672A\u5339\u914D\u9759\u6001\u7ED3\u6784\u3002",
|
|
205
|
+
"Not selected": "\u672A\u9009\u62E9",
|
|
206
|
+
"Open {0}": "\u6253\u5F00 {0}",
|
|
207
|
+
"Page methods": "\u9875\u9762\u65B9\u6CD5",
|
|
208
|
+
"Parameter mode": "\u53C2\u6570\u6A21\u5F0F",
|
|
209
|
+
Parameters: "\u53C2\u6570",
|
|
210
|
+
"Parameters JSON": "\u53C2\u6570 JSON",
|
|
211
|
+
Password: "\u5BC6\u7801",
|
|
212
|
+
"Pending approval": "\u5F85\u786E\u8BA4",
|
|
213
|
+
Phone: "\u624B\u673A",
|
|
214
|
+
Plan: "\u8BA1\u5212",
|
|
215
|
+
"Please confirm {0}": "\u8BF7\u786E\u8BA4 {0}",
|
|
216
|
+
"Please enter {0}.": "\u8BF7\u586B\u5199 {0}\u3002",
|
|
217
|
+
"Preview size": "\u9884\u89C8\u5C3A\u5BF8",
|
|
218
|
+
Previous: "\u4E0A\u4E00\u4E2A",
|
|
219
|
+
"Previous question": "\u4E0A\u4E00\u95EE",
|
|
220
|
+
"Project connection expired": "\u9879\u76EE\u8FDE\u63A5\u5DF2\u5931\u6548",
|
|
221
|
+
"Project connection expired.": "\u9879\u76EE\u8FDE\u63A5\u5DF2\u5931\u6548\u3002",
|
|
222
|
+
"Queued messages ({0})": "\u5F85\u53D1\u6D88\u606F ({0})",
|
|
223
|
+
Read: "\u8BFB\u53D6",
|
|
224
|
+
"Read data": "\u8BFB\u53D6\u6570\u636E",
|
|
225
|
+
"Read file": "\u8BFB\u53D6\u6587\u4EF6",
|
|
226
|
+
"Read knowledge": "\u8BFB\u53D6\u77E5\u8BC6",
|
|
227
|
+
"Read web page": "\u8BFB\u53D6\u7F51\u9875",
|
|
228
|
+
Reasoning: "\u601D\u8003\u8FC7\u7A0B",
|
|
229
|
+
Reconnect: "\u91CD\u65B0\u8FDE\u63A5",
|
|
230
|
+
"Reconnecting...": "\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5...",
|
|
231
|
+
"Reconnection will start when the network is available.": "\u7F51\u7EDC\u6062\u590D\u540E\u5C06\u81EA\u52A8\u91CD\u8FDE\u3002",
|
|
232
|
+
"Refresh Kanban": "\u5237\u65B0\u770B\u677F",
|
|
233
|
+
"Refresh parameters": "\u5237\u65B0\u53C2\u6570",
|
|
234
|
+
"Refresh usage": "\u5237\u65B0\u7528\u91CF",
|
|
235
|
+
"Related projects are unavailable: {0}": "\u5173\u8054\u9879\u76EE\u4E0D\u53EF\u7528\uFF1A{0}",
|
|
236
|
+
"Remove attachment": "\u79FB\u9664\u9644\u4EF6",
|
|
237
|
+
"Remove file": "\u79FB\u9664\u6587\u4EF6",
|
|
238
|
+
Rename: "\u91CD\u547D\u540D",
|
|
239
|
+
"Rename configuration": "\u91CD\u547D\u540D\u914D\u7F6E",
|
|
240
|
+
"Rename conversation": "\u91CD\u547D\u540D\u5BF9\u8BDD",
|
|
241
|
+
"Reopen using the project's run URL.": "\u8BF7\u4F7F\u7528\u9879\u76EE\u8FD0\u884C\u5730\u5740\u91CD\u65B0\u6253\u5F00\u3002",
|
|
242
|
+
Rerun: "\u91CD\u65B0\u8FD0\u884C",
|
|
243
|
+
Reset: "\u91CD\u7F6E",
|
|
244
|
+
"Reset options": "\u91CD\u7F6E\u9009\u9879",
|
|
245
|
+
"Resize preview": "\u8C03\u6574\u9884\u89C8\u5C3A\u5BF8",
|
|
246
|
+
Responding: "\u56DE\u590D\u4E2D",
|
|
247
|
+
"Responding...": "\u6B63\u5728\u56DE\u590D...",
|
|
248
|
+
Result: "\u7ED3\u679C",
|
|
249
|
+
"Resume queue": "\u7EE7\u7EED\u53D1\u9001\u961F\u5217",
|
|
250
|
+
"Resume run": "\u7EE7\u7EED\u8FD0\u884C",
|
|
251
|
+
Retry: "\u91CD\u8BD5",
|
|
252
|
+
"Retry loading parameters": "\u91CD\u8BD5\u52A0\u8F7D\u53C2\u6570",
|
|
253
|
+
"Retry save": "\u91CD\u8BD5\u4FDD\u5B58",
|
|
254
|
+
"Revise instructions": "\u4FEE\u6539\u8981\u6C42",
|
|
255
|
+
"Revise plan": "\u4FEE\u6539\u8BA1\u5212",
|
|
256
|
+
Run: "\u8FD0\u884C",
|
|
257
|
+
"Run arguments must be an array of strings.": "\u8FD0\u884C\u53C2\u6570\u9700\u8981\u662F\u5B57\u7B26\u4E32\u6570\u7EC4\u3002",
|
|
258
|
+
"Run command": "\u8FD0\u884C\u547D\u4EE4",
|
|
259
|
+
"Run failed": "\u8FD0\u884C\u5931\u8D25",
|
|
260
|
+
"Run interrupted": "\u8FD0\u884C\u5DF2\u4E2D\u65AD",
|
|
261
|
+
"Run logs": "\u8FD0\u884C\u65E5\u5FD7",
|
|
262
|
+
"Run parameters": "\u8FD0\u884C\u53C2\u6570",
|
|
263
|
+
"Run records \xB7 {0} items, {1} failed": "\u8FD0\u884C\u8BB0\u5F55 \xB7 {0} \u9879\uFF0C{1} \u9879\u5931\u8D25",
|
|
264
|
+
"Run subtask": "\u8FD0\u884C\u5B50\u4EFB\u52A1",
|
|
265
|
+
"Run timed out": "\u8FD0\u884C\u8D85\u65F6",
|
|
266
|
+
Running: "\u8FD0\u884C\u4E2D",
|
|
267
|
+
"Running...": "\u6B63\u5728\u8FD0\u884C...",
|
|
268
|
+
Runs: "\u8FD0\u884C\u8BB0\u5F55",
|
|
269
|
+
Save: "\u4FDD\u5B58",
|
|
270
|
+
"Save queued message": "\u4FDD\u5B58\u5F85\u53D1\u6D88\u606F",
|
|
271
|
+
Search: "\u641C\u7D22",
|
|
272
|
+
"Search all conversations": "\u641C\u7D22\u5168\u90E8\u5BF9\u8BDD",
|
|
273
|
+
"Search content": "\u641C\u7D22\u5185\u5BB9",
|
|
274
|
+
"Search conversation content": "\u641C\u7D22\u5BF9\u8BDD\u5185\u5BB9",
|
|
275
|
+
"Search conversations": "\u641C\u7D22\u5BF9\u8BDD",
|
|
276
|
+
"Search knowledge": "\u641C\u7D22\u77E5\u8BC6",
|
|
277
|
+
"Search runs": "\u641C\u7D22\u8FD0\u884C\u8BB0\u5F55",
|
|
278
|
+
"Search web": "\u641C\u7D22\u7F51\u9875",
|
|
279
|
+
"Select up to 32 files.": "\u6700\u591A\u9009\u62E9 32 \u4E2A\u6587\u4EF6\u3002",
|
|
280
|
+
"Select...": "\u9009\u62E9...",
|
|
281
|
+
Send: "\u53D1\u9001",
|
|
282
|
+
"Send a message...": "\u53D1\u9001\u6D88\u606F...",
|
|
283
|
+
"Send now": "\u7ACB\u5373\u53D1\u9001",
|
|
284
|
+
Server: "\u670D\u52A1\u5668",
|
|
285
|
+
"Show conversations": "\u663E\u793A\u5BF9\u8BDD\u5217\u8868",
|
|
286
|
+
"Show Kanban configurations": "\u663E\u793A\u770B\u677F\u914D\u7F6E",
|
|
287
|
+
"Show password": "\u663E\u793A\u5BC6\u7801",
|
|
288
|
+
"Show runs": "\u663E\u793A\u8FD0\u884C\u8BB0\u5F55",
|
|
289
|
+
"Sign in": "\u767B\u5F55",
|
|
290
|
+
"Sign in to Workflow": "\u767B\u5F55 Workflow",
|
|
291
|
+
"Sign in to Workflow Server to connect Kanban data.": "\u8BF7\u767B\u5F55 Workflow Server \u4EE5\u8FDE\u63A5\u770B\u677F\u6570\u636E\u3002",
|
|
292
|
+
"Sign in to Workflow Server, then refresh parameters.": "\u8BF7\u5148\u767B\u5F55 Workflow Server\uFF0C\u7136\u540E\u5237\u65B0\u53C2\u6570\u3002",
|
|
293
|
+
"Sign out": "\u9000\u51FA\u767B\u5F55",
|
|
294
|
+
"Sign-in expired": "\u767B\u5F55\u5DF2\u5931\u6548",
|
|
295
|
+
"Sign-in expired. Sign in to Workflow Server again.": "\u767B\u5F55\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 Workflow Server\u3002",
|
|
296
|
+
"Signed in; account verified": "\u5DF2\u767B\u5F55\uFF0C\u8D26\u53F7\u9A8C\u8BC1\u901A\u8FC7",
|
|
297
|
+
"Signed out": "\u672A\u767B\u5F55",
|
|
298
|
+
"Signing in...": "\u767B\u5F55\u4E2D...",
|
|
299
|
+
"Started: {0}": "\u5F00\u59CB\uFF1A{0}",
|
|
300
|
+
Stop: "\u505C\u6B62",
|
|
301
|
+
"Stop run": "\u505C\u6B62\u8FD0\u884C",
|
|
302
|
+
Stopped: "\u5DF2\u505C\u6B62",
|
|
303
|
+
"Stopped \xB7 {0}/{1} items completed": "\u5DF2\u505C\u6B62 \xB7 {0}/{1} \u9879\u5B8C\u6210",
|
|
304
|
+
"Stopped {0}": "\u5DF2\u505C\u6B62{0}",
|
|
305
|
+
"Submitting...": "\u6B63\u5728\u63D0\u4EA4...",
|
|
306
|
+
"System AI": "\u7CFB\u7EDF AI",
|
|
307
|
+
"System AI usage": "\u7CFB\u7EDF AI \u7528\u91CF",
|
|
308
|
+
Tablet: "\u5E73\u677F",
|
|
309
|
+
"The current session is running.": "\u5F53\u524D\u4F1A\u8BDD\u6B63\u5728\u8FD0\u884C\u3002",
|
|
310
|
+
"The Kanban connection is unavailable.": "\u770B\u677F\u8FDE\u63A5\u4E0D\u53EF\u7528\u3002",
|
|
311
|
+
"The options for {0} have changed. Please select again.": "{0} \u7684\u9009\u9879\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u3002",
|
|
312
|
+
"The project does not support this attachment type.": "\u9879\u76EE\u4E0D\u652F\u6301\u6B64\u7C7B\u9644\u4EF6\u3002",
|
|
313
|
+
"The related project connection was interrupted. Reconnecting.": "\u5173\u8054\u9879\u76EE\u66F4\u65B0\u8FDE\u63A5\u5DF2\u4E2D\u65AD\uFF0C\u6B63\u5728\u91CD\u8FDE\u3002",
|
|
314
|
+
"The request timed out. Check the current status.": "\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u5F53\u524D\u72B6\u6001\u3002",
|
|
315
|
+
Thinking: "\u601D\u8003",
|
|
316
|
+
"Thought complete": "\u601D\u8003\u5B8C\u6210",
|
|
317
|
+
"Timed out": "\u5DF2\u8D85\u65F6",
|
|
318
|
+
Tool: "\u5DE5\u5177",
|
|
319
|
+
"Tool input": "\u5DE5\u5177\u53C2\u6570",
|
|
320
|
+
"Tool permissions": "\u5DE5\u5177\u6743\u9650",
|
|
321
|
+
Trace: "\u8F68\u8FF9",
|
|
322
|
+
Unlimited: "\u4E0D\u9650",
|
|
323
|
+
"Unlock title": "\u89E3\u9664\u6807\u9898\u9501\u5B9A",
|
|
324
|
+
"Unsupported attachment type, or the 50 MiB per file / 32 attachment limit was exceeded.": "\u9644\u4EF6\u7C7B\u578B\u4E0D\u53D7\u652F\u6301\uFF0C\u6216\u8D85\u8FC7\u5355\u6587\u4EF6 50 MiB / 32 \u4E2A\u9644\u4EF6\u9650\u5236\u3002",
|
|
325
|
+
"Update data": "\u66F4\u65B0\u6570\u636E",
|
|
326
|
+
"Update knowledge": "\u66F4\u65B0\u77E5\u8BC6",
|
|
327
|
+
"Update tasks": "\u66F4\u65B0\u4EFB\u52A1\u5217\u8868",
|
|
328
|
+
Validity: "\u6709\u6548\u671F",
|
|
329
|
+
"Verify sign-in": "\u9A8C\u8BC1\u767B\u5F55",
|
|
330
|
+
"Verifying account...": "\u6B63\u5728\u9A8C\u8BC1\u8D26\u53F7...",
|
|
331
|
+
"View all ({0})": "\u67E5\u770B\u5168\u90E8\uFF08{0}\uFF09",
|
|
332
|
+
"Waiting for input": "\u7B49\u5F85\u8F93\u5165",
|
|
333
|
+
"Waiting for tool results...": "\u7B49\u5F85\u5DE5\u5177\u7ED3\u679C...",
|
|
334
|
+
Weekly: "\u6BCF\u5468",
|
|
335
|
+
Width: "\u5BBD\u5EA6",
|
|
336
|
+
Workflow: "\u5DE5\u4F5C\u6D41",
|
|
337
|
+
"Workflow Account": "Workflow \u8D26\u53F7",
|
|
338
|
+
"Workflow entrypoint": "\u5DE5\u4F5C\u6D41\u5165\u53E3",
|
|
339
|
+
"Workflow run": "\u5DE5\u4F5C\u6D41\u8FD0\u884C",
|
|
340
|
+
Write: "\u5199\u5165",
|
|
341
|
+
"Write anything": "\u968F\u5FC3\u8F93\u5165",
|
|
342
|
+
"Write file": "\u5199\u5165\u6587\u4EF6",
|
|
343
|
+
Yes: "\u662F",
|
|
344
|
+
"Zoom in": "\u653E\u5927",
|
|
345
|
+
"Zoom out": "\u7F29\u5C0F",
|
|
346
|
+
"Open resource": "\u6253\u5F00\u8D44\u6E90",
|
|
347
|
+
"Show in conversation": "\u5728\u5BF9\u8BDD\u4E2D\u5C55\u793A",
|
|
348
|
+
"Hide preview": "\u6536\u8D77\u9884\u89C8",
|
|
349
|
+
"Open in new window": "\u5728\u65B0\u7A97\u53E3\u4E2D\u6253\u5F00",
|
|
350
|
+
"Tool call details": "\u5DE5\u5177\u8C03\u7528\u8BE6\u60C5",
|
|
351
|
+
"Please sign in to preview this server resource.": "\u8BF7\u767B\u5F55\u540E\u9884\u89C8\u670D\u52A1\u5668\u8D44\u6E90\u3002",
|
|
352
|
+
"You do not have access to this resource.": "\u4F60\u6CA1\u6709\u6B64\u8D44\u6E90\u7684\u8BBF\u95EE\u6743\u9650\u3002",
|
|
353
|
+
"The resource project is unavailable.": "\u8D44\u6E90\u6240\u5C5E\u9879\u76EE\u4E0D\u53EF\u7528\u3002",
|
|
354
|
+
"The resource project is unavailable. Import or open the target project first.": "\u8D44\u6E90\u6240\u5C5E\u9879\u76EE\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5148\u5BFC\u5165\u6216\u6253\u5F00\u76EE\u6807\u9879\u76EE\u3002",
|
|
355
|
+
"The project does not allow this storage location.": "\u9879\u76EE\u4E0D\u5141\u8BB8\u4F7F\u7528\u6B64\u5B58\u50A8\u4F4D\u7F6E\u3002",
|
|
356
|
+
"This resource no longer exists.": "\u6B64\u8D44\u6E90\u5DF2\u4E0D\u5B58\u5728\u3002",
|
|
357
|
+
"Server source files cannot be previewed by this card.": "\u6B64\u5361\u7247\u4E0D\u652F\u6301\u9884\u89C8\u670D\u52A1\u5668\u6E90\u7801\u6587\u4EF6\u3002",
|
|
358
|
+
"File is outside this project.": "\u6587\u4EF6\u4E0D\u5728\u6B64\u9879\u76EE\u4E2D\u3002",
|
|
359
|
+
"File preview exceeds 2 MiB.": "\u6587\u4EF6\u9884\u89C8\u5927\u5C0F\u8D85\u8FC7 2 MiB\u3002",
|
|
360
|
+
"This project does not provide a Kanban preview.": "\u6B64\u9879\u76EE\u4E0D\u63D0\u4F9B\u770B\u677F\u9884\u89C8\u3002",
|
|
361
|
+
"Invalid resource preview URL.": "\u8D44\u6E90\u9884\u89C8\u5730\u5740\u65E0\u6548\u3002",
|
|
362
|
+
"This preview configuration no longer exists.": "\u6B64\u9884\u89C8\u914D\u7F6E\u5DF2\u4E0D\u5B58\u5728\u3002",
|
|
363
|
+
"Resource access changed. Open the card again.": "\u8D44\u6E90\u8BBF\u95EE\u72B6\u6001\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u5361\u7247\u3002",
|
|
364
|
+
"This resource preview is read-only.": "\u6B64\u8D44\u6E90\u9884\u89C8\u4E3A\u53EA\u8BFB\u3002",
|
|
365
|
+
"Invalid resource action.": "\u8D44\u6E90\u64CD\u4F5C\u65E0\u6548\u3002",
|
|
366
|
+
"Run no longer exists.": "\u6B64\u8FD0\u884C\u5DF2\u4E0D\u5B58\u5728\u3002",
|
|
367
|
+
"Conversation changed.": "\u4F1A\u8BDD\u5DF2\u53D8\u5316\u3002",
|
|
368
|
+
"Unable to load this preview. Retry or open it separately.": "\u65E0\u6CD5\u52A0\u8F7D\u6B64\u9884\u89C8\uFF0C\u8BF7\u91CD\u8BD5\u6216\u5355\u72EC\u6253\u5F00\u3002",
|
|
369
|
+
"Open in browser": "\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00",
|
|
370
|
+
"No resources": "\u6682\u65E0\u8D44\u6E90",
|
|
371
|
+
"Allow popups to open this resource in a new window.": "\u8BF7\u5141\u8BB8\u5F39\u51FA\u7A97\u53E3\u4EE5\u5355\u72EC\u6253\u5F00\u6B64\u8D44\u6E90\u3002"
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
// src/web/localization.ts
|
|
375
|
+
if (typeof document !== "undefined" && document.documentElement) {
|
|
376
|
+
const requested = new URL(location.href).searchParams.get("workflowLocale");
|
|
377
|
+
initI18n({ preference: normalizeLocale(requested || document.documentElement.lang || navigator.language) });
|
|
378
|
+
} else if (!i18n.isInitialized) {
|
|
379
|
+
initI18n();
|
|
380
|
+
}
|
|
381
|
+
i18n.addResourceBundle("zh-CN", "cliWeb", messages_zh_cn_default);
|
|
382
|
+
i18n.addResourceBundle("en", "cliWeb", Object.fromEntries(Object.keys(messages_zh_cn_default).map((message) => [message, message])));
|
|
383
|
+
var locale = getCurrentLocale;
|
|
384
|
+
function t(message, ...args) {
|
|
385
|
+
const translate = i18n.t;
|
|
386
|
+
const text = translate(message, { ns: "cliWeb", keySeparator: false, nsSeparator: false, fallbackLng: false, defaultValue: message });
|
|
387
|
+
return text.replace(/\{(\d+)\}/g, (placeholder, index) => Number(index) < args.length ? String(args[Number(index)]) : placeholder);
|
|
388
|
+
}
|
|
389
|
+
|
|
45
390
|
// src/web/server.ts
|
|
46
391
|
import { createServer } from "http";
|
|
47
392
|
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "crypto";
|
|
@@ -140,33 +485,33 @@ function validateWorkflowValues(fields, values, states = {}) {
|
|
|
140
485
|
const value = workflowParamValue(field, values[field.name]);
|
|
141
486
|
const label = field.label || field.name;
|
|
142
487
|
if (field.required && (typeof value === "string" && !value.trim() || Array.isArray(value) && !value.length)) {
|
|
143
|
-
throw new Error(
|
|
488
|
+
throw new Error(t("Please enter {0}.", label));
|
|
144
489
|
}
|
|
145
490
|
if (value === "" || Array.isArray(value) && !value.length) {
|
|
146
491
|
continue;
|
|
147
492
|
}
|
|
148
493
|
if (field.type === "number" && (typeof value !== "string" || !Number.isFinite(Number(value)))) {
|
|
149
|
-
throw new Error(
|
|
494
|
+
throw new Error(t("{0} must be a valid number.", label));
|
|
150
495
|
}
|
|
151
496
|
if (field.type === "boolean" && value !== "true" && value !== "false") {
|
|
152
|
-
throw new Error(
|
|
497
|
+
throw new Error(t("{0} must be a valid boolean.", label));
|
|
153
498
|
}
|
|
154
499
|
if (field.format === "json" || field.type === "file" || field.control === "audio") {
|
|
155
500
|
let parsed;
|
|
156
501
|
try {
|
|
157
502
|
parsed = JSON.parse(typeof value === "string" ? value : JSON.stringify(value));
|
|
158
503
|
} catch {
|
|
159
|
-
throw new Error(
|
|
504
|
+
throw new Error(t("{0} must be valid JSON.", label));
|
|
160
505
|
}
|
|
161
506
|
if (field.required && (field.type === "file" || field.control === "audio") && (parsed === null || Array.isArray(parsed) && !parsed.length)) {
|
|
162
|
-
throw new Error(
|
|
507
|
+
throw new Error(t("Please enter {0}.", label));
|
|
163
508
|
}
|
|
164
509
|
}
|
|
165
510
|
const choices = states[field.name]?.resolvedOptions ?? field.options;
|
|
166
511
|
if (choices && choices.length > 0) {
|
|
167
512
|
const allowed = new Set(choices.filter((option) => states[field.name]?.options?.[option.value]?.visible !== false && !states[field.name]?.options?.[option.value]?.disabled).map((option) => option.value));
|
|
168
513
|
if ((Array.isArray(value) ? value : [value]).some((entry) => !allowed.has(entry))) {
|
|
169
|
-
throw new Error(
|
|
514
|
+
throw new Error(t("The options for {0} have changed. Please select again.", label));
|
|
170
515
|
}
|
|
171
516
|
}
|
|
172
517
|
}
|
|
@@ -226,7 +571,7 @@ var ConversationController = class {
|
|
|
226
571
|
delete message.submittingInput;
|
|
227
572
|
if (message.status === "running") {
|
|
228
573
|
message.status = message.pendingInput ? "waiting_for_input" : "aborted";
|
|
229
|
-
message.error = "
|
|
574
|
+
message.error = t("Run interrupted");
|
|
230
575
|
}
|
|
231
576
|
}
|
|
232
577
|
}
|
|
@@ -250,7 +595,7 @@ var ConversationController = class {
|
|
|
250
595
|
}
|
|
251
596
|
blank() {
|
|
252
597
|
const now = Date.now();
|
|
253
|
-
return { id: globalThis.crypto.randomUUID(), title: "
|
|
598
|
+
return { id: globalThis.crypto.randomUUID(), title: t("New conversation"), createdAt: now, updatedAt: now, messages: [], parameterValues: {}, toolPermissionPolicies: {}, queuedInputs: [], draft: { text: "", attachments: [] } };
|
|
254
599
|
}
|
|
255
600
|
async persist() {
|
|
256
601
|
const sessions = this.sessions.map((entry) => ({ ...entry, messages: entry.messages.map(({ submittingInput: _, ...message }) => message) }));
|
|
@@ -355,7 +700,7 @@ var ConversationController = class {
|
|
|
355
700
|
const waiting = entry.messages.some((message) => message.pendingInput);
|
|
356
701
|
if (entry.activeRunId || waiting || entry.queuedInputs?.length) {
|
|
357
702
|
if (!this.project.conversation?.inputQueue?.enabled) {
|
|
358
|
-
throw new Error("
|
|
703
|
+
throw new Error(t("The current session is running."));
|
|
359
704
|
}
|
|
360
705
|
if (entry.draft?.editingQueueId) {
|
|
361
706
|
const index = entry.queuedInputs.findIndex((item) => item.id === entry.draft?.editingQueueId);
|
|
@@ -399,7 +744,7 @@ var ConversationController = class {
|
|
|
399
744
|
}
|
|
400
745
|
if (!answer) {
|
|
401
746
|
if (!entry.messages.length && !entry.titleLocked) {
|
|
402
|
-
entry.title = (input.text || input.attachments[0]?.name || "
|
|
747
|
+
entry.title = (input.text || input.attachments[0]?.name || t("Workflow run")).slice(0, 80);
|
|
403
748
|
}
|
|
404
749
|
entry.messages.push({ id: globalThis.crypto.randomUUID(), role: "user", content: input.text || "", attachments: input.attachments, args, input, entrypointId: input.entrypointId, createdAt: Date.now() });
|
|
405
750
|
answer = { id: globalThis.crypto.randomUUID(), role: "assistant", content: "", items: [], status: "running", createdAt: Date.now(), model: typeof input.params.model === "string" ? input.params.model : void 0, args, entrypointId: input.entrypointId, events: [] };
|
|
@@ -431,7 +776,7 @@ var ConversationController = class {
|
|
|
431
776
|
rawArgs(text) {
|
|
432
777
|
const raw = JSON.parse(text);
|
|
433
778
|
if (!Array.isArray(raw) || !raw.every((arg) => typeof arg === "string")) {
|
|
434
|
-
throw new Error("
|
|
779
|
+
throw new Error(t("Run arguments must be an array of strings."));
|
|
435
780
|
}
|
|
436
781
|
return raw;
|
|
437
782
|
}
|
|
@@ -440,7 +785,7 @@ var ConversationController = class {
|
|
|
440
785
|
try {
|
|
441
786
|
await this.persist();
|
|
442
787
|
if (task.cancelled || this.disposed) {
|
|
443
|
-
throw new Error("
|
|
788
|
+
throw new Error(t("Stopped"));
|
|
444
789
|
}
|
|
445
790
|
const report = await this.runtime.run(request, (event) => {
|
|
446
791
|
if (task.cancelled || this.disposed || this.tasks.get(answer.runId) !== task) {
|
|
@@ -476,13 +821,14 @@ var ConversationController = class {
|
|
|
476
821
|
this.changed();
|
|
477
822
|
});
|
|
478
823
|
if (task.cancelled || this.disposed) {
|
|
479
|
-
throw new Error("
|
|
824
|
+
throw new Error(t("Stopped"));
|
|
480
825
|
}
|
|
481
826
|
answer.report = report;
|
|
482
827
|
answer.status = report.status;
|
|
483
828
|
answer.durationMs = report.durationMs;
|
|
484
|
-
|
|
485
|
-
|
|
829
|
+
const items = report.output?.items ?? [];
|
|
830
|
+
if (report.output && (report.status === "success" || !answer.items?.length || report.status !== "waiting_for_input" && items.length)) {
|
|
831
|
+
answer.items = items;
|
|
486
832
|
answer.content = outputText(answer.items);
|
|
487
833
|
}
|
|
488
834
|
answer.pendingInput = report.status === "waiting_for_input" ? report.pendingUserInput : void 0;
|
|
@@ -497,17 +843,17 @@ var ConversationController = class {
|
|
|
497
843
|
answer.timers = report.timers ?? answer.timers;
|
|
498
844
|
if (report.status === "failed") {
|
|
499
845
|
const data = report.result?.data;
|
|
500
|
-
answer.error = report.result?.error?.message || (data && typeof data === "object" && "errMessage" in data && typeof data.errMessage === "string" ? data.errMessage : "
|
|
846
|
+
answer.error = report.result?.error?.message || (data && typeof data === "object" && "errMessage" in data && typeof data.errMessage === "string" ? data.errMessage : t("Run failed"));
|
|
501
847
|
}
|
|
502
848
|
if (report.status === "aborted" || report.status === "timed_out") {
|
|
503
|
-
answer.error = report.status === "aborted" ? "
|
|
849
|
+
answer.error = report.status === "aborted" ? t("Stopped") : t("Run timed out");
|
|
504
850
|
}
|
|
505
851
|
if (answer.submittingInput && pending && report.status === "failed") {
|
|
506
852
|
answer.status = "waiting_for_input";
|
|
507
853
|
answer.pendingInput = pending;
|
|
508
854
|
}
|
|
509
855
|
if (!answer.content && !answer.items?.length && report.status === "success") {
|
|
510
|
-
answer.content = report.result?.data === void 0 ? "
|
|
856
|
+
answer.content = report.result?.data === void 0 ? t("Completed") : JSON.stringify(report.result.data, null, 2);
|
|
511
857
|
}
|
|
512
858
|
} catch (error) {
|
|
513
859
|
answer.error = error instanceof Error ? error.message : String(error);
|
|
@@ -567,7 +913,7 @@ var ConversationController = class {
|
|
|
567
913
|
if (pending) {
|
|
568
914
|
pending.pendingInput = void 0;
|
|
569
915
|
pending.status = "aborted";
|
|
570
|
-
pending.error = "
|
|
916
|
+
pending.error = t("Stopped");
|
|
571
917
|
this.changed();
|
|
572
918
|
void this.persist().catch((error) => this.options.log(String(error)));
|
|
573
919
|
}
|
|
@@ -789,7 +1135,7 @@ var WorkflowController = class {
|
|
|
789
1135
|
delete record2.submittingInput;
|
|
790
1136
|
if (record2.status === "running" || record2.status === "waiting_for_input") {
|
|
791
1137
|
record2.status = "interrupted";
|
|
792
|
-
record2.error = "
|
|
1138
|
+
record2.error = t("Run interrupted");
|
|
793
1139
|
this.clearTimers(record2);
|
|
794
1140
|
}
|
|
795
1141
|
}
|
|
@@ -1004,8 +1350,9 @@ var WorkflowController = class {
|
|
|
1004
1350
|
record2.report = report;
|
|
1005
1351
|
record2.durationMs = report.durationMs;
|
|
1006
1352
|
record2.status = report.status;
|
|
1007
|
-
|
|
1008
|
-
|
|
1353
|
+
const items = report.output?.items ?? [];
|
|
1354
|
+
if (report.output && (report.status === "success" || !record2.items.length || report.status !== "waiting_for_input" && items.length)) {
|
|
1355
|
+
record2.items = items;
|
|
1009
1356
|
}
|
|
1010
1357
|
record2.nodes = report.nodes ?? record2.nodes;
|
|
1011
1358
|
record2.logs = report.logs ?? record2.logs;
|
|
@@ -1563,10 +1910,10 @@ var KanbanController = class {
|
|
|
1563
1910
|
create(workspace, params, values = createDefaultKanbanValues(params), name) {
|
|
1564
1911
|
if (!name) {
|
|
1565
1912
|
let index = 1;
|
|
1566
|
-
while (workspace.configurations.some((item2) => item2.name ===
|
|
1913
|
+
while (workspace.configurations.some((item2) => item2.name === t("Configuration {0}", index))) {
|
|
1567
1914
|
index++;
|
|
1568
1915
|
}
|
|
1569
|
-
name =
|
|
1916
|
+
name = t("Configuration {0}", index);
|
|
1570
1917
|
}
|
|
1571
1918
|
const date = (/* @__PURE__ */ new Date()).toISOString(), item = { id: randomUUID2(), name, values: { ...values }, createdAt: date, updatedAt: date };
|
|
1572
1919
|
workspace.configurations.push(item);
|
|
@@ -1742,7 +2089,7 @@ var KanbanController = class {
|
|
|
1742
2089
|
this.missingRelatedProjectAliases = [];
|
|
1743
2090
|
const connection = this.hooks.connection();
|
|
1744
2091
|
if (!connection) {
|
|
1745
|
-
this.relatedProjectWarning = "
|
|
2092
|
+
this.relatedProjectWarning = t("Sign in to Workflow Server to connect Kanban data.");
|
|
1746
2093
|
return;
|
|
1747
2094
|
}
|
|
1748
2095
|
const abort = this.feed = new AbortController();
|
|
@@ -1764,7 +2111,7 @@ var KanbanController = class {
|
|
|
1764
2111
|
} }, abort.signal);
|
|
1765
2112
|
} catch {
|
|
1766
2113
|
if (!abort.signal.aborted) {
|
|
1767
|
-
this.relatedProjectWarning = "
|
|
2114
|
+
this.relatedProjectWarning = t("The related project connection was interrupted. Reconnecting.");
|
|
1768
2115
|
this.hooks.changed();
|
|
1769
2116
|
}
|
|
1770
2117
|
}
|
|
@@ -1824,114 +2171,7 @@ var KanbanController = class {
|
|
|
1824
2171
|
}
|
|
1825
2172
|
};
|
|
1826
2173
|
|
|
1827
|
-
// src/web/resource-cards.ts
|
|
1828
|
-
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1829
|
-
var string = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1830
|
-
function conversationResourceCards(items, projectId, conversationId) {
|
|
1831
|
-
const cards = /* @__PURE__ */ new Map();
|
|
1832
|
-
for (const [index, item] of items.entries()) {
|
|
1833
|
-
if (!["agent-part", "opencode-part"].includes(String(item.metadata?.kind)) || item.metadata?.partType !== "tool") {
|
|
1834
|
-
continue;
|
|
1835
|
-
}
|
|
1836
|
-
const content = record(item.content);
|
|
1837
|
-
if (content?.status !== "success") {
|
|
1838
|
-
continue;
|
|
1839
|
-
}
|
|
1840
|
-
let output = record(content.output);
|
|
1841
|
-
if (Array.isArray(output?.content) && output.content.length === 1) {
|
|
1842
|
-
try {
|
|
1843
|
-
output = record(JSON.parse(String(record(output.content[0])?.text)));
|
|
1844
|
-
} catch {
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
let descriptor = record(record(output?.details)?.resourceCard);
|
|
1848
|
-
const input = record(content.input) ?? {};
|
|
1849
|
-
const tool = string(content.tool) ?? "";
|
|
1850
|
-
if (tool === "workflow-ui_present_card") {
|
|
1851
|
-
const presentation = output?.action ? output : input;
|
|
1852
|
-
if (presentation.action === "remove") {
|
|
1853
|
-
cards.delete(String(presentation.cardId));
|
|
1854
|
-
continue;
|
|
1855
|
-
}
|
|
1856
|
-
if (presentation.action !== "upsert") {
|
|
1857
|
-
continue;
|
|
1858
|
-
}
|
|
1859
|
-
descriptor = presentation;
|
|
1860
|
-
}
|
|
1861
|
-
if (!descriptor) {
|
|
1862
|
-
const target2 = { source: "local", projectId, conversationId };
|
|
1863
|
-
let kind2;
|
|
1864
|
-
if (tool === "workflow-kv_kv_store" || /workflow-desktop_(?:read|list|write)_project_kv/.test(tool)) {
|
|
1865
|
-
kind2 = "kv";
|
|
1866
|
-
Object.assign(target2, { projectId: input.projectId ?? projectId, key: input.key, scope: input.scope ?? "project", conversationId: input.conversationId ?? conversationId });
|
|
1867
|
-
} else if (tool === "workflow-knowledge_knowledge_documents" || /workflow-desktop_(?:query|mutate)_project_knowledge/.test(tool)) {
|
|
1868
|
-
kind2 = "knowledge";
|
|
1869
|
-
Object.assign(target2, { projectId: input.projectId ?? projectId, documentId: input.documentId ?? record(output?.document)?.id });
|
|
1870
|
-
} else if (tool === "read" || tool === "workflow-desktop_open_dock_tool" && input.tool === "files") {
|
|
1871
|
-
kind2 = "file";
|
|
1872
|
-
target2.filePath = input.filePath ?? input.path;
|
|
1873
|
-
} else if (tool === "workflow-desktop_open_project" && input.kind === "project") {
|
|
1874
|
-
kind2 = "project";
|
|
1875
|
-
target2.projectId = input.targetId;
|
|
1876
|
-
} else if (tool === "workflow-desktop_open_dock_tool") {
|
|
1877
|
-
if (input.tool === "browser") {
|
|
1878
|
-
kind2 = "browser";
|
|
1879
|
-
target2.url = input.url;
|
|
1880
|
-
}
|
|
1881
|
-
if (input.tool === "preview") {
|
|
1882
|
-
kind2 = "kanban";
|
|
1883
|
-
}
|
|
1884
|
-
if (input.tool === "open-project" && input.openedProjectKind === "project") {
|
|
1885
|
-
kind2 = "project";
|
|
1886
|
-
target2.projectId = input.openedProjectId;
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
if (!kind2) {
|
|
1890
|
-
continue;
|
|
1891
|
-
}
|
|
1892
|
-
descriptor = { kind: kind2, target: target2, title: record(output?.document)?.title };
|
|
1893
|
-
}
|
|
1894
|
-
const kind = descriptor.kind;
|
|
1895
|
-
if (!["kv", "knowledge", "project", "browser", "file", "kanban"].includes(kind)) {
|
|
1896
|
-
continue;
|
|
1897
|
-
}
|
|
1898
|
-
const target = Object.fromEntries(Object.entries(record(descriptor.target) ?? {}).filter((entry) => typeof entry[1] === "string" && entry[1].length < 8192));
|
|
1899
|
-
if (target.source === "current") {
|
|
1900
|
-
target.source = "local";
|
|
1901
|
-
target.projectId = projectId;
|
|
1902
|
-
}
|
|
1903
|
-
if (target.source && !["local", "server"].includes(target.source)) {
|
|
1904
|
-
continue;
|
|
1905
|
-
}
|
|
1906
|
-
if (kind === "browser") {
|
|
1907
|
-
try {
|
|
1908
|
-
const url = new URL(target.url);
|
|
1909
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
|
|
1910
|
-
continue;
|
|
1911
|
-
}
|
|
1912
|
-
} catch {
|
|
1913
|
-
continue;
|
|
1914
|
-
}
|
|
1915
|
-
} else {
|
|
1916
|
-
target.source ??= "local";
|
|
1917
|
-
target.projectId ??= projectId;
|
|
1918
|
-
if (target.scope === "conversation") {
|
|
1919
|
-
target.conversationId ??= conversationId;
|
|
1920
|
-
}
|
|
1921
|
-
if (kind === "file" && (!target.filePath || /^(?:[\\/]|[a-z]:)|(?:^|[\\/])\.\.(?:[\\/]|$)/i.test(target.filePath))) {
|
|
1922
|
-
continue;
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
const id = string(descriptor.cardId) ?? `${kind}:${JSON.stringify(target)}`;
|
|
1926
|
-
const title = (string(descriptor.title) ?? target.key ?? target.documentId ?? target.filePath ?? target.url ?? target.projectId ?? kind).slice(0, 200);
|
|
1927
|
-
cards.set(id, { id, kind, title, target, itemId: item.id ?? String(index) });
|
|
1928
|
-
}
|
|
1929
|
-
return [...cards.values()];
|
|
1930
|
-
}
|
|
1931
|
-
|
|
1932
2174
|
// src/web/workflow-actions.ts
|
|
1933
|
-
import { readFile as readFile4, realpath as realpath3 } from "fs/promises";
|
|
1934
|
-
import path4 from "path";
|
|
1935
2175
|
function createWorkflowActions(controller, runtime, project, revision, changed) {
|
|
1936
2176
|
const states = /* @__PURE__ */ new Map();
|
|
1937
2177
|
const epochs = /* @__PURE__ */ new Map();
|
|
@@ -2026,6 +2266,14 @@ function createWorkflowActions(controller, runtime, project, revision, changed)
|
|
|
2026
2266
|
case "workflow-select":
|
|
2027
2267
|
await controller.select(message.id ? String(message.id) : void 0);
|
|
2028
2268
|
break;
|
|
2269
|
+
case "workflow-trace": {
|
|
2270
|
+
const run = controller.runs.find((record2) => record2.id === message.id);
|
|
2271
|
+
if (!run) {
|
|
2272
|
+
throw new Error("Run no longer exists.");
|
|
2273
|
+
}
|
|
2274
|
+
replies.push({ type: "trace-run", runId: run.id, run });
|
|
2275
|
+
break;
|
|
2276
|
+
}
|
|
2029
2277
|
case "workflow-draft": {
|
|
2030
2278
|
const draft = { values: values(message.params ?? {}, id) };
|
|
2031
2279
|
if (message.inputMode !== void 0) {
|
|
@@ -2108,28 +2356,6 @@ function createWorkflowActions(controller, runtime, project, revision, changed)
|
|
|
2108
2356
|
replies.push({ type: "attachments", entrypointId: id, runId: message.runId, requestId: message.requestId, paramName: message.paramName, files });
|
|
2109
2357
|
break;
|
|
2110
2358
|
}
|
|
2111
|
-
case "resource": {
|
|
2112
|
-
const run = controller.runs.find((record2) => record2.id === message.runId);
|
|
2113
|
-
if (!run) {
|
|
2114
|
-
throw new Error("Run no longer exists.");
|
|
2115
|
-
}
|
|
2116
|
-
const card = conversationResourceCards(run.items, project.id, run.scopeId).find((card2) => card2.id === message.cardId);
|
|
2117
|
-
if (!card || card.target.projectId && card.target.projectId !== project.id || card.target.source === "server") {
|
|
2118
|
-
throw new Error("Resource is outside this project.");
|
|
2119
|
-
}
|
|
2120
|
-
if (card.kind === "kv" || card.kind === "knowledge") {
|
|
2121
|
-
replies.push({ type: "preview", title: card.title, content: await runtime.readResource(project, run.scopeId, card.kind, card.target) });
|
|
2122
|
-
} else if (card.kind === "file") {
|
|
2123
|
-
const root = await realpath3(project.folderPath), filename = await realpath3(path4.resolve(root, card.target.filePath)), relative = path4.relative(root, filename);
|
|
2124
|
-
if (relative.startsWith("..") || path4.isAbsolute(relative)) {
|
|
2125
|
-
throw new Error("File is outside this project.");
|
|
2126
|
-
}
|
|
2127
|
-
replies.push({ type: "preview", title: card.title, content: await readFile4(filename, "utf8") });
|
|
2128
|
-
} else {
|
|
2129
|
-
throw new Error("This resource requires an editor host.");
|
|
2130
|
-
}
|
|
2131
|
-
break;
|
|
2132
|
-
}
|
|
2133
2359
|
default:
|
|
2134
2360
|
throw new Error(`Unsupported workflow action: ${message.type}`);
|
|
2135
2361
|
}
|
|
@@ -2141,25 +2367,26 @@ function createWorkflowActions(controller, runtime, project, revision, changed)
|
|
|
2141
2367
|
// src/web/runtime.ts
|
|
2142
2368
|
import { spawn } from "child_process";
|
|
2143
2369
|
import { existsSync, readFileSync } from "fs";
|
|
2144
|
-
import
|
|
2370
|
+
import path4 from "path";
|
|
2145
2371
|
import { fileURLToPath } from "url";
|
|
2372
|
+
import { WORKFLOW_RUNTIME_LOCALE_ENV } from "workflow-code";
|
|
2146
2373
|
function cliRoot() {
|
|
2147
|
-
let directory =
|
|
2374
|
+
let directory = path4.dirname(fileURLToPath(import.meta.url));
|
|
2148
2375
|
for (let depth = 0; depth < 8; depth++) {
|
|
2149
2376
|
try {
|
|
2150
|
-
if (JSON.parse(readFileSync(
|
|
2377
|
+
if (JSON.parse(readFileSync(path4.join(directory, "package.json"), "utf8")).name === "@workflow-code/cli") {
|
|
2151
2378
|
return directory;
|
|
2152
2379
|
}
|
|
2153
2380
|
} catch {
|
|
2154
2381
|
}
|
|
2155
|
-
directory =
|
|
2382
|
+
directory = path4.dirname(directory);
|
|
2156
2383
|
}
|
|
2157
2384
|
throw new Error("Cannot locate the CLI installation.");
|
|
2158
2385
|
}
|
|
2159
|
-
function createWebRuntime(projectDir, storageDir, connection) {
|
|
2386
|
+
function createWebRuntime(projectDir, storageDir, connection, relatedProjects = () => []) {
|
|
2160
2387
|
const root = cliRoot();
|
|
2161
|
-
const builtWorker =
|
|
2162
|
-
const worker = existsSync(builtWorker) ? builtWorker :
|
|
2388
|
+
const builtWorker = path4.join(root, "dist/web/worker.mjs");
|
|
2389
|
+
const worker = existsSync(builtWorker) ? builtWorker : path4.join(root, "src/web/worker.mjs");
|
|
2163
2390
|
const children = /* @__PURE__ */ new Set();
|
|
2164
2391
|
const active = /* @__PURE__ */ new Map();
|
|
2165
2392
|
let disposed = false;
|
|
@@ -2176,12 +2403,14 @@ function createWebRuntime(projectDir, storageDir, connection) {
|
|
|
2176
2403
|
const env = {
|
|
2177
2404
|
...process.env,
|
|
2178
2405
|
WORKFLOW_RUNTIME_PLATFORM: "cli",
|
|
2406
|
+
[WORKFLOW_RUNTIME_LOCALE_ENV]: locale(),
|
|
2407
|
+
WORKFLOW_CODE_LOCALE: locale(),
|
|
2179
2408
|
WORKFLOW_SERVER_URL: auth?.serverUrl ?? "",
|
|
2180
2409
|
WORKFLOW_SERVER_ADMIN_KEY: auth?.accessToken ?? "",
|
|
2181
2410
|
WORKFLOW_SERVER_TOKEN: "",
|
|
2182
2411
|
WORKFLOW_LLM_SERVER_URL: auth?.serverUrl ?? "",
|
|
2183
2412
|
WORKFLOW_LLM_SERVER_TOKEN: auth?.accessToken ?? "",
|
|
2184
|
-
WORKFLOW_CLI_AUTH_DIR:
|
|
2413
|
+
WORKFLOW_CLI_AUTH_DIR: path4.join(storageDir, "runtime-auth")
|
|
2185
2414
|
};
|
|
2186
2415
|
for (const key of [
|
|
2187
2416
|
"TSX_TSCONFIG_PATH",
|
|
@@ -2266,7 +2495,7 @@ function createWebRuntime(projectDir, storageDir, connection) {
|
|
|
2266
2495
|
}
|
|
2267
2496
|
});
|
|
2268
2497
|
child.stdin.on("error", () => void 0);
|
|
2269
|
-
child.stdin.end(JSON.stringify({ ...request, storageDir, runtimeProjectDir: projectDir }));
|
|
2498
|
+
child.stdin.end(JSON.stringify({ ...request, storageDir, runtimeProjectDir: projectDir, relatedProjects: relatedProjects() }));
|
|
2270
2499
|
});
|
|
2271
2500
|
return {
|
|
2272
2501
|
available: true,
|
|
@@ -2304,10 +2533,129 @@ function createWebRuntime(projectDir, storageDir, connection) {
|
|
|
2304
2533
|
};
|
|
2305
2534
|
}
|
|
2306
2535
|
|
|
2536
|
+
// src/web/resource-cards.ts
|
|
2537
|
+
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2538
|
+
var string = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
2539
|
+
function conversationResourceCards(items, projectId, conversationId, storageSource = "local") {
|
|
2540
|
+
const cards = /* @__PURE__ */ new Map();
|
|
2541
|
+
for (const [index, item] of items.entries()) {
|
|
2542
|
+
if (!["agent-part", "opencode-part"].includes(String(item.metadata?.kind)) || item.metadata?.partType !== "tool") {
|
|
2543
|
+
continue;
|
|
2544
|
+
}
|
|
2545
|
+
const content = record(item.content);
|
|
2546
|
+
if (content?.status !== "success") {
|
|
2547
|
+
continue;
|
|
2548
|
+
}
|
|
2549
|
+
let output = record(content.output);
|
|
2550
|
+
const details = record(output?.details);
|
|
2551
|
+
if (Array.isArray(output?.content) && output.content.length === 1) {
|
|
2552
|
+
try {
|
|
2553
|
+
output = record(JSON.parse(String(record(output.content[0])?.text)));
|
|
2554
|
+
} catch {
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
let descriptor = record(details?.resourceCard) ?? record(record(output?.details)?.resourceCard);
|
|
2558
|
+
const input = record(content.input) ?? {};
|
|
2559
|
+
const tool = string(content.tool) ?? "";
|
|
2560
|
+
if (tool === "workflow-ui_present_card") {
|
|
2561
|
+
const presentation = output?.action ? output : input;
|
|
2562
|
+
if (presentation.action === "remove") {
|
|
2563
|
+
cards.delete(String(presentation.cardId));
|
|
2564
|
+
continue;
|
|
2565
|
+
}
|
|
2566
|
+
if (presentation.action !== "upsert") {
|
|
2567
|
+
continue;
|
|
2568
|
+
}
|
|
2569
|
+
descriptor = presentation;
|
|
2570
|
+
}
|
|
2571
|
+
if (!descriptor) {
|
|
2572
|
+
const target2 = { source: "local", projectId, conversationId };
|
|
2573
|
+
let kind2;
|
|
2574
|
+
if (tool === "workflow-kv_kv_store" || /workflow-desktop_(?:read|list|write)_project_kv/.test(tool)) {
|
|
2575
|
+
kind2 = "kv";
|
|
2576
|
+
Object.assign(target2, { projectId: input.projectId ?? projectId, key: input.key, scope: input.scope ?? "project", conversationId: input.conversationId ?? conversationId });
|
|
2577
|
+
} else if (tool === "workflow-knowledge_knowledge_documents" || /workflow-desktop_(?:query|mutate)_project_knowledge/.test(tool)) {
|
|
2578
|
+
kind2 = "knowledge";
|
|
2579
|
+
Object.assign(target2, { projectId: input.projectId ?? projectId, documentId: input.documentId ?? record(output?.document)?.id });
|
|
2580
|
+
} else if (tool === "read" || tool === "workflow-desktop_open_dock_tool" && input.tool === "files") {
|
|
2581
|
+
kind2 = "file";
|
|
2582
|
+
target2.filePath = input.filePath ?? input.path;
|
|
2583
|
+
} else if (tool === "workflow-desktop_open_project" && input.kind === "project") {
|
|
2584
|
+
kind2 = "project";
|
|
2585
|
+
target2.projectId = input.targetId;
|
|
2586
|
+
} else if (tool === "workflow-desktop_open_dock_tool") {
|
|
2587
|
+
if (input.tool === "browser") {
|
|
2588
|
+
kind2 = "browser";
|
|
2589
|
+
target2.url = input.url;
|
|
2590
|
+
}
|
|
2591
|
+
if (input.tool === "preview") {
|
|
2592
|
+
kind2 = "kanban";
|
|
2593
|
+
}
|
|
2594
|
+
if (input.tool === "open-project" && input.openedProjectKind === "project") {
|
|
2595
|
+
kind2 = "project";
|
|
2596
|
+
target2.projectId = input.openedProjectId;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
if (!kind2) {
|
|
2600
|
+
continue;
|
|
2601
|
+
}
|
|
2602
|
+
if (kind2 === "kv" || kind2 === "knowledge") {
|
|
2603
|
+
target2.source = input.source ?? storageSource;
|
|
2604
|
+
}
|
|
2605
|
+
descriptor = { kind: kind2, target: target2, title: record(output?.document)?.title };
|
|
2606
|
+
}
|
|
2607
|
+
const kind = descriptor.kind;
|
|
2608
|
+
if (!["kv", "knowledge", "project", "browser", "file", "kanban"].includes(kind)) {
|
|
2609
|
+
continue;
|
|
2610
|
+
}
|
|
2611
|
+
const target = Object.fromEntries(Object.entries(record(descriptor.target) ?? {}).filter((entry) => typeof entry[1] === "string" && entry[1].length < 8192));
|
|
2612
|
+
if (target.source === "current") {
|
|
2613
|
+
target.source = kind === "kv" || kind === "knowledge" ? storageSource : "local";
|
|
2614
|
+
target.projectId = projectId;
|
|
2615
|
+
}
|
|
2616
|
+
if (target.source && !["local", "server"].includes(target.source)) {
|
|
2617
|
+
continue;
|
|
2618
|
+
}
|
|
2619
|
+
if (kind === "browser") {
|
|
2620
|
+
try {
|
|
2621
|
+
const url = new URL(target.url);
|
|
2622
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
|
|
2623
|
+
continue;
|
|
2624
|
+
}
|
|
2625
|
+
} catch {
|
|
2626
|
+
continue;
|
|
2627
|
+
}
|
|
2628
|
+
} else {
|
|
2629
|
+
target.source ??= "local";
|
|
2630
|
+
target.projectId ??= projectId;
|
|
2631
|
+
if (target.scope === "conversation") {
|
|
2632
|
+
target.conversationId ??= conversationId;
|
|
2633
|
+
}
|
|
2634
|
+
if (kind === "file" && (!target.filePath || /^(?:[\\/]|[a-z]:)|(?:^|[\\/])\.\.(?:[\\/]|$)/i.test(target.filePath))) {
|
|
2635
|
+
continue;
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
const id = string(descriptor.cardId) ?? `${kind}:${JSON.stringify(target)}`;
|
|
2639
|
+
const title = (string(descriptor.title) ?? string(record(output?.document)?.title) ?? target.key ?? target.documentId ?? target.filePath ?? target.url ?? target.projectId ?? kind).slice(0, 200);
|
|
2640
|
+
const kanban = record(descriptor.kanban), values = record(kanban?.values);
|
|
2641
|
+
cards.set(id, {
|
|
2642
|
+
id,
|
|
2643
|
+
kind,
|
|
2644
|
+
title,
|
|
2645
|
+
target,
|
|
2646
|
+
itemId: item.id ?? String(index),
|
|
2647
|
+
...typeof descriptor.preview === "boolean" ? { preview: descriptor.preview } : {},
|
|
2648
|
+
...typeof descriptor.pinned === "boolean" ? { pinned: descriptor.pinned } : {},
|
|
2649
|
+
...kind === "kanban" && ["preview", "save"].includes(String(kanban?.mode)) && values && Object.keys(values).length <= 100 && Object.values(values).every((value) => typeof value === "string") ? { kanban: { mode: kanban.mode, values } } : {}
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
return [...cards.values()];
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2307
2655
|
// src/web/account-auth.ts
|
|
2308
2656
|
async function loginWebAccount(serverUrl, email, password) {
|
|
2309
2657
|
if (!email.trim() || !password) {
|
|
2310
|
-
throw new Error("
|
|
2658
|
+
throw new Error(t("Enter your email and password."));
|
|
2311
2659
|
}
|
|
2312
2660
|
let token = "";
|
|
2313
2661
|
let cookie = "";
|
|
@@ -2317,7 +2665,7 @@ async function loginWebAccount(serverUrl, email, password) {
|
|
|
2317
2665
|
const response = await fetch(`${serverUrl}${endpoint}`, { ...options, redirect: "error", signal: AbortSignal.timeout(15e3) });
|
|
2318
2666
|
const result = await response.json();
|
|
2319
2667
|
if (!response.ok || result.errCode !== 0 || !result.data) {
|
|
2320
|
-
const message = response.status === 401 ? "
|
|
2668
|
+
const message = response.status === 401 ? t("Incorrect email or password, or sign-in expired.") : result.errMessage || "Workflow Server login failed.";
|
|
2321
2669
|
throw new Error(String(message).replaceAll(password, "[redacted]").replaceAll(token || "\0", "[redacted]"));
|
|
2322
2670
|
}
|
|
2323
2671
|
return { response, data: result.data };
|
|
@@ -2387,6 +2735,268 @@ async function inspectWebProject(runtime, projectDir, host) {
|
|
|
2387
2735
|
};
|
|
2388
2736
|
}
|
|
2389
2737
|
|
|
2738
|
+
// src/web/resource-preview.ts
|
|
2739
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2740
|
+
import { readFile as readFile4, realpath as realpath3, stat as stat3 } from "fs/promises";
|
|
2741
|
+
import path5 from "path";
|
|
2742
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2743
|
+
var ResourcePreviews = class {
|
|
2744
|
+
constructor(owner, runtime, storageDir, connection, related, resourceProjects = () => []) {
|
|
2745
|
+
this.owner = owner;
|
|
2746
|
+
this.runtime = runtime;
|
|
2747
|
+
this.storageDir = storageDir;
|
|
2748
|
+
this.connection = connection;
|
|
2749
|
+
this.related = related;
|
|
2750
|
+
this.resourceProjects = resourceProjects;
|
|
2751
|
+
}
|
|
2752
|
+
owner;
|
|
2753
|
+
runtime;
|
|
2754
|
+
storageDir;
|
|
2755
|
+
connection;
|
|
2756
|
+
related;
|
|
2757
|
+
resourceProjects;
|
|
2758
|
+
entries = /* @__PURE__ */ new Map();
|
|
2759
|
+
generation = 0;
|
|
2760
|
+
clear() {
|
|
2761
|
+
this.generation++;
|
|
2762
|
+
this.entries.clear();
|
|
2763
|
+
}
|
|
2764
|
+
async server(id, suffix, init = {}) {
|
|
2765
|
+
const connection = this.connection();
|
|
2766
|
+
if (!connection) {
|
|
2767
|
+
throw new Error(t("Please sign in to preview this server resource."));
|
|
2768
|
+
}
|
|
2769
|
+
const response = await fetch(`${connection.serverUrl}/api/workflows/${encodeURIComponent(id)}${suffix}`, {
|
|
2770
|
+
...init,
|
|
2771
|
+
headers: { Authorization: `Bearer ${connection.accessToken}`, "Content-Type": "application/json" },
|
|
2772
|
+
signal: AbortSignal.timeout(3e4),
|
|
2773
|
+
redirect: "error"
|
|
2774
|
+
});
|
|
2775
|
+
const result = await response.json();
|
|
2776
|
+
if (!response.ok || result.errCode !== 0) {
|
|
2777
|
+
throw new Error(result.errMessage || t("You do not have access to this resource."));
|
|
2778
|
+
}
|
|
2779
|
+
return result.data;
|
|
2780
|
+
}
|
|
2781
|
+
async resolve(card) {
|
|
2782
|
+
if (card.kind === "browser") {
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
const id = card.target.projectId ?? this.owner.id;
|
|
2786
|
+
if (card.target.source === "server") {
|
|
2787
|
+
if (card.kind === "kv" || card.kind === "knowledge") {
|
|
2788
|
+
return { id, uri: `workflow-server:${id}`, folderPath: "", name: id, projectType: "workflow", fields: [], entrypoints: [] };
|
|
2789
|
+
}
|
|
2790
|
+
const snapshot = await this.server(id, "/structure"), structure = snapshot.structure;
|
|
2791
|
+
return {
|
|
2792
|
+
...structure,
|
|
2793
|
+
id,
|
|
2794
|
+
uri: `workflow-server:${id}`,
|
|
2795
|
+
folderPath: "",
|
|
2796
|
+
name: snapshot.workflowName ?? card.title,
|
|
2797
|
+
projectType: structure.projectType,
|
|
2798
|
+
fields: structure.params ?? [],
|
|
2799
|
+
entrypoints: structure.entrypoints ?? []
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
if (id === this.owner.id) {
|
|
2803
|
+
return this.owner;
|
|
2804
|
+
}
|
|
2805
|
+
for (const directory of /* @__PURE__ */ new Set([...this.related().map((binding) => binding.projectDir), ...this.resourceProjects()])) {
|
|
2806
|
+
let root, pkg;
|
|
2807
|
+
try {
|
|
2808
|
+
root = await realpath3(directory);
|
|
2809
|
+
pkg = JSON.parse(await readFile4(path5.join(root, "package.json"), "utf8"));
|
|
2810
|
+
} catch {
|
|
2811
|
+
continue;
|
|
2812
|
+
}
|
|
2813
|
+
if (String(pkg.id).toLowerCase() !== id.toLowerCase()) {
|
|
2814
|
+
continue;
|
|
2815
|
+
}
|
|
2816
|
+
const inspected = await this.runtime.inspect([root]);
|
|
2817
|
+
const project = inspected.projects.find((project2) => project2.id.toLowerCase() === id.toLowerCase());
|
|
2818
|
+
if (!project) {
|
|
2819
|
+
throw new Error(t("The resource project is unavailable."));
|
|
2820
|
+
}
|
|
2821
|
+
return { ...project, uri: pathToFileURL2(root).href };
|
|
2822
|
+
}
|
|
2823
|
+
throw new Error(t("The resource project is unavailable. Import or open the target project first."));
|
|
2824
|
+
}
|
|
2825
|
+
async open(card) {
|
|
2826
|
+
const generation = this.generation;
|
|
2827
|
+
const project = await this.resolve(card);
|
|
2828
|
+
const id = randomUUID3(), data = { card, project }, entry = { data };
|
|
2829
|
+
const target = card.target, scopeId = target.conversationId ?? "";
|
|
2830
|
+
if (card.kind === "kv" || card.kind === "knowledge") {
|
|
2831
|
+
if (target.source === "server") {
|
|
2832
|
+
const query = new URLSearchParams({ scope: target.scope ?? "project" });
|
|
2833
|
+
if (target.conversationId) {
|
|
2834
|
+
query.set("conversationId", target.conversationId);
|
|
2835
|
+
}
|
|
2836
|
+
if (target.key) {
|
|
2837
|
+
query.set("key", target.key);
|
|
2838
|
+
}
|
|
2839
|
+
data.content = await this.server(project.id, card.kind === "knowledge" ? `/knowledge-documents${target.documentId ? `/${encodeURIComponent(target.documentId)}` : ""}` : `/kv${target.key ? "/entry" : ""}?${query}`);
|
|
2840
|
+
} else {
|
|
2841
|
+
const info = readWorkflowProjectInfo(JSON.parse(await readFile4(path5.join(project.folderPath, "package.json"), "utf8")));
|
|
2842
|
+
if (info.dataStorage && !isWorkflowProjectDataStorageLocationAllowed(info, "local")) {
|
|
2843
|
+
throw new Error(t("The project does not allow this storage location."));
|
|
2844
|
+
}
|
|
2845
|
+
data.content = await this.runtime.readResource(project, scopeId, card.kind, target);
|
|
2846
|
+
}
|
|
2847
|
+
if (card.kind === "knowledge" && (data.content === null || data.content === void 0)) {
|
|
2848
|
+
throw new Error(t("This resource no longer exists."));
|
|
2849
|
+
}
|
|
2850
|
+
data.format = card.kind === "knowledge" && typeof data.content.markdown === "string" ? "markdown" : "json";
|
|
2851
|
+
} else if (card.kind === "file") {
|
|
2852
|
+
if (target.source === "server") {
|
|
2853
|
+
throw new Error(t("Server source files cannot be previewed by this card."));
|
|
2854
|
+
}
|
|
2855
|
+
const root = await realpath3(project.assistant?.workdir ?? project.folderPath);
|
|
2856
|
+
const filename = await realpath3(path5.resolve(root, target.filePath));
|
|
2857
|
+
const relative = path5.relative(root, filename);
|
|
2858
|
+
if (relative === ".." || relative.startsWith(`..${path5.sep}`) || path5.isAbsolute(relative)) {
|
|
2859
|
+
throw new Error(t("File is outside this project."));
|
|
2860
|
+
}
|
|
2861
|
+
if (!(await stat3(filename)).isFile() || (await stat3(filename)).size > 2 * 1024 * 1024) {
|
|
2862
|
+
throw new Error(t("File preview exceeds 2 MiB."));
|
|
2863
|
+
}
|
|
2864
|
+
data.content = await readFile4(filename, "utf8");
|
|
2865
|
+
data.format = /\.mdx?$/i.test(filename) ? "markdown" : "text";
|
|
2866
|
+
} else if (card.kind === "browser") {
|
|
2867
|
+
data.format = "browser";
|
|
2868
|
+
data.content = target.url;
|
|
2869
|
+
} else if (card.kind === "kanban" || project?.projectType === "kanban" && card.preview !== false) {
|
|
2870
|
+
if (project?.projectType !== "kanban") {
|
|
2871
|
+
throw new Error(t("This project does not provide a Kanban preview."));
|
|
2872
|
+
}
|
|
2873
|
+
let params, configurations, activeId;
|
|
2874
|
+
if (target.source === "server") {
|
|
2875
|
+
const workspace = await this.server(project.id, "/kanban");
|
|
2876
|
+
params = workspace.manifest?.params ?? [];
|
|
2877
|
+
configurations = workspace.configurations;
|
|
2878
|
+
activeId = workspace.activeConfigurationId;
|
|
2879
|
+
const connection = this.connection(), url = new URL(workspace.preview.url, `${connection.serverUrl}/`);
|
|
2880
|
+
const expected = `/api/workflows/${encodeURIComponent(project.id)}/kanban/previews/`;
|
|
2881
|
+
if (url.origin !== new URL(connection.serverUrl).origin || !url.pathname.startsWith(expected) || url.search || url.hash) {
|
|
2882
|
+
throw new Error(t("Invalid resource preview URL."));
|
|
2883
|
+
}
|
|
2884
|
+
const segments = url.pathname.slice(expected.length).split("/");
|
|
2885
|
+
const assetPrefix = `${expected}${segments.slice(0, 3).join("/")}/`;
|
|
2886
|
+
entry.remote = { url, prefix: assetPrefix, connection };
|
|
2887
|
+
data.kanban = { url: `/resource-assets/${id}/${url.pathname.slice(assetPrefix.length)}`, nonce: workspace.preview.nonce, configuration: { id: "", name: "", params: {} } };
|
|
2888
|
+
} else {
|
|
2889
|
+
params = project.fields;
|
|
2890
|
+
try {
|
|
2891
|
+
params = parseKanbanManifestText(await readFile4(path5.join(project.folderPath, "kanban.json"), "utf8")).params;
|
|
2892
|
+
} catch (error) {
|
|
2893
|
+
if (error.code !== "ENOENT") {
|
|
2894
|
+
throw error;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
let workspace = {};
|
|
2898
|
+
try {
|
|
2899
|
+
workspace = JSON.parse(await readFile4(path5.join(webProjectDataDirectory(this.storageDir, project.folderPath), "kanban.json"), "utf8"));
|
|
2900
|
+
} catch (error) {
|
|
2901
|
+
if (error.code !== "ENOENT") {
|
|
2902
|
+
throw error;
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
configurations = workspace.configurations ?? [];
|
|
2906
|
+
activeId = workspace.activeConfigurationId;
|
|
2907
|
+
}
|
|
2908
|
+
const selected = configurations.find((item) => item.id === (target.configurationId ?? activeId));
|
|
2909
|
+
if (target.configurationId && !selected) {
|
|
2910
|
+
throw new Error(t("This preview configuration no longer exists."));
|
|
2911
|
+
}
|
|
2912
|
+
const values = sanitizeKanbanValues(params, { ...selected?.values ?? createDefaultKanbanValues(params), ...card.kanban?.mode === "preview" ? card.kanban.values : {} });
|
|
2913
|
+
const configuration = { id: selected?.id ?? id, name: selected?.name ?? card.title, params: createInjectedKanbanParams(params, values) };
|
|
2914
|
+
if (!entry.remote) {
|
|
2915
|
+
if (!project.kanban) {
|
|
2916
|
+
throw new Error(t("This project does not provide a Kanban preview."));
|
|
2917
|
+
}
|
|
2918
|
+
entry.local = new KanbanPreview(project.folderPath, project.kanban, () => configuration);
|
|
2919
|
+
data.kanban = { url: entry.local.url, nonce: entry.local.nonce, configuration };
|
|
2920
|
+
} else {
|
|
2921
|
+
data.kanban.configuration = configuration;
|
|
2922
|
+
}
|
|
2923
|
+
data.format = "kanban";
|
|
2924
|
+
} else {
|
|
2925
|
+
data.format = "project";
|
|
2926
|
+
data.content = project;
|
|
2927
|
+
}
|
|
2928
|
+
if (generation !== this.generation) {
|
|
2929
|
+
throw new Error(t("Resource access changed. Open the card again."));
|
|
2930
|
+
}
|
|
2931
|
+
if (this.entries.size >= 64) {
|
|
2932
|
+
this.entries.delete(this.entries.keys().next().value);
|
|
2933
|
+
}
|
|
2934
|
+
this.entries.set(id, entry);
|
|
2935
|
+
return { id, data, url: `/resource/${id}?workflowLocale=${locale()}` };
|
|
2936
|
+
}
|
|
2937
|
+
get(id) {
|
|
2938
|
+
const entry = this.entries.get(id);
|
|
2939
|
+
if (!entry) {
|
|
2940
|
+
throw new Error(t("Resource access changed. Open the card again."));
|
|
2941
|
+
}
|
|
2942
|
+
return entry.data;
|
|
2943
|
+
}
|
|
2944
|
+
async asset(url) {
|
|
2945
|
+
if (url.pathname.startsWith("/kanban/")) {
|
|
2946
|
+
for (const entry of this.entries.values()) {
|
|
2947
|
+
if (entry.local && url.pathname.startsWith(`/kanban/${entry.local.nonce}/`)) {
|
|
2948
|
+
return entry.local.asset(url);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
return;
|
|
2952
|
+
}
|
|
2953
|
+
const match = /^\/resource-assets\/([^/]+)\/(.+)$/.exec(url.pathname);
|
|
2954
|
+
const remote = match && this.entries.get(match[1])?.remote;
|
|
2955
|
+
if (!remote || remote.connection !== this.connection()) {
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
const asset = decodeURIComponent(match[2]);
|
|
2959
|
+
if (asset.includes("\\") || asset.split("/").some((part) => part === ".." || !part) || !/\.[a-z\d]+$/i.test(asset)) {
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
const address = new URL(`${remote.prefix}${match[2]}`, remote.url.origin);
|
|
2963
|
+
const response = await fetch(address, { headers: { Authorization: `Bearer ${remote.connection.accessToken}` }, redirect: "error", signal: AbortSignal.timeout(3e4) });
|
|
2964
|
+
if (!response.ok) {
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
return { content: Buffer.from(await response.arrayBuffer()), headers: {
|
|
2968
|
+
"Content-Type": response.headers.get("content-type") ?? "application/octet-stream",
|
|
2969
|
+
"Content-Security-Policy": `${response.headers.get("content-security-policy") ?? "default-src 'none'"}; frame-ancestors 'self' vscode-webview: vscode-file: http://127.0.0.1:* http://localhost:*`,
|
|
2970
|
+
"Cache-Control": "no-store",
|
|
2971
|
+
"Access-Control-Allow-Origin": "*",
|
|
2972
|
+
"X-Content-Type-Options": "nosniff"
|
|
2973
|
+
} };
|
|
2974
|
+
}
|
|
2975
|
+
async bridge(id, value) {
|
|
2976
|
+
const data = this.get(id), project = data.project, call = parseKanbanBridgeCall(value);
|
|
2977
|
+
if (data.format !== "kanban" || !project) {
|
|
2978
|
+
throw new Error(t("This project does not provide a Kanban preview."));
|
|
2979
|
+
}
|
|
2980
|
+
if (!["runtime.getLocale", "kv.getValue", "relatedProject.kv.getValue", "knowledge.listDocuments", "knowledge.getDocument", "knowledge.searchDocuments", "knowledge.readDocumentLines", "files.readFile"].includes(call.operation)) {
|
|
2981
|
+
throw new Error(t("This resource preview is read-only."));
|
|
2982
|
+
}
|
|
2983
|
+
if (call.operation === "runtime.getLocale") {
|
|
2984
|
+
return locale();
|
|
2985
|
+
}
|
|
2986
|
+
if (data.card.target.source === "server" || project.projectInfo?.dataStorage?.mode === "server") {
|
|
2987
|
+
return this.server(project.id, "/kanban/bridge", { method: "POST", body: JSON.stringify({ call }) });
|
|
2988
|
+
}
|
|
2989
|
+
if (call.operation === "files.readFile") {
|
|
2990
|
+
const file = await this.runtime.readFile(project, String(call.input.fileId));
|
|
2991
|
+
return { file: file.reference, base64: (await readFile4(file.path)).toString("base64") };
|
|
2992
|
+
}
|
|
2993
|
+
if (!this.runtime.kanbanBridge) {
|
|
2994
|
+
throw new Error(t("The resource project is unavailable."));
|
|
2995
|
+
}
|
|
2996
|
+
return this.runtime.kanbanBridge(project, call);
|
|
2997
|
+
}
|
|
2998
|
+
};
|
|
2999
|
+
|
|
2390
3000
|
// src/web/server.ts
|
|
2391
3001
|
async function startWebServer(options) {
|
|
2392
3002
|
const projectDir = await realpath4(path6.resolve(options.projectDir));
|
|
@@ -2399,12 +3009,14 @@ async function startWebServer(options) {
|
|
|
2399
3009
|
let login;
|
|
2400
3010
|
let passwordLogin = false;
|
|
2401
3011
|
let hostConnection = options.host?.connection;
|
|
2402
|
-
let hostManaged =
|
|
3012
|
+
let hostManaged = options.host !== void 0;
|
|
2403
3013
|
let authEpoch = 0;
|
|
2404
3014
|
let authenticationInitialized = false;
|
|
2405
3015
|
let authCheck;
|
|
2406
3016
|
const sensitiveTokens = /* @__PURE__ */ new Set();
|
|
2407
|
-
|
|
3017
|
+
let relatedProjects = options.host?.relatedProjects ?? [];
|
|
3018
|
+
let resourceProjects = options.host?.resourceProjects ?? [];
|
|
3019
|
+
const runtime = options.runtime ?? createWebRuntime(projectDir, storageDir, () => connection, () => relatedProjects);
|
|
2408
3020
|
let inspected;
|
|
2409
3021
|
try {
|
|
2410
3022
|
inspected = await inspectWebProject(runtime, projectDir, options.host);
|
|
@@ -2425,6 +3037,7 @@ async function startWebServer(options) {
|
|
|
2425
3037
|
throw new Error("Unsupported Workflow project type.");
|
|
2426
3038
|
}
|
|
2427
3039
|
const project = descriptor;
|
|
3040
|
+
const resources = new ResourcePreviews(project, runtime, storageDir, () => connection, () => relatedProjects, () => resourceProjects);
|
|
2428
3041
|
const key = createHash3("sha256").update(project.assistant ? `assistant:${project.id}:${project.assistant.workdir}` : projectDir).digest("hex").slice(0, 24);
|
|
2429
3042
|
const historyPath = path6.join(storageDir, key, "conversations.json");
|
|
2430
3043
|
let saved = {};
|
|
@@ -2562,7 +3175,7 @@ async function startWebServer(options) {
|
|
|
2562
3175
|
return;
|
|
2563
3176
|
}
|
|
2564
3177
|
const readingEpoch = authEpoch;
|
|
2565
|
-
const config = await readCliAuthConfig();
|
|
3178
|
+
const config = hostManaged ? createEmptyWorkflowAuthConfig() : await readCliAuthConfig();
|
|
2566
3179
|
if (readingEpoch !== authEpoch) {
|
|
2567
3180
|
return;
|
|
2568
3181
|
}
|
|
@@ -2584,6 +3197,7 @@ ${token}`;
|
|
|
2584
3197
|
authIdentity = identity;
|
|
2585
3198
|
const candidate = !signedOut && serverUrl && token ? { serverUrl: serverUrl.replace(/\/+$/, ""), accessToken: token } : void 0;
|
|
2586
3199
|
if (changed) {
|
|
3200
|
+
resources.clear();
|
|
2587
3201
|
connection = void 0;
|
|
2588
3202
|
authRevision++;
|
|
2589
3203
|
parameterStates.clear();
|
|
@@ -2625,6 +3239,7 @@ ${token}`;
|
|
|
2625
3239
|
return;
|
|
2626
3240
|
}
|
|
2627
3241
|
connection = void 0;
|
|
3242
|
+
resources.clear();
|
|
2628
3243
|
kanbanController?.authenticationChanged();
|
|
2629
3244
|
if (!changed) {
|
|
2630
3245
|
authRevision++;
|
|
@@ -2695,6 +3310,62 @@ ${token}`;
|
|
|
2695
3310
|
if (message.projectUri && message.projectUri !== project.uri) {
|
|
2696
3311
|
throw new Error("Project changed.");
|
|
2697
3312
|
}
|
|
3313
|
+
if (message.type === "resource-data") {
|
|
3314
|
+
return [{ type: "resource-data", requestId: message.requestId, data: resources.get(String(message.id)) }];
|
|
3315
|
+
}
|
|
3316
|
+
if (message.type === "resource-refresh" || message.type === "resource-select") {
|
|
3317
|
+
const card = resources.get(String(message.id)).card;
|
|
3318
|
+
const selection = message.type === "resource-select" ? message.selection : void 0;
|
|
3319
|
+
const target = { ...card.target };
|
|
3320
|
+
if (selection && ["kv", "knowledge"].includes(card.kind)) {
|
|
3321
|
+
for (const key2 of card.kind === "knowledge" ? ["documentId"] : ["key", "scope", "conversationId"]) {
|
|
3322
|
+
if (typeof selection[key2] === "string" && selection[key2].length <= 8192) {
|
|
3323
|
+
target[key2] = selection[key2];
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
const preview = await resources.open({ ...card, target });
|
|
3328
|
+
return [{ type: "resource-data", requestId: message.requestId, id: preview.id, data: preview.data }];
|
|
3329
|
+
}
|
|
3330
|
+
if (message.type === "resource-bridge") {
|
|
3331
|
+
return [{ type: "resource-bridge", requestId: message.requestId, value: await resources.bridge(String(message.id), message.call) }];
|
|
3332
|
+
}
|
|
3333
|
+
if (message.type === "resource") {
|
|
3334
|
+
if (!["open", "inline", "window", "project", void 0].includes(message.action)) {
|
|
3335
|
+
throw new Error(t("Invalid resource action."));
|
|
3336
|
+
}
|
|
3337
|
+
let items, scopeId;
|
|
3338
|
+
if (workflowController) {
|
|
3339
|
+
const run = workflowController.runs.find((run2) => run2.id === message.runId);
|
|
3340
|
+
if (!run) {
|
|
3341
|
+
throw new Error(t("Run no longer exists."));
|
|
3342
|
+
}
|
|
3343
|
+
items = run.items;
|
|
3344
|
+
scopeId = run.scopeId;
|
|
3345
|
+
} else {
|
|
3346
|
+
const current2 = conversationController?.current;
|
|
3347
|
+
if (!current2 || message.sessionId && message.sessionId !== current2.id) {
|
|
3348
|
+
throw new Error(t("Conversation changed."));
|
|
3349
|
+
}
|
|
3350
|
+
items = current2.messages.find((answer) => answer.id === message.messageId)?.items ?? [];
|
|
3351
|
+
scopeId = current2.id;
|
|
3352
|
+
}
|
|
3353
|
+
const card = conversationResourceCards(items, project.id, scopeId, project.projectInfo?.dataStorage?.mode === "server" ? "server" : "local").find((card2) => card2.id === message.cardId);
|
|
3354
|
+
if (!card) {
|
|
3355
|
+
throw new Error(t("This resource no longer exists."));
|
|
3356
|
+
}
|
|
3357
|
+
const preview = await resources.open(card);
|
|
3358
|
+
return [{
|
|
3359
|
+
type: "resource-preview",
|
|
3360
|
+
requestId: message.requestId,
|
|
3361
|
+
cardId: card.id,
|
|
3362
|
+
action: message.action ?? "open",
|
|
3363
|
+
id: preview.id,
|
|
3364
|
+
url: preview.url,
|
|
3365
|
+
card,
|
|
3366
|
+
project: preview.data.project
|
|
3367
|
+
}];
|
|
3368
|
+
}
|
|
2698
3369
|
if (kanbanController && !["ready", "login", "verify", "logout", "usage"].includes(message.type)) {
|
|
2699
3370
|
return kanbanController.handle(message);
|
|
2700
3371
|
}
|
|
@@ -2853,28 +3524,7 @@ ${token}`;
|
|
|
2853
3524
|
if (message.type === "export-message") {
|
|
2854
3525
|
replies.push({ type: "download", name: `run-${answer.id}.json`, content: JSON.stringify(redactWorkflowData(answer, [...sensitiveTokens], false), null, 2) }, { type: "exported", sessionId: current.id, messageId: answer.id });
|
|
2855
3526
|
} else {
|
|
2856
|
-
replies.push({ type: "preview", title: message.type === "logs" ? "
|
|
2857
|
-
}
|
|
2858
|
-
break;
|
|
2859
|
-
}
|
|
2860
|
-
case "resource": {
|
|
2861
|
-
const answer = current.messages.find((entry) => entry.id === message.messageId);
|
|
2862
|
-
const card = conversationResourceCards(answer?.items ?? [], project.id, current.id).find((entry) => entry.id === message.cardId);
|
|
2863
|
-
if (!card || card.target.projectId && card.target.projectId !== project.id || card.target.source === "server") {
|
|
2864
|
-
throw new Error("Resource is outside this project.");
|
|
2865
|
-
}
|
|
2866
|
-
if (card.kind === "kv" || card.kind === "knowledge") {
|
|
2867
|
-
replies.push({ type: "preview", title: card.title, content: await runtime.readResource(project, current.id, card.kind, card.target) });
|
|
2868
|
-
} else if (card.kind === "file") {
|
|
2869
|
-
const root = await realpath4(project.assistant?.workdir ?? projectDir);
|
|
2870
|
-
const filename = await realpath4(path6.resolve(root, card.target.filePath));
|
|
2871
|
-
const relative = path6.relative(root, filename);
|
|
2872
|
-
if (relative === ".." || relative.startsWith(`..${path6.sep}`) || path6.isAbsolute(relative)) {
|
|
2873
|
-
throw new Error("File is outside this project.");
|
|
2874
|
-
}
|
|
2875
|
-
replies.push({ type: "preview", title: card.title, content: await readFile5(filename, "utf8") });
|
|
2876
|
-
} else {
|
|
2877
|
-
throw new Error("This resource requires an editor host.");
|
|
3527
|
+
replies.push({ type: "preview", title: message.type === "logs" ? t("Run logs") : t("Trace"), content: message.type === "logs" ? answer.report?.logs ?? "" : { report: answer.report, events: answer.events, items: answer.items } });
|
|
2878
3528
|
}
|
|
2879
3529
|
break;
|
|
2880
3530
|
}
|
|
@@ -2891,6 +3541,9 @@ ${token}`;
|
|
|
2891
3541
|
break;
|
|
2892
3542
|
}
|
|
2893
3543
|
case "login": {
|
|
3544
|
+
if (hostManaged) {
|
|
3545
|
+
throw new Error(t("Manage this account through Studio."));
|
|
3546
|
+
}
|
|
2894
3547
|
if (!options.host && process.env.WORKFLOW_SERVER_ADMIN_KEY !== void 0) {
|
|
2895
3548
|
throw new Error("This session is supplied by the host. Sign in through the host.");
|
|
2896
3549
|
}
|
|
@@ -2946,15 +3599,19 @@ ${token}`;
|
|
|
2946
3599
|
case "verify":
|
|
2947
3600
|
await refreshAuth(true);
|
|
2948
3601
|
break;
|
|
2949
|
-
case "logout":
|
|
3602
|
+
case "logout": {
|
|
3603
|
+
if (hostManaged) {
|
|
3604
|
+
throw new Error(t("Manage this account through Studio."));
|
|
3605
|
+
}
|
|
2950
3606
|
signedOut = true;
|
|
2951
3607
|
login?.kill();
|
|
2952
|
-
if (
|
|
3608
|
+
if (process.env.WORKFLOW_SERVER_ADMIN_KEY === void 0) {
|
|
2953
3609
|
await clearCliAuthConfig();
|
|
2954
3610
|
}
|
|
2955
3611
|
authIdentity = "signed-out";
|
|
2956
3612
|
await refreshAuth();
|
|
2957
3613
|
break;
|
|
3614
|
+
}
|
|
2958
3615
|
default:
|
|
2959
3616
|
throw new Error(`Unsupported action: ${message.type}`);
|
|
2960
3617
|
}
|
|
@@ -2968,13 +3625,14 @@ ${token}`;
|
|
|
2968
3625
|
const host = new URL(`http://${request.headers.host ?? ""}`);
|
|
2969
3626
|
const forwarded = !!options.host && ["127.0.0.1", "localhost", "[::1]"].includes(host.hostname) && !host.username && !host.password && host.pathname === "/" && !host.search && !host.hash;
|
|
2970
3627
|
const url = new URL(request.url ?? "/", origin);
|
|
2971
|
-
const
|
|
3628
|
+
const resourceAsset = url.pathname.startsWith("/kanban/") || url.pathname.startsWith("/resource-assets/");
|
|
3629
|
+
const sandboxAsset = resourceAsset && ["GET", "HEAD"].includes(request.method ?? "") && request.headers.origin === "null";
|
|
2972
3630
|
if (request.headers.host !== new URL(origin).host && !forwarded || request.headers.origin && request.headers.origin !== (forwarded ? host.origin : origin) && !sandboxAsset) {
|
|
2973
3631
|
json(response, 403, { message: "Invalid request origin." });
|
|
2974
3632
|
return;
|
|
2975
3633
|
}
|
|
2976
|
-
if (
|
|
2977
|
-
const asset = await kanbanController
|
|
3634
|
+
if (resourceAsset && ["GET", "HEAD"].includes(request.method ?? "")) {
|
|
3635
|
+
const asset = await kanbanController?.preview.asset(url) ?? await resources.asset(url);
|
|
2978
3636
|
if (!asset) {
|
|
2979
3637
|
json(response, 404, { message: "Kanban asset not found." });
|
|
2980
3638
|
return;
|
|
@@ -3008,6 +3666,10 @@ ${token}`;
|
|
|
3008
3666
|
json(response, 200, { connected: true });
|
|
3009
3667
|
return;
|
|
3010
3668
|
}
|
|
3669
|
+
if (url.pathname === "/api/resource-state" && request.method === "GET") {
|
|
3670
|
+
json(response, 200, { type: "state", authenticationRevision: authRevision });
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3011
3673
|
if (url.pathname === "/api/events" && request.method === "GET") {
|
|
3012
3674
|
const disconnectKanban = kanbanController?.methods.connect(url.searchParams.get("clientId") ?? "");
|
|
3013
3675
|
response.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" });
|
|
@@ -3029,7 +3691,7 @@ ${token}`;
|
|
|
3029
3691
|
const operation = () => handleMessage(message).then((value2) => {
|
|
3030
3692
|
messages = value2;
|
|
3031
3693
|
});
|
|
3032
|
-
const concurrent = ["resolve", "ready", "kanban-bridge", "kanban-method-state", "kanban-method-reply", "kanban-methods"].includes(message.type);
|
|
3694
|
+
const concurrent = ["resolve", "ready", "resource", "resource-data", "resource-refresh", "resource-select", "resource-bridge", "kanban-bridge", "kanban-method-state", "kanban-method-reply", "kanban-methods"].includes(message.type);
|
|
3033
3695
|
const task = concurrent ? operation() : mutations.catch(() => void 0).then(operation);
|
|
3034
3696
|
if (!concurrent) {
|
|
3035
3697
|
mutations = task;
|
|
@@ -3058,11 +3720,11 @@ ${token}`;
|
|
|
3058
3720
|
response.end();
|
|
3059
3721
|
return;
|
|
3060
3722
|
}
|
|
3061
|
-
if (url.pathname === "/") {
|
|
3723
|
+
if (url.pathname === "/" || /^\/resource\/[a-f\d-]{36}$/.test(url.pathname)) {
|
|
3062
3724
|
const ancestors = options.host ? "'self' vscode-webview: vscode-file: https://*.vscode-cdn.net https://*.vscode-webview.net http://127.0.0.1:* http://localhost:*" : "'self' vscode-webview:";
|
|
3063
|
-
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store", "Content-Security-Policy": `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: http:; font-src 'self'; connect-src 'self'; media-src 'self' blob:; frame-src 'self'
|
|
3064
|
-
const ui = isKanban ? "kanban-workspace" : isWorkflow ? "workflow-workspace" : "workflow-ui";
|
|
3065
|
-
response.end(`<!doctype html><html lang="
|
|
3725
|
+
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store", "Content-Security-Policy": `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: http:; font-src 'self'; connect-src 'self'; media-src 'self' blob:; frame-src 'self' https: http:; object-src 'none'; base-uri 'none'; frame-ancestors ${ancestors}; form-action 'self'` });
|
|
3726
|
+
const ui = url.pathname !== "/" ? "resource-preview" : isKanban ? "kanban-workspace" : isWorkflow ? "workflow-workspace" : "workflow-ui";
|
|
3727
|
+
response.end(`<!doctype html><html lang="${locale()}" data-theme="${options.theme ?? "auto"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="workflow-project" content="${key}"><title>Workflow</title><link rel="stylesheet" href="/assets/codicons/codicon.css"><link rel="stylesheet" href="/assets/${ui}.css"><link rel="stylesheet" href="/assets/theme.css"></head><body data-surface="${options.host?.surface ?? "editor"}" data-account-host="${hostManaged}"><div id="app"></div><script src="/assets/${ui}.js" defer></script></body></html>`);
|
|
3066
3728
|
return;
|
|
3067
3729
|
}
|
|
3068
3730
|
const assets = { "/assets/workflow-ui.js": "application/javascript", "/assets/workflow-ui.css": "text/css", "/assets/theme.css": "text/css", "/assets/codicons/codicon.css": "text/css", "/assets/codicons/codicon.ttf": "font/ttf" };
|
|
@@ -3070,6 +3732,8 @@ ${token}`;
|
|
|
3070
3732
|
assets["/assets/workflow-workspace.css"] = "text/css";
|
|
3071
3733
|
assets["/assets/kanban-workspace.js"] = "application/javascript";
|
|
3072
3734
|
assets["/assets/kanban-workspace.css"] = "text/css";
|
|
3735
|
+
assets["/assets/resource-preview.js"] = "application/javascript";
|
|
3736
|
+
assets["/assets/resource-preview.css"] = "text/css";
|
|
3073
3737
|
if (!assets[url.pathname]) {
|
|
3074
3738
|
json(response, 404, { message: "Not found." });
|
|
3075
3739
|
return;
|
|
@@ -3110,9 +3774,21 @@ ${token}`;
|
|
|
3110
3774
|
controller,
|
|
3111
3775
|
workflowController,
|
|
3112
3776
|
kanbanController,
|
|
3777
|
+
setHostRelatedProjects: (value) => {
|
|
3778
|
+
relatedProjects = value;
|
|
3779
|
+
resources.clear();
|
|
3780
|
+
},
|
|
3781
|
+
setHostResourceProjects: (value) => {
|
|
3782
|
+
resourceProjects = value;
|
|
3783
|
+
resources.clear();
|
|
3784
|
+
},
|
|
3113
3785
|
setHostConnection: async (value) => {
|
|
3786
|
+
if (hostManaged && JSON.stringify(value) === JSON.stringify(hostConnection)) {
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3114
3789
|
authEpoch++;
|
|
3115
3790
|
authCheck?.abort();
|
|
3791
|
+
resources.clear();
|
|
3116
3792
|
hostConnection = value;
|
|
3117
3793
|
hostManaged = true;
|
|
3118
3794
|
signedOut = !value;
|
|
@@ -3148,6 +3824,32 @@ import { createInterface } from "readline";
|
|
|
3148
3824
|
|
|
3149
3825
|
// src/web/host-config.ts
|
|
3150
3826
|
import path7 from "path";
|
|
3827
|
+
function parseHostResourceProjects(value) {
|
|
3828
|
+
if (value === void 0) {
|
|
3829
|
+
return [];
|
|
3830
|
+
}
|
|
3831
|
+
if (!Array.isArray(value) || value.length > 100 || value.some((directory) => typeof directory !== "string" || !path7.isAbsolute(directory))) {
|
|
3832
|
+
throw new Error("Invalid host resource projects.");
|
|
3833
|
+
}
|
|
3834
|
+
return [...new Set(value)];
|
|
3835
|
+
}
|
|
3836
|
+
function parseHostRelatedProjects(value) {
|
|
3837
|
+
if (value === void 0) {
|
|
3838
|
+
return [];
|
|
3839
|
+
}
|
|
3840
|
+
if (!Array.isArray(value) || value.length > 100) {
|
|
3841
|
+
throw new Error("Invalid host related projects.");
|
|
3842
|
+
}
|
|
3843
|
+
const aliases = /* @__PURE__ */ new Set(), directories = /* @__PURE__ */ new Set();
|
|
3844
|
+
return value.map((project) => {
|
|
3845
|
+
if (!project || typeof project.alias !== "string" || !/^[a-z][a-z0-9_-]{0,63}$/.test(project.alias) || typeof project.projectDir !== "string" || !path7.isAbsolute(project.projectDir) || aliases.has(project.alias) || directories.has(project.projectDir)) {
|
|
3846
|
+
throw new Error("Invalid host related projects.");
|
|
3847
|
+
}
|
|
3848
|
+
aliases.add(project.alias);
|
|
3849
|
+
directories.add(project.projectDir);
|
|
3850
|
+
return { alias: project.alias, projectDir: project.projectDir };
|
|
3851
|
+
});
|
|
3852
|
+
}
|
|
3151
3853
|
function parseHostConnection(value) {
|
|
3152
3854
|
if (value === null || value === void 0) {
|
|
3153
3855
|
return;
|
|
@@ -3181,7 +3883,14 @@ function parseHostConfiguration(value) {
|
|
|
3181
3883
|
if (configuration.surface !== "assistant" && (assistant || assistantProjectDir !== void 0)) {
|
|
3182
3884
|
throw new Error("Assistant configuration requires the assistant surface.");
|
|
3183
3885
|
}
|
|
3184
|
-
return {
|
|
3886
|
+
return {
|
|
3887
|
+
surface: configuration.surface,
|
|
3888
|
+
assistant,
|
|
3889
|
+
assistantProjectDir,
|
|
3890
|
+
connection: parseHostConnection(configuration.connection),
|
|
3891
|
+
relatedProjects: parseHostRelatedProjects(configuration.relatedProjects),
|
|
3892
|
+
...configuration.resourceProjects === void 0 ? {} : { resourceProjects: parseHostResourceProjects(configuration.resourceProjects) }
|
|
3893
|
+
};
|
|
3185
3894
|
}
|
|
3186
3895
|
|
|
3187
3896
|
// src/web.ts
|
|
@@ -3275,7 +3984,7 @@ async function main(argv) {
|
|
|
3275
3984
|
};
|
|
3276
3985
|
process.once("SIGINT", close);
|
|
3277
3986
|
process.once("SIGTERM", close);
|
|
3278
|
-
console.log(options.json ? JSON.stringify({ type: "ready", protocolVersion: 1, hostProtocolVersion: 1, assistantProtocolVersion: host?.assistant ? ASSISTANT_PROTOCOL_VERSION : void 0, url: app.url, project: { id: app.project.id, name: app.project.name, projectType: app.project.projectType }, runtime: app.project.runtime }) : `Workflow Web: ${app.url}`);
|
|
3987
|
+
console.log(options.json ? JSON.stringify({ type: "ready", protocolVersion: 1, hostProtocolVersion: 1, accountProtocolVersion: 1, relatedProjectsProtocolVersion: 1, traceProtocolVersion: 1, assistantProtocolVersion: host?.assistant ? ASSISTANT_PROTOCOL_VERSION : void 0, url: app.url, project: { id: app.project.id, name: app.project.name, projectType: app.project.projectType }, runtime: app.project.runtime }) : `Workflow Web: ${app.url}`);
|
|
3279
3988
|
if (lines) {
|
|
3280
3989
|
void (async () => {
|
|
3281
3990
|
try {
|
|
@@ -3290,6 +3999,11 @@ async function main(argv) {
|
|
|
3290
3999
|
process.exitCode = 1;
|
|
3291
4000
|
close();
|
|
3292
4001
|
});
|
|
4002
|
+
} else if (message.type === "related-projects") {
|
|
4003
|
+
app.setHostRelatedProjects(parseHostRelatedProjects(message.relatedProjects));
|
|
4004
|
+
if (message.resourceProjects !== void 0) {
|
|
4005
|
+
app.setHostResourceProjects(parseHostResourceProjects(message.resourceProjects));
|
|
4006
|
+
}
|
|
3293
4007
|
} else if (message.type === "close") {
|
|
3294
4008
|
break;
|
|
3295
4009
|
} else {
|