@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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  assertLocalCliCoreCompatible
4
- } from "./chunk-WN7VAGK6.js";
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-MNY562W5.js";
24
+ import "./chunk-5FPIZZE4.js";
25
25
  import {
26
26
  clearCliAuthConfig,
27
+ createEmptyWorkflowAuthConfig,
27
28
  readCliAuthConfig,
28
29
  writeCliAuthConfig
29
- } from "./chunk-PGM3IPWA.js";
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(`\u8BF7\u586B\u5199 ${label}\u3002`);
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(`${label} \u9700\u8981\u662F\u6709\u6548\u6570\u5B57\u3002`);
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(`${label} \u9700\u8981\u662F\u6709\u6548\u5E03\u5C14\u503C\u3002`);
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(`${label} \u9700\u8981\u662F\u6709\u6548\u7684 JSON\u3002`);
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(`\u8BF7\u586B\u5199 ${label}\u3002`);
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(`${label} \u7684\u9009\u9879\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u3002`);
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 = "\u8FD0\u884C\u5DF2\u4E2D\u65AD";
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: "\u65B0\u5BF9\u8BDD", createdAt: now, updatedAt: now, messages: [], parameterValues: {}, toolPermissionPolicies: {}, queuedInputs: [], draft: { text: "", attachments: [] } };
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("\u5F53\u524D\u4F1A\u8BDD\u6B63\u5728\u8FD0\u884C\u3002");
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 || "\u5DE5\u4F5C\u6D41\u8FD0\u884C").slice(0, 80);
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("\u8FD0\u884C\u53C2\u6570\u9700\u8981\u662F\u5B57\u7B26\u4E32\u6570\u7EC4\u3002");
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("\u5DF2\u505C\u6B62");
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("\u5DF2\u505C\u6B62");
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
- if (report.output) {
485
- answer.items = report.output.items ?? [];
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 : "\u8FD0\u884C\u5931\u8D25");
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" ? "\u5DF2\u505C\u6B62" : "\u8FD0\u884C\u8D85\u65F6";
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 ? "\u5DF2\u5B8C\u6210" : JSON.stringify(report.result.data, null, 2);
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 = "\u5DF2\u505C\u6B62";
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 = "\u8FD0\u884C\u5DF2\u4E2D\u65AD";
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
- if (report.output) {
1008
- record2.items = report.output.items ?? [];
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 === `\u914D\u7F6E ${index}`)) {
1913
+ while (workspace.configurations.some((item2) => item2.name === t("Configuration {0}", index))) {
1567
1914
  index++;
1568
1915
  }
1569
- name = `\u914D\u7F6E ${index}`;
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 = "\u8BF7\u767B\u5F55 Workflow Server \u4EE5\u8FDE\u63A5\u770B\u677F\u6570\u636E\u3002";
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 = "\u5173\u8054\u9879\u76EE\u66F4\u65B0\u8FDE\u63A5\u5DF2\u4E2D\u65AD\uFF0C\u6B63\u5728\u91CD\u8FDE\u3002";
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 path5 from "path";
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 = path5.dirname(fileURLToPath(import.meta.url));
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(path5.join(directory, "package.json"), "utf8")).name === "@workflow-code/cli") {
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 = path5.dirname(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 = path5.join(root, "dist/web/worker.mjs");
2162
- const worker = existsSync(builtWorker) ? builtWorker : path5.join(root, "src/web/worker.mjs");
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: path5.join(storageDir, "runtime-auth")
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("\u8BF7\u8F93\u5165\u90AE\u7BB1\u548C\u5BC6\u7801\u3002");
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 ? "\u90AE\u7BB1\u6216\u5BC6\u7801\u4E0D\u6B63\u786E\uFF0C\u6216\u767B\u5F55\u5DF2\u5931\u6548\u3002" : result.errMessage || "Workflow Server login failed.";
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 = !!hostConnection;
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
- const runtime = options.runtime ?? createWebRuntime(projectDir, storageDir, () => connection);
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" ? "\u8FD0\u884C\u65E5\u5FD7" : "\u8FD0\u884C\u8DDF\u8E2A", content: message.type === "logs" ? answer.report?.logs ?? "" : answer.report });
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 (!hostManaged && process.env.WORKFLOW_SERVER_ADMIN_KEY === void 0) {
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 sandboxAsset = !!kanbanController && url.pathname.startsWith("/kanban/") && ["GET", "HEAD"].includes(request.method ?? "") && request.headers.origin === "null";
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 (kanbanController && url.pathname.startsWith("/kanban/") && ["GET", "HEAD"].includes(request.method ?? "")) {
2977
- const asset = await kanbanController.preview.asset(url);
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'; object-src 'none'; base-uri 'none'; frame-ancestors ${ancestors}; form-action 'self'` });
3064
- const ui = isKanban ? "kanban-workspace" : isWorkflow ? "workflow-workspace" : "workflow-ui";
3065
- response.end(`<!doctype html><html lang="zh-CN" 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"}"><div id="app"></div><script src="/assets/${ui}.js" defer></script></body></html>`);
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 { surface: configuration.surface, assistant, assistantProjectDir, connection: parseHostConnection(configuration.connection) };
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 {