@rimori/client 2.5.29 → 2.5.30-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.
@@ -1,4 +1,4 @@
1
- import { EventBusMessage } from '../fromRimori/EventBus';
1
+ import { EventBusHandler, EventBusMessage } from '../fromRimori/EventBus';
2
2
  export type AccomplishmentMessage = EventBusMessage<MicroAccomplishmentPayload>;
3
3
  export declare const skillCategories: readonly ["reading", "listening", "speaking", "writing", "learning", "community"];
4
4
  interface BaseAccomplishmentPayload {
@@ -23,7 +23,8 @@ export interface MacroAccomplishmentPayload extends BaseAccomplishmentPayload {
23
23
  export type AccomplishmentPayload = MicroAccomplishmentPayload | MacroAccomplishmentPayload;
24
24
  export declare class AccomplishmentController {
25
25
  private pluginId;
26
- constructor(pluginId: string);
26
+ private eventBus;
27
+ constructor(pluginId: string, eventBus?: EventBusHandler);
27
28
  emitAccomplishment(payload: Omit<AccomplishmentPayload, 'type'>): void;
28
29
  private validateAccomplishment;
29
30
  private sanitizeAccomplishment;
@@ -1,8 +1,9 @@
1
1
  import { EventBus } from '../fromRimori/EventBus';
2
2
  export const skillCategories = ['reading', 'listening', 'speaking', 'writing', 'learning', 'community'];
3
3
  export class AccomplishmentController {
4
- constructor(pluginId) {
4
+ constructor(pluginId, eventBus) {
5
5
  this.pluginId = pluginId;
6
+ this.eventBus = eventBus !== null && eventBus !== void 0 ? eventBus : EventBus;
6
7
  }
7
8
  emitAccomplishment(payload) {
8
9
  const accomplishmentPayload = Object.assign(Object.assign({}, payload), { type: 'durationMinutes' in payload ? 'macro' : 'micro' });
@@ -11,7 +12,7 @@ export class AccomplishmentController {
11
12
  }
12
13
  const sanitizedPayload = this.sanitizeAccomplishment(accomplishmentPayload);
13
14
  const topic = 'global.accomplishment.trigger' + (accomplishmentPayload.type === 'macro' ? 'Macro' : 'Micro');
14
- EventBus.emit(this.pluginId, topic, sanitizedPayload);
15
+ this.eventBus.emit(this.pluginId, topic, sanitizedPayload);
15
16
  }
16
17
  validateAccomplishment(payload) {
17
18
  if (!skillCategories.includes(payload.skillCategory)) {
@@ -85,7 +86,7 @@ export class AccomplishmentController {
85
86
  else if (topicLength !== 3) {
86
87
  throw new Error('Invalid accomplishment topic pattern. The pattern must be plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword');
87
88
  }
88
- EventBus.on(['global.accomplishment.triggerMicro', 'global.accomplishment.triggerMacro'], (event) => {
89
+ this.eventBus.on(['global.accomplishment.triggerMicro', 'global.accomplishment.triggerMacro'], (event) => {
89
90
  const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
90
91
  if (plugin !== '*' && event.sender !== plugin)
91
92
  return;
@@ -79,12 +79,13 @@ export class Translator {
79
79
  });
80
80
  }
81
81
  getTranslationUrl(language) {
82
+ const baseUrl = this.translationUrl || window.location.origin;
82
83
  // For localhost development, use local- prefix for non-English languages
83
- if (window.location.hostname === 'localhost') {
84
+ if (window.location.hostname === 'localhost' || new URL(baseUrl).hostname === 'localhost') {
84
85
  const filename = language !== 'en' ? `local-${language}` : language;
85
- return `${window.location.origin}/locales/${filename}.json`;
86
+ return `${baseUrl}/locales/${filename}.json`;
86
87
  }
87
- return `${this.translationUrl}/locales/${language}.json`;
88
+ return `${baseUrl}/locales/${language}.json`;
88
89
  }
89
90
  usePlugin(plugin) {
90
91
  if (!this.i18n) {
@@ -134,7 +135,7 @@ export class Translator {
134
135
  if (!this.i18n) {
135
136
  throw new Error('Translator is not initialized');
136
137
  }
137
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
138
139
  return this.i18n.t(key, options);
139
140
  }
140
141
  /**
@@ -29,6 +29,11 @@ export declare class EventBusHandler {
29
29
  private generatedIds;
30
30
  private cleanupInterval;
31
31
  private constructor();
32
+ /**
33
+ * Creates a new non-singleton EventBusHandler instance.
34
+ * Used in federation mode where each plugin needs its own isolated EventBus.
35
+ */
36
+ static create(name?: string): EventBusHandler;
32
37
  static getInstance(name?: string): EventBusHandler;
33
38
  /**
34
39
  * Starts the interval to cleanup the generated ids.
@@ -109,6 +114,10 @@ export declare class EventBusHandler {
109
114
  */
110
115
  private validateTopic;
111
116
  private logIfDebug;
117
+ /**
118
+ * Destroys this EventBus instance, cleaning up all listeners and intervals.
119
+ */
120
+ destroy(): void;
112
121
  private logAndThrowError;
113
122
  }
114
123
  export declare const EventBus: EventBusHandler;
@@ -15,9 +15,18 @@ export class EventBusHandler {
15
15
  this.evName = '';
16
16
  this.generatedIds = new Map(); // Map<id, timestamp>
17
17
  this.cleanupInterval = null;
18
- //private constructor
19
18
  this.startIdCleanup();
20
19
  }
20
+ /**
21
+ * Creates a new non-singleton EventBusHandler instance.
22
+ * Used in federation mode where each plugin needs its own isolated EventBus.
23
+ */
24
+ static create(name) {
25
+ const instance = new EventBusHandler();
26
+ if (name)
27
+ instance.evName = name;
28
+ return instance;
29
+ }
21
30
  static getInstance(name) {
22
31
  if (!EventBusHandler.instance) {
23
32
  EventBusHandler.instance = new EventBusHandler();
@@ -307,6 +316,18 @@ export class EventBusHandler {
307
316
  console.debug(`[${this.evName}] ` + args[0], ...args.slice(1));
308
317
  }
309
318
  }
319
+ /**
320
+ * Destroys this EventBus instance, cleaning up all listeners and intervals.
321
+ */
322
+ destroy() {
323
+ this.listeners.clear();
324
+ this.responseResolvers.clear();
325
+ if (this.cleanupInterval) {
326
+ clearInterval(this.cleanupInterval);
327
+ this.cleanupInterval = null;
328
+ }
329
+ this.generatedIds.clear();
330
+ }
310
331
  logAndThrowError(throwError, ...args) {
311
332
  const message = `[${this.evName}] ` + args[0];
312
333
  console.error(message, ...args.slice(1));
@@ -86,6 +86,8 @@ export interface RimoriPluginConfig<T extends object = object> {
86
86
  sidebar: (SidebarPage & T)[];
87
87
  /** Optional path to the plugin's settings/configuration page */
88
88
  settings?: string;
89
+ /** When true, rimori-main loads this plugin via Module Federation instead of an iframe. */
90
+ federated?: boolean;
89
91
  /** Optional array of event topics the plugin pages can listen to for cross-plugin communication */
90
92
  topics?: string[];
91
93
  };
@@ -67,8 +67,9 @@ export declare class RimoriCommunicationHandler {
67
67
  /**
68
68
  * Handles updates to RimoriInfo from rimori-main.
69
69
  * Updates the cached info and Supabase client, then notifies all registered callbacks.
70
+ * Public so that federated mode can call it when the update event arrives via the plugin's isolated EventBus.
70
71
  */
71
- private handleRimoriInfoUpdate;
72
+ handleRimoriInfoUpdate(newInfo: RimoriInfo): void;
72
73
  /**
73
74
  * Registers a callback to be called when RimoriInfo is updated.
74
75
  * @param callback - Function to call with the new RimoriInfo
@@ -77,7 +77,7 @@ export class RimoriCommunicationHandler {
77
77
  // Listen for updates from rimori-main (data changes, token refresh, etc.)
78
78
  // Topic format: {pluginId}.supabase.triggerUpdate
79
79
  EventBus.on(`${this.pluginId}.supabase.triggerUpdate`, (ev) => {
80
- // console.log('[RimoriCommunicationHandler] Received update from rimori-main', ev.data);
80
+ // console.log('[RimoriCommunicationHandler] Received triggerUpdate via MessageChannel for', this.pluginId);
81
81
  this.handleRimoriInfoUpdate(ev.data);
82
82
  });
83
83
  // Mark MessageChannel as ready and process pending requests
@@ -205,12 +205,14 @@ export class RimoriCommunicationHandler {
205
205
  /**
206
206
  * Handles updates to RimoriInfo from rimori-main.
207
207
  * Updates the cached info and Supabase client, then notifies all registered callbacks.
208
+ * Public so that federated mode can call it when the update event arrives via the plugin's isolated EventBus.
208
209
  */
209
210
  handleRimoriInfoUpdate(newInfo) {
210
211
  if (JSON.stringify(this.rimoriInfo) === JSON.stringify(newInfo)) {
211
- // console.log('[RimoriCommunicationHandler] RimoriInfo update is the same as the cached info, skipping update');
212
+ // console.log('[RimoriCommunicationHandler] RimoriInfo update identical to cached info, skipping', this.pluginId);
212
213
  return;
213
214
  }
215
+ // console.log('[RimoriCommunicationHandler] Applying RimoriInfo update for', this.pluginId, '| ttsEnabled:', newInfo.ttsEnabled);
214
216
  // Update cached rimoriInfo
215
217
  this.rimoriInfo = newInfo;
216
218
  // Update Supabase client with new token
@@ -1,10 +1,12 @@
1
1
  import { SharedContentController } from './module/SharedContentController';
2
+ import { RimoriInfo } from './CommunicationHandler';
2
3
  import { PluginModule } from './module/PluginModule';
3
4
  import { DbModule } from './module/DbModule';
4
5
  import { EventModule } from './module/EventModule';
5
6
  import { AIModule } from './module/AIModule';
6
7
  import { ExerciseModule } from './module/ExerciseModule';
7
8
  import { StorageModule } from './module/StorageModule';
9
+ import { EventBusHandler } from '../fromRimori/EventBus';
8
10
  export declare class RimoriClient {
9
11
  private static instance;
10
12
  sharedContent: SharedContentController;
@@ -16,7 +18,15 @@ export declare class RimoriClient {
16
18
  /** Upload and manage images stored in Supabase via the backend. */
17
19
  storage: StorageModule;
18
20
  private rimoriInfo;
21
+ /** The EventBus instance used by this client. In federation mode this is a per-plugin instance. */
22
+ eventBus: EventBusHandler;
19
23
  private constructor();
24
+ /**
25
+ * Creates a RimoriClient with pre-existing RimoriInfo (federation mode).
26
+ * Uses a fresh per-plugin EventBus instance instead of the global singleton.
27
+ * Creates the Supabase PostgrestClient internally from the info.
28
+ */
29
+ static createWithInfo(info: RimoriInfo): RimoriClient;
20
30
  static getInstance(pluginId?: string): Promise<RimoriClient>;
21
31
  navigation: {
22
32
  toDashboard: () => void;
@@ -16,9 +16,10 @@ import { EventModule } from './module/EventModule';
16
16
  import { AIModule } from './module/AIModule';
17
17
  import { ExerciseModule } from './module/ExerciseModule';
18
18
  import { StorageModule } from './module/StorageModule';
19
- import { EventBus } from '../fromRimori/EventBus';
19
+ import { PostgrestClient } from '@supabase/postgrest-js';
20
+ import { EventBus, EventBusHandler } from '../fromRimori/EventBus';
20
21
  export class RimoriClient {
21
- constructor(controller, supabase, info) {
22
+ constructor(controller, supabase, info, eventBus) {
22
23
  this.navigation = {
23
24
  toDashboard: () => {
24
25
  this.event.emit('global.navigation.triggerToDashboard');
@@ -30,12 +31,13 @@ export class RimoriClient {
30
31
  }),
31
32
  };
32
33
  this.rimoriInfo = info;
34
+ this.eventBus = eventBus !== null && eventBus !== void 0 ? eventBus : EventBus;
33
35
  this.sharedContent = new SharedContentController(supabase, this);
34
36
  this.ai = new AIModule(info.backendUrl, () => this.rimoriInfo.token, info.pluginId);
35
37
  this.ai.setOnRateLimited((exercisesRemaining) => {
36
- EventBus.emit(info.pluginId, 'global.quota.triggerExceeded', { exercises_remaining: exercisesRemaining });
38
+ this.eventBus.emit(info.pluginId, 'global.quota.triggerExceeded', { exercises_remaining: exercisesRemaining });
37
39
  });
38
- this.event = new EventModule(info.pluginId, info.backendUrl, () => this.rimoriInfo.token, this.ai);
40
+ this.event = new EventModule(info.pluginId, info.backendUrl, () => this.rimoriInfo.token, this.ai, this.eventBus);
39
41
  this.db = new DbModule(supabase, controller, info);
40
42
  this.plugin = new PluginModule(supabase, controller, info, this.ai);
41
43
  this.exercise = new ExerciseModule(supabase, controller, info, this.event);
@@ -48,6 +50,32 @@ export class RimoriClient {
48
50
  Logger.getInstance(this);
49
51
  }
50
52
  }
53
+ /**
54
+ * Creates a RimoriClient with pre-existing RimoriInfo (federation mode).
55
+ * Uses a fresh per-plugin EventBus instance instead of the global singleton.
56
+ * Creates the Supabase PostgrestClient internally from the info.
57
+ */
58
+ static createWithInfo(info) {
59
+ const eventBus = EventBusHandler.create('Plugin EventBus ' + info.pluginId);
60
+ const controller = new RimoriCommunicationHandler(info.pluginId, true);
61
+ const supabase = new PostgrestClient(`${info.url}/rest/v1`, {
62
+ schema: info.dbSchema,
63
+ headers: {
64
+ apikey: info.key,
65
+ Authorization: `Bearer ${info.token}`,
66
+ },
67
+ });
68
+ const client = new RimoriClient(controller, supabase, info, eventBus);
69
+ // In federated mode, CommunicationHandler skips MessageChannel setup so it never
70
+ // subscribes to triggerUpdate events. PluginEventBridge forwards host-bus events to
71
+ // this plugin's isolated eventBus, so we listen here and hand off to the controller.
72
+ eventBus.on(`${info.pluginId}.supabase.triggerUpdate`, (ev) => {
73
+ var _a;
74
+ console.log('[RimoriClient] Federated triggerUpdate received for', info.pluginId, '| ttsEnabled:', (_a = ev.data) === null || _a === void 0 ? void 0 : _a.ttsEnabled);
75
+ controller.handleRimoriInfoUpdate(ev.data);
76
+ });
77
+ return client;
78
+ }
51
79
  static getInstance(pluginId) {
52
80
  return __awaiter(this, void 0, void 0, function* () {
53
81
  if (!RimoriClient.instance) {
@@ -73,7 +73,6 @@ export declare class AIModule {
73
73
  * @param params.messages The messages to generate text from.
74
74
  * @param params.tools Optional tools to use for generation.
75
75
  * @param params.cache Whether to cache the result (default: false).
76
- * @param params.model The model to use for generation.
77
76
  * @param params.prompt Server-side prompt name (e.g. 'writing.analysis').
78
77
  * @param params.variables Variables for the server-side prompt template.
79
78
  * @returns The generated text.
@@ -82,7 +81,6 @@ export declare class AIModule {
82
81
  messages: Message[];
83
82
  tools?: Tool[];
84
83
  cache?: boolean;
85
- model?: string;
86
84
  prompt?: string;
87
85
  variables?: Record<string, any>;
88
86
  }): Promise<string>;
@@ -92,7 +90,6 @@ export declare class AIModule {
92
90
  * @param params.onMessage Callback for each message chunk.
93
91
  * @param params.tools Optional tools to use for generation.
94
92
  * @param params.cache Whether to cache the result (default: false).
95
- * @param params.model The model to use for generation.
96
93
  * @param params.prompt Server-side prompt name (e.g. 'writing.analysis').
97
94
  * @param params.variables Variables for the server-side prompt template.
98
95
  */
@@ -101,7 +98,6 @@ export declare class AIModule {
101
98
  onMessage: OnLLMResponse;
102
99
  tools?: Tool[];
103
100
  cache?: boolean;
104
- model?: string;
105
101
  prompt?: string;
106
102
  variables?: Record<string, any>;
107
103
  }): Promise<string>;
@@ -126,7 +122,6 @@ export declare class AIModule {
126
122
  * Generate a structured object from a request using AI.
127
123
  * @param request.cache Whether to cache the result (default: false).
128
124
  * @param request.tools The tools to use for generation.
129
- * @param request.model The model to use for generation.
130
125
  * @param request.prompt Server-side prompt name (e.g. 'writing.analysis').
131
126
  * @param request.variables Variables for the server-side prompt template.
132
127
  * @returns The generated object.
@@ -134,7 +129,6 @@ export declare class AIModule {
134
129
  getObject<T = any>(params: {
135
130
  cache?: boolean;
136
131
  tools?: Tool[];
137
- model?: string;
138
132
  prompt?: string;
139
133
  variables?: Record<string, any>;
140
134
  }): Promise<T>;
@@ -143,7 +137,6 @@ export declare class AIModule {
143
137
  * @param request.onResult Callback for each result chunk.
144
138
  * @param request.cache Whether to cache the result (default: false).
145
139
  * @param request.tools The tools to use for generation.
146
- * @param request.model The model to use for generation.
147
140
  * @param request.prompt Server-side prompt name (e.g. 'writing.analysis').
148
141
  * @param request.variables Variables for the server-side prompt template.
149
142
  */
@@ -151,7 +144,6 @@ export declare class AIModule {
151
144
  onResult: OnStreamedObjectResult<T>;
152
145
  cache?: boolean;
153
146
  tools?: Tool[];
154
- model?: string;
155
147
  prompt?: string;
156
148
  variables?: Record<string, any>;
157
149
  }): Promise<T>;
@@ -78,18 +78,16 @@ export class AIModule {
78
78
  * @param params.messages The messages to generate text from.
79
79
  * @param params.tools Optional tools to use for generation.
80
80
  * @param params.cache Whether to cache the result (default: false).
81
- * @param params.model The model to use for generation.
82
81
  * @param params.prompt Server-side prompt name (e.g. 'writing.analysis').
83
82
  * @param params.variables Variables for the server-side prompt template.
84
83
  * @returns The generated text.
85
84
  */
86
85
  getText(params) {
87
86
  return __awaiter(this, void 0, void 0, function* () {
88
- const { messages, tools, cache = false, model, prompt, variables } = params;
87
+ const { messages, tools, cache = false, prompt, variables } = params;
89
88
  const { result } = yield this.streamObject({
90
89
  cache,
91
90
  tools,
92
- model,
93
91
  messages,
94
92
  prompt,
95
93
  variables,
@@ -103,18 +101,16 @@ export class AIModule {
103
101
  * @param params.onMessage Callback for each message chunk.
104
102
  * @param params.tools Optional tools to use for generation.
105
103
  * @param params.cache Whether to cache the result (default: false).
106
- * @param params.model The model to use for generation.
107
104
  * @param params.prompt Server-side prompt name (e.g. 'writing.analysis').
108
105
  * @param params.variables Variables for the server-side prompt template.
109
106
  */
110
107
  getStreamedText(params) {
111
108
  return __awaiter(this, void 0, void 0, function* () {
112
- const { messages, onMessage, tools, cache = false, model, prompt, variables, } = params;
109
+ const { messages, onMessage, tools, cache = false, prompt, variables, } = params;
113
110
  const messageId = Math.random().toString(36).substring(3);
114
111
  const { result } = yield this.streamObject({
115
112
  cache,
116
113
  tools,
117
- model,
118
114
  messages,
119
115
  prompt,
120
116
  variables,
@@ -188,19 +184,17 @@ export class AIModule {
188
184
  * Generate a structured object from a request using AI.
189
185
  * @param request.cache Whether to cache the result (default: false).
190
186
  * @param request.tools The tools to use for generation.
191
- * @param request.model The model to use for generation.
192
187
  * @param request.prompt Server-side prompt name (e.g. 'writing.analysis').
193
188
  * @param request.variables Variables for the server-side prompt template.
194
189
  * @returns The generated object.
195
190
  */
196
191
  getObject(params) {
197
192
  return __awaiter(this, void 0, void 0, function* () {
198
- const { cache = false, tools = [], model = undefined, prompt, variables } = params;
193
+ const { cache = false, tools = [], prompt, variables } = params;
199
194
  return yield this.streamObject({
200
195
  messages: [],
201
196
  cache,
202
197
  tools,
203
- model,
204
198
  prompt,
205
199
  variables,
206
200
  });
@@ -211,19 +205,17 @@ export class AIModule {
211
205
  * @param request.onResult Callback for each result chunk.
212
206
  * @param request.cache Whether to cache the result (default: false).
213
207
  * @param request.tools The tools to use for generation.
214
- * @param request.model The model to use for generation.
215
208
  * @param request.prompt Server-side prompt name (e.g. 'writing.analysis').
216
209
  * @param request.variables Variables for the server-side prompt template.
217
210
  */
218
211
  getStreamedObject(params) {
219
212
  return __awaiter(this, void 0, void 0, function* () {
220
- const { onResult, cache = false, tools = [], model = undefined, prompt, variables } = params;
213
+ const { onResult, cache = false, tools = [], prompt, variables } = params;
221
214
  return yield this.streamObject({
222
215
  messages: [],
223
216
  onResult,
224
217
  cache,
225
218
  tools,
226
- model,
227
219
  prompt,
228
220
  variables,
229
221
  });
@@ -232,14 +224,13 @@ export class AIModule {
232
224
  streamObject(params) {
233
225
  return __awaiter(this, void 0, void 0, function* () {
234
226
  var _a, _b, _c, _d, _e;
235
- const { messages, onResult = () => null, cache = false, tools = [], model = undefined, prompt, variables, } = params;
227
+ const { messages, onResult = () => null, cache = false, tools = [], prompt, variables, } = params;
236
228
  const chatMessages = messages.map((message, index) => (Object.assign(Object.assign({}, message), { id: `${index + 1}` })));
237
229
  const payload = {
238
230
  cache,
239
231
  tools,
240
232
  stream: true,
241
233
  messages: chatMessages,
242
- model,
243
234
  session_token_id: (_a = this.sessionTokenId) !== null && _a !== void 0 ? _a : undefined,
244
235
  };
245
236
  if (prompt) {
@@ -1,6 +1,6 @@
1
1
  import { MainPanelAction, SidebarAction } from '../../fromRimori/PluginTypes';
2
2
  import { AccomplishmentPayload } from '../../controller/AccomplishmentController';
3
- import { EventBusMessage, EventHandler, EventPayload, EventListener } from '../../fromRimori/EventBus';
3
+ import { EventBusHandler, EventBusMessage, EventHandler, EventPayload, EventListener } from '../../fromRimori/EventBus';
4
4
  import { AIModule } from './AIModule';
5
5
  /**
6
6
  * Event module for plugin event bus operations.
@@ -12,7 +12,8 @@ export declare class EventModule {
12
12
  private aiModule;
13
13
  private backendUrl;
14
14
  private getToken;
15
- constructor(pluginId: string, backendUrl: string, getToken: () => string, aiModule: AIModule);
15
+ private eventBus;
16
+ constructor(pluginId: string, backendUrl: string, getToken: () => string, aiModule: AIModule, eventBus?: EventBusHandler);
16
17
  getGlobalEventTopic(preliminaryTopic: string): string;
17
18
  /**
18
19
  * Emit an event to Rimori or a plugin.
@@ -14,12 +14,13 @@ import { EventBus } from '../../fromRimori/EventBus';
14
14
  * Provides methods for emitting, listening to, and responding to events.
15
15
  */
16
16
  export class EventModule {
17
- constructor(pluginId, backendUrl, getToken, aiModule) {
17
+ constructor(pluginId, backendUrl, getToken, aiModule, eventBus) {
18
18
  this.pluginId = pluginId;
19
19
  this.backendUrl = backendUrl;
20
20
  this.getToken = getToken;
21
21
  this.aiModule = aiModule;
22
- this.accomplishmentController = new AccomplishmentController(pluginId);
22
+ this.eventBus = eventBus !== null && eventBus !== void 0 ? eventBus : EventBus;
23
+ this.accomplishmentController = new AccomplishmentController(pluginId, this.eventBus);
23
24
  }
24
25
  getGlobalEventTopic(preliminaryTopic) {
25
26
  var _a;
@@ -54,7 +55,7 @@ export class EventModule {
54
55
  */
55
56
  emit(topic, data, eventId) {
56
57
  const globalTopic = this.getGlobalEventTopic(topic);
57
- EventBus.emit(this.pluginId, globalTopic, data, eventId);
58
+ this.eventBus.emit(this.pluginId, globalTopic, data, eventId);
58
59
  }
59
60
  /**
60
61
  * Request an event.
@@ -68,7 +69,7 @@ export class EventModule {
68
69
  const globalTopic = this.getGlobalEventTopic(topic);
69
70
  yield this.aiModule.session.ensure();
70
71
  const sessionToken = (_a = this.aiModule.session.get()) !== null && _a !== void 0 ? _a : undefined;
71
- return EventBus.request(this.pluginId, globalTopic, data, sessionToken);
72
+ return this.eventBus.request(this.pluginId, globalTopic, data, sessionToken);
72
73
  });
73
74
  }
74
75
  /**
@@ -79,7 +80,7 @@ export class EventModule {
79
80
  */
80
81
  on(topic, callback) {
81
82
  const topics = Array.isArray(topic) ? topic : [topic];
82
- return EventBus.on(topics.map((t) => this.getGlobalEventTopic(t)), (event) => {
83
+ return this.eventBus.on(topics.map((t) => this.getGlobalEventTopic(t)), (event) => {
83
84
  if (event.ai_session_token && !this.aiModule.session.get()) {
84
85
  this.aiModule.session.set(event.ai_session_token);
85
86
  }
@@ -92,7 +93,7 @@ export class EventModule {
92
93
  * @param callback The callback to call when the event is emitted.
93
94
  */
94
95
  once(topic, callback) {
95
- EventBus.once(this.getGlobalEventTopic(topic), callback);
96
+ this.eventBus.once(this.getGlobalEventTopic(topic), callback);
96
97
  }
97
98
  /**
98
99
  * Respond to an event.
@@ -124,7 +125,7 @@ export class EventModule {
124
125
  }
125
126
  });
126
127
  }
127
- EventBus.respond(this.pluginId, topics.map((t) => this.getGlobalEventTopic(t)), wrappedData);
128
+ this.eventBus.respond(this.pluginId, topics.map((t) => this.getGlobalEventTopic(t)), wrappedData);
128
129
  }
129
130
  /**
130
131
  * Emit an accomplishment.
@@ -219,15 +220,21 @@ export class EventModule {
219
220
  */
220
221
  onSidePanelAction(callback, actionsToListen = []) {
221
222
  const listeningActions = Array.isArray(actionsToListen) ? actionsToListen : [actionsToListen];
222
- // this needs to be a emit and on because the main panel action is triggered by the user and not by the plugin
223
- this.emit('action.requestSidebar');
224
- return this.on('action.requestSidebar', ({ data }) => {
225
- // console.log("eventHandler .onSidePanelAction", data);
226
- // console.log('Received action for sidebar ' + data.action);
227
- // console.log('Listening to actions', listeningActions);
223
+ // Register the listener BEFORE emitting the request, so the synchronous response
224
+ // from the bridge/responder is captured (emit → bridge outbound → host respond → bridge inbound is synchronous).
225
+ console.log('[EventModule] onSidePanelAction: setting up listener for', this.pluginId, 'listening to:', listeningActions);
226
+ const listener = this.on('action.requestSidebar', ({ data }) => {
227
+ console.log('[EventModule] onSidePanelAction: received event', { data, listeningActions });
228
228
  if (listeningActions.length === 0 || listeningActions.includes(data.action)) {
229
+ console.log('[EventModule] onSidePanelAction: action matched, calling callback');
229
230
  callback(data);
230
231
  }
232
+ else {
233
+ console.log('[EventModule] onSidePanelAction: action NOT matched. Got:', data.action, 'expected:', listeningActions);
234
+ }
231
235
  });
236
+ console.log('[EventModule] onSidePanelAction: emitting action.requestSidebar for', this.pluginId);
237
+ this.emit('action.requestSidebar');
238
+ return listener;
232
239
  }
233
240
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimori/client",
3
- "version": "2.5.29",
3
+ "version": "2.5.30-next.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  "scripts": {
33
33
  "build": "tsc",
34
34
  "dev": "tsc -w --preserveWatchOutput",
35
- "lint": "eslint . --fix",
35
+ "lint": "npx eslint . --fix",
36
36
  "format": "prettier --write ."
37
37
  },
38
38
  "dependencies": {
@@ -41,10 +41,8 @@
41
41
  "i18next": "^25.10.10"
42
42
  },
43
43
  "devDependencies": {
44
- "@eslint/js": "^9.37.0",
45
44
  "@types/node": "^25.0.1",
46
45
  "eslint-config-prettier": "^10.1.8",
47
- "eslint-plugin-prettier": "^5.5.4",
48
46
  "html2canvas": "^1.4.1",
49
47
  "globals": "^16.4.0",
50
48
  "prettier": "^3.6.2",