@rimori/client 2.5.18 → 2.5.19-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fromRimori/EventBus.d.ts +4 -2
- package/dist/fromRimori/EventBus.js +9 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/plugin/CommunicationHandler.js +2 -2
- package/dist/plugin/module/AIModule.d.ts +16 -13
- package/dist/plugin/module/AIModule.js +41 -41
- package/dist/plugin/module/DbModule.d.ts +1 -3
- package/dist/plugin/module/DbModule.js +0 -5
- package/dist/plugin/module/EventModule.js +40 -7
- package/dist/plugin/module/PluginModule.d.ts +18 -1
- package/dist/plugin/module/PluginModule.js +4 -0
- package/dist/worker/WorkerSetup.d.ts +2 -1
- package/dist/worker/WorkerSetup.js +9 -3
- package/package.json +1 -1
|
@@ -13,6 +13,8 @@ export interface EventBusMessage<T = EventPayload> {
|
|
|
13
13
|
topic: string;
|
|
14
14
|
data: T;
|
|
15
15
|
debug: boolean;
|
|
16
|
+
/** Session token inherited from the calling plugin, used to attribute AI calls to the originating exercise session. */
|
|
17
|
+
ai_session_token?: string;
|
|
16
18
|
}
|
|
17
19
|
export type EventHandler<T = EventPayload> = (event: EventBusMessage<T>) => void | Promise<void>;
|
|
18
20
|
export interface EventListener {
|
|
@@ -56,7 +58,7 @@ export declare class EventBusHandler {
|
|
|
56
58
|
* - pl1234.card.delete
|
|
57
59
|
* - pl1234.card.triggerBackup
|
|
58
60
|
*/
|
|
59
|
-
emit<T = EventPayload>(sender: string, topic: string, data?: T, eventId?: number): void;
|
|
61
|
+
emit<T = EventPayload>(sender: string, topic: string, data?: T, eventId?: number, aiSessionToken?: string): void;
|
|
60
62
|
private emitInternal;
|
|
61
63
|
/**
|
|
62
64
|
* Subscribes to an event on the event bus.
|
|
@@ -93,7 +95,7 @@ export declare class EventBusHandler {
|
|
|
93
95
|
* @param data - The data of the event.
|
|
94
96
|
* @returns A promise that resolves to the event.
|
|
95
97
|
*/
|
|
96
|
-
request<T = EventPayload>(sender: string, topic: string, data?: EventPayload): Promise<EventBusMessage<T>>;
|
|
98
|
+
request<T = EventPayload>(sender: string, topic: string, data?: EventPayload, aiSessionToken?: string): Promise<EventBusMessage<T>>;
|
|
97
99
|
/**
|
|
98
100
|
* Gets the matching handlers for an event.
|
|
99
101
|
* @param topic - The topic of the event.
|
|
@@ -63,7 +63,7 @@ export class EventBusHandler {
|
|
|
63
63
|
this.generatedIds.set(id, now);
|
|
64
64
|
return id;
|
|
65
65
|
}
|
|
66
|
-
createEvent(sender, topic, data, eventId) {
|
|
66
|
+
createEvent(sender, topic, data, eventId, aiSessionToken) {
|
|
67
67
|
const generatedEventId = eventId || this.generateUniqueId();
|
|
68
68
|
return {
|
|
69
69
|
eventId: generatedEventId,
|
|
@@ -72,6 +72,7 @@ export class EventBusHandler {
|
|
|
72
72
|
topic,
|
|
73
73
|
data,
|
|
74
74
|
debug: this.debugEnabled,
|
|
75
|
+
ai_session_token: aiSessionToken,
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
/**
|
|
@@ -92,15 +93,15 @@ export class EventBusHandler {
|
|
|
92
93
|
* - pl1234.card.delete
|
|
93
94
|
* - pl1234.card.triggerBackup
|
|
94
95
|
*/
|
|
95
|
-
emit(sender, topic, data, eventId) {
|
|
96
|
-
this.emitInternal(sender, topic, data || {}, eventId);
|
|
96
|
+
emit(sender, topic, data, eventId, aiSessionToken) {
|
|
97
|
+
this.emitInternal(sender, topic, data || {}, eventId, false, aiSessionToken);
|
|
97
98
|
}
|
|
98
|
-
emitInternal(sender, topic, data, eventId, skipResponseTrigger = false) {
|
|
99
|
+
emitInternal(sender, topic, data, eventId, skipResponseTrigger = false, aiSessionToken) {
|
|
99
100
|
if (!this.validateTopic(topic)) {
|
|
100
101
|
this.logAndThrowError(false, `Invalid topic: ` + topic);
|
|
101
102
|
return;
|
|
102
103
|
}
|
|
103
|
-
const event = this.createEvent(sender, topic, data, eventId);
|
|
104
|
+
const event = this.createEvent(sender, topic, data, eventId, aiSessionToken);
|
|
104
105
|
const handlers = this.getMatchingHandlers(event.topic);
|
|
105
106
|
handlers.forEach((handler) => {
|
|
106
107
|
if (handler.ignoreSender && handler.ignoreSender.includes(sender)) {
|
|
@@ -245,16 +246,16 @@ export class EventBusHandler {
|
|
|
245
246
|
* @param data - The data of the event.
|
|
246
247
|
* @returns A promise that resolves to the event.
|
|
247
248
|
*/
|
|
248
|
-
request(sender, topic, data) {
|
|
249
|
+
request(sender, topic, data, aiSessionToken) {
|
|
249
250
|
return __awaiter(this, void 0, void 0, function* () {
|
|
250
251
|
if (!this.validateTopic(topic)) {
|
|
251
252
|
this.logAndThrowError(true, `Invalid topic: ` + topic);
|
|
252
253
|
}
|
|
253
|
-
const event = this.createEvent(sender, topic, data || {});
|
|
254
|
+
const event = this.createEvent(sender, topic, data || {}, undefined, aiSessionToken);
|
|
254
255
|
this.logIfDebug(`Requesting data from ` + topic, { event });
|
|
255
256
|
return new Promise((resolve) => {
|
|
256
257
|
this.responseResolvers.set(event.eventId, (value) => resolve(value));
|
|
257
|
-
this.emitInternal(sender, topic, data || {}, event.eventId, true);
|
|
258
|
+
this.emitInternal(sender, topic, data || {}, event.eventId, true, aiSessionToken);
|
|
258
259
|
});
|
|
259
260
|
});
|
|
260
261
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export { Translator } from './controller/TranslationController';
|
|
|
15
15
|
export type { TriggerAction } from './plugin/module/ExerciseModule';
|
|
16
16
|
export type { Message, ToolInvocation } from './plugin/module/AIModule';
|
|
17
17
|
export type { Theme, ApplicationMode } from './plugin/module/PluginModule';
|
|
18
|
-
export type { UserInfo, Language, UserRole, ExplicitUndefined } from './plugin/module/PluginModule';
|
|
18
|
+
export type { UserInfo, Language, UserRole, SubscriptionTier, ExplicitUndefined, BasePluginSettings } from './plugin/module/PluginModule';
|
|
19
|
+
export { TIER_ORDER, ROLE_ORDER } from './plugin/module/PluginModule';
|
|
19
20
|
export type { SharedContent, BasicSharedContent, ContentStatus } from './plugin/module/SharedContentController';
|
|
20
21
|
export type { MacroAccomplishmentPayload, MicroAccomplishmentPayload } from './controller/AccomplishmentController';
|
|
21
22
|
export { StorageModule } from './plugin/module/StorageModule';
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,5 @@ export * from './plugin/CommunicationHandler';
|
|
|
10
10
|
export { setupWorker } from './worker/WorkerSetup';
|
|
11
11
|
export { AudioController } from './controller/AudioController';
|
|
12
12
|
export { Translator } from './controller/TranslationController';
|
|
13
|
+
export { TIER_ORDER, ROLE_ORDER } from './plugin/module/PluginModule';
|
|
13
14
|
export { StorageModule } from './plugin/module/StorageModule';
|
|
@@ -58,9 +58,9 @@ export class RimoriCommunicationHandler {
|
|
|
58
58
|
EventBus.emit(this.pluginId, 'error', { error }, eventId);
|
|
59
59
|
}
|
|
60
60
|
else if (event) {
|
|
61
|
-
const { topic, sender, data: eventData, eventId } = event;
|
|
61
|
+
const { topic, sender, data: eventData, eventId, ai_session_token } = event;
|
|
62
62
|
if (sender !== this.pluginId) {
|
|
63
|
-
EventBus.emit(sender, topic, eventData, eventId);
|
|
63
|
+
EventBus.emit(sender, topic, eventData, eventId, ai_session_token);
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
66
|
console.log('[PluginController] event from self', event);
|
|
@@ -61,17 +61,20 @@ export declare class AIModule {
|
|
|
61
61
|
private sessionTokenId;
|
|
62
62
|
private onRateLimitedCb?;
|
|
63
63
|
constructor(backendUrl: string, getToken: () => string);
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
/** Exercise session management. */
|
|
65
|
+
readonly session: {
|
|
66
|
+
/** Returns the current exercise session token ID (null if no active session). */
|
|
67
|
+
get: () => string | null;
|
|
68
|
+
/** Sets the session token ID. */
|
|
69
|
+
set: (id: string) => void;
|
|
70
|
+
/** Clears the stored session token. */
|
|
71
|
+
clear: () => void;
|
|
72
|
+
/**
|
|
73
|
+
* Ensures a session token exists, creating one from the backend if needed.
|
|
74
|
+
* Mirrors the lazy-issuance pattern used by the AI/LLM endpoint.
|
|
75
|
+
*/
|
|
76
|
+
ensure: () => Promise<void>;
|
|
77
|
+
};
|
|
75
78
|
/** Registers a callback invoked whenever a 429 rate-limit response is received. */
|
|
76
79
|
setOnRateLimited(cb: (exercisesRemaining: number) => void): void;
|
|
77
80
|
/**
|
|
@@ -91,7 +94,7 @@ export declare class AIModule {
|
|
|
91
94
|
* @param cache Whether to cache the result (default: false).
|
|
92
95
|
* @param model The model to use for generation.
|
|
93
96
|
*/
|
|
94
|
-
getSteamedText(messages: Message[], onMessage: OnLLMResponse, tools?: Tool[], cache?: boolean, model?: string, knowledgeId?: string): Promise<
|
|
97
|
+
getSteamedText(messages: Message[], onMessage: OnLLMResponse, tools?: Tool[], cache?: boolean, model?: string, knowledgeId?: string): Promise<string>;
|
|
95
98
|
/**
|
|
96
99
|
* Generate voice audio from text using AI.
|
|
97
100
|
* @param text The text to convert to voice.
|
|
@@ -151,7 +154,7 @@ export declare class AIModule {
|
|
|
151
154
|
tools?: Tool[];
|
|
152
155
|
model?: string;
|
|
153
156
|
knowledgeId?: string;
|
|
154
|
-
}): Promise<
|
|
157
|
+
}): Promise<T>;
|
|
155
158
|
private streamObject;
|
|
156
159
|
private sendToolResult;
|
|
157
160
|
}
|
|
@@ -14,47 +14,46 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
14
14
|
export class AIModule {
|
|
15
15
|
constructor(backendUrl, getToken) {
|
|
16
16
|
this.sessionTokenId = null;
|
|
17
|
+
/** Exercise session management. */
|
|
18
|
+
this.session = {
|
|
19
|
+
/** Returns the current exercise session token ID (null if no active session). */
|
|
20
|
+
get: () => this.sessionTokenId,
|
|
21
|
+
/** Sets the session token ID. */
|
|
22
|
+
set: (id) => {
|
|
23
|
+
this.sessionTokenId = id;
|
|
24
|
+
},
|
|
25
|
+
/** Clears the stored session token. */
|
|
26
|
+
clear: () => {
|
|
27
|
+
this.sessionTokenId = null;
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* Ensures a session token exists, creating one from the backend if needed.
|
|
31
|
+
* Mirrors the lazy-issuance pattern used by the AI/LLM endpoint.
|
|
32
|
+
*/
|
|
33
|
+
ensure: () => __awaiter(this, void 0, void 0, function* () {
|
|
34
|
+
var _a, _b, _c;
|
|
35
|
+
if (this.sessionTokenId)
|
|
36
|
+
return;
|
|
37
|
+
const response = yield fetch(`${this.backendUrl}/ai/session`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { Authorization: `Bearer ${this.getToken()}` },
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
if (response.status === 429) {
|
|
43
|
+
const body = yield response.json().catch(() => ({}));
|
|
44
|
+
const remaining = (_a = body.exercises_remaining) !== null && _a !== void 0 ? _a : 0;
|
|
45
|
+
(_b = this.onRateLimitedCb) === null || _b === void 0 ? void 0 : _b.call(this, remaining);
|
|
46
|
+
throw new Error(`Rate limit exceeded: ${(_c = body.error) !== null && _c !== void 0 ? _c : 'Daily exercise limit reached'}. exercises_remaining: ${remaining}`);
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`Failed to create session: ${response.status} ${response.statusText}`);
|
|
49
|
+
}
|
|
50
|
+
const { session_token_id } = yield response.json();
|
|
51
|
+
this.sessionTokenId = session_token_id;
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
17
54
|
this.backendUrl = backendUrl;
|
|
18
55
|
this.getToken = getToken;
|
|
19
56
|
}
|
|
20
|
-
/** Returns the current exercise session token ID (null if no active session). */
|
|
21
|
-
getSessionTokenId() {
|
|
22
|
-
return this.sessionTokenId;
|
|
23
|
-
}
|
|
24
|
-
/** Sets the session token ID (used by workers inheriting a session from the plugin). */
|
|
25
|
-
setSessionToken(id) {
|
|
26
|
-
this.sessionTokenId = id;
|
|
27
|
-
}
|
|
28
|
-
/** Clears the stored session token (called after macro accomplishment). */
|
|
29
|
-
clearSessionToken() {
|
|
30
|
-
this.sessionTokenId = null;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Ensures a session token exists, requesting one from the backend if needed.
|
|
34
|
-
* Mirrors the lazy-issuance pattern used by the AI/LLM endpoint.
|
|
35
|
-
*/
|
|
36
|
-
ensureSessionToken() {
|
|
37
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
-
var _a, _b, _c;
|
|
39
|
-
if (this.sessionTokenId)
|
|
40
|
-
return;
|
|
41
|
-
const response = yield fetch(`${this.backendUrl}/ai/session`, {
|
|
42
|
-
method: 'POST',
|
|
43
|
-
headers: { Authorization: `Bearer ${this.getToken()}` },
|
|
44
|
-
});
|
|
45
|
-
if (!response.ok) {
|
|
46
|
-
if (response.status === 429) {
|
|
47
|
-
const body = yield response.json().catch(() => ({}));
|
|
48
|
-
const remaining = (_a = body.exercises_remaining) !== null && _a !== void 0 ? _a : 0;
|
|
49
|
-
(_b = this.onRateLimitedCb) === null || _b === void 0 ? void 0 : _b.call(this, remaining);
|
|
50
|
-
throw new Error(`Rate limit exceeded: ${(_c = body.error) !== null && _c !== void 0 ? _c : 'Daily exercise limit reached'}. exercises_remaining: ${remaining}`);
|
|
51
|
-
}
|
|
52
|
-
throw new Error(`Failed to create session: ${response.status} ${response.statusText}`);
|
|
53
|
-
}
|
|
54
|
-
const { session_token_id } = yield response.json();
|
|
55
|
-
this.sessionTokenId = session_token_id;
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
57
|
/** Registers a callback invoked whenever a 429 rate-limit response is received. */
|
|
59
58
|
setOnRateLimited(cb) {
|
|
60
59
|
this.onRateLimitedCb = cb;
|
|
@@ -108,6 +107,7 @@ export class AIModule {
|
|
|
108
107
|
onResult: ({ result }) => onMessage(messageId, result, false),
|
|
109
108
|
});
|
|
110
109
|
onMessage(messageId, result, true);
|
|
110
|
+
return result;
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
@@ -122,7 +122,7 @@ export class AIModule {
|
|
|
122
122
|
getVoice(text_1) {
|
|
123
123
|
return __awaiter(this, arguments, void 0, function* (text, voice = 'alloy', speed = 1, language, cache = false) {
|
|
124
124
|
var _a;
|
|
125
|
-
yield this.
|
|
125
|
+
yield this.session.ensure();
|
|
126
126
|
return yield fetch(`${this.backendUrl}/voice/tts`, {
|
|
127
127
|
method: 'POST',
|
|
128
128
|
headers: {
|
|
@@ -141,7 +141,7 @@ export class AIModule {
|
|
|
141
141
|
*/
|
|
142
142
|
getTextFromVoice(file, language) {
|
|
143
143
|
return __awaiter(this, void 0, void 0, function* () {
|
|
144
|
-
yield this.
|
|
144
|
+
yield this.session.ensure();
|
|
145
145
|
const formData = new FormData();
|
|
146
146
|
formData.append('file', file);
|
|
147
147
|
if (language) {
|
|
@@ -208,7 +208,7 @@ export class AIModule {
|
|
|
208
208
|
getStreamedObject(params) {
|
|
209
209
|
return __awaiter(this, void 0, void 0, function* () {
|
|
210
210
|
const { systemPrompt, responseSchema, userPrompt, onResult, cache = false, tools = [], model = undefined, knowledgeId, } = params;
|
|
211
|
-
yield this.streamObject({
|
|
211
|
+
return yield this.streamObject({
|
|
212
212
|
responseSchema,
|
|
213
213
|
messages: this.getChatMessage(systemPrompt, userPrompt),
|
|
214
214
|
onResult,
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { PostgrestClientOptions, PostgrestQueryBuilder } from '@supabase/postgrest-js';
|
|
2
1
|
import { SupabaseClient } from '../CommunicationHandler';
|
|
3
2
|
import { RimoriCommunicationHandler, RimoriInfo } from '../CommunicationHandler';
|
|
4
|
-
import { GenericSchema, GenericTable } from '@supabase/postgrest-js/dist/cjs/types/common/common';
|
|
5
3
|
/**
|
|
6
4
|
* Database module for plugin database operations.
|
|
7
5
|
* Provides access to plugin tables with automatic prefixing and schema management.
|
|
@@ -19,7 +17,7 @@ export declare class DbModule {
|
|
|
19
17
|
* @param relation The table name (without prefix for plugin tables, with 'global_' for global tables).
|
|
20
18
|
* @returns A Postgrest query builder for the table.
|
|
21
19
|
*/
|
|
22
|
-
from
|
|
20
|
+
from(relation: string): import("@supabase/postgrest-js").PostgrestQueryBuilder<{} | import("@supabase/postgrest-js").PostgrestClientOptions, any, any, string, unknown>;
|
|
23
21
|
/**
|
|
24
22
|
* Get the table name for a given plugin table.
|
|
25
23
|
* Internally all tables are prefixed with the plugin id. This function is used to get the correct table name for a given public table.
|
|
@@ -23,14 +23,9 @@ export class DbModule {
|
|
|
23
23
|
*/
|
|
24
24
|
from(relation) {
|
|
25
25
|
const tableName = this.getTableName(relation);
|
|
26
|
-
// Use the schema determined by rimori-main based on release channel
|
|
27
|
-
// Global tables (starting with 'global_') remain in public schema
|
|
28
|
-
// Plugin tables use the schema provided by rimori-main (plugins or plugins_alpha)
|
|
29
26
|
if (relation.startsWith('global_')) {
|
|
30
|
-
// Global tables stay in public schema
|
|
31
27
|
return this.supabase.schema('public').from(tableName);
|
|
32
28
|
}
|
|
33
|
-
// Plugin tables go to the schema provided by rimori-main
|
|
34
29
|
return this.supabase.schema(this.rimoriInfo.dbSchema).from(tableName);
|
|
35
30
|
}
|
|
36
31
|
/**
|
|
@@ -32,7 +32,7 @@ export class EventModule {
|
|
|
32
32
|
const topicParts = preliminaryTopic.split('.');
|
|
33
33
|
if (topicParts.length === 3) {
|
|
34
34
|
if (!topicParts[0].startsWith('pl') && topicParts[0] !== 'global') {
|
|
35
|
-
throw new Error(
|
|
35
|
+
throw new Error(`Invalid event topic '${preliminaryTopic}'. The topic must start with the plugin id, 'self' or 'global'.`);
|
|
36
36
|
}
|
|
37
37
|
return preliminaryTopic;
|
|
38
38
|
}
|
|
@@ -63,8 +63,13 @@ export class EventModule {
|
|
|
63
63
|
* @returns The response from the event.
|
|
64
64
|
*/
|
|
65
65
|
request(topic, data) {
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
var _a;
|
|
68
|
+
const globalTopic = this.getGlobalEventTopic(topic);
|
|
69
|
+
yield this.aiModule.session.ensure();
|
|
70
|
+
const sessionToken = (_a = this.aiModule.session.get()) !== null && _a !== void 0 ? _a : undefined;
|
|
71
|
+
return EventBus.request(this.pluginId, globalTopic, data, sessionToken);
|
|
72
|
+
});
|
|
68
73
|
}
|
|
69
74
|
/**
|
|
70
75
|
* Subscribe to an event.
|
|
@@ -74,7 +79,12 @@ export class EventModule {
|
|
|
74
79
|
*/
|
|
75
80
|
on(topic, callback) {
|
|
76
81
|
const topics = Array.isArray(topic) ? topic : [topic];
|
|
77
|
-
return EventBus.on(topics.map((t) => this.getGlobalEventTopic(t)),
|
|
82
|
+
return EventBus.on(topics.map((t) => this.getGlobalEventTopic(t)), (event) => {
|
|
83
|
+
if (event.ai_session_token && !this.aiModule.session.get()) {
|
|
84
|
+
this.aiModule.session.set(event.ai_session_token);
|
|
85
|
+
}
|
|
86
|
+
callback(event);
|
|
87
|
+
});
|
|
78
88
|
}
|
|
79
89
|
/**
|
|
80
90
|
* Subscribe to an event once.
|
|
@@ -91,7 +101,30 @@ export class EventModule {
|
|
|
91
101
|
*/
|
|
92
102
|
respond(topic, data) {
|
|
93
103
|
const topics = Array.isArray(topic) ? topic : [topic];
|
|
94
|
-
|
|
104
|
+
let wrappedData = data;
|
|
105
|
+
if (typeof data === 'function') {
|
|
106
|
+
wrappedData = (event) => __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
const previousToken = this.aiModule.session.get();
|
|
108
|
+
if (event.ai_session_token) {
|
|
109
|
+
this.aiModule.session.set(event.ai_session_token);
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
console.log('responding to event', event);
|
|
113
|
+
return yield data(event);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
if (event.ai_session_token) {
|
|
117
|
+
if (previousToken) {
|
|
118
|
+
this.aiModule.session.set(previousToken);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this.aiModule.session.clear();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
EventBus.respond(this.pluginId, topics.map((t) => this.getGlobalEventTopic(t)), wrappedData);
|
|
95
128
|
}
|
|
96
129
|
/**
|
|
97
130
|
* Emit an accomplishment.
|
|
@@ -100,7 +133,7 @@ export class EventModule {
|
|
|
100
133
|
emitAccomplishment(payload) {
|
|
101
134
|
return __awaiter(this, void 0, void 0, function* () {
|
|
102
135
|
if (payload.type === 'macro') {
|
|
103
|
-
const sessionId = this.aiModule.
|
|
136
|
+
const sessionId = this.aiModule.session.get();
|
|
104
137
|
if (sessionId) {
|
|
105
138
|
try {
|
|
106
139
|
yield fetch(`${this.backendUrl}/ai/session/${sessionId}/complete`, {
|
|
@@ -111,7 +144,7 @@ export class EventModule {
|
|
|
111
144
|
catch (_a) {
|
|
112
145
|
// non-fatal — session will expire naturally
|
|
113
146
|
}
|
|
114
|
-
this.aiModule.
|
|
147
|
+
this.aiModule.session.clear();
|
|
115
148
|
}
|
|
116
149
|
}
|
|
117
150
|
this.accomplishmentController.emitAccomplishment(payload);
|
|
@@ -44,7 +44,7 @@ export declare class PluginModule {
|
|
|
44
44
|
* @param defaultSettings The default settings to use if no settings are found.
|
|
45
45
|
* @returns The settings for the plugin.
|
|
46
46
|
*/
|
|
47
|
-
getSettings<T>(defaultSettings: ExplicitUndefined<T>): Promise<ExplicitUndefined<T>>;
|
|
47
|
+
getSettings<T extends BasePluginSettings>(defaultSettings: ExplicitUndefined<T>): Promise<ExplicitUndefined<T>>;
|
|
48
48
|
/**
|
|
49
49
|
* Get the current user info.
|
|
50
50
|
* Note: For reactive updates in React components, use the userInfo from useRimori() hook instead.
|
|
@@ -105,11 +105,23 @@ export interface Language {
|
|
|
105
105
|
uppercase: string;
|
|
106
106
|
}
|
|
107
107
|
export type UserRole = 'user' | 'plugin_moderator' | 'lang_moderator' | 'admin';
|
|
108
|
+
export type SubscriptionTier = 'anonymous' | 'free' | 'standard' | 'premium' | 'early_access';
|
|
109
|
+
/** Ordered tiers from lowest to highest access level */
|
|
110
|
+
export declare const TIER_ORDER: SubscriptionTier[];
|
|
111
|
+
/** Ordered roles from lowest to highest access level */
|
|
112
|
+
export declare const ROLE_ORDER: UserRole[];
|
|
108
113
|
export declare const LEARNING_REASONS: readonly ["work", "partner", "friends", "study", "living", "culture", "growth", "citizenship", "other"];
|
|
109
114
|
export type LearningReason = (typeof LEARNING_REASONS)[number];
|
|
110
115
|
export type ExplicitUndefined<T> = {
|
|
111
116
|
[K in Exclude<keyof T, never>]-?: {} extends Pick<T, K> ? T[K] | undefined : T[K];
|
|
112
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* All plugin settings must include is_inited so rimori-main can detect
|
|
120
|
+
* plugins whose one-time worker init did not complete and re-trigger them.
|
|
121
|
+
*/
|
|
122
|
+
export type BasePluginSettings = {
|
|
123
|
+
is_inited: boolean;
|
|
124
|
+
};
|
|
113
125
|
export interface UserInfo {
|
|
114
126
|
/**
|
|
115
127
|
* The user's unique ID
|
|
@@ -154,4 +166,9 @@ export interface UserInfo {
|
|
|
154
166
|
* The user's role: 'user', 'plugin_moderator', 'lang_moderator', or 'admin'
|
|
155
167
|
*/
|
|
156
168
|
user_role: UserRole;
|
|
169
|
+
/**
|
|
170
|
+
* The user's subscription tier. Higher tiers include all privileges of lower tiers.
|
|
171
|
+
* Order (ascending): anonymous < free < standard < premium < early_access
|
|
172
|
+
*/
|
|
173
|
+
subscription_tier: SubscriptionTier;
|
|
157
174
|
}
|
|
@@ -178,6 +178,10 @@ export class PluginModule {
|
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
+
/** Ordered tiers from lowest to highest access level */
|
|
182
|
+
export const TIER_ORDER = ['anonymous', 'free', 'standard', 'premium', 'early_access'];
|
|
183
|
+
/** Ordered roles from lowest to highest access level */
|
|
184
|
+
export const ROLE_ORDER = ['user', 'plugin_moderator', 'lang_moderator', 'admin'];
|
|
181
185
|
export const LEARNING_REASONS = [
|
|
182
186
|
'work',
|
|
183
187
|
'partner',
|
|
@@ -2,6 +2,7 @@ import { RimoriClient } from '../plugin/RimoriClient';
|
|
|
2
2
|
/**
|
|
3
3
|
* Sets up the web worker for the plugin to be able receive and send messages to Rimori.
|
|
4
4
|
* @param pluginId - The id of the plugin to setup the worker for.
|
|
5
|
-
* @param init - The function containing the initialization logic.
|
|
5
|
+
* @param init - The function containing the initialization logic. The init must be completed within 5s. For long running tasks use the init event (e.g. onboarding.triggerInitPlugin) or run the work async.
|
|
6
|
+
* @returns A promise that resolves when the worker is setup.
|
|
6
7
|
*/
|
|
7
8
|
export declare function setupWorker(pluginId: string, init: (client: RimoriClient) => void | Promise<void>): Promise<void>;
|
|
@@ -12,7 +12,8 @@ import { EventBusHandler } from '../fromRimori/EventBus';
|
|
|
12
12
|
/**
|
|
13
13
|
* Sets up the web worker for the plugin to be able receive and send messages to Rimori.
|
|
14
14
|
* @param pluginId - The id of the plugin to setup the worker for.
|
|
15
|
-
* @param init - The function containing the initialization logic.
|
|
15
|
+
* @param init - The function containing the initialization logic. The init must be completed within 5s. For long running tasks use the init event (e.g. onboarding.triggerInitPlugin) or run the work async.
|
|
16
|
+
* @returns A promise that resolves when the worker is setup.
|
|
16
17
|
*/
|
|
17
18
|
export function setupWorker(pluginId, init) {
|
|
18
19
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -27,10 +28,15 @@ export function setupWorker(pluginId, init) {
|
|
|
27
28
|
};
|
|
28
29
|
// Assign the mock to globalThis.
|
|
29
30
|
Object.assign(globalThis, { window: mockWindow });
|
|
30
|
-
EventBusHandler.getInstance('Worker EventBus');
|
|
31
|
+
EventBusHandler.getInstance('Worker ' + pluginId + ' EventBus');
|
|
31
32
|
const rimoriClient = yield RimoriClient.getInstance(pluginId);
|
|
32
33
|
console.debug('[Worker] RimoriClient initialized.');
|
|
33
|
-
|
|
34
|
+
const timoutError = new Error('[Worker ' +
|
|
35
|
+
pluginId +
|
|
36
|
+
'] Worker setup must complete within 5s. Use init event (e.g. onboarding.triggerInitPlugin) or run work async.');
|
|
37
|
+
const initPromise = Promise.resolve(init(rimoriClient));
|
|
38
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(timoutError), 5000));
|
|
39
|
+
yield Promise.race([initPromise, timeout]);
|
|
34
40
|
console.debug('[Worker] Worker initialized.');
|
|
35
41
|
rimoriClient.event.emit('self.rimori.triggerInitFinished');
|
|
36
42
|
});
|