openbot 0.5.8 → 0.5.10

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.
@@ -10,404 +10,464 @@ export const storagePlugin: Plugin = {
10
10
  id: 'storage',
11
11
  name: 'Storage',
12
12
  description: 'Infrastructure storage handlers for channels, threads, agents, and files.',
13
- factory: ({ storage, publicBaseUrl }) => (builder) => {
14
- const resolvePublicBaseUrl = () => publicBaseUrl;
13
+ factory:
14
+ ({ storage, publicBaseUrl }) =>
15
+ (builder) => {
16
+ const resolvePublicBaseUrl = () => publicBaseUrl;
17
+
18
+ builder.on('action:create_thread', async function* (event, context) {
19
+ const threadId = event.meta?.threadId;
20
+ const channelId = context.state.channelId;
21
+ const { threadTitle, initialState } = (event as any).data;
22
+
23
+ if (!threadId) {
24
+ console.warn('[storage] Cannot create thread: meta.threadId is missing');
25
+ return;
26
+ }
27
+
28
+ context.state.threadId = threadId;
29
+
30
+ if (channelId) {
31
+ try {
32
+ await storage.createThread({
33
+ channelId,
34
+ threadId,
35
+ threadTitle,
36
+ initialState: (initialState as Record<string, unknown>) || {},
37
+ });
38
+
39
+ context.state.threadDetails = await storage.getThreadDetails({
40
+ channelId,
41
+ threadId,
42
+ });
43
+ } catch (error) {
44
+ console.warn(
45
+ `[storage] Failed to initialize thread for channel ${channelId} thread ${threadId}`,
46
+ error,
47
+ );
48
+ }
49
+ }
50
+
51
+ yield {
52
+ type: 'action:create_thread:result',
53
+ data: { success: true, threadId, threadTitle },
54
+ meta: { ...(event.meta || {}), threadId, agentId: context.state.agentId },
55
+ } as OpenBotEvent;
56
+ });
57
+
58
+ builder.on('agent:usage', async function* (event, context) {
59
+ const { usage } = event.data;
60
+ const channelId = context.state.channelId;
61
+ if (!channelId || !context.state.threadId) return;
62
+
63
+ const currentState = (context.state.threadDetails?.state as Record<string, any>) || {};
64
+ const currentUsage = currentState.usage || {
65
+ promptTokens: 0,
66
+ completionTokens: 0,
67
+ totalTokens: 0,
68
+ };
69
+
70
+ const nextUsage = {
71
+ promptTokens: (currentUsage.promptTokens || 0) + usage.promptTokens,
72
+ completionTokens: (currentUsage.completionTokens || 0) + usage.completionTokens,
73
+ totalTokens: (currentUsage.totalTokens || 0) + usage.totalTokens,
74
+ };
75
+
76
+ await storage.patchThreadState({
77
+ channelId,
78
+ threadId: context.state.threadId,
79
+ state: { usage: nextUsage },
80
+ });
15
81
 
16
- builder.on('action:create_thread', async function* (event, context) {
17
- const threadId = event.meta?.threadId;
18
- const channelId = context.state.channelId;
19
- const { threadTitle, initialState } = (event as any).data;
82
+ context.state.threadDetails = await storage.getThreadDetails({
83
+ channelId,
84
+ threadId: context.state.threadId,
85
+ });
86
+ });
20
87
 
21
- if (!threadId) {
22
- console.warn('[storage] Cannot create thread: meta.threadId is missing');
23
- return;
24
- }
88
+ builder.on('action:storage:get-channels', async function* () {
89
+ const channels = await storage.getChannels();
90
+ yield { type: 'action:storage:get-channels-result', data: { channels } };
91
+ });
25
92
 
26
- context.state.threadId = threadId;
93
+ builder.on('action:storage:get-threads', async function* (event) {
94
+ const threads = await storage.getThreads({ channelId: event.data.channelId });
95
+ yield { type: 'action:storage:get-threads-result', data: { threads } };
96
+ });
27
97
 
28
- if (channelId) {
98
+ builder.on('action:storage:set-last-read', async function* (event, context) {
99
+ const { channelId, threadId, lastReadEventId } = event.data;
100
+ const resolvedChannelId = channelId || context.state.channelId;
101
+ if (!resolvedChannelId) {
102
+ yield {
103
+ type: 'action:storage:set-last-read-result',
104
+ data: { success: false, error: 'channelId is required' },
105
+ };
106
+ return;
107
+ }
29
108
  try {
30
- await storage.createThread({
31
- channelId,
32
- threadId,
33
- threadTitle,
34
- initialState: (initialState as Record<string, unknown>) || {},
109
+ await storage.setLastRead({
110
+ channelId: resolvedChannelId,
111
+ threadId: threadId || context.state.threadId,
112
+ lastReadEventId,
35
113
  });
114
+ yield { type: 'action:storage:set-last-read-result', data: { success: true } };
115
+ } catch (error) {
116
+ yield {
117
+ type: 'action:storage:set-last-read-result',
118
+ data: {
119
+ success: false,
120
+ error: error instanceof Error ? error.message : 'Unknown error',
121
+ },
122
+ };
123
+ }
124
+ });
36
125
 
37
- context.state.threadDetails = await storage.getThreadDetails({
38
- channelId,
39
- threadId,
126
+ builder.on('action:storage:get-channel-details', async function* (_, state) {
127
+ const channelId = state.state.channelId;
128
+ if (!channelId) {
129
+ yield {
130
+ type: 'action:storage:get-channel-details-result',
131
+ data: { channelDetails: null as any, error: 'channelId is required' },
132
+ };
133
+ return;
134
+ }
135
+ const channelDetails = await storage.getChannelDetails({ channelId });
136
+ yield { type: 'action:storage:get-channel-details-result', data: { channelDetails } };
137
+ });
138
+
139
+ builder.on('action:storage:get-thread-details', async function* (_, state) {
140
+ const channelId = state.state.channelId;
141
+ const threadId = state.state.threadId;
142
+ if (!channelId) {
143
+ yield {
144
+ type: 'action:storage:get-thread-details-result',
145
+ data: { threadDetails: null, error: 'channelId is required' },
146
+ };
147
+ return;
148
+ }
149
+ const threadDetails = threadId
150
+ ? await storage.getThreadDetails({ channelId, threadId })
151
+ : null;
152
+ yield { type: 'action:storage:get-thread-details-result', data: { threadDetails } };
153
+ });
154
+
155
+ builder.on('action:storage:get-agents', async function* () {
156
+ const agents = await storage.getAgents();
157
+ yield { type: 'action:storage:get-agents-result', data: { agents } };
158
+ });
159
+
160
+ builder.on('action:storage:get-plugins', async function* () {
161
+ const plugins = await storage.getPlugins();
162
+ yield { type: 'action:storage:get-plugins-result', data: { plugins } };
163
+ });
164
+
165
+ builder.on('action:storage:get-agent-details', async function* (event) {
166
+ try {
167
+ const agentDetails = await storage.getAgentDetails({ agentId: event.data.agentId });
168
+ yield { type: 'action:storage:get-agent-details-result', data: { agentDetails } };
169
+ } catch (error) {
170
+ console.error(`[storage] Failed to get agent details for ${event.data.agentId}`, error);
171
+ yield {
172
+ type: 'action:storage:get-agent-details-result',
173
+ data: {
174
+ agentDetails: null as any,
175
+ error: error instanceof Error ? error.message : 'Unknown error',
176
+ },
177
+ };
178
+ }
179
+ });
180
+
181
+ builder.on('action:storage:create-agent', async function* (event) {
182
+ try {
183
+ const { agentId, name, description, image, hidden, instructions, plugins } = event.data;
184
+ await storage.createAgent({
185
+ agentId,
186
+ name,
187
+ description,
188
+ image,
189
+ hidden,
190
+ instructions,
191
+ plugins,
40
192
  });
193
+ yield { type: 'action:storage:create-agent-result', data: { success: true } };
41
194
  } catch (error) {
42
- console.warn(
43
- `[storage] Failed to initialize thread for channel ${channelId} thread ${threadId}`,
44
- error,
45
- );
195
+ yield {
196
+ type: 'action:storage:create-agent-result',
197
+ data: {
198
+ success: false,
199
+ error: error instanceof Error ? error.message : 'Unknown error',
200
+ },
201
+ };
46
202
  }
47
- }
48
-
49
- yield {
50
- type: 'action:create_thread:result',
51
- data: { success: true, threadId, threadTitle },
52
- meta: { ...(event.meta || {}), threadId, agentId: context.state.agentId },
53
- } as OpenBotEvent;
54
- });
55
-
56
- builder.on('agent:usage', async function* (event, context) {
57
- const { usage } = event.data;
58
- if (!context.state.threadId) return;
59
-
60
- const currentState = (context.state.threadDetails?.state as Record<string, any>) || {};
61
- const currentUsage = currentState.usage || {
62
- promptTokens: 0,
63
- completionTokens: 0,
64
- totalTokens: 0,
65
- };
66
-
67
- const nextUsage = {
68
- promptTokens: (currentUsage.promptTokens || 0) + usage.promptTokens,
69
- completionTokens: (currentUsage.completionTokens || 0) + usage.completionTokens,
70
- totalTokens: (currentUsage.totalTokens || 0) + usage.totalTokens,
71
- };
72
-
73
- await storage.patchThreadState({
74
- channelId: context.state.channelId,
75
- threadId: context.state.threadId,
76
- state: { usage: nextUsage },
77
203
  });
78
204
 
79
- context.state.threadDetails = await storage.getThreadDetails({
80
- channelId: context.state.channelId,
81
- threadId: context.state.threadId,
205
+ builder.on('action:storage:update-agent', async function* (event) {
206
+ try {
207
+ const { agentId, name, description, image, hidden, instructions, plugins } = event.data;
208
+ await storage.updateAgent({
209
+ agentId,
210
+ name,
211
+ description,
212
+ image,
213
+ hidden,
214
+ instructions,
215
+ plugins,
216
+ });
217
+ yield { type: 'action:storage:update-agent-result', data: { success: true } };
218
+ } catch (error) {
219
+ yield {
220
+ type: 'action:storage:update-agent-result',
221
+ data: {
222
+ success: false,
223
+ error: error instanceof Error ? error.message : 'Unknown error',
224
+ },
225
+ };
226
+ }
82
227
  });
83
- });
84
-
85
- builder.on('action:storage:get-channels', async function* () {
86
- const channels = await storage.getChannels();
87
- yield { type: 'action:storage:get-channels-result', data: { channels } };
88
- });
89
-
90
- builder.on('action:storage:get-threads', async function* (event) {
91
- const threads = await storage.getThreads({ channelId: event.data.channelId });
92
- yield { type: 'action:storage:get-threads-result', data: { threads } };
93
- });
94
-
95
- builder.on('action:storage:set-last-read', async function* (event, context) {
96
- const { channelId, threadId, lastReadEventId } = event.data;
97
- try {
98
- await storage.setLastRead({
99
- channelId: channelId || context.state.channelId,
100
- threadId: threadId || context.state.threadId,
101
- lastReadEventId,
102
- });
103
- yield { type: 'action:storage:set-last-read-result', data: { success: true } };
104
- } catch (error) {
105
- yield {
106
- type: 'action:storage:set-last-read-result',
107
- data: {
108
- success: false,
109
- error: error instanceof Error ? error.message : 'Unknown error',
110
- },
111
- };
112
- }
113
- });
114
228
 
115
- builder.on('action:storage:get-channel-details', async function* (_, state) {
116
- const channelDetails = await storage.getChannelDetails({
117
- channelId: state.state.channelId,
229
+ builder.on('action:storage:delete-channel', async function* (event) {
230
+ try {
231
+ await storage.deleteChannel({ channelId: event.data.channelId });
232
+ yield { type: 'action:storage:delete-channel-result', data: { success: true } };
233
+ } catch (error) {
234
+ yield {
235
+ type: 'action:storage:delete-channel-result',
236
+ data: {
237
+ success: false,
238
+ error: error instanceof Error ? error.message : 'Unknown error',
239
+ },
240
+ };
241
+ }
118
242
  });
119
- yield { type: 'action:storage:get-channel-details-result', data: { channelDetails } };
120
- });
121
-
122
- builder.on('action:storage:get-thread-details', async function* (_, state) {
123
- const threadId = state.state.threadId;
124
- const threadDetails = threadId
125
- ? await storage.getThreadDetails({ channelId: state.state.channelId, threadId })
126
- : null;
127
- yield { type: 'action:storage:get-thread-details-result', data: { threadDetails } };
128
- });
129
-
130
- builder.on('action:storage:get-agents', async function* () {
131
- const agents = await storage.getAgents();
132
- yield { type: 'action:storage:get-agents-result', data: { agents } };
133
- });
134
-
135
- builder.on('action:storage:get-plugins', async function* () {
136
- const plugins = await storage.getPlugins();
137
- yield { type: 'action:storage:get-plugins-result', data: { plugins } };
138
- });
139
-
140
- builder.on('action:storage:get-agent-details', async function* (event) {
141
- try {
142
- const agentDetails = await storage.getAgentDetails({ agentId: event.data.agentId });
143
- yield { type: 'action:storage:get-agent-details-result', data: { agentDetails } };
144
- } catch (error) {
145
- console.error(`[storage] Failed to get agent details for ${event.data.agentId}`, error);
146
- yield {
147
- type: 'action:storage:get-agent-details-result',
148
- data: {
149
- agentDetails: null as any,
150
- error: error instanceof Error ? error.message : 'Unknown error',
151
- },
152
- };
153
- }
154
- });
155
-
156
- builder.on('action:storage:create-agent', async function* (event) {
157
- try {
158
- const { agentId, name, description, image, hidden, instructions, plugins } = event.data;
159
- await storage.createAgent({
160
- agentId,
161
- name,
162
- description,
163
- image,
164
- hidden,
165
- instructions,
166
- plugins,
167
- });
168
- yield { type: 'action:storage:create-agent-result', data: { success: true } };
169
- } catch (error) {
170
- yield {
171
- type: 'action:storage:create-agent-result',
172
- data: {
173
- success: false,
174
- error: error instanceof Error ? error.message : 'Unknown error',
175
- },
176
- };
177
- }
178
- });
179
-
180
- builder.on('action:storage:update-agent', async function* (event) {
181
- try {
182
- const { agentId, name, description, image, hidden, instructions, plugins } = event.data;
183
- await storage.updateAgent({
184
- agentId,
185
- name,
186
- description,
187
- image,
188
- hidden,
189
- instructions,
190
- plugins,
191
- });
192
- yield { type: 'action:storage:update-agent-result', data: { success: true } };
193
- } catch (error) {
194
- yield {
195
- type: 'action:storage:update-agent-result',
196
- data: {
197
- success: false,
198
- error: error instanceof Error ? error.message : 'Unknown error',
199
- },
200
- };
201
- }
202
- });
203
-
204
- builder.on('action:storage:delete-channel', async function* (event) {
205
- try {
206
- await storage.deleteChannel({ channelId: event.data.channelId });
207
- yield { type: 'action:storage:delete-channel-result', data: { success: true } };
208
- } catch (error) {
209
- yield {
210
- type: 'action:storage:delete-channel-result',
211
- data: {
212
- success: false,
213
- error: error instanceof Error ? error.message : 'Unknown error',
214
- },
215
- };
216
- }
217
- });
218
-
219
- builder.on('action:storage:delete-agent', async function* (event) {
220
- try {
221
- await storage.deleteAgent({ agentId: event.data.agentId });
222
- yield { type: 'action:storage:delete-agent-result', data: { success: true } };
223
- } catch (error) {
224
- yield {
225
- type: 'action:storage:delete-agent-result',
226
- data: {
227
- success: false,
228
- error: error instanceof Error ? error.message : 'Unknown error',
229
- },
230
- };
231
- }
232
- });
233
-
234
- builder.on('action:storage:get-events', async function* (_, state) {
235
- const events = await storage.getEvents(state.state);
236
- yield { type: 'action:storage:get-events-result', data: { events } };
237
- });
238
-
239
- builder.on('action:storage:get-variables', async function* () {
240
- const variables = await storage.getVariables();
241
- const masked: Record<string, string> = {};
242
- for (const [key, val] of Object.entries(variables)) {
243
- if (typeof val === 'object' && val !== null && val.secret) {
244
- masked[key] = '********';
245
- } else {
246
- masked[key] = typeof val === 'string' ? val : val.value;
243
+
244
+ builder.on('action:storage:delete-agent', async function* (event) {
245
+ try {
246
+ await storage.deleteAgent({ agentId: event.data.agentId });
247
+ yield { type: 'action:storage:delete-agent-result', data: { success: true } };
248
+ } catch (error) {
249
+ yield {
250
+ type: 'action:storage:delete-agent-result',
251
+ data: {
252
+ success: false,
253
+ error: error instanceof Error ? error.message : 'Unknown error',
254
+ },
255
+ };
247
256
  }
248
- }
249
- yield {
250
- type: 'action:storage:get-variables-result',
251
- data: { variables: masked },
252
- } as OpenBotEvent;
253
- });
254
-
255
- builder.on('action:storage:create-variable', async function* (event) {
256
- try {
257
- const { key, value, secret } = event.data;
258
- await storage.createVariable({ key, value, secret });
259
- yield { type: 'action:storage:create-variable-result', data: { success: true } };
260
- } catch (error) {
261
- yield {
262
- type: 'action:storage:create-variable-result',
263
- data: {
264
- success: false,
265
- error: error instanceof Error ? error.message : 'Unknown error',
266
- },
267
- };
268
- }
269
- });
270
-
271
- builder.on('action:storage:delete-variable', async function* (event) {
272
- try {
273
- await storage.deleteVariable({ key: event.data.key });
274
- yield { type: 'action:storage:delete-variable-result', data: { success: true } };
275
- } catch (error) {
276
- yield {
277
- type: 'action:storage:delete-variable-result',
278
- data: {
279
- success: false,
280
- error: error instanceof Error ? error.message : 'Unknown error',
281
- },
282
- };
283
- }
284
- });
285
-
286
- builder.on('action:storage:patch-channel-state', async function* (event, state) {
287
- try {
288
- await storage.patchChannelState({
289
- channelId: state.state.channelId,
290
- state: event.data.state,
291
- });
292
- yield { type: 'action:storage:patch-channel-state-result', data: { success: true } };
293
- } catch {
294
- yield { type: 'action:storage:patch-channel-state-result', data: { success: false } };
295
- }
296
- });
297
-
298
- builder.on('action:storage:patch-thread-state', async function* (event, state) {
299
- try {
300
- if (!state.state.threadId) {
301
- throw new Error('Missing threadId in state for patch-thread-state');
257
+ });
258
+
259
+ builder.on('action:storage:get-events', async function* (_, state) {
260
+ const channelId = state.state.channelId;
261
+ if (!channelId) {
262
+ yield { type: 'action:storage:get-events-result', data: { events: [] } };
263
+ return;
264
+ }
265
+ const events = await storage.getEvents({ channelId, threadId: state.state.threadId });
266
+ yield { type: 'action:storage:get-events-result', data: { events } };
267
+ });
268
+
269
+ builder.on('action:storage:get-variables', async function* () {
270
+ const variables = await storage.getVariables();
271
+ const masked: Record<string, string> = {};
272
+ for (const [key, val] of Object.entries(variables)) {
273
+ if (typeof val === 'object' && val !== null && val.secret) {
274
+ masked[key] = '********';
275
+ } else {
276
+ masked[key] = typeof val === 'string' ? val : val.value;
277
+ }
302
278
  }
303
- await storage.patchThreadState({
304
- channelId: state.state.channelId,
305
- threadId: state.state.threadId,
306
- state: event.data.state,
307
- });
308
- yield { type: 'action:storage:patch-thread-state-result', data: { success: true } };
309
- } catch {
310
- yield { type: 'action:storage:patch-thread-state-result', data: { success: false } };
311
- }
312
- });
313
-
314
- builder.on('action:storage:list-files', async function* (event, context) {
315
- const channelId = context.state.channelId;
316
- const subPath = (event.data as any)?.path || '';
317
- try {
318
- const files = await storage.listFiles({ channelId, path: subPath });
319
- yield {
320
- type: 'action:storage:list-files:result',
321
- data: { success: true, files },
322
- };
323
- } catch (error) {
324
- yield {
325
- type: 'action:storage:list-files:result',
326
- data: {
327
- success: false,
328
- files: [],
329
- error: error instanceof Error ? error.message : 'Unknown error',
330
- },
331
- };
332
- }
333
- });
334
-
335
- builder.on('action:storage:read-file', async function* (event, context) {
336
- const channelId = context.state.channelId;
337
- const data = event.data as { path?: string; encoding?: 'utf8' | 'base64' };
338
- const filePath = data?.path;
339
- const encoding = data?.encoding ?? 'utf8';
340
- if (!filePath) {
341
279
  yield {
342
- type: 'action:storage:read-file:result',
343
- data: { success: false, path: '', error: 'Path is required' },
344
- };
345
- return;
346
- }
347
- try {
348
- if (encoding === 'utf8') {
349
- const content = await storage.readFile({ channelId, path: filePath });
280
+ type: 'action:storage:get-variables-result',
281
+ data: { variables: masked },
282
+ } as OpenBotEvent;
283
+ });
284
+
285
+ builder.on('action:storage:create-variable', async function* (event) {
286
+ try {
287
+ const { key, value, secret } = event.data;
288
+ await storage.createVariable({ key, value, secret });
289
+ yield { type: 'action:storage:create-variable-result', data: { success: true } };
290
+ } catch (error) {
350
291
  yield {
351
- type: 'action:storage:read-file:result',
352
- data: { success: true, content, path: filePath, encoding },
292
+ type: 'action:storage:create-variable-result',
293
+ data: {
294
+ success: false,
295
+ error: error instanceof Error ? error.message : 'Unknown error',
296
+ },
297
+ };
298
+ }
299
+ });
300
+
301
+ builder.on('action:storage:delete-variable', async function* (event) {
302
+ try {
303
+ await storage.deleteVariable({ key: event.data.key });
304
+ yield { type: 'action:storage:delete-variable-result', data: { success: true } };
305
+ } catch (error) {
306
+ yield {
307
+ type: 'action:storage:delete-variable-result',
308
+ data: {
309
+ success: false,
310
+ error: error instanceof Error ? error.message : 'Unknown error',
311
+ },
353
312
  };
313
+ }
314
+ });
315
+
316
+ builder.on('action:storage:patch-channel-state', async function* (event, state) {
317
+ const channelId = state.state.channelId;
318
+ if (!channelId) {
319
+ yield { type: 'action:storage:patch-channel-state-result', data: { success: false } };
354
320
  return;
355
321
  }
322
+ try {
323
+ await storage.patchChannelState({
324
+ channelId,
325
+ state: event.data.state,
326
+ });
327
+ yield { type: 'action:storage:patch-channel-state-result', data: { success: true } };
328
+ } catch {
329
+ yield { type: 'action:storage:patch-channel-state-result', data: { success: false } };
330
+ }
331
+ });
356
332
 
357
- const { content, mimeType, size } = await storage.readChannelFile({
358
- channelId,
359
- path: filePath,
360
- encoding,
361
- });
362
- yield {
363
- type: 'action:storage:read-file:result',
364
- data: { success: true, content, path: filePath, encoding, mimeType, size },
365
- };
366
- } catch (error) {
367
- yield {
368
- type: 'action:storage:read-file:result',
369
- data: {
370
- success: false,
333
+ builder.on('action:storage:patch-thread-state', async function* (event, state) {
334
+ const channelId = state.state.channelId;
335
+ try {
336
+ if (!channelId || !state.state.threadId) {
337
+ throw new Error('Missing channelId or threadId in state for patch-thread-state');
338
+ }
339
+ await storage.patchThreadState({
340
+ channelId,
341
+ threadId: state.state.threadId,
342
+ state: event.data.state,
343
+ });
344
+ yield { type: 'action:storage:patch-thread-state-result', data: { success: true } };
345
+ } catch {
346
+ yield { type: 'action:storage:patch-thread-state-result', data: { success: false } };
347
+ }
348
+ });
349
+
350
+ builder.on('action:storage:list-files', async function* (event, context) {
351
+ const channelId = context.state.channelId;
352
+ const subPath = (event.data as any)?.path || '';
353
+ if (!channelId) {
354
+ yield {
355
+ type: 'action:storage:list-files:result',
356
+ data: { success: false, files: [], error: 'channelId is required' },
357
+ };
358
+ return;
359
+ }
360
+ try {
361
+ const files = await storage.listFiles({ channelId, path: subPath });
362
+ yield {
363
+ type: 'action:storage:list-files:result',
364
+ data: { success: true, files },
365
+ };
366
+ } catch (error) {
367
+ yield {
368
+ type: 'action:storage:list-files:result',
369
+ data: {
370
+ success: false,
371
+ files: [],
372
+ error: error instanceof Error ? error.message : 'Unknown error',
373
+ },
374
+ };
375
+ }
376
+ });
377
+
378
+ builder.on('action:storage:read-file', async function* (event, context) {
379
+ const channelId = context.state.channelId;
380
+ const data = event.data as { path?: string; encoding?: 'utf8' | 'base64' };
381
+ const filePath = data?.path;
382
+ const encoding = data?.encoding ?? 'utf8';
383
+ if (!filePath) {
384
+ yield {
385
+ type: 'action:storage:read-file:result',
386
+ data: { success: false, path: '', error: 'Path is required' },
387
+ };
388
+ return;
389
+ }
390
+ if (!channelId) {
391
+ yield {
392
+ type: 'action:storage:read-file:result',
393
+ data: { success: false, path: filePath, error: 'channelId is required' },
394
+ };
395
+ return;
396
+ }
397
+ try {
398
+ if (encoding === 'utf8') {
399
+ const content = await storage.readFile({ channelId, path: filePath });
400
+ yield {
401
+ type: 'action:storage:read-file:result',
402
+ data: { success: true, content, path: filePath, encoding },
403
+ };
404
+ return;
405
+ }
406
+
407
+ const { content, mimeType, size } = await storage.readChannelFile({
408
+ channelId,
371
409
  path: filePath,
372
- error: error instanceof Error ? error.message : 'Unknown error',
373
- },
374
- };
375
- }
376
- });
410
+ encoding,
411
+ });
412
+ yield {
413
+ type: 'action:storage:read-file:result',
414
+ data: { success: true, content, path: filePath, encoding, mimeType, size },
415
+ };
416
+ } catch (error) {
417
+ yield {
418
+ type: 'action:storage:read-file:result',
419
+ data: {
420
+ success: false,
421
+ path: filePath,
422
+ error: error instanceof Error ? error.message : 'Unknown error',
423
+ },
424
+ };
425
+ }
426
+ });
377
427
 
378
- builder.on('action:storage:get-file-url', async function* (event, context) {
379
- const channelId = context.state.channelId;
380
- const filePath = (event.data as { path?: string })?.path;
381
- if (!filePath) {
382
- yield {
383
- type: 'action:storage:get-file-url:result',
384
- data: { success: false, path: '', error: 'Path is required' },
385
- };
386
- return;
387
- }
388
- try {
389
- const { size, mimeType } = await storage.getChannelFileStat({ channelId, path: filePath });
390
- const url = buildWorkspaceFileUrl({
391
- baseUrl: resolvePublicBaseUrl(),
392
- channelId,
393
- filePath,
394
- });
395
- yield {
396
- type: 'action:storage:get-file-url:result',
397
- data: { success: true, path: filePath, url, mimeType, size },
398
- };
399
- } catch (error) {
400
- yield {
401
- type: 'action:storage:get-file-url:result',
402
- data: {
403
- success: false,
428
+ builder.on('action:storage:get-file-url', async function* (event, context) {
429
+ const channelId = context.state.channelId;
430
+ const filePath = (event.data as { path?: string })?.path;
431
+ if (!filePath) {
432
+ yield {
433
+ type: 'action:storage:get-file-url:result',
434
+ data: { success: false, path: '', error: 'Path is required' },
435
+ };
436
+ return;
437
+ }
438
+ if (!channelId) {
439
+ yield {
440
+ type: 'action:storage:get-file-url:result',
441
+ data: { success: false, path: filePath, error: 'channelId is required' },
442
+ };
443
+ return;
444
+ }
445
+ try {
446
+ const { size, mimeType } = await storage.getChannelFileStat({
447
+ channelId,
404
448
  path: filePath,
405
- error: error instanceof Error ? error.message : 'Unknown error',
406
- },
407
- };
408
- }
409
- });
410
- },
449
+ });
450
+ const url = buildWorkspaceFileUrl({
451
+ baseUrl: resolvePublicBaseUrl(),
452
+ channelId,
453
+ filePath,
454
+ });
455
+ yield {
456
+ type: 'action:storage:get-file-url:result',
457
+ data: { success: true, path: filePath, url, mimeType, size },
458
+ };
459
+ } catch (error) {
460
+ yield {
461
+ type: 'action:storage:get-file-url:result',
462
+ data: {
463
+ success: false,
464
+ path: filePath,
465
+ error: error instanceof Error ? error.message : 'Unknown error',
466
+ },
467
+ };
468
+ }
469
+ });
470
+ },
411
471
  };
412
472
 
413
473
  export default storagePlugin;