@rimori/client 1.3.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +35 -0
- package/README.md +77 -71
- package/dist/cli/scripts/init/dev-registration.d.ts +1 -1
- package/dist/cli/scripts/init/dev-registration.js +4 -4
- package/dist/cli/scripts/init/main.js +1 -1
- package/dist/cli/scripts/init/package-setup.d.ts +1 -1
- package/dist/cli/scripts/init/package-setup.js +3 -3
- package/dist/cli/scripts/init/router-transformer.js +19 -12
- package/dist/cli/scripts/init/vite-config.d.ts +2 -2
- package/dist/cli/scripts/init/vite-config.js +2 -2
- package/dist/cli/scripts/release/release-config-upload.js +9 -9
- package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
- package/dist/cli/scripts/release/release-db-update.js +9 -9
- package/dist/cli/scripts/release/release-file-upload.js +2 -2
- package/dist/cli/scripts/release/release.js +2 -2
- package/dist/cli/types/DatabaseTypes.d.ts +2 -2
- package/dist/components/CRUDModal.d.ts +1 -1
- package/dist/components/CRUDModal.js +3 -3
- package/dist/components/MarkdownEditor.js +16 -16
- package/dist/components/Spinner.js +2 -2
- package/dist/components/ai/Assistant.js +7 -8
- package/dist/components/ai/Avatar.d.ts +2 -2
- package/dist/components/ai/Avatar.js +14 -7
- package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +5 -6
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +1 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +4 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +1 -1
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -3
- package/dist/components/audio/Playbutton.js +10 -7
- package/dist/components/components/ContextMenu.d.ts +1 -1
- package/dist/components/components/ContextMenu.js +19 -16
- package/dist/components.d.ts +10 -10
- package/dist/components.js +10 -10
- package/dist/core/controller/AIController.d.ts +2 -2
- package/dist/core/controller/AIController.js +20 -18
- package/dist/core/controller/ExerciseController.d.ts +52 -0
- package/dist/core/controller/ExerciseController.js +73 -0
- package/dist/core/controller/ObjectController.js +5 -5
- package/dist/core/controller/SettingsController.d.ts +22 -7
- package/dist/core/controller/SettingsController.js +73 -8
- package/dist/core/controller/SharedContentController.d.ts +3 -3
- package/dist/core/controller/SharedContentController.js +38 -20
- package/dist/core/controller/VoiceController.js +6 -4
- package/dist/core/core.d.ts +15 -14
- package/dist/core/core.js +7 -7
- package/dist/fromRimori/EventBus.js +23 -23
- package/dist/fromRimori/PluginTypes.d.ts +4 -4
- package/dist/hooks/UseChatHook.d.ts +3 -3
- package/dist/hooks/UseChatHook.js +9 -3
- package/dist/index.d.ts +10 -10
- package/dist/index.js +9 -9
- package/dist/plugin/AccomplishmentHandler.d.ts +5 -5
- package/dist/plugin/AccomplishmentHandler.js +31 -27
- package/dist/plugin/AudioController.d.ts +1 -1
- package/dist/plugin/AudioController.js +6 -6
- package/dist/plugin/Logger.d.ts +5 -0
- package/dist/plugin/Logger.js +65 -13
- package/dist/plugin/PluginController.d.ts +7 -1
- package/dist/plugin/PluginController.js +32 -27
- package/dist/plugin/RimoriClient.d.ts +39 -14
- package/dist/plugin/RimoriClient.js +60 -31
- package/dist/plugin/StandaloneClient.d.ts +1 -1
- package/dist/plugin/StandaloneClient.js +35 -16
- package/dist/plugin/ThemeSetter.js +4 -4
- package/dist/providers/PluginProvider.js +44 -14
- package/dist/utils/Language.js +57 -57
- package/dist/utils/PluginUtils.js +3 -3
- package/dist/utils/difficultyConverter.d.ts +1 -1
- package/dist/utils/difficultyConverter.js +1 -1
- package/dist/utils/endpoint.js +2 -2
- package/dist/worker/WorkerSetup.d.ts +1 -1
- package/dist/worker/WorkerSetup.js +6 -6
- package/eslint.config.js +53 -0
- package/example/docs/devdocs.md +50 -40
- package/example/docs/overview.md +1 -1
- package/example/docs/userdocs.md +4 -1
- package/example/rimori.config.ts +51 -49
- package/example/worker/vite.config.ts +3 -3
- package/example/worker/worker.ts +2 -2
- package/package.json +17 -4
- package/prettier.config.js +8 -0
- package/src/cli/scripts/init/dev-registration.ts +5 -8
- package/src/cli/scripts/init/env-setup.ts +1 -1
- package/src/cli/scripts/init/file-operations.ts +1 -1
- package/src/cli/scripts/init/html-cleaner.ts +2 -5
- package/src/cli/scripts/init/main.ts +16 -13
- package/src/cli/scripts/init/package-setup.ts +11 -15
- package/src/cli/scripts/init/router-transformer.ts +40 -37
- package/src/cli/scripts/init/tailwind-config.ts +17 -26
- package/src/cli/scripts/init/vite-config.ts +3 -3
- package/src/cli/scripts/release/release-config-upload.ts +11 -11
- package/src/cli/scripts/release/release-db-update.ts +12 -12
- package/src/cli/scripts/release/release-file-upload.ts +3 -3
- package/src/cli/scripts/release/release.ts +4 -4
- package/src/cli/types/DatabaseTypes.ts +7 -8
- package/src/components/CRUDModal.tsx +64 -48
- package/src/components/MarkdownEditor.tsx +58 -27
- package/src/components/Spinner.tsx +24 -17
- package/src/components/ai/Assistant.tsx +70 -70
- package/src/components/ai/Avatar.tsx +20 -16
- package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +63 -54
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +14 -5
- package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +75 -74
- package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +177 -178
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +109 -94
- package/src/components/ai/utils.ts +4 -4
- package/src/components/audio/Playbutton.tsx +101 -93
- package/src/components/components/ContextMenu.tsx +47 -35
- package/src/components.ts +10 -10
- package/src/core/controller/AIController.ts +62 -50
- package/src/core/controller/ExerciseController.ts +98 -0
- package/src/core/controller/ObjectController.ts +15 -10
- package/src/core/controller/SettingsController.ts +89 -16
- package/src/core/controller/SharedContentController.ts +80 -44
- package/src/core/controller/VoiceController.ts +10 -8
- package/src/core/core.ts +15 -15
- package/src/fromRimori/EventBus.ts +76 -47
- package/src/fromRimori/PluginTypes.ts +26 -17
- package/src/fromRimori/readme.md +2 -2
- package/src/hooks/UseChatHook.ts +25 -15
- package/src/index.ts +10 -10
- package/src/plugin/AccomplishmentHandler.ts +53 -35
- package/src/plugin/AudioController.ts +18 -12
- package/src/plugin/Logger.ts +77 -19
- package/src/plugin/PluginController.ts +60 -44
- package/src/plugin/RimoriClient.ts +133 -69
- package/src/plugin/StandaloneClient.ts +51 -24
- package/src/plugin/ThemeSetter.ts +5 -5
- package/src/providers/PluginProvider.tsx +90 -36
- package/src/style.scss +3 -3
- package/src/utils/Language.ts +58 -58
- package/src/utils/PluginUtils.ts +16 -20
- package/src/utils/difficultyConverter.ts +2 -2
- package/src/utils/endpoint.ts +3 -2
- package/src/worker/WorkerSetup.ts +8 -9
- package/tsconfig.json +2 -4
- package/dist/components/LoggerExample.d.ts +0 -6
- package/dist/components/LoggerExample.js +0 -79
- package/dist/core/controller/AudioController.d.ts +0 -0
- package/dist/core/controller/AudioController.js +0 -1
- package/dist/hooks/UseLogger.d.ts +0 -30
- package/dist/hooks/UseLogger.js +0 -122
- package/dist/plugin/LoggerExample.d.ts +0 -16
- package/dist/plugin/LoggerExample.js +0 -140
- package/dist/utils/audioFormats.d.ts +0 -26
- package/dist/utils/audioFormats.js +0 -67
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
import { RimoriClient } from
|
|
3
|
-
import { ObjectRequest } from
|
|
2
|
+
import { RimoriClient } from '../../plugin/RimoriClient';
|
|
3
|
+
import { ObjectRequest } from './ObjectController';
|
|
4
4
|
|
|
5
5
|
export interface SharedContentObjectRequest extends ObjectRequest {
|
|
6
|
-
fixedProperties?: Record<string, string | number | boolean
|
|
6
|
+
fixedProperties?: Record<string, string | number | boolean>;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export type SharedContentFilter = Record<string, string | number | boolean
|
|
9
|
+
export type SharedContentFilter = Record<string, string | number | boolean>;
|
|
10
10
|
|
|
11
11
|
export class SharedContentController {
|
|
12
12
|
private supabase: SupabaseClient;
|
|
@@ -34,10 +34,11 @@ export class SharedContentController {
|
|
|
34
34
|
generatorInstructions: SharedContentObjectRequest,
|
|
35
35
|
//this filter is there if the content should be filtered additionally by a column and value
|
|
36
36
|
filter?: SharedContentFilter,
|
|
37
|
-
options?: { privateTopic?: boolean
|
|
37
|
+
options?: { privateTopic?: boolean; skipDbSave?: boolean; alwaysGenerateNew?: boolean; excludeIds?: string[] },
|
|
38
38
|
): Promise<SharedContent<T>> {
|
|
39
|
-
let query = this.supabase
|
|
40
|
-
.
|
|
39
|
+
let query = this.supabase
|
|
40
|
+
.from('shared_content')
|
|
41
|
+
.select('*, scc:shared_content_completed(id, state)')
|
|
41
42
|
.eq('content_type', contentType)
|
|
42
43
|
.not('scc.state', 'in', '("completed","ongoing","hidden")')
|
|
43
44
|
.is('deleted_at', null);
|
|
@@ -62,7 +63,7 @@ export class SharedContentController {
|
|
|
62
63
|
|
|
63
64
|
// console.log('newAssignments:', newAssignments);
|
|
64
65
|
|
|
65
|
-
if (!
|
|
66
|
+
if (!options?.alwaysGenerateNew && newAssignments.length > 0) {
|
|
66
67
|
const index = Math.floor(Math.random() * newAssignments.length);
|
|
67
68
|
return newAssignments[index];
|
|
68
69
|
}
|
|
@@ -73,13 +74,13 @@ export class SharedContentController {
|
|
|
73
74
|
|
|
74
75
|
//create the shared content object
|
|
75
76
|
const data: SharedContent<T> = {
|
|
76
|
-
id:
|
|
77
|
+
id: 'internal-temp-id-' + Math.random().toString(36).substring(2, 15),
|
|
77
78
|
contentType,
|
|
78
79
|
title: instructions.title,
|
|
79
80
|
keywords: instructions.keywords.map(({ text }: { text: string }) => text),
|
|
80
81
|
data: { ...instructions, title: undefined, keywords: undefined, ...generatorInstructions.fixedProperties },
|
|
81
82
|
privateTopic: options?.privateTopic,
|
|
82
|
-
}
|
|
83
|
+
};
|
|
83
84
|
|
|
84
85
|
if (options?.skipDbSave) {
|
|
85
86
|
return data;
|
|
@@ -88,7 +89,11 @@ export class SharedContentController {
|
|
|
88
89
|
return await this.createSharedContent(data);
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
private async generateNewAssignment(
|
|
92
|
+
private async generateNewAssignment(
|
|
93
|
+
contentType: string,
|
|
94
|
+
generatorInstructions: SharedContentObjectRequest,
|
|
95
|
+
filter?: SharedContentFilter,
|
|
96
|
+
): Promise<any> {
|
|
92
97
|
const fullInstructions = await this.getGeneratorInstructions(contentType, generatorInstructions, filter);
|
|
93
98
|
|
|
94
99
|
console.log('fullInstructions:', fullInstructions);
|
|
@@ -96,29 +101,34 @@ export class SharedContentController {
|
|
|
96
101
|
return await this.rimoriClient.ai.getObject(fullInstructions);
|
|
97
102
|
}
|
|
98
103
|
|
|
99
|
-
private async getGeneratorInstructions(
|
|
104
|
+
private async getGeneratorInstructions(
|
|
105
|
+
contentType: string,
|
|
106
|
+
generatorInstructions: ObjectRequest,
|
|
107
|
+
filter?: SharedContentFilter,
|
|
108
|
+
): Promise<ObjectRequest> {
|
|
100
109
|
const completedTopics = await this.getCompletedTopics(contentType, filter);
|
|
101
110
|
|
|
102
111
|
generatorInstructions.instructions += `
|
|
103
112
|
The following topics are already taken: ${completedTopics.join(', ')}`;
|
|
104
113
|
|
|
105
114
|
generatorInstructions.tool.title = {
|
|
106
|
-
type:
|
|
107
|
-
description:
|
|
108
|
-
}
|
|
115
|
+
type: 'string',
|
|
116
|
+
description: 'What the topic is about. Short. ',
|
|
117
|
+
};
|
|
109
118
|
generatorInstructions.tool.keywords = {
|
|
110
|
-
type: [{ text: { type:
|
|
111
|
-
description:
|
|
112
|
-
}
|
|
119
|
+
type: [{ text: { type: 'string' } }],
|
|
120
|
+
description: 'Keywords around the topic of the assignment.',
|
|
121
|
+
};
|
|
113
122
|
return generatorInstructions;
|
|
114
123
|
}
|
|
115
124
|
|
|
116
125
|
private async getCompletedTopics(contentType: string, filter?: SharedContentFilter): Promise<string[]> {
|
|
117
|
-
const query = this.supabase
|
|
118
|
-
.
|
|
126
|
+
const query = this.supabase
|
|
127
|
+
.from('shared_content')
|
|
128
|
+
.select('title, keywords, scc:shared_content_completed(id)')
|
|
119
129
|
.eq('content_type', contentType)
|
|
120
130
|
.not('scc.id', 'is', null)
|
|
121
|
-
.is('deleted_at', null)
|
|
131
|
+
.is('deleted_at', null);
|
|
122
132
|
|
|
123
133
|
if (filter) {
|
|
124
134
|
query.contains('data', filter);
|
|
@@ -134,7 +144,13 @@ export class SharedContentController {
|
|
|
134
144
|
}
|
|
135
145
|
|
|
136
146
|
public async getSharedContent<T>(contentType: string, id: string): Promise<SharedContent<T>> {
|
|
137
|
-
const { data, error } = await this.supabase
|
|
147
|
+
const { data, error } = await this.supabase
|
|
148
|
+
.from('shared_content')
|
|
149
|
+
.select()
|
|
150
|
+
.eq('content_type', contentType)
|
|
151
|
+
.eq('id', id)
|
|
152
|
+
.is('deleted_at', null)
|
|
153
|
+
.single();
|
|
138
154
|
if (error) {
|
|
139
155
|
console.error('error fetching shared content:', error);
|
|
140
156
|
throw new Error('error fetching shared content');
|
|
@@ -145,7 +161,7 @@ export class SharedContentController {
|
|
|
145
161
|
public async completeSharedContent(contentType: string, assignmentId: string) {
|
|
146
162
|
// Idempotent completion: upsert on (id, user_id) so repeated calls don't fail
|
|
147
163
|
const { error } = await this.supabase
|
|
148
|
-
.from(
|
|
164
|
+
.from('shared_content_completed')
|
|
149
165
|
.upsert({ content_type: contentType, id: assignmentId } as any, { onConflict: 'id' });
|
|
150
166
|
|
|
151
167
|
if (error) {
|
|
@@ -172,11 +188,11 @@ export class SharedContentController {
|
|
|
172
188
|
reaction,
|
|
173
189
|
bookmarked,
|
|
174
190
|
}: {
|
|
175
|
-
contentType: string
|
|
176
|
-
id: string
|
|
177
|
-
state?: 'completed' | 'ongoing' | 'hidden'
|
|
178
|
-
reaction?: 'liked' | 'disliked' | null
|
|
179
|
-
bookmarked?: boolean
|
|
191
|
+
contentType: string;
|
|
192
|
+
id: string;
|
|
193
|
+
state?: 'completed' | 'ongoing' | 'hidden';
|
|
194
|
+
reaction?: 'liked' | 'disliked' | null;
|
|
195
|
+
bookmarked?: boolean;
|
|
180
196
|
}): Promise<void> {
|
|
181
197
|
const payload: Record<string, unknown> = { content_type: contentType, id };
|
|
182
198
|
if (state !== undefined) payload.state = state;
|
|
@@ -184,9 +200,7 @@ export class SharedContentController {
|
|
|
184
200
|
if (bookmarked !== undefined) payload.bookmarked = bookmarked;
|
|
185
201
|
|
|
186
202
|
// Prefer upsert, fall back to insert/update if upsert not allowed
|
|
187
|
-
const { error } = await this.supabase
|
|
188
|
-
.from('shared_content_completed')
|
|
189
|
-
.upsert(payload as any, { onConflict: 'id' });
|
|
203
|
+
const { error } = await this.supabase.from('shared_content_completed').upsert(payload as any, { onConflict: 'id' });
|
|
190
204
|
|
|
191
205
|
if (error) {
|
|
192
206
|
console.error('error updating shared content state:', error);
|
|
@@ -201,8 +215,17 @@ export class SharedContentController {
|
|
|
201
215
|
* @param limit - Optional limit for the number of results.
|
|
202
216
|
* @returns Array of shared content matching the criteria.
|
|
203
217
|
*/
|
|
204
|
-
public async getSharedContentList<T>(
|
|
205
|
-
|
|
218
|
+
public async getSharedContentList<T>(
|
|
219
|
+
contentType: string,
|
|
220
|
+
filter?: SharedContentFilter,
|
|
221
|
+
limit?: number,
|
|
222
|
+
): Promise<SharedContent<T>[]> {
|
|
223
|
+
const query = this.supabase
|
|
224
|
+
.from('shared_content')
|
|
225
|
+
.select('*')
|
|
226
|
+
.eq('content_type', contentType)
|
|
227
|
+
.is('deleted_at', null)
|
|
228
|
+
.limit(limit ?? 30);
|
|
206
229
|
|
|
207
230
|
if (filter) {
|
|
208
231
|
query.contains('data', filter);
|
|
@@ -229,14 +252,23 @@ export class SharedContentController {
|
|
|
229
252
|
* @returns The inserted shared content.
|
|
230
253
|
* @throws {Error} if insertion fails.
|
|
231
254
|
*/
|
|
232
|
-
public async createSharedContent<T>({
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
255
|
+
public async createSharedContent<T>({
|
|
256
|
+
contentType,
|
|
257
|
+
title,
|
|
258
|
+
keywords,
|
|
259
|
+
data,
|
|
260
|
+
privateTopic,
|
|
261
|
+
}: Omit<SharedContent<T>, 'id'>): Promise<SharedContent<T>> {
|
|
262
|
+
const { data: newContent, error } = await this.supabase
|
|
263
|
+
.from('shared_content')
|
|
264
|
+
.insert({
|
|
265
|
+
private: privateTopic,
|
|
266
|
+
content_type: contentType,
|
|
267
|
+
title,
|
|
268
|
+
keywords,
|
|
269
|
+
data,
|
|
270
|
+
})
|
|
271
|
+
.select();
|
|
240
272
|
|
|
241
273
|
if (error) {
|
|
242
274
|
console.error('error inserting shared content:', error);
|
|
@@ -262,7 +294,11 @@ export class SharedContentController {
|
|
|
262
294
|
if (updates.data) updateData.data = updates.data;
|
|
263
295
|
if (updates.privateTopic !== undefined) updateData.private = updates.privateTopic;
|
|
264
296
|
|
|
265
|
-
const { data: updatedContent, error } = await this.supabase
|
|
297
|
+
const { data: updatedContent, error } = await this.supabase
|
|
298
|
+
.from('shared_content')
|
|
299
|
+
.update(updateData)
|
|
300
|
+
.eq('id', id)
|
|
301
|
+
.select();
|
|
266
302
|
|
|
267
303
|
if (error) {
|
|
268
304
|
console.error('error updating shared content:', error);
|
|
@@ -284,7 +320,7 @@ export class SharedContentController {
|
|
|
284
320
|
*/
|
|
285
321
|
public async removeSharedContent(id: string): Promise<SharedContent<any>> {
|
|
286
322
|
const { data: deletedContent, error } = await this.supabase
|
|
287
|
-
.from(
|
|
323
|
+
.from('shared_content')
|
|
288
324
|
.update({ deleted_at: new Date().toISOString() })
|
|
289
325
|
.eq('id', id)
|
|
290
326
|
.select();
|
|
@@ -324,4 +360,4 @@ export interface SharedContent<T> {
|
|
|
324
360
|
|
|
325
361
|
/** Whether this content should only be visible to the creator. Defaults to false if not specified */
|
|
326
362
|
privateTopic?: boolean;
|
|
327
|
-
}
|
|
363
|
+
}
|
|
@@ -4,12 +4,14 @@ export async function getSTTResponse(backendUrl: string, audio: Blob, token: str
|
|
|
4
4
|
|
|
5
5
|
return await fetch(`${backendUrl}/voice/stt`, {
|
|
6
6
|
method: 'POST',
|
|
7
|
-
headers: {
|
|
7
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
8
8
|
body: formData,
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
})
|
|
10
|
+
.then((r) => r.json())
|
|
11
|
+
.then((r) => {
|
|
12
|
+
// console.log("STT response: ", r);
|
|
13
|
+
return r.text;
|
|
14
|
+
});
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export async function getTTSResponse(backendUrl: string, request: TTSRequest, token: string) {
|
|
@@ -17,10 +19,10 @@ export async function getTTSResponse(backendUrl: string, request: TTSRequest, to
|
|
|
17
19
|
method: 'POST',
|
|
18
20
|
headers: {
|
|
19
21
|
'Content-Type': 'application/json',
|
|
20
|
-
|
|
22
|
+
Authorization: `Bearer ${token}`,
|
|
21
23
|
},
|
|
22
24
|
body: JSON.stringify(request),
|
|
23
|
-
}).then(r => r.blob());
|
|
25
|
+
}).then((r) => r.blob());
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
interface TTSRequest {
|
|
@@ -28,4 +30,4 @@ interface TTSRequest {
|
|
|
28
30
|
voice: string;
|
|
29
31
|
speed: number;
|
|
30
32
|
language?: string;
|
|
31
|
-
}
|
|
33
|
+
}
|
package/src/core/core.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// Core functionality exports
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export { EventBusMessage } from
|
|
10
|
-
export { Buddy, UserInfo } from
|
|
11
|
-
export { SharedContent } from
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
|
|
2
|
+
export * from '../fromRimori/PluginTypes';
|
|
3
|
+
export * from '../plugin/PluginController';
|
|
4
|
+
export * from '../plugin/RimoriClient';
|
|
5
|
+
export * from '../utils/difficultyConverter';
|
|
6
|
+
export * from '../utils/Language';
|
|
7
|
+
export * from '../utils/PluginUtils';
|
|
8
|
+
export * from '../worker/WorkerSetup';
|
|
9
|
+
export { EventBusMessage } from '../fromRimori/EventBus';
|
|
10
|
+
export { Buddy, UserInfo } from './controller/SettingsController';
|
|
11
|
+
export { SharedContent } from './controller/SharedContentController';
|
|
12
|
+
export { Exercise, TriggerAction } from './controller/ExerciseController';
|
|
13
|
+
export { Message, OnLLMResponse, ToolInvocation } from './controller/AIController';
|
|
14
|
+
export { MacroAccomplishmentPayload, MicroAccomplishmentPayload } from '../plugin/AccomplishmentHandler';
|
|
15
|
+
export { Tool } from '../fromRimori/PluginTypes';
|
|
16
|
+
export { SharedContentObjectRequest } from './controller/SharedContentController';
|
|
@@ -3,9 +3,9 @@ export type EventPayload = Record<string, any>;
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Interface representing a message sent through the EventBus
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* Debug capabilities:
|
|
8
|
-
* - System-wide debugging: Send an event to "global.system.requestDebug"
|
|
8
|
+
* - System-wide debugging: Send an event to "global.system.requestDebug"
|
|
9
9
|
* Example: `EventBus.emit("yourPluginId", "global.system.requestDebug");`
|
|
10
10
|
*/
|
|
11
11
|
export interface EventBusMessage<T = EventPayload> {
|
|
@@ -40,7 +40,7 @@ export class EventBusHandler {
|
|
|
40
40
|
private responseResolvers: Map<number, (value: EventBusMessage<unknown>) => void> = new Map();
|
|
41
41
|
private static instance: EventBusHandler | null = null;
|
|
42
42
|
private debugEnabled: boolean = false;
|
|
43
|
-
private evName: string =
|
|
43
|
+
private evName: string = '';
|
|
44
44
|
|
|
45
45
|
private constructor() {
|
|
46
46
|
//private constructor
|
|
@@ -50,12 +50,14 @@ export class EventBusHandler {
|
|
|
50
50
|
if (!EventBusHandler.instance) {
|
|
51
51
|
EventBusHandler.instance = new EventBusHandler();
|
|
52
52
|
|
|
53
|
-
EventBusHandler.instance.on(
|
|
53
|
+
EventBusHandler.instance.on('global.system.requestDebug', () => {
|
|
54
54
|
EventBusHandler.instance!.debugEnabled = true;
|
|
55
|
-
console.log(
|
|
55
|
+
console.log(
|
|
56
|
+
`[${EventBusHandler.instance!.evName}] Debug mode enabled. Make sure debugging messages are enabled in the browser console.`,
|
|
57
|
+
);
|
|
56
58
|
});
|
|
57
59
|
}
|
|
58
|
-
if (name && EventBusHandler.instance.evName ===
|
|
60
|
+
if (name && EventBusHandler.instance.evName === '') {
|
|
59
61
|
EventBusHandler.instance.evName = name;
|
|
60
62
|
}
|
|
61
63
|
return EventBusHandler.instance;
|
|
@@ -80,9 +82,9 @@ export class EventBusHandler {
|
|
|
80
82
|
* @param topic - The topic of the event.
|
|
81
83
|
* @param data - The data of the event.
|
|
82
84
|
* @param eventId - The event id of the event.
|
|
83
|
-
*
|
|
85
|
+
*
|
|
84
86
|
* The topic format is: **pluginId.area.action**
|
|
85
|
-
*
|
|
87
|
+
*
|
|
86
88
|
* Example topics:
|
|
87
89
|
* - pl1234.card.requestHard
|
|
88
90
|
* - pl1234.card.requestNew
|
|
@@ -96,7 +98,13 @@ export class EventBusHandler {
|
|
|
96
98
|
this.emitInternal(sender, topic, data || {}, eventId);
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
private emitInternal(
|
|
101
|
+
private emitInternal(
|
|
102
|
+
sender: string,
|
|
103
|
+
topic: string,
|
|
104
|
+
data: EventPayload,
|
|
105
|
+
eventId?: number,
|
|
106
|
+
skipResponseTrigger = false,
|
|
107
|
+
): void {
|
|
100
108
|
if (!this.validateTopic(topic)) {
|
|
101
109
|
this.logAndThrowError(false, `Invalid topic: ` + topic);
|
|
102
110
|
return;
|
|
@@ -105,7 +113,7 @@ export class EventBusHandler {
|
|
|
105
113
|
const event = this.createEvent(sender, topic, data, eventId);
|
|
106
114
|
|
|
107
115
|
const handlers = this.getMatchingHandlers(event.topic);
|
|
108
|
-
handlers.forEach(handler => {
|
|
116
|
+
handlers.forEach((handler) => {
|
|
109
117
|
if (handler.ignoreSender && handler.ignoreSender.includes(sender)) {
|
|
110
118
|
// console.log("ignore event as its in the ignoreSender list", { event, ignoreList: handler.ignoreSender });
|
|
111
119
|
return;
|
|
@@ -132,8 +140,12 @@ export class EventBusHandler {
|
|
|
132
140
|
* @param ignoreSender - The senders to ignore.
|
|
133
141
|
* @returns An EventListener object containing an off() method to unsubscribe the listeners.
|
|
134
142
|
*/
|
|
135
|
-
public on<T = EventPayload>(
|
|
136
|
-
|
|
143
|
+
public on<T = EventPayload>(
|
|
144
|
+
topics: string | string[],
|
|
145
|
+
handler: EventHandler<T>,
|
|
146
|
+
ignoreSender: string[] = [],
|
|
147
|
+
): EventListener {
|
|
148
|
+
const ids = this.toArray(topics).map((topic) => {
|
|
137
149
|
this.logIfDebug(`Subscribing to ` + topic, { ignoreSender });
|
|
138
150
|
if (!this.validateTopic(topic)) {
|
|
139
151
|
this.logAndThrowError(true, `Invalid topic: ` + topic);
|
|
@@ -154,7 +166,7 @@ export class EventBusHandler {
|
|
|
154
166
|
});
|
|
155
167
|
|
|
156
168
|
return {
|
|
157
|
-
off: () => this.off(ids)
|
|
169
|
+
off: () => this.off(ids),
|
|
158
170
|
};
|
|
159
171
|
}
|
|
160
172
|
|
|
@@ -165,33 +177,41 @@ export class EventBusHandler {
|
|
|
165
177
|
* @param handler - The handler to be called when the event is received. The handler returns the data to be emitted. Can be a static object or a function.
|
|
166
178
|
* @returns An EventListener object containing an off() method to unsubscribe the listeners.
|
|
167
179
|
*/
|
|
168
|
-
public respond(
|
|
180
|
+
public respond(
|
|
181
|
+
sender: string,
|
|
182
|
+
topic: string | string[],
|
|
183
|
+
handler: EventPayload | ((data: EventBusMessage) => EventPayload | Promise<EventPayload>),
|
|
184
|
+
): EventListener {
|
|
169
185
|
const topics = Array.isArray(topic) ? topic : [topic];
|
|
170
|
-
const listeners = topics.map(topic => {
|
|
186
|
+
const listeners = topics.map((topic) => {
|
|
171
187
|
const blackListedEventIds: number[] = [];
|
|
172
188
|
//To allow event communication inside the same plugin the sender needs to be ignored but the events still need to be checked for the same event just reaching the subscriber to prevent infinite loops
|
|
173
|
-
const finalIgnoreSender = !topic.startsWith(
|
|
174
|
-
|
|
175
|
-
const listener = this.on(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
blackListedEventIds.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
+
const finalIgnoreSender = !topic.startsWith('self.') ? [sender] : [];
|
|
190
|
+
|
|
191
|
+
const listener = this.on(
|
|
192
|
+
topic,
|
|
193
|
+
async (data: EventBusMessage) => {
|
|
194
|
+
if (blackListedEventIds.includes(data.eventId)) {
|
|
195
|
+
// console.log("BLACKLISTED EVENT ID", data.eventId);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
blackListedEventIds.push(data.eventId);
|
|
199
|
+
if (blackListedEventIds.length > 20) {
|
|
200
|
+
blackListedEventIds.shift();
|
|
201
|
+
}
|
|
202
|
+
const response = typeof handler === 'function' ? await handler(data) : handler;
|
|
203
|
+
this.emit(sender, topic, response, data.eventId);
|
|
204
|
+
},
|
|
205
|
+
finalIgnoreSender,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
this.logIfDebug(`Added respond listener ` + sender + ' to topic ' + topic, { listener, sender });
|
|
189
209
|
return {
|
|
190
|
-
off: () => listener.off()
|
|
210
|
+
off: () => listener.off(),
|
|
191
211
|
};
|
|
192
212
|
});
|
|
193
213
|
return {
|
|
194
|
-
off: () => listeners.forEach(listener => listener.off())
|
|
214
|
+
off: () => listeners.forEach((listener) => listener.off()),
|
|
195
215
|
};
|
|
196
216
|
}
|
|
197
217
|
|
|
@@ -221,12 +241,12 @@ export class EventBusHandler {
|
|
|
221
241
|
* @param listenerIds - The ids of the listeners to unsubscribe from.
|
|
222
242
|
*/
|
|
223
243
|
private off(listenerIds: string | string[]): void {
|
|
224
|
-
this.toArray(listenerIds).forEach(fullId => {
|
|
244
|
+
this.toArray(listenerIds).forEach((fullId) => {
|
|
225
245
|
const { topic, id } = JSON.parse(atob(fullId));
|
|
226
246
|
|
|
227
247
|
const listeners = this.listeners.get(topic) || new Set();
|
|
228
248
|
|
|
229
|
-
listeners.forEach(listener => {
|
|
249
|
+
listeners.forEach((listener) => {
|
|
230
250
|
if (listener.id === Number(id)) {
|
|
231
251
|
listeners.delete(listener);
|
|
232
252
|
this.logIfDebug(`Removed listener ` + fullId, { topic, listenerId: id });
|
|
@@ -246,7 +266,11 @@ export class EventBusHandler {
|
|
|
246
266
|
* @param data - The data of the event.
|
|
247
267
|
* @returns A promise that resolves to the event.
|
|
248
268
|
*/
|
|
249
|
-
public async request<T = EventPayload>(
|
|
269
|
+
public async request<T = EventPayload>(
|
|
270
|
+
sender: string,
|
|
271
|
+
topic: string,
|
|
272
|
+
data?: EventPayload,
|
|
273
|
+
): Promise<EventBusMessage<T>> {
|
|
250
274
|
if (!this.validateTopic(topic)) {
|
|
251
275
|
this.logAndThrowError(true, `Invalid topic: ` + topic);
|
|
252
276
|
}
|
|
@@ -255,8 +279,10 @@ export class EventBusHandler {
|
|
|
255
279
|
|
|
256
280
|
this.logIfDebug(`Requesting data from ` + topic, { event });
|
|
257
281
|
|
|
258
|
-
return new Promise<EventBusMessage<T>>(resolve => {
|
|
259
|
-
this.responseResolvers.set(event.eventId, (value: EventBusMessage<unknown>) =>
|
|
282
|
+
return new Promise<EventBusMessage<T>>((resolve) => {
|
|
283
|
+
this.responseResolvers.set(event.eventId, (value: EventBusMessage<unknown>) =>
|
|
284
|
+
resolve(value as EventBusMessage<T>),
|
|
285
|
+
);
|
|
260
286
|
this.emitInternal(sender, topic, data || {}, event.eventId, true);
|
|
261
287
|
});
|
|
262
288
|
}
|
|
@@ -271,7 +297,7 @@ export class EventBusHandler {
|
|
|
271
297
|
|
|
272
298
|
// Find wildcard matches
|
|
273
299
|
const wildcard = [...this.listeners.entries()]
|
|
274
|
-
.filter(([key]) => key.endsWith(
|
|
300
|
+
.filter(([key]) => key.endsWith('*') && topic.startsWith(key.slice(0, -1)))
|
|
275
301
|
.flatMap(([_, handlers]) => [...handlers]);
|
|
276
302
|
return new Set([...exact, ...wildcard]);
|
|
277
303
|
}
|
|
@@ -283,32 +309,35 @@ export class EventBusHandler {
|
|
|
283
309
|
*/
|
|
284
310
|
private validateTopic(topic: string): boolean {
|
|
285
311
|
// Split event type into parts
|
|
286
|
-
const parts = topic.split(
|
|
312
|
+
const parts = topic.split('.');
|
|
287
313
|
const [plugin, area, action] = parts;
|
|
288
314
|
|
|
289
315
|
if (parts.length !== 3) {
|
|
290
|
-
if (parts.length === 1 && plugin ===
|
|
316
|
+
if (parts.length === 1 && plugin === '*') {
|
|
291
317
|
return true;
|
|
292
318
|
}
|
|
293
|
-
if (parts.length === 2 && plugin !==
|
|
319
|
+
if (parts.length === 2 && plugin !== '*' && area === '*') {
|
|
294
320
|
return true;
|
|
295
321
|
}
|
|
296
322
|
this.logAndThrowError(false, `Event type must have 3 parts separated by dots. Received: ` + topic);
|
|
297
323
|
return false;
|
|
298
324
|
}
|
|
299
325
|
|
|
300
|
-
if (action ===
|
|
326
|
+
if (action === '*') {
|
|
301
327
|
return true;
|
|
302
328
|
}
|
|
303
329
|
|
|
304
330
|
// Validate action part
|
|
305
|
-
const validActions = [
|
|
331
|
+
const validActions = ['request', 'create', 'update', 'delete', 'trigger'];
|
|
306
332
|
|
|
307
|
-
if (validActions.some(a => action.startsWith(a))) {
|
|
333
|
+
if (validActions.some((a) => action.startsWith(a))) {
|
|
308
334
|
return true;
|
|
309
335
|
}
|
|
310
336
|
|
|
311
|
-
this.logAndThrowError(
|
|
337
|
+
this.logAndThrowError(
|
|
338
|
+
false,
|
|
339
|
+
`Invalid event topic name. The action: ` + action + '. Must be or start with one of: ' + validActions.join(', '),
|
|
340
|
+
);
|
|
312
341
|
return false;
|
|
313
342
|
}
|
|
314
343
|
|
|
@@ -327,4 +356,4 @@ export class EventBusHandler {
|
|
|
327
356
|
}
|
|
328
357
|
}
|
|
329
358
|
|
|
330
|
-
export const EventBus = EventBusHandler.getInstance();
|
|
359
|
+
export const EventBus = EventBusHandler.getInstance();
|