@rimori/client 2.2.0 → 2.3.0-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.
@@ -0,0 +1,126 @@
1
+ name: Pre-Release Rimori Client
2
+
3
+ on:
4
+ push:
5
+ branches: [dev]
6
+ paths:
7
+ - '**'
8
+ - '!.github/workflows/**'
9
+
10
+ jobs:
11
+ pre-release:
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: write
15
+ id-token: write
16
+
17
+ steps:
18
+ - name: Checkout repository
19
+ uses: actions/checkout@v4
20
+ with:
21
+ token: ${{ secrets.GITHUB_TOKEN }}
22
+ fetch-depth: 0
23
+
24
+ - name: Setup Node.js
25
+ uses: actions/setup-node@v4
26
+ with:
27
+ node-version: '20'
28
+ registry-url: 'https://registry.npmjs.org'
29
+ cache: 'yarn'
30
+ cache-dependency-path: yarn.lock
31
+
32
+ - name: Update npm
33
+ run: npm install -g npm@latest
34
+
35
+ - name: Install dependencies
36
+ run: yarn install --frozen-lockfile
37
+
38
+ - name: Build rimori-client (TypeScript verification)
39
+ run: yarn build
40
+
41
+ - name: Calculate next pre-release version
42
+ id: version
43
+ run: |
44
+ # Read current version from package.json (may be base or pre-release)
45
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
46
+
47
+ # Extract base version (strip any pre-release suffix)
48
+ # Examples: "2.2.0" -> "2.2.0", "2.2.0-next.5" -> "2.2.0"
49
+ if [[ "$CURRENT_VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+) ]]; then
50
+ BASE_VERSION="${BASH_REMATCH[1]}"
51
+ else
52
+ BASE_VERSION="$CURRENT_VERSION"
53
+ fi
54
+
55
+ # Try to get latest next version from npm
56
+ LATEST_NEXT=$(npm view @rimori/client@next version 2>/dev/null || echo "none")
57
+
58
+ if [ "$LATEST_NEXT" != "none" ]; then
59
+ # Extract base version and pre-release number from latest next version
60
+ # Example: "2.2.0-next.5" -> extract "2.2.0" and "5"
61
+ if [[ "$LATEST_NEXT" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-next\.([0-9]+)$ ]]; then
62
+ LATEST_BASE="${BASH_REMATCH[1]}"
63
+ PRERELEASE_NUM="${BASH_REMATCH[2]}"
64
+
65
+ # If base version changed, reset to 1, otherwise increment
66
+ if [ "$LATEST_BASE" != "$BASE_VERSION" ]; then
67
+ NEW_NUM=1
68
+ else
69
+ NEW_NUM=$((PRERELEASE_NUM + 1))
70
+ fi
71
+ else
72
+ # Fallback: if format doesn't match, start at 1
73
+ NEW_NUM=1
74
+ fi
75
+ else
76
+ # First pre-release
77
+ NEW_NUM=1
78
+ fi
79
+
80
+ NEW_VERSION="${BASE_VERSION}-next.${NEW_NUM}"
81
+ echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
82
+ echo "Base version: $BASE_VERSION"
83
+ echo "Calculated next version: $NEW_VERSION"
84
+
85
+ - name: Update package.json version
86
+ run: |
87
+ # Use node to update version directly (yarn version creates git tags)
88
+ node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json')); pkg.version = '${{ steps.version.outputs.new_version }}'; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');"
89
+
90
+ - name: Publish to npm
91
+ run: npm publish --tag next --access public
92
+ # Uses OIDC token automatically (no NODE_AUTH_TOKEN needed)
93
+ # Requires npm 11.5.1+ and id-token: write permission (already set)
94
+
95
+ - name: Output published version
96
+ run: |
97
+ echo "✅ Published @rimori/client@${{ steps.version.outputs.new_version }} to npm with @next tag"
98
+
99
+ - name: Create git tag
100
+ run: |
101
+ git config --local user.email "action@github.com"
102
+ git config --local user.name "GitHub Action"
103
+ git tag "v${{ steps.version.outputs.new_version }}" -m "Pre-release v${{ steps.version.outputs.new_version }}"
104
+ git push origin "v${{ steps.version.outputs.new_version }}"
105
+ echo "🏷️ Created and pushed tag v${{ steps.version.outputs.new_version }}"
106
+
107
+ - name: Notify Slack
108
+ if: always()
109
+ uses: slackapi/slack-github-action@v1.24.0
110
+ with:
111
+ channel-id: ${{ secrets.SLACK_CHANNEL_ID }}
112
+ payload: |
113
+ {
114
+ "text": "Pre-Release Pipeline Status",
115
+ "blocks": [
116
+ {
117
+ "type": "section",
118
+ "text": {
119
+ "type": "mrkdwn",
120
+ "text": "📦 *@rimori/client Pre-Release*\n\n*Branch:* ${{ github.ref_name }}\n*Version:* ${{ steps.version.outputs.new_version }}\n*Author:* ${{ github.actor }}\n*Pipeline:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>\n\n${{ job.status == 'success' && '✅ Successfully published to npm with @next tag!' || '❌ Pipeline failed. Check the logs for details.' }}"
121
+ }
122
+ }
123
+ ]
124
+ }
125
+ env:
126
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  The `@rimori/client` package is the framework-agnostic runtime and CLI that powers Rimori plugins. Use it inside plugin iframes, workers, and build scripts to access Rimori platform features such as database access, AI, shared content, and the event bus. All React-specific helpers and UI components are now published separately in `@rimori/react-client`.
4
4
 
5
5
  ## Table of Contents
6
+
6
7
  - [Overview](#overview)
7
8
  - [Installation](#installation)
8
9
  - [Relationship to @rimori/react-client](#relationship-to-rimori-react-client)
@@ -24,6 +25,7 @@ The `@rimori/client` package is the framework-agnostic runtime and CLI that powe
24
25
  ## Overview
25
26
 
26
27
  `@rimori/client` gives you direct, typed access to the Rimori platform:
28
+
27
29
  - Bootstrap authenticated plugin sessions and fetch Rimori context.
28
30
  - Run Supabase queries against your plugin's dedicated schema.
29
31
  - Call AI services for text, structured data, or voice.
@@ -54,18 +56,15 @@ npm install @rimori/react-client
54
56
  Instantiate the client once in your application entry point and reuse it everywhere:
55
57
 
56
58
  ```ts
57
- import { RimoriClient } from "@rimori/client";
59
+ import { RimoriClient } from '@rimori/client';
58
60
 
59
61
  async function bootstrap() {
60
- const client = await RimoriClient.getInstance("your-plugin-id");
62
+ const client = await RimoriClient.getInstance('your-plugin-id');
61
63
 
62
64
  const user = client.plugin.getUserInfo();
63
- const { data } = await client.db
64
- .from("notes")
65
- .select("*")
66
- .eq("user_id", user.profile_id);
65
+ const { data } = await client.db.from('notes').select('*').eq('user_id', user.profile_id);
67
66
 
68
- console.log("Loaded notes", data);
67
+ console.log('Loaded notes', data);
69
68
  }
70
69
 
71
70
  bootstrap().catch(console.error);
@@ -127,10 +126,7 @@ Access metadata and settings through `client.plugin`:
127
126
  `client.db` wraps the Supabase client that is scoped to your plugin tables:
128
127
 
129
128
  ```ts
130
- const { data, error } = await client.db
131
- .from("study_sessions")
132
- .select("*")
133
- .order("completed_at", { ascending: false });
129
+ const { data, error } = await client.db.from('study_sessions').select('*').order('completed_at', { ascending: false });
134
130
  ```
135
131
 
136
132
  Helpers:
@@ -196,7 +192,7 @@ Import additional helpers as needed:
196
192
  All exports are fully typed. You can import the type definitions directly:
197
193
 
198
194
  ```ts
199
- import type { Message, Tool, SharedContent, MacroAccomplishmentPayload } from "@rimori/client";
195
+ import type { Message, Tool, SharedContent, MacroAccomplishmentPayload } from '@rimori/client';
200
196
  ```
201
197
 
202
198
  The generated declaration files cover every controller and helper to keep plugins strictly typed.
@@ -206,7 +202,7 @@ The generated declaration files cover every controller and helper to keep plugin
206
202
  React users should install `@rimori/react-client` and wrap their app:
207
203
 
208
204
  ```tsx
209
- import { PluginProvider, useRimori, useChat } from "@rimori/react-client";
205
+ import { PluginProvider, useRimori, useChat } from '@rimori/react-client';
210
206
 
211
207
  function Dashboard() {
212
208
  const client = useRimori();
File without changes
File without changes
@@ -16,6 +16,7 @@ export interface Language {
16
16
  capitalized: string;
17
17
  uppercase: string;
18
18
  }
19
+ export type UserRole = 'user' | 'plugin_moderator' | 'lang_moderator' | 'admin';
19
20
  export interface UserInfo {
20
21
  skill_level_reading: LanguageLevel;
21
22
  skill_level_writing: LanguageLevel;
@@ -29,8 +30,7 @@ export interface UserInfo {
29
30
  story_genre: string;
30
31
  study_duration: number;
31
32
  /**
32
- * The 2 letter language code of the language the user speaks natively.
33
- * With the function getLanguageName, the language name can be retrieved.
33
+ * The language the user speaks natively.
34
34
  */
35
35
  mother_tongue: Language;
36
36
  /**
@@ -49,6 +49,10 @@ export interface UserInfo {
49
49
  * Optional: nearest big city (>100,000) near user's location
50
50
  */
51
51
  target_city?: string;
52
+ /**
53
+ * The user's role: 'user', 'plugin_moderator', 'lang_moderator', or 'admin'
54
+ */
55
+ user_role: UserRole;
52
56
  }
53
57
  export declare class SettingsController {
54
58
  private pluginId;
@@ -4,9 +4,11 @@ import { ThirdPartyModule, TOptions } from 'i18next';
4
4
  */
5
5
  export declare class Translator {
6
6
  private currentLanguage;
7
- private isInitialized;
7
+ private initializationState;
8
+ private initializationPromise;
8
9
  private i18n;
9
- constructor(initialLanguage: string);
10
+ private translationUrl;
11
+ constructor(initialLanguage: string, translationUrl: string);
10
12
  /**
11
13
  * Initialize translator with user's language
12
14
  * @param userLanguage - Language code from user info
@@ -12,9 +12,11 @@ import { createInstance } from 'i18next';
12
12
  * Translator class for handling internationalization
13
13
  */
14
14
  export class Translator {
15
- constructor(initialLanguage) {
16
- this.isInitialized = false;
15
+ constructor(initialLanguage, translationUrl) {
17
16
  this.currentLanguage = initialLanguage;
17
+ this.initializationState = 'not-inited';
18
+ this.initializationPromise = null;
19
+ this.translationUrl = translationUrl;
18
20
  }
19
21
  /**
20
22
  * Initialize translator with user's language
@@ -22,21 +24,41 @@ export class Translator {
22
24
  */
23
25
  initialize() {
24
26
  return __awaiter(this, void 0, void 0, function* () {
25
- if (this.isInitialized)
27
+ // If already finished, return immediately
28
+ if (this.initializationState === 'finished') {
26
29
  return;
27
- const translations = yield this.fetchTranslations(this.currentLanguage);
28
- const instance = createInstance({
29
- lng: this.currentLanguage,
30
- resources: {
31
- [this.currentLanguage]: {
32
- translation: translations,
33
- },
34
- },
35
- debug: window.location.hostname === 'localhost',
36
- });
37
- yield instance.init();
38
- this.i18n = instance;
39
- this.isInitialized = true;
30
+ }
31
+ // If currently initializing, wait for the existing initialization to complete
32
+ if (this.initializationState === 'initing' && this.initializationPromise) {
33
+ return this.initializationPromise;
34
+ }
35
+ // Start initialization
36
+ this.initializationState = 'initing';
37
+ // Create a promise that will be resolved when initialization completes
38
+ this.initializationPromise = (() => __awaiter(this, void 0, void 0, function* () {
39
+ try {
40
+ const translations = yield this.fetchTranslations(this.currentLanguage);
41
+ const instance = createInstance({
42
+ lng: this.currentLanguage,
43
+ resources: {
44
+ [this.currentLanguage]: {
45
+ translation: translations,
46
+ },
47
+ },
48
+ debug: window.location.hostname === 'localhost',
49
+ });
50
+ yield instance.init();
51
+ this.i18n = instance;
52
+ this.initializationState = 'finished';
53
+ }
54
+ catch (error) {
55
+ // Reset state on error so it can be retried
56
+ this.initializationState = 'not-inited';
57
+ this.initializationPromise = null;
58
+ throw error;
59
+ }
60
+ }))();
61
+ return this.initializationPromise;
40
62
  });
41
63
  }
42
64
  getTranslationUrl(language) {
@@ -45,7 +67,7 @@ export class Translator {
45
67
  const filename = language !== 'en' ? `local-${language}` : language;
46
68
  return `${window.location.origin}/locales/${filename}.json`;
47
69
  }
48
- return `./locales/${language}.json`;
70
+ return `${this.translationUrl}/locales/${language}.json`;
49
71
  }
50
72
  usePlugin(plugin) {
51
73
  if (!this.i18n) {
@@ -101,6 +123,6 @@ export class Translator {
101
123
  * Check if translator is initialized
102
124
  */
103
125
  isReady() {
104
- return this.isInitialized;
126
+ return this.initializationState === 'finished';
105
127
  }
106
128
  }
@@ -12,18 +12,18 @@ export class EventBusHandler {
12
12
  this.listeners = new Map();
13
13
  this.responseResolvers = new Map();
14
14
  this.debugEnabled = false;
15
- this.evName = "";
15
+ this.evName = '';
16
16
  //private constructor
17
17
  }
18
18
  static getInstance(name) {
19
19
  if (!EventBusHandler.instance) {
20
20
  EventBusHandler.instance = new EventBusHandler();
21
- EventBusHandler.instance.on("global.system.requestDebug", () => {
21
+ EventBusHandler.instance.on('global.system.requestDebug', () => {
22
22
  EventBusHandler.instance.debugEnabled = true;
23
23
  console.log(`[${EventBusHandler.instance.evName}] Debug mode enabled. Make sure debugging messages are enabled in the browser console.`);
24
24
  });
25
25
  }
26
- if (name && EventBusHandler.instance.evName === "") {
26
+ if (name && EventBusHandler.instance.evName === '') {
27
27
  EventBusHandler.instance.evName = name;
28
28
  }
29
29
  return EventBusHandler.instance;
@@ -67,7 +67,7 @@ export class EventBusHandler {
67
67
  }
68
68
  const event = this.createEvent(sender, topic, data, eventId);
69
69
  const handlers = this.getMatchingHandlers(event.topic);
70
- handlers.forEach(handler => {
70
+ handlers.forEach((handler) => {
71
71
  if (handler.ignoreSender && handler.ignoreSender.includes(sender)) {
72
72
  // console.log("ignore event as its in the ignoreSender list", { event, ignoreList: handler.ignoreSender });
73
73
  return;
@@ -93,7 +93,7 @@ export class EventBusHandler {
93
93
  * @returns An EventListener object containing an off() method to unsubscribe the listeners.
94
94
  */
95
95
  on(topics, handler, ignoreSender = []) {
96
- const ids = this.toArray(topics).map(topic => {
96
+ const ids = this.toArray(topics).map((topic) => {
97
97
  this.logIfDebug(`Subscribing to ` + topic, { ignoreSender });
98
98
  if (!this.validateTopic(topic)) {
99
99
  this.logAndThrowError(true, `Invalid topic: ` + topic);
@@ -102,14 +102,31 @@ export class EventBusHandler {
102
102
  this.listeners.set(topic, new Set());
103
103
  }
104
104
  const id = Math.floor(Math.random() * 10000000000);
105
- // Type assertion to handle the generic type mismatch
106
- const eventHandler = handler;
105
+ // To prevent infinite loops and processing the same eventId multiple times
106
+ const blackListedEventIds = [];
107
+ const eventHandler = (data) => {
108
+ if (blackListedEventIds.some((item) => item.eventId === data.eventId && item.sender === data.sender)) {
109
+ console.log('BLACKLISTED EVENT ID', data.eventId, data);
110
+ return;
111
+ }
112
+ blackListedEventIds.push({
113
+ eventId: data.eventId,
114
+ sender: data.sender,
115
+ });
116
+ if (blackListedEventIds.length > 100) {
117
+ blackListedEventIds.shift();
118
+ }
119
+ return handler(data);
120
+ };
107
121
  this.listeners.get(topic).add({ id, handler: eventHandler, ignoreSender });
108
- this.logIfDebug(`Subscribed to ` + topic, { listenerId: id, ignoreSender });
122
+ this.logIfDebug(`Subscribed to ` + topic, {
123
+ listenerId: id,
124
+ ignoreSender,
125
+ });
109
126
  return btoa(JSON.stringify({ topic, id }));
110
127
  });
111
128
  return {
112
- off: () => this.off(ids)
129
+ off: () => this.off(ids),
113
130
  };
114
131
  }
115
132
  /**
@@ -121,29 +138,29 @@ export class EventBusHandler {
121
138
  */
122
139
  respond(sender, topic, handler) {
123
140
  const topics = Array.isArray(topic) ? topic : [topic];
124
- const listeners = topics.map(topic => {
141
+ const listeners = topics.map((topic) => {
125
142
  const blackListedEventIds = [];
126
143
  //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
127
- const finalIgnoreSender = !topic.startsWith("self.") ? [sender] : [];
144
+ const finalIgnoreSender = !topic.startsWith('self.') ? [sender] : [];
128
145
  const listener = this.on(topic, (data) => __awaiter(this, void 0, void 0, function* () {
129
146
  if (blackListedEventIds.includes(data.eventId)) {
130
147
  // console.log("BLACKLISTED EVENT ID", data.eventId);
131
148
  return;
132
149
  }
133
150
  blackListedEventIds.push(data.eventId);
134
- if (blackListedEventIds.length > 20) {
151
+ if (blackListedEventIds.length > 100) {
135
152
  blackListedEventIds.shift();
136
153
  }
137
- const response = typeof handler === "function" ? yield handler(data) : handler;
154
+ const response = typeof handler === 'function' ? yield handler(data) : handler;
138
155
  this.emit(sender, topic, response, data.eventId);
139
156
  }), finalIgnoreSender);
140
- this.logIfDebug(`Added respond listener ` + sender + " to topic " + topic, { listener, sender });
157
+ this.logIfDebug(`Added respond listener ` + sender + ' to topic ' + topic, { listener, sender });
141
158
  return {
142
- off: () => listener.off()
159
+ off: () => listener.off(),
143
160
  };
144
161
  });
145
162
  return {
146
- off: () => listeners.forEach(listener => listener.off())
163
+ off: () => listeners.forEach((listener) => listener.off()),
147
164
  };
148
165
  }
149
166
  /**
@@ -156,7 +173,7 @@ export class EventBusHandler {
156
173
  this.logAndThrowError(false, `Invalid topic: ` + topic);
157
174
  return;
158
175
  }
159
- let listener;
176
+ let listener = undefined;
160
177
  const wrapper = (event) => {
161
178
  handler(event);
162
179
  listener === null || listener === void 0 ? void 0 : listener.off();
@@ -169,13 +186,16 @@ export class EventBusHandler {
169
186
  * @param listenerIds - The ids of the listeners to unsubscribe from.
170
187
  */
171
188
  off(listenerIds) {
172
- this.toArray(listenerIds).forEach(fullId => {
189
+ this.toArray(listenerIds).forEach((fullId) => {
173
190
  const { topic, id } = JSON.parse(atob(fullId));
174
191
  const listeners = this.listeners.get(topic) || new Set();
175
- listeners.forEach(listener => {
192
+ listeners.forEach((listener) => {
176
193
  if (listener.id === Number(id)) {
177
194
  listeners.delete(listener);
178
- this.logIfDebug(`Removed listener ` + fullId, { topic, listenerId: id });
195
+ this.logIfDebug(`Removed listener ` + fullId, {
196
+ topic,
197
+ listenerId: id,
198
+ });
179
199
  }
180
200
  });
181
201
  });
@@ -197,7 +217,7 @@ export class EventBusHandler {
197
217
  }
198
218
  const event = this.createEvent(sender, topic, data || {});
199
219
  this.logIfDebug(`Requesting data from ` + topic, { event });
200
- return new Promise(resolve => {
220
+ return new Promise((resolve) => {
201
221
  this.responseResolvers.set(event.eventId, (value) => resolve(value));
202
222
  this.emitInternal(sender, topic, data || {}, event.eventId, true);
203
223
  });
@@ -212,7 +232,7 @@ export class EventBusHandler {
212
232
  const exact = this.listeners.get(topic) || new Set();
213
233
  // Find wildcard matches
214
234
  const wildcard = [...this.listeners.entries()]
215
- .filter(([key]) => key.endsWith("*") && topic.startsWith(key.slice(0, -1)))
235
+ .filter(([key]) => key.endsWith('*') && topic.startsWith(key.slice(0, -1)))
216
236
  .flatMap(([_, handlers]) => [...handlers]);
217
237
  return new Set([...exact, ...wildcard]);
218
238
  }
@@ -223,27 +243,27 @@ export class EventBusHandler {
223
243
  */
224
244
  validateTopic(topic) {
225
245
  // Split event type into parts
226
- const parts = topic.split(".");
246
+ const parts = topic.split('.');
227
247
  const [plugin, area, action] = parts;
228
248
  if (parts.length !== 3) {
229
- if (parts.length === 1 && plugin === "*") {
249
+ if (parts.length === 1 && plugin === '*') {
230
250
  return true;
231
251
  }
232
- if (parts.length === 2 && plugin !== "*" && area === "*") {
252
+ if (parts.length === 2 && plugin !== '*' && area === '*') {
233
253
  return true;
234
254
  }
235
255
  this.logAndThrowError(false, `Event type must have 3 parts separated by dots. Received: ` + topic);
236
256
  return false;
237
257
  }
238
- if (action === "*") {
258
+ if (action === '*') {
239
259
  return true;
240
260
  }
241
261
  // Validate action part
242
- const validActions = ["request", "create", "update", "delete", "trigger"];
243
- if (validActions.some(a => action.startsWith(a))) {
262
+ const validActions = ['request', 'create', 'update', 'delete', 'trigger'];
263
+ if (validActions.some((a) => action.startsWith(a))) {
244
264
  return true;
245
265
  }
246
- this.logAndThrowError(false, `Invalid event topic name. The action: ` + action + ". Must be or start with one of: " + validActions.join(", "));
266
+ this.logAndThrowError(false, `Invalid event topic name. The action: ` + action + '. Must be or start with one of: ' + validActions.join(', '));
247
267
  return false;
248
268
  }
249
269
  logIfDebug(...args) {
@@ -1,9 +1,9 @@
1
- export type Plugin<T extends {} = {}> = Omit<RimoriPluginConfig<T>, 'context_menu_actions'> & {
1
+ export type Plugin<T extends object = object> = Omit<RimoriPluginConfig<T>, 'context_menu_actions'> & {
2
2
  version: string;
3
3
  endpoint: string;
4
4
  assetEndpoint: string;
5
5
  context_menu_actions: MenuEntry[];
6
- release_channel: "alpha" | "beta" | "stable";
6
+ release_channel: 'alpha' | 'beta' | 'stable';
7
7
  };
8
8
  export type ActivePlugin = Plugin<{
9
9
  active?: boolean;
@@ -14,7 +14,7 @@ export interface PluginPage {
14
14
  url: string;
15
15
  show: boolean;
16
16
  description: string;
17
- root: "vocabulary" | "grammar" | "reading" | "listening" | "watching" | "writing" | "speaking" | "other" | "community";
17
+ root: 'vocabulary' | 'grammar' | 'reading' | 'listening' | 'watching' | 'writing' | 'speaking' | 'other' | 'community';
18
18
  action?: {
19
19
  key: string;
20
20
  parameters: ObjectTool;
@@ -46,7 +46,7 @@ export interface ContextMenuAction {
46
46
  * Rimori plugin structure representing the complete configuration
47
47
  * of a Rimori plugin with all metadata and configuration options.
48
48
  */
49
- export interface RimoriPluginConfig<T extends {} = {}> {
49
+ export interface RimoriPluginConfig<T extends object = object> {
50
50
  id: string;
51
51
  /**
52
52
  * Basic information about the plugin including branding and core details.
@@ -79,7 +79,7 @@ export interface RimoriPluginConfig<T extends {} = {}> {
79
79
  /**
80
80
  * Context menu actions that the plugin registers to appear in right-click menus throughout the application.
81
81
  */
82
- context_menu_actions: Omit<MenuEntry, "plugin_id">[];
82
+ context_menu_actions: Omit<MenuEntry, 'plugin_id'>[];
83
83
  /**
84
84
  * Documentation paths for different types of plugin documentation.
85
85
  */
@@ -107,7 +107,7 @@ export interface Tool {
107
107
  parameters: {
108
108
  name: string;
109
109
  description: string;
110
- type: "string" | "number" | "boolean";
110
+ type: 'string' | 'number' | 'boolean';
111
111
  }[];
112
112
  execute?: (args: Record<string, any>) => Promise<unknown> | unknown | void;
113
113
  }
package/dist/index.d.ts CHANGED
@@ -11,7 +11,8 @@ export { Translator } from './controller/TranslationController';
11
11
  export type { TOptions } from 'i18next';
12
12
  export type { SharedContent, SharedContentObjectRequest } from './controller/SharedContentController';
13
13
  export type { Exercise } from './controller/ExerciseController';
14
- export type { UserInfo, Language } from './controller/SettingsController';
14
+ export type { UserInfo, Language, UserRole } from './controller/SettingsController';
15
15
  export type { Message, ToolInvocation } from './controller/AIController';
16
16
  export type { TriggerAction } from './controller/ExerciseController';
17
17
  export type { MacroAccomplishmentPayload, MicroAccomplishmentPayload } from './controller/AccomplishmentController';
18
+ export type { EventBusMessage } from './fromRimori/EventBus';
@@ -27,6 +27,17 @@ export interface RimoriInfo {
27
27
  mainPanelPlugin?: ActivePlugin;
28
28
  sidePanelPlugin?: ActivePlugin;
29
29
  interfaceLanguage: string;
30
+ /**
31
+ * The release channel of the plugin installation.
32
+ */
33
+ releaseChannel: 'alpha' | 'beta' | 'stable';
34
+ /**
35
+ * The database schema to use for plugin tables.
36
+ * Determined by rimori-main based on release channel:
37
+ * - 'plugins_alpha' for alpha release channel
38
+ * - 'plugins' for beta and stable release channels
39
+ */
40
+ dbSchema: 'plugins' | 'plugins_alpha';
30
41
  }
31
42
  export declare class RimoriCommunicationHandler {
32
43
  private port;
@@ -26,15 +26,17 @@ export class RimoriCommunicationHandler {
26
26
  }
27
27
  initMessageChannel(worker = false) {
28
28
  const listener = (event) => {
29
- console.log('[PluginController] window message', { origin: event.origin, data: event.data });
29
+ // console.log('[PluginController] window message', { origin: event.origin, data: event.data });
30
30
  const { type, pluginId, queryParams, rimoriInfo } = event.data || {};
31
31
  const [transferredPort] = event.ports || [];
32
32
  if (type !== 'rimori:init' || !transferredPort || pluginId !== this.pluginId) {
33
- console.log('[PluginController] message ignored (not init or wrong plugin)', {
34
- type,
35
- pluginId,
36
- hasPort: !!transferredPort,
37
- });
33
+ // console.log('[PluginController] message ignored (not init or wrong plugin)', {
34
+ // type,
35
+ // pluginId,
36
+ // currentPluginId: this.pluginId,
37
+ // hasPortProperty: !!transferredPort,
38
+ // event
39
+ // });
38
40
  return;
39
41
  }
40
42
  this.queryParams = queryParams || {};
@@ -182,7 +184,7 @@ export class RimoriCommunicationHandler {
182
184
  else {
183
185
  // In main thread context, use EventBus
184
186
  const { data } = yield EventBus.request(this.pluginId, 'global.supabase.requestAccess');
185
- console.log({ data });
187
+ // console.log({ data });
186
188
  this.rimoriInfo = data;
187
189
  this.supabase = createClient(this.rimoriInfo.url, this.rimoriInfo.key, {
188
190
  accessToken: () => Promise.resolve(this.getToken()),
@@ -54,6 +54,7 @@ export declare class Logger {
54
54
  private getBrowserInfo;
55
55
  /**
56
56
  * Capture a screenshot of the current page.
57
+ * Dynamically imports html2canvas only in browser environments.
57
58
  * @returns Promise resolving to base64 screenshot or null if failed
58
59
  */
59
60
  private captureScreenshot;