@workflow-code/cli 0.2.4-20261011009.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-RDWHQ2ST.js → web-ROA37F4H.js} +915 -203
- package/dist/{workspace-V3UXVFK6.js → workspace-OKFR57T6.js} +1 -1
- package/package.json +4 -4
|
@@ -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,7 +821,7 @@ 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;
|
|
@@ -498,17 +843,17 @@ var ConversationController = class {
|
|
|
498
843
|
answer.timers = report.timers ?? answer.timers;
|
|
499
844
|
if (report.status === "failed") {
|
|
500
845
|
const data = report.result?.data;
|
|
501
|
-
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"));
|
|
502
847
|
}
|
|
503
848
|
if (report.status === "aborted" || report.status === "timed_out") {
|
|
504
|
-
answer.error = report.status === "aborted" ? "
|
|
849
|
+
answer.error = report.status === "aborted" ? t("Stopped") : t("Run timed out");
|
|
505
850
|
}
|
|
506
851
|
if (answer.submittingInput && pending && report.status === "failed") {
|
|
507
852
|
answer.status = "waiting_for_input";
|
|
508
853
|
answer.pendingInput = pending;
|
|
509
854
|
}
|
|
510
855
|
if (!answer.content && !answer.items?.length && report.status === "success") {
|
|
511
|
-
answer.content = report.result?.data === void 0 ? "
|
|
856
|
+
answer.content = report.result?.data === void 0 ? t("Completed") : JSON.stringify(report.result.data, null, 2);
|
|
512
857
|
}
|
|
513
858
|
} catch (error) {
|
|
514
859
|
answer.error = error instanceof Error ? error.message : String(error);
|
|
@@ -568,7 +913,7 @@ var ConversationController = class {
|
|
|
568
913
|
if (pending) {
|
|
569
914
|
pending.pendingInput = void 0;
|
|
570
915
|
pending.status = "aborted";
|
|
571
|
-
pending.error = "
|
|
916
|
+
pending.error = t("Stopped");
|
|
572
917
|
this.changed();
|
|
573
918
|
void this.persist().catch((error) => this.options.log(String(error)));
|
|
574
919
|
}
|
|
@@ -790,7 +1135,7 @@ var WorkflowController = class {
|
|
|
790
1135
|
delete record2.submittingInput;
|
|
791
1136
|
if (record2.status === "running" || record2.status === "waiting_for_input") {
|
|
792
1137
|
record2.status = "interrupted";
|
|
793
|
-
record2.error = "
|
|
1138
|
+
record2.error = t("Run interrupted");
|
|
794
1139
|
this.clearTimers(record2);
|
|
795
1140
|
}
|
|
796
1141
|
}
|
|
@@ -1565,10 +1910,10 @@ var KanbanController = class {
|
|
|
1565
1910
|
create(workspace, params, values = createDefaultKanbanValues(params), name) {
|
|
1566
1911
|
if (!name) {
|
|
1567
1912
|
let index = 1;
|
|
1568
|
-
while (workspace.configurations.some((item2) => item2.name ===
|
|
1913
|
+
while (workspace.configurations.some((item2) => item2.name === t("Configuration {0}", index))) {
|
|
1569
1914
|
index++;
|
|
1570
1915
|
}
|
|
1571
|
-
name =
|
|
1916
|
+
name = t("Configuration {0}", index);
|
|
1572
1917
|
}
|
|
1573
1918
|
const date = (/* @__PURE__ */ new Date()).toISOString(), item = { id: randomUUID2(), name, values: { ...values }, createdAt: date, updatedAt: date };
|
|
1574
1919
|
workspace.configurations.push(item);
|
|
@@ -1744,7 +2089,7 @@ var KanbanController = class {
|
|
|
1744
2089
|
this.missingRelatedProjectAliases = [];
|
|
1745
2090
|
const connection = this.hooks.connection();
|
|
1746
2091
|
if (!connection) {
|
|
1747
|
-
this.relatedProjectWarning = "
|
|
2092
|
+
this.relatedProjectWarning = t("Sign in to Workflow Server to connect Kanban data.");
|
|
1748
2093
|
return;
|
|
1749
2094
|
}
|
|
1750
2095
|
const abort = this.feed = new AbortController();
|
|
@@ -1766,7 +2111,7 @@ var KanbanController = class {
|
|
|
1766
2111
|
} }, abort.signal);
|
|
1767
2112
|
} catch {
|
|
1768
2113
|
if (!abort.signal.aborted) {
|
|
1769
|
-
this.relatedProjectWarning = "
|
|
2114
|
+
this.relatedProjectWarning = t("The related project connection was interrupted. Reconnecting.");
|
|
1770
2115
|
this.hooks.changed();
|
|
1771
2116
|
}
|
|
1772
2117
|
}
|
|
@@ -1826,114 +2171,7 @@ var KanbanController = class {
|
|
|
1826
2171
|
}
|
|
1827
2172
|
};
|
|
1828
2173
|
|
|
1829
|
-
// src/web/resource-cards.ts
|
|
1830
|
-
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1831
|
-
var string = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1832
|
-
function conversationResourceCards(items, projectId, conversationId) {
|
|
1833
|
-
const cards = /* @__PURE__ */ new Map();
|
|
1834
|
-
for (const [index, item] of items.entries()) {
|
|
1835
|
-
if (!["agent-part", "opencode-part"].includes(String(item.metadata?.kind)) || item.metadata?.partType !== "tool") {
|
|
1836
|
-
continue;
|
|
1837
|
-
}
|
|
1838
|
-
const content = record(item.content);
|
|
1839
|
-
if (content?.status !== "success") {
|
|
1840
|
-
continue;
|
|
1841
|
-
}
|
|
1842
|
-
let output = record(content.output);
|
|
1843
|
-
if (Array.isArray(output?.content) && output.content.length === 1) {
|
|
1844
|
-
try {
|
|
1845
|
-
output = record(JSON.parse(String(record(output.content[0])?.text)));
|
|
1846
|
-
} catch {
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
let descriptor = record(record(output?.details)?.resourceCard);
|
|
1850
|
-
const input = record(content.input) ?? {};
|
|
1851
|
-
const tool = string(content.tool) ?? "";
|
|
1852
|
-
if (tool === "workflow-ui_present_card") {
|
|
1853
|
-
const presentation = output?.action ? output : input;
|
|
1854
|
-
if (presentation.action === "remove") {
|
|
1855
|
-
cards.delete(String(presentation.cardId));
|
|
1856
|
-
continue;
|
|
1857
|
-
}
|
|
1858
|
-
if (presentation.action !== "upsert") {
|
|
1859
|
-
continue;
|
|
1860
|
-
}
|
|
1861
|
-
descriptor = presentation;
|
|
1862
|
-
}
|
|
1863
|
-
if (!descriptor) {
|
|
1864
|
-
const target2 = { source: "local", projectId, conversationId };
|
|
1865
|
-
let kind2;
|
|
1866
|
-
if (tool === "workflow-kv_kv_store" || /workflow-desktop_(?:read|list|write)_project_kv/.test(tool)) {
|
|
1867
|
-
kind2 = "kv";
|
|
1868
|
-
Object.assign(target2, { projectId: input.projectId ?? projectId, key: input.key, scope: input.scope ?? "project", conversationId: input.conversationId ?? conversationId });
|
|
1869
|
-
} else if (tool === "workflow-knowledge_knowledge_documents" || /workflow-desktop_(?:query|mutate)_project_knowledge/.test(tool)) {
|
|
1870
|
-
kind2 = "knowledge";
|
|
1871
|
-
Object.assign(target2, { projectId: input.projectId ?? projectId, documentId: input.documentId ?? record(output?.document)?.id });
|
|
1872
|
-
} else if (tool === "read" || tool === "workflow-desktop_open_dock_tool" && input.tool === "files") {
|
|
1873
|
-
kind2 = "file";
|
|
1874
|
-
target2.filePath = input.filePath ?? input.path;
|
|
1875
|
-
} else if (tool === "workflow-desktop_open_project" && input.kind === "project") {
|
|
1876
|
-
kind2 = "project";
|
|
1877
|
-
target2.projectId = input.targetId;
|
|
1878
|
-
} else if (tool === "workflow-desktop_open_dock_tool") {
|
|
1879
|
-
if (input.tool === "browser") {
|
|
1880
|
-
kind2 = "browser";
|
|
1881
|
-
target2.url = input.url;
|
|
1882
|
-
}
|
|
1883
|
-
if (input.tool === "preview") {
|
|
1884
|
-
kind2 = "kanban";
|
|
1885
|
-
}
|
|
1886
|
-
if (input.tool === "open-project" && input.openedProjectKind === "project") {
|
|
1887
|
-
kind2 = "project";
|
|
1888
|
-
target2.projectId = input.openedProjectId;
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
if (!kind2) {
|
|
1892
|
-
continue;
|
|
1893
|
-
}
|
|
1894
|
-
descriptor = { kind: kind2, target: target2, title: record(output?.document)?.title };
|
|
1895
|
-
}
|
|
1896
|
-
const kind = descriptor.kind;
|
|
1897
|
-
if (!["kv", "knowledge", "project", "browser", "file", "kanban"].includes(kind)) {
|
|
1898
|
-
continue;
|
|
1899
|
-
}
|
|
1900
|
-
const target = Object.fromEntries(Object.entries(record(descriptor.target) ?? {}).filter((entry) => typeof entry[1] === "string" && entry[1].length < 8192));
|
|
1901
|
-
if (target.source === "current") {
|
|
1902
|
-
target.source = "local";
|
|
1903
|
-
target.projectId = projectId;
|
|
1904
|
-
}
|
|
1905
|
-
if (target.source && !["local", "server"].includes(target.source)) {
|
|
1906
|
-
continue;
|
|
1907
|
-
}
|
|
1908
|
-
if (kind === "browser") {
|
|
1909
|
-
try {
|
|
1910
|
-
const url = new URL(target.url);
|
|
1911
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
|
|
1912
|
-
continue;
|
|
1913
|
-
}
|
|
1914
|
-
} catch {
|
|
1915
|
-
continue;
|
|
1916
|
-
}
|
|
1917
|
-
} else {
|
|
1918
|
-
target.source ??= "local";
|
|
1919
|
-
target.projectId ??= projectId;
|
|
1920
|
-
if (target.scope === "conversation") {
|
|
1921
|
-
target.conversationId ??= conversationId;
|
|
1922
|
-
}
|
|
1923
|
-
if (kind === "file" && (!target.filePath || /^(?:[\\/]|[a-z]:)|(?:^|[\\/])\.\.(?:[\\/]|$)/i.test(target.filePath))) {
|
|
1924
|
-
continue;
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
const id = string(descriptor.cardId) ?? `${kind}:${JSON.stringify(target)}`;
|
|
1928
|
-
const title = (string(descriptor.title) ?? target.key ?? target.documentId ?? target.filePath ?? target.url ?? target.projectId ?? kind).slice(0, 200);
|
|
1929
|
-
cards.set(id, { id, kind, title, target, itemId: item.id ?? String(index) });
|
|
1930
|
-
}
|
|
1931
|
-
return [...cards.values()];
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
2174
|
// src/web/workflow-actions.ts
|
|
1935
|
-
import { readFile as readFile4, realpath as realpath3 } from "fs/promises";
|
|
1936
|
-
import path4 from "path";
|
|
1937
2175
|
function createWorkflowActions(controller, runtime, project, revision, changed) {
|
|
1938
2176
|
const states = /* @__PURE__ */ new Map();
|
|
1939
2177
|
const epochs = /* @__PURE__ */ new Map();
|
|
@@ -2028,6 +2266,14 @@ function createWorkflowActions(controller, runtime, project, revision, changed)
|
|
|
2028
2266
|
case "workflow-select":
|
|
2029
2267
|
await controller.select(message.id ? String(message.id) : void 0);
|
|
2030
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
|
+
}
|
|
2031
2277
|
case "workflow-draft": {
|
|
2032
2278
|
const draft = { values: values(message.params ?? {}, id) };
|
|
2033
2279
|
if (message.inputMode !== void 0) {
|
|
@@ -2110,28 +2356,6 @@ function createWorkflowActions(controller, runtime, project, revision, changed)
|
|
|
2110
2356
|
replies.push({ type: "attachments", entrypointId: id, runId: message.runId, requestId: message.requestId, paramName: message.paramName, files });
|
|
2111
2357
|
break;
|
|
2112
2358
|
}
|
|
2113
|
-
case "resource": {
|
|
2114
|
-
const run = controller.runs.find((record2) => record2.id === message.runId);
|
|
2115
|
-
if (!run) {
|
|
2116
|
-
throw new Error("Run no longer exists.");
|
|
2117
|
-
}
|
|
2118
|
-
const card = conversationResourceCards(run.items, project.id, run.scopeId).find((card2) => card2.id === message.cardId);
|
|
2119
|
-
if (!card || card.target.projectId && card.target.projectId !== project.id || card.target.source === "server") {
|
|
2120
|
-
throw new Error("Resource is outside this project.");
|
|
2121
|
-
}
|
|
2122
|
-
if (card.kind === "kv" || card.kind === "knowledge") {
|
|
2123
|
-
replies.push({ type: "preview", title: card.title, content: await runtime.readResource(project, run.scopeId, card.kind, card.target) });
|
|
2124
|
-
} else if (card.kind === "file") {
|
|
2125
|
-
const root = await realpath3(project.folderPath), filename = await realpath3(path4.resolve(root, card.target.filePath)), relative = path4.relative(root, filename);
|
|
2126
|
-
if (relative.startsWith("..") || path4.isAbsolute(relative)) {
|
|
2127
|
-
throw new Error("File is outside this project.");
|
|
2128
|
-
}
|
|
2129
|
-
replies.push({ type: "preview", title: card.title, content: await readFile4(filename, "utf8") });
|
|
2130
|
-
} else {
|
|
2131
|
-
throw new Error("This resource requires an editor host.");
|
|
2132
|
-
}
|
|
2133
|
-
break;
|
|
2134
|
-
}
|
|
2135
2359
|
default:
|
|
2136
2360
|
throw new Error(`Unsupported workflow action: ${message.type}`);
|
|
2137
2361
|
}
|
|
@@ -2143,25 +2367,26 @@ function createWorkflowActions(controller, runtime, project, revision, changed)
|
|
|
2143
2367
|
// src/web/runtime.ts
|
|
2144
2368
|
import { spawn } from "child_process";
|
|
2145
2369
|
import { existsSync, readFileSync } from "fs";
|
|
2146
|
-
import
|
|
2370
|
+
import path4 from "path";
|
|
2147
2371
|
import { fileURLToPath } from "url";
|
|
2372
|
+
import { WORKFLOW_RUNTIME_LOCALE_ENV } from "workflow-code";
|
|
2148
2373
|
function cliRoot() {
|
|
2149
|
-
let directory =
|
|
2374
|
+
let directory = path4.dirname(fileURLToPath(import.meta.url));
|
|
2150
2375
|
for (let depth = 0; depth < 8; depth++) {
|
|
2151
2376
|
try {
|
|
2152
|
-
if (JSON.parse(readFileSync(
|
|
2377
|
+
if (JSON.parse(readFileSync(path4.join(directory, "package.json"), "utf8")).name === "@workflow-code/cli") {
|
|
2153
2378
|
return directory;
|
|
2154
2379
|
}
|
|
2155
2380
|
} catch {
|
|
2156
2381
|
}
|
|
2157
|
-
directory =
|
|
2382
|
+
directory = path4.dirname(directory);
|
|
2158
2383
|
}
|
|
2159
2384
|
throw new Error("Cannot locate the CLI installation.");
|
|
2160
2385
|
}
|
|
2161
|
-
function createWebRuntime(projectDir, storageDir, connection) {
|
|
2386
|
+
function createWebRuntime(projectDir, storageDir, connection, relatedProjects = () => []) {
|
|
2162
2387
|
const root = cliRoot();
|
|
2163
|
-
const builtWorker =
|
|
2164
|
-
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");
|
|
2165
2390
|
const children = /* @__PURE__ */ new Set();
|
|
2166
2391
|
const active = /* @__PURE__ */ new Map();
|
|
2167
2392
|
let disposed = false;
|
|
@@ -2178,12 +2403,14 @@ function createWebRuntime(projectDir, storageDir, connection) {
|
|
|
2178
2403
|
const env = {
|
|
2179
2404
|
...process.env,
|
|
2180
2405
|
WORKFLOW_RUNTIME_PLATFORM: "cli",
|
|
2406
|
+
[WORKFLOW_RUNTIME_LOCALE_ENV]: locale(),
|
|
2407
|
+
WORKFLOW_CODE_LOCALE: locale(),
|
|
2181
2408
|
WORKFLOW_SERVER_URL: auth?.serverUrl ?? "",
|
|
2182
2409
|
WORKFLOW_SERVER_ADMIN_KEY: auth?.accessToken ?? "",
|
|
2183
2410
|
WORKFLOW_SERVER_TOKEN: "",
|
|
2184
2411
|
WORKFLOW_LLM_SERVER_URL: auth?.serverUrl ?? "",
|
|
2185
2412
|
WORKFLOW_LLM_SERVER_TOKEN: auth?.accessToken ?? "",
|
|
2186
|
-
WORKFLOW_CLI_AUTH_DIR:
|
|
2413
|
+
WORKFLOW_CLI_AUTH_DIR: path4.join(storageDir, "runtime-auth")
|
|
2187
2414
|
};
|
|
2188
2415
|
for (const key of [
|
|
2189
2416
|
"TSX_TSCONFIG_PATH",
|
|
@@ -2268,7 +2495,7 @@ function createWebRuntime(projectDir, storageDir, connection) {
|
|
|
2268
2495
|
}
|
|
2269
2496
|
});
|
|
2270
2497
|
child.stdin.on("error", () => void 0);
|
|
2271
|
-
child.stdin.end(JSON.stringify({ ...request, storageDir, runtimeProjectDir: projectDir }));
|
|
2498
|
+
child.stdin.end(JSON.stringify({ ...request, storageDir, runtimeProjectDir: projectDir, relatedProjects: relatedProjects() }));
|
|
2272
2499
|
});
|
|
2273
2500
|
return {
|
|
2274
2501
|
available: true,
|
|
@@ -2306,10 +2533,129 @@ function createWebRuntime(projectDir, storageDir, connection) {
|
|
|
2306
2533
|
};
|
|
2307
2534
|
}
|
|
2308
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
|
+
|
|
2309
2655
|
// src/web/account-auth.ts
|
|
2310
2656
|
async function loginWebAccount(serverUrl, email, password) {
|
|
2311
2657
|
if (!email.trim() || !password) {
|
|
2312
|
-
throw new Error("
|
|
2658
|
+
throw new Error(t("Enter your email and password."));
|
|
2313
2659
|
}
|
|
2314
2660
|
let token = "";
|
|
2315
2661
|
let cookie = "";
|
|
@@ -2319,7 +2665,7 @@ async function loginWebAccount(serverUrl, email, password) {
|
|
|
2319
2665
|
const response = await fetch(`${serverUrl}${endpoint}`, { ...options, redirect: "error", signal: AbortSignal.timeout(15e3) });
|
|
2320
2666
|
const result = await response.json();
|
|
2321
2667
|
if (!response.ok || result.errCode !== 0 || !result.data) {
|
|
2322
|
-
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.";
|
|
2323
2669
|
throw new Error(String(message).replaceAll(password, "[redacted]").replaceAll(token || "\0", "[redacted]"));
|
|
2324
2670
|
}
|
|
2325
2671
|
return { response, data: result.data };
|
|
@@ -2389,6 +2735,268 @@ async function inspectWebProject(runtime, projectDir, host) {
|
|
|
2389
2735
|
};
|
|
2390
2736
|
}
|
|
2391
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
|
+
|
|
2392
3000
|
// src/web/server.ts
|
|
2393
3001
|
async function startWebServer(options) {
|
|
2394
3002
|
const projectDir = await realpath4(path6.resolve(options.projectDir));
|
|
@@ -2401,12 +3009,14 @@ async function startWebServer(options) {
|
|
|
2401
3009
|
let login;
|
|
2402
3010
|
let passwordLogin = false;
|
|
2403
3011
|
let hostConnection = options.host?.connection;
|
|
2404
|
-
let hostManaged =
|
|
3012
|
+
let hostManaged = options.host !== void 0;
|
|
2405
3013
|
let authEpoch = 0;
|
|
2406
3014
|
let authenticationInitialized = false;
|
|
2407
3015
|
let authCheck;
|
|
2408
3016
|
const sensitiveTokens = /* @__PURE__ */ new Set();
|
|
2409
|
-
|
|
3017
|
+
let relatedProjects = options.host?.relatedProjects ?? [];
|
|
3018
|
+
let resourceProjects = options.host?.resourceProjects ?? [];
|
|
3019
|
+
const runtime = options.runtime ?? createWebRuntime(projectDir, storageDir, () => connection, () => relatedProjects);
|
|
2410
3020
|
let inspected;
|
|
2411
3021
|
try {
|
|
2412
3022
|
inspected = await inspectWebProject(runtime, projectDir, options.host);
|
|
@@ -2427,6 +3037,7 @@ async function startWebServer(options) {
|
|
|
2427
3037
|
throw new Error("Unsupported Workflow project type.");
|
|
2428
3038
|
}
|
|
2429
3039
|
const project = descriptor;
|
|
3040
|
+
const resources = new ResourcePreviews(project, runtime, storageDir, () => connection, () => relatedProjects, () => resourceProjects);
|
|
2430
3041
|
const key = createHash3("sha256").update(project.assistant ? `assistant:${project.id}:${project.assistant.workdir}` : projectDir).digest("hex").slice(0, 24);
|
|
2431
3042
|
const historyPath = path6.join(storageDir, key, "conversations.json");
|
|
2432
3043
|
let saved = {};
|
|
@@ -2564,7 +3175,7 @@ async function startWebServer(options) {
|
|
|
2564
3175
|
return;
|
|
2565
3176
|
}
|
|
2566
3177
|
const readingEpoch = authEpoch;
|
|
2567
|
-
const config = await readCliAuthConfig();
|
|
3178
|
+
const config = hostManaged ? createEmptyWorkflowAuthConfig() : await readCliAuthConfig();
|
|
2568
3179
|
if (readingEpoch !== authEpoch) {
|
|
2569
3180
|
return;
|
|
2570
3181
|
}
|
|
@@ -2586,6 +3197,7 @@ ${token}`;
|
|
|
2586
3197
|
authIdentity = identity;
|
|
2587
3198
|
const candidate = !signedOut && serverUrl && token ? { serverUrl: serverUrl.replace(/\/+$/, ""), accessToken: token } : void 0;
|
|
2588
3199
|
if (changed) {
|
|
3200
|
+
resources.clear();
|
|
2589
3201
|
connection = void 0;
|
|
2590
3202
|
authRevision++;
|
|
2591
3203
|
parameterStates.clear();
|
|
@@ -2627,6 +3239,7 @@ ${token}`;
|
|
|
2627
3239
|
return;
|
|
2628
3240
|
}
|
|
2629
3241
|
connection = void 0;
|
|
3242
|
+
resources.clear();
|
|
2630
3243
|
kanbanController?.authenticationChanged();
|
|
2631
3244
|
if (!changed) {
|
|
2632
3245
|
authRevision++;
|
|
@@ -2697,6 +3310,62 @@ ${token}`;
|
|
|
2697
3310
|
if (message.projectUri && message.projectUri !== project.uri) {
|
|
2698
3311
|
throw new Error("Project changed.");
|
|
2699
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
|
+
}
|
|
2700
3369
|
if (kanbanController && !["ready", "login", "verify", "logout", "usage"].includes(message.type)) {
|
|
2701
3370
|
return kanbanController.handle(message);
|
|
2702
3371
|
}
|
|
@@ -2855,28 +3524,7 @@ ${token}`;
|
|
|
2855
3524
|
if (message.type === "export-message") {
|
|
2856
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 });
|
|
2857
3526
|
} else {
|
|
2858
|
-
replies.push({ type: "preview", title: message.type === "logs" ? "
|
|
2859
|
-
}
|
|
2860
|
-
break;
|
|
2861
|
-
}
|
|
2862
|
-
case "resource": {
|
|
2863
|
-
const answer = current.messages.find((entry) => entry.id === message.messageId);
|
|
2864
|
-
const card = conversationResourceCards(answer?.items ?? [], project.id, current.id).find((entry) => entry.id === message.cardId);
|
|
2865
|
-
if (!card || card.target.projectId && card.target.projectId !== project.id || card.target.source === "server") {
|
|
2866
|
-
throw new Error("Resource is outside this project.");
|
|
2867
|
-
}
|
|
2868
|
-
if (card.kind === "kv" || card.kind === "knowledge") {
|
|
2869
|
-
replies.push({ type: "preview", title: card.title, content: await runtime.readResource(project, current.id, card.kind, card.target) });
|
|
2870
|
-
} else if (card.kind === "file") {
|
|
2871
|
-
const root = await realpath4(project.assistant?.workdir ?? projectDir);
|
|
2872
|
-
const filename = await realpath4(path6.resolve(root, card.target.filePath));
|
|
2873
|
-
const relative = path6.relative(root, filename);
|
|
2874
|
-
if (relative === ".." || relative.startsWith(`..${path6.sep}`) || path6.isAbsolute(relative)) {
|
|
2875
|
-
throw new Error("File is outside this project.");
|
|
2876
|
-
}
|
|
2877
|
-
replies.push({ type: "preview", title: card.title, content: await readFile5(filename, "utf8") });
|
|
2878
|
-
} else {
|
|
2879
|
-
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 } });
|
|
2880
3528
|
}
|
|
2881
3529
|
break;
|
|
2882
3530
|
}
|
|
@@ -2893,6 +3541,9 @@ ${token}`;
|
|
|
2893
3541
|
break;
|
|
2894
3542
|
}
|
|
2895
3543
|
case "login": {
|
|
3544
|
+
if (hostManaged) {
|
|
3545
|
+
throw new Error(t("Manage this account through Studio."));
|
|
3546
|
+
}
|
|
2896
3547
|
if (!options.host && process.env.WORKFLOW_SERVER_ADMIN_KEY !== void 0) {
|
|
2897
3548
|
throw new Error("This session is supplied by the host. Sign in through the host.");
|
|
2898
3549
|
}
|
|
@@ -2948,15 +3599,19 @@ ${token}`;
|
|
|
2948
3599
|
case "verify":
|
|
2949
3600
|
await refreshAuth(true);
|
|
2950
3601
|
break;
|
|
2951
|
-
case "logout":
|
|
3602
|
+
case "logout": {
|
|
3603
|
+
if (hostManaged) {
|
|
3604
|
+
throw new Error(t("Manage this account through Studio."));
|
|
3605
|
+
}
|
|
2952
3606
|
signedOut = true;
|
|
2953
3607
|
login?.kill();
|
|
2954
|
-
if (
|
|
3608
|
+
if (process.env.WORKFLOW_SERVER_ADMIN_KEY === void 0) {
|
|
2955
3609
|
await clearCliAuthConfig();
|
|
2956
3610
|
}
|
|
2957
3611
|
authIdentity = "signed-out";
|
|
2958
3612
|
await refreshAuth();
|
|
2959
3613
|
break;
|
|
3614
|
+
}
|
|
2960
3615
|
default:
|
|
2961
3616
|
throw new Error(`Unsupported action: ${message.type}`);
|
|
2962
3617
|
}
|
|
@@ -2970,13 +3625,14 @@ ${token}`;
|
|
|
2970
3625
|
const host = new URL(`http://${request.headers.host ?? ""}`);
|
|
2971
3626
|
const forwarded = !!options.host && ["127.0.0.1", "localhost", "[::1]"].includes(host.hostname) && !host.username && !host.password && host.pathname === "/" && !host.search && !host.hash;
|
|
2972
3627
|
const url = new URL(request.url ?? "/", origin);
|
|
2973
|
-
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";
|
|
2974
3630
|
if (request.headers.host !== new URL(origin).host && !forwarded || request.headers.origin && request.headers.origin !== (forwarded ? host.origin : origin) && !sandboxAsset) {
|
|
2975
3631
|
json(response, 403, { message: "Invalid request origin." });
|
|
2976
3632
|
return;
|
|
2977
3633
|
}
|
|
2978
|
-
if (
|
|
2979
|
-
const asset = await kanbanController
|
|
3634
|
+
if (resourceAsset && ["GET", "HEAD"].includes(request.method ?? "")) {
|
|
3635
|
+
const asset = await kanbanController?.preview.asset(url) ?? await resources.asset(url);
|
|
2980
3636
|
if (!asset) {
|
|
2981
3637
|
json(response, 404, { message: "Kanban asset not found." });
|
|
2982
3638
|
return;
|
|
@@ -3010,6 +3666,10 @@ ${token}`;
|
|
|
3010
3666
|
json(response, 200, { connected: true });
|
|
3011
3667
|
return;
|
|
3012
3668
|
}
|
|
3669
|
+
if (url.pathname === "/api/resource-state" && request.method === "GET") {
|
|
3670
|
+
json(response, 200, { type: "state", authenticationRevision: authRevision });
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3013
3673
|
if (url.pathname === "/api/events" && request.method === "GET") {
|
|
3014
3674
|
const disconnectKanban = kanbanController?.methods.connect(url.searchParams.get("clientId") ?? "");
|
|
3015
3675
|
response.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" });
|
|
@@ -3031,7 +3691,7 @@ ${token}`;
|
|
|
3031
3691
|
const operation = () => handleMessage(message).then((value2) => {
|
|
3032
3692
|
messages = value2;
|
|
3033
3693
|
});
|
|
3034
|
-
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);
|
|
3035
3695
|
const task = concurrent ? operation() : mutations.catch(() => void 0).then(operation);
|
|
3036
3696
|
if (!concurrent) {
|
|
3037
3697
|
mutations = task;
|
|
@@ -3060,11 +3720,11 @@ ${token}`;
|
|
|
3060
3720
|
response.end();
|
|
3061
3721
|
return;
|
|
3062
3722
|
}
|
|
3063
|
-
if (url.pathname === "/") {
|
|
3723
|
+
if (url.pathname === "/" || /^\/resource\/[a-f\d-]{36}$/.test(url.pathname)) {
|
|
3064
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:";
|
|
3065
|
-
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'
|
|
3066
|
-
const ui = isKanban ? "kanban-workspace" : isWorkflow ? "workflow-workspace" : "workflow-ui";
|
|
3067
|
-
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>`);
|
|
3068
3728
|
return;
|
|
3069
3729
|
}
|
|
3070
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" };
|
|
@@ -3072,6 +3732,8 @@ ${token}`;
|
|
|
3072
3732
|
assets["/assets/workflow-workspace.css"] = "text/css";
|
|
3073
3733
|
assets["/assets/kanban-workspace.js"] = "application/javascript";
|
|
3074
3734
|
assets["/assets/kanban-workspace.css"] = "text/css";
|
|
3735
|
+
assets["/assets/resource-preview.js"] = "application/javascript";
|
|
3736
|
+
assets["/assets/resource-preview.css"] = "text/css";
|
|
3075
3737
|
if (!assets[url.pathname]) {
|
|
3076
3738
|
json(response, 404, { message: "Not found." });
|
|
3077
3739
|
return;
|
|
@@ -3112,9 +3774,21 @@ ${token}`;
|
|
|
3112
3774
|
controller,
|
|
3113
3775
|
workflowController,
|
|
3114
3776
|
kanbanController,
|
|
3777
|
+
setHostRelatedProjects: (value) => {
|
|
3778
|
+
relatedProjects = value;
|
|
3779
|
+
resources.clear();
|
|
3780
|
+
},
|
|
3781
|
+
setHostResourceProjects: (value) => {
|
|
3782
|
+
resourceProjects = value;
|
|
3783
|
+
resources.clear();
|
|
3784
|
+
},
|
|
3115
3785
|
setHostConnection: async (value) => {
|
|
3786
|
+
if (hostManaged && JSON.stringify(value) === JSON.stringify(hostConnection)) {
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3116
3789
|
authEpoch++;
|
|
3117
3790
|
authCheck?.abort();
|
|
3791
|
+
resources.clear();
|
|
3118
3792
|
hostConnection = value;
|
|
3119
3793
|
hostManaged = true;
|
|
3120
3794
|
signedOut = !value;
|
|
@@ -3150,6 +3824,32 @@ import { createInterface } from "readline";
|
|
|
3150
3824
|
|
|
3151
3825
|
// src/web/host-config.ts
|
|
3152
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
|
+
}
|
|
3153
3853
|
function parseHostConnection(value) {
|
|
3154
3854
|
if (value === null || value === void 0) {
|
|
3155
3855
|
return;
|
|
@@ -3183,7 +3883,14 @@ function parseHostConfiguration(value) {
|
|
|
3183
3883
|
if (configuration.surface !== "assistant" && (assistant || assistantProjectDir !== void 0)) {
|
|
3184
3884
|
throw new Error("Assistant configuration requires the assistant surface.");
|
|
3185
3885
|
}
|
|
3186
|
-
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
|
+
};
|
|
3187
3894
|
}
|
|
3188
3895
|
|
|
3189
3896
|
// src/web.ts
|
|
@@ -3277,7 +3984,7 @@ async function main(argv) {
|
|
|
3277
3984
|
};
|
|
3278
3985
|
process.once("SIGINT", close);
|
|
3279
3986
|
process.once("SIGTERM", close);
|
|
3280
|
-
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}`);
|
|
3281
3988
|
if (lines) {
|
|
3282
3989
|
void (async () => {
|
|
3283
3990
|
try {
|
|
@@ -3292,6 +3999,11 @@ async function main(argv) {
|
|
|
3292
3999
|
process.exitCode = 1;
|
|
3293
4000
|
close();
|
|
3294
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
|
+
}
|
|
3295
4007
|
} else if (message.type === "close") {
|
|
3296
4008
|
break;
|
|
3297
4009
|
} else {
|