@rool-dev/extension 0.3.7-dev.2ccb343 → 0.3.7-dev.d9f341e
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/README.md +13 -1
- package/dist/dev/DevHostController.d.ts +1 -0
- package/dist/dev/DevHostController.d.ts.map +1 -1
- package/dist/dev/DevHostController.js +5 -1
- package/dist/dev/host-shell.js +14 -2
- package/dist/dev/host-shell.js.map +1 -1
- package/dist/host.d.ts +4 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/protocol.d.ts +7 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/reactive.svelte.d.ts +64 -40
- package/dist/reactive.svelte.d.ts.map +1 -1
- package/dist/reactive.svelte.js +374 -109
- package/package.json +2 -2
- package/dist/client.d.ts +0 -145
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -378
package/dist/reactive.svelte.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Extension channel — bridge client with Svelte 5 reactivity.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* The underlying transport is the postMessage bridge, not the SDK.
|
|
4
|
+
* Handles the postMessage bridge to the host and provides reactive $state
|
|
5
|
+
* properties, matching the @rool-dev/svelte ReactiveChannel API.
|
|
7
6
|
*/
|
|
7
|
+
import { isBridgeMessage } from './protocol.js';
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Helpers
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
let _nextId = 0;
|
|
12
|
+
function nextRequestId() {
|
|
13
|
+
return `req-${++_nextId}-${Date.now().toString(36)}`;
|
|
14
|
+
}
|
|
8
15
|
// ---------------------------------------------------------------------------
|
|
9
16
|
// ReactiveWatch
|
|
10
17
|
// ---------------------------------------------------------------------------
|
|
@@ -160,128 +167,324 @@ class ReactiveObjectImpl {
|
|
|
160
167
|
// ReactiveChannel
|
|
161
168
|
// ---------------------------------------------------------------------------
|
|
162
169
|
class ReactiveChannelImpl {
|
|
163
|
-
|
|
170
|
+
_pending = new Map();
|
|
171
|
+
_listeners = new Map();
|
|
172
|
+
_schema;
|
|
173
|
+
_metadata;
|
|
174
|
+
#unsubscribers = [];
|
|
164
175
|
#closed = false;
|
|
176
|
+
// Metadata from handshake
|
|
177
|
+
channelId;
|
|
178
|
+
spaceId;
|
|
179
|
+
spaceName;
|
|
180
|
+
role;
|
|
181
|
+
linkAccess;
|
|
182
|
+
userId;
|
|
183
|
+
/** Current user info (id, name, email). */
|
|
184
|
+
user;
|
|
165
185
|
// Reactive state
|
|
166
186
|
interactions = $state([]);
|
|
167
187
|
objectIds = $state([]);
|
|
168
188
|
collections = $state([]);
|
|
169
189
|
conversations = $state([]);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
this
|
|
190
|
+
constructor(init) {
|
|
191
|
+
this.channelId = init.channelId;
|
|
192
|
+
this.spaceId = init.spaceId;
|
|
193
|
+
this.spaceName = init.spaceName;
|
|
194
|
+
this.role = init.role;
|
|
195
|
+
this.linkAccess = init.linkAccess;
|
|
196
|
+
this.userId = init.userId;
|
|
197
|
+
this.user = init.user;
|
|
198
|
+
this._schema = init.schema;
|
|
199
|
+
this._metadata = init.metadata;
|
|
200
|
+
window.addEventListener('message', this._onMessage);
|
|
173
201
|
// Load initial data
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
this.collections = Object.keys(
|
|
202
|
+
this.getInteractions().then((list) => { this.interactions = list; });
|
|
203
|
+
this._call('getObjectIds').then((ids) => { this.objectIds = ids; });
|
|
204
|
+
this.getConversations().then((list) => { this.conversations = list; });
|
|
205
|
+
this.collections = Object.keys(this._schema);
|
|
178
206
|
// Subscribe to channel updates → refresh interactions
|
|
179
207
|
const onChannelUpdated = () => {
|
|
180
|
-
|
|
208
|
+
this.getInteractions().then((list) => { this.interactions = list; });
|
|
181
209
|
};
|
|
182
|
-
|
|
183
|
-
this.#unsubscribers.push(() =>
|
|
210
|
+
this.on('channelUpdated', onChannelUpdated);
|
|
211
|
+
this.#unsubscribers.push(() => this.off('channelUpdated', onChannelUpdated));
|
|
184
212
|
// Subscribe to conversation updates → refresh conversations
|
|
185
213
|
const onConversationUpdated = () => {
|
|
186
|
-
|
|
214
|
+
this.getConversations().then((list) => { this.conversations = list; });
|
|
187
215
|
};
|
|
188
|
-
|
|
189
|
-
this.#unsubscribers.push(() =>
|
|
216
|
+
this.on('conversationUpdated', onConversationUpdated);
|
|
217
|
+
this.#unsubscribers.push(() => this.off('conversationUpdated', onConversationUpdated));
|
|
190
218
|
// Subscribe to object events → refresh objectIds
|
|
191
219
|
const refreshObjectIds = () => {
|
|
192
|
-
|
|
220
|
+
this._call('getObjectIds').then((ids) => { this.objectIds = ids; });
|
|
193
221
|
};
|
|
194
|
-
|
|
195
|
-
this.#unsubscribers.push(() =>
|
|
196
|
-
|
|
197
|
-
this.#unsubscribers.push(() =>
|
|
222
|
+
this.on('objectCreated', refreshObjectIds);
|
|
223
|
+
this.#unsubscribers.push(() => this.off('objectCreated', refreshObjectIds));
|
|
224
|
+
this.on('objectDeleted', refreshObjectIds);
|
|
225
|
+
this.#unsubscribers.push(() => this.off('objectDeleted', refreshObjectIds));
|
|
198
226
|
// Subscribe to schema updates → refresh collections
|
|
199
227
|
const onSchemaUpdated = () => {
|
|
200
|
-
this.collections = Object.keys(
|
|
228
|
+
this.collections = Object.keys(this._schema);
|
|
201
229
|
};
|
|
202
|
-
|
|
203
|
-
this.#unsubscribers.push(() =>
|
|
230
|
+
this.on('schemaUpdated', onSchemaUpdated);
|
|
231
|
+
this.#unsubscribers.push(() => this.off('schemaUpdated', onSchemaUpdated));
|
|
204
232
|
// Full resets
|
|
205
233
|
const onReset = () => {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.collections = Object.keys(
|
|
234
|
+
this.getInteractions().then((list) => { this.interactions = list; });
|
|
235
|
+
this._call('getObjectIds').then((ids) => { this.objectIds = ids; });
|
|
236
|
+
this.getConversations().then((list) => { this.conversations = list; });
|
|
237
|
+
this.collections = Object.keys(this._schema);
|
|
210
238
|
};
|
|
211
|
-
|
|
212
|
-
this.#unsubscribers.push(() =>
|
|
239
|
+
this.on('reset', onReset);
|
|
240
|
+
this.#unsubscribers.push(() => this.off('reset', onReset));
|
|
241
|
+
}
|
|
242
|
+
get isReadOnly() {
|
|
243
|
+
return this.role === 'viewer';
|
|
244
|
+
}
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// Event emitter
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
on(event, callback) {
|
|
249
|
+
let set = this._listeners.get(event);
|
|
250
|
+
if (!set) {
|
|
251
|
+
set = new Set();
|
|
252
|
+
this._listeners.set(event, set);
|
|
253
|
+
}
|
|
254
|
+
set.add(callback);
|
|
255
|
+
}
|
|
256
|
+
off(event, callback) {
|
|
257
|
+
this._listeners.get(event)?.delete(callback);
|
|
258
|
+
}
|
|
259
|
+
_emit(event, data) {
|
|
260
|
+
const set = this._listeners.get(event);
|
|
261
|
+
if (set) {
|
|
262
|
+
for (const cb of set) {
|
|
263
|
+
try {
|
|
264
|
+
cb(data);
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
console.error(`[Channel] Error in ${event} listener:`, e);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// postMessage transport
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
_call(method, ...args) {
|
|
276
|
+
return this._callScoped(method, args);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Send a bridge request, optionally scoped to a conversation.
|
|
280
|
+
* @internal
|
|
281
|
+
*/
|
|
282
|
+
_callScoped(method, args, conversationId) {
|
|
283
|
+
return new Promise((resolve, reject) => {
|
|
284
|
+
const id = nextRequestId();
|
|
285
|
+
this._pending.set(id, { resolve, reject });
|
|
286
|
+
const msg = { type: 'rool:request', id, method, args };
|
|
287
|
+
if (conversationId !== undefined)
|
|
288
|
+
msg.conversationId = conversationId;
|
|
289
|
+
window.parent.postMessage(msg, '*');
|
|
290
|
+
});
|
|
213
291
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
292
|
+
_onMessage = (event) => {
|
|
293
|
+
if (!isBridgeMessage(event.data))
|
|
294
|
+
return;
|
|
295
|
+
if (event.data.type === 'rool:response') {
|
|
296
|
+
const msg = event.data;
|
|
297
|
+
const pending = this._pending.get(msg.id);
|
|
298
|
+
if (pending) {
|
|
299
|
+
this._pending.delete(msg.id);
|
|
300
|
+
if (msg.error) {
|
|
301
|
+
pending.reject(new Error(msg.error));
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
pending.resolve(msg.result);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (event.data.type === 'rool:event') {
|
|
310
|
+
const msg = event.data;
|
|
311
|
+
// Update local caches before emitting so listeners see fresh data
|
|
312
|
+
if (msg.name === 'metadataUpdated') {
|
|
313
|
+
const payload = msg.data;
|
|
314
|
+
this._metadata = payload.metadata;
|
|
315
|
+
}
|
|
316
|
+
else if (msg.name === 'schemaUpdated') {
|
|
317
|
+
const payload = msg.data;
|
|
318
|
+
this._schema = payload.schema;
|
|
319
|
+
}
|
|
320
|
+
else if (msg.name === 'reset') {
|
|
321
|
+
// Full reload happened on the host — refresh cached schema and metadata
|
|
322
|
+
Promise.all([
|
|
323
|
+
this._call('getSchema'),
|
|
324
|
+
this._call('getAllMetadata'),
|
|
325
|
+
]).then(([schema, metadata]) => {
|
|
326
|
+
this._schema = schema;
|
|
327
|
+
this._metadata = metadata;
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
this._emit(msg.name, msg.data);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
// ---------------------------------------------------------------------------
|
|
222
335
|
// Object operations
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
getActiveLeafId() { return this.#channel.getActiveLeafId(); }
|
|
247
|
-
setActiveLeaf(interactionId) { return this.#channel.setActiveLeaf(interactionId); }
|
|
248
|
-
getSystemInstruction() { return this.#channel.getSystemInstruction(); }
|
|
249
|
-
setSystemInstruction(instruction) { return this.#channel.setSystemInstruction(instruction); }
|
|
250
|
-
getConversations() { return this.#channel.getConversations(); }
|
|
251
|
-
deleteConversation(conversationId) { return this.#channel.deleteConversation(conversationId); }
|
|
252
|
-
renameConversation(name) { return this.#channel.renameConversation(name); }
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
async getObject(objectId) {
|
|
338
|
+
return this._call('getObject', objectId);
|
|
339
|
+
}
|
|
340
|
+
async stat(objectId) {
|
|
341
|
+
return this._call('stat', objectId);
|
|
342
|
+
}
|
|
343
|
+
async findObjects(options) {
|
|
344
|
+
return this._call('findObjects', options);
|
|
345
|
+
}
|
|
346
|
+
async getObjectIds(options) {
|
|
347
|
+
return this._call('getObjectIds', options);
|
|
348
|
+
}
|
|
349
|
+
async createObject(options) {
|
|
350
|
+
return this._call('createObject', options);
|
|
351
|
+
}
|
|
352
|
+
async updateObject(objectId, options) {
|
|
353
|
+
return this._call('updateObject', objectId, options);
|
|
354
|
+
}
|
|
355
|
+
async deleteObjects(objectIds) {
|
|
356
|
+
await this._call('deleteObjects', objectIds);
|
|
357
|
+
}
|
|
358
|
+
// ---------------------------------------------------------------------------
|
|
253
359
|
// Schema
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
360
|
+
// ---------------------------------------------------------------------------
|
|
361
|
+
getSchema() {
|
|
362
|
+
return this._schema;
|
|
363
|
+
}
|
|
364
|
+
async createCollection(name, fields) {
|
|
365
|
+
const result = await this._call('createCollection', name, fields);
|
|
366
|
+
this._schema[name] = result;
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
async alterCollection(name, fields) {
|
|
370
|
+
const result = await this._call('alterCollection', name, fields);
|
|
371
|
+
this._schema[name] = result;
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
async dropCollection(name) {
|
|
375
|
+
await this._call('dropCollection', name);
|
|
376
|
+
delete this._schema[name];
|
|
377
|
+
}
|
|
378
|
+
// ---------------------------------------------------------------------------
|
|
379
|
+
// Interactions & system instruction
|
|
380
|
+
// ---------------------------------------------------------------------------
|
|
381
|
+
async getInteractions() {
|
|
382
|
+
return this._call('getInteractions');
|
|
383
|
+
}
|
|
384
|
+
async getTree() {
|
|
385
|
+
return this._call('getTree');
|
|
386
|
+
}
|
|
387
|
+
async getActiveLeafId() {
|
|
388
|
+
return this._call('getActiveLeafId');
|
|
389
|
+
}
|
|
390
|
+
async setActiveLeaf(interactionId) {
|
|
391
|
+
await this._call('setActiveLeaf', interactionId);
|
|
392
|
+
}
|
|
393
|
+
async getSystemInstruction() {
|
|
394
|
+
return this._call('getSystemInstruction');
|
|
395
|
+
}
|
|
396
|
+
async setSystemInstruction(instruction) {
|
|
397
|
+
await this._call('setSystemInstruction', instruction);
|
|
398
|
+
}
|
|
399
|
+
async getConversations() {
|
|
400
|
+
return this._call('getConversations');
|
|
401
|
+
}
|
|
402
|
+
async deleteConversation(conversationId) {
|
|
403
|
+
await this._call('deleteConversation', conversationId);
|
|
404
|
+
}
|
|
405
|
+
async renameConversation(name) {
|
|
406
|
+
await this._call('renameConversation', name);
|
|
407
|
+
}
|
|
408
|
+
// ---------------------------------------------------------------------------
|
|
258
409
|
// Conversations
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
/**
|
|
412
|
+
* Get a reactive handle for a specific conversation within this channel.
|
|
413
|
+
* Scopes AI and mutation operations to that conversation's interaction history.
|
|
414
|
+
* Conversations are auto-created on first interaction.
|
|
415
|
+
*/
|
|
259
416
|
conversation(conversationId) {
|
|
260
417
|
if (this.#closed)
|
|
261
418
|
throw new Error('Cannot create reactive conversation: channel is closed');
|
|
262
|
-
return new ReactiveConversationHandleImpl(this
|
|
419
|
+
return new ReactiveConversationHandleImpl(this, conversationId);
|
|
420
|
+
}
|
|
421
|
+
// ---------------------------------------------------------------------------
|
|
422
|
+
// Metadata
|
|
423
|
+
// ---------------------------------------------------------------------------
|
|
424
|
+
async setMetadata(key, value) {
|
|
425
|
+
await this._call('setMetadata', key, value);
|
|
426
|
+
this._metadata[key] = value;
|
|
263
427
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
428
|
+
getMetadata(key) {
|
|
429
|
+
return this._metadata[key];
|
|
430
|
+
}
|
|
431
|
+
getAllMetadata() {
|
|
432
|
+
return { ...this._metadata };
|
|
433
|
+
}
|
|
434
|
+
// ---------------------------------------------------------------------------
|
|
435
|
+
// AI
|
|
436
|
+
// ---------------------------------------------------------------------------
|
|
437
|
+
async prompt(text, options) {
|
|
438
|
+
return this._call('prompt', text, options);
|
|
439
|
+
}
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
// Undo/redo
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
async checkpoint(label) {
|
|
444
|
+
return this._call('checkpoint', label);
|
|
445
|
+
}
|
|
446
|
+
async canUndo() {
|
|
447
|
+
return this._call('canUndo');
|
|
448
|
+
}
|
|
449
|
+
async canRedo() {
|
|
450
|
+
return this._call('canRedo');
|
|
451
|
+
}
|
|
452
|
+
async undo() {
|
|
453
|
+
return this._call('undo');
|
|
454
|
+
}
|
|
455
|
+
async redo() {
|
|
456
|
+
return this._call('redo');
|
|
457
|
+
}
|
|
458
|
+
async clearHistory() {
|
|
459
|
+
await this._call('clearHistory');
|
|
460
|
+
}
|
|
461
|
+
// ---------------------------------------------------------------------------
|
|
267
462
|
// Reactive primitives
|
|
463
|
+
// ---------------------------------------------------------------------------
|
|
268
464
|
object(objectId) {
|
|
269
465
|
if (this.#closed)
|
|
270
466
|
throw new Error('Cannot create reactive object: channel is closed');
|
|
271
|
-
return new ReactiveObjectImpl(this
|
|
467
|
+
return new ReactiveObjectImpl(this, objectId);
|
|
272
468
|
}
|
|
273
469
|
watch(options) {
|
|
274
470
|
if (this.#closed)
|
|
275
471
|
throw new Error('Cannot create reactive watch: channel is closed');
|
|
276
|
-
return new ReactiveWatchImpl(this
|
|
472
|
+
return new ReactiveWatchImpl(this, options);
|
|
277
473
|
}
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
278
475
|
// Cleanup
|
|
476
|
+
// ---------------------------------------------------------------------------
|
|
279
477
|
destroy() {
|
|
280
478
|
this.#closed = true;
|
|
281
479
|
for (const unsub of this.#unsubscribers)
|
|
282
480
|
unsub();
|
|
283
481
|
this.#unsubscribers.length = 0;
|
|
284
|
-
this
|
|
482
|
+
window.removeEventListener('message', this._onMessage);
|
|
483
|
+
for (const { reject } of this._pending.values()) {
|
|
484
|
+
reject(new Error('Channel destroyed'));
|
|
485
|
+
}
|
|
486
|
+
this._pending.clear();
|
|
487
|
+
this._listeners.clear();
|
|
285
488
|
}
|
|
286
489
|
}
|
|
287
490
|
// ---------------------------------------------------------------------------
|
|
@@ -289,59 +492,91 @@ class ReactiveChannelImpl {
|
|
|
289
492
|
// ---------------------------------------------------------------------------
|
|
290
493
|
/**
|
|
291
494
|
* A reactive conversation handle for the extension bridge.
|
|
292
|
-
*
|
|
293
|
-
*
|
|
495
|
+
* Scopes AI and mutation operations to a specific conversation's
|
|
496
|
+
* interaction history, while sharing the channel's bridge connection.
|
|
294
497
|
*
|
|
295
498
|
* Call `close()` when done to stop listening for updates.
|
|
296
499
|
*/
|
|
297
500
|
class ReactiveConversationHandleImpl {
|
|
298
|
-
#
|
|
501
|
+
#channel;
|
|
299
502
|
#conversationId;
|
|
300
503
|
#unsubscribers = [];
|
|
301
504
|
// Reactive state
|
|
302
505
|
interactions = $state([]);
|
|
303
506
|
constructor(channel, conversationId) {
|
|
507
|
+
this.#channel = channel;
|
|
304
508
|
this.#conversationId = conversationId;
|
|
305
|
-
this.#handle = channel.conversation(conversationId);
|
|
306
509
|
// Initial load
|
|
307
|
-
this
|
|
510
|
+
this.getInteractions().then((list) => { this.interactions = list; });
|
|
308
511
|
// Listen for updates to this conversation
|
|
309
512
|
const onConversationUpdated = ({ conversationId: cid }) => {
|
|
310
513
|
if (cid === this.#conversationId) {
|
|
311
|
-
this
|
|
514
|
+
this.getInteractions().then((list) => { this.interactions = list; });
|
|
312
515
|
}
|
|
313
516
|
};
|
|
314
517
|
channel.on('conversationUpdated', onConversationUpdated);
|
|
315
518
|
this.#unsubscribers.push(() => channel.off('conversationUpdated', onConversationUpdated));
|
|
316
519
|
// Handle full resets
|
|
317
520
|
const onReset = () => {
|
|
318
|
-
this
|
|
521
|
+
this.getInteractions().then((list) => { this.interactions = list; });
|
|
319
522
|
};
|
|
320
523
|
channel.on('reset', onReset);
|
|
321
524
|
this.#unsubscribers.push(() => channel.off('reset', onReset));
|
|
322
525
|
}
|
|
323
526
|
get conversationId() { return this.#conversationId; }
|
|
324
527
|
// Conversation history
|
|
325
|
-
getInteractions() {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
528
|
+
async getInteractions() {
|
|
529
|
+
return this.#channel._callScoped('getInteractions', [], this.#conversationId);
|
|
530
|
+
}
|
|
531
|
+
async getTree() {
|
|
532
|
+
return this.#channel._callScoped('getTree', [], this.#conversationId);
|
|
533
|
+
}
|
|
534
|
+
async getActiveLeafId() {
|
|
535
|
+
return this.#channel._callScoped('getActiveLeafId', [], this.#conversationId);
|
|
536
|
+
}
|
|
537
|
+
async setActiveLeaf(interactionId) {
|
|
538
|
+
await this.#channel._callScoped('setActiveLeaf', [interactionId], this.#conversationId);
|
|
539
|
+
}
|
|
540
|
+
async getSystemInstruction() {
|
|
541
|
+
return this.#channel._callScoped('getSystemInstruction', [], this.#conversationId);
|
|
542
|
+
}
|
|
543
|
+
async setSystemInstruction(instruction) {
|
|
544
|
+
await this.#channel._callScoped('setSystemInstruction', [instruction], this.#conversationId);
|
|
545
|
+
}
|
|
546
|
+
async rename(name) {
|
|
547
|
+
await this.#channel._callScoped('renameConversation', [name], this.#conversationId);
|
|
548
|
+
}
|
|
332
549
|
// Object operations
|
|
333
|
-
findObjects(options) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
550
|
+
async findObjects(options) {
|
|
551
|
+
return this.#channel._callScoped('findObjects', [options], this.#conversationId);
|
|
552
|
+
}
|
|
553
|
+
async createObject(options) {
|
|
554
|
+
return this.#channel._callScoped('createObject', [options], this.#conversationId);
|
|
555
|
+
}
|
|
556
|
+
async updateObject(objectId, options) {
|
|
557
|
+
return this.#channel._callScoped('updateObject', [objectId, options], this.#conversationId);
|
|
558
|
+
}
|
|
559
|
+
async deleteObjects(objectIds) {
|
|
560
|
+
await this.#channel._callScoped('deleteObjects', [objectIds], this.#conversationId);
|
|
561
|
+
}
|
|
337
562
|
// AI
|
|
338
|
-
prompt(text, options) {
|
|
563
|
+
async prompt(text, options) {
|
|
564
|
+
return this.#channel._callScoped('prompt', [text, options], this.#conversationId);
|
|
565
|
+
}
|
|
339
566
|
// Schema
|
|
340
|
-
createCollection(name, fields) {
|
|
341
|
-
|
|
342
|
-
|
|
567
|
+
async createCollection(name, fields) {
|
|
568
|
+
return this.#channel._callScoped('createCollection', [name, fields], this.#conversationId);
|
|
569
|
+
}
|
|
570
|
+
async alterCollection(name, fields) {
|
|
571
|
+
return this.#channel._callScoped('alterCollection', [name, fields], this.#conversationId);
|
|
572
|
+
}
|
|
573
|
+
async dropCollection(name) {
|
|
574
|
+
await this.#channel._callScoped('dropCollection', [name], this.#conversationId);
|
|
575
|
+
}
|
|
343
576
|
// Metadata
|
|
344
|
-
setMetadata(key, value) {
|
|
577
|
+
async setMetadata(key, value) {
|
|
578
|
+
await this.#channel._callScoped('setMetadata', [key, value], this.#conversationId);
|
|
579
|
+
}
|
|
345
580
|
/**
|
|
346
581
|
* Stop listening for updates and clean up.
|
|
347
582
|
*/
|
|
@@ -352,17 +587,47 @@ class ReactiveConversationHandleImpl {
|
|
|
352
587
|
}
|
|
353
588
|
}
|
|
354
589
|
// ---------------------------------------------------------------------------
|
|
355
|
-
// initExtension
|
|
590
|
+
// initExtension
|
|
356
591
|
// ---------------------------------------------------------------------------
|
|
357
|
-
import { initExtension as initBridge } from './client.js';
|
|
358
592
|
/**
|
|
359
593
|
* Initialize the extension and return a reactive channel.
|
|
360
594
|
*
|
|
361
595
|
* Sends `rool:ready` to the host, waits for the handshake, and returns
|
|
362
596
|
* a reactive channel with $state properties (interactions, objectIds)
|
|
363
597
|
* and reactive primitives (object(), watch()).
|
|
598
|
+
*
|
|
599
|
+
* If the extension is opened directly (not in an iframe), redirects to the Rool
|
|
600
|
+
* console with `?openExtension={extensionId}` so the user can install or navigate to it.
|
|
601
|
+
*
|
|
602
|
+
* @param timeout - How long to wait for the handshake (ms). Default: 10000.
|
|
364
603
|
*/
|
|
365
|
-
export
|
|
366
|
-
|
|
367
|
-
|
|
604
|
+
export function initExtension(timeout = 10000) {
|
|
605
|
+
// Deep link: if not in an iframe, redirect to the Rool console
|
|
606
|
+
if (window.self === window.top) {
|
|
607
|
+
const host = window.location.hostname;
|
|
608
|
+
const dot = host.indexOf('.');
|
|
609
|
+
if (dot > 0) {
|
|
610
|
+
const extensionId = host.slice(0, dot);
|
|
611
|
+
const domain = host.slice(dot + 1);
|
|
612
|
+
window.location.href = `https://${domain}/?openExtension=${extensionId}`;
|
|
613
|
+
}
|
|
614
|
+
// Never resolve — the redirect will unload the page
|
|
615
|
+
return new Promise(() => { });
|
|
616
|
+
}
|
|
617
|
+
return new Promise((resolve, reject) => {
|
|
618
|
+
const timer = setTimeout(() => {
|
|
619
|
+
window.removeEventListener('message', onMessage);
|
|
620
|
+
reject(new Error('Extension handshake timed out — is this running inside a Rool host?'));
|
|
621
|
+
}, timeout);
|
|
622
|
+
function onMessage(event) {
|
|
623
|
+
if (!isBridgeMessage(event.data) || event.data.type !== 'rool:init')
|
|
624
|
+
return;
|
|
625
|
+
clearTimeout(timer);
|
|
626
|
+
window.removeEventListener('message', onMessage);
|
|
627
|
+
resolve(new ReactiveChannelImpl(event.data));
|
|
628
|
+
}
|
|
629
|
+
window.addEventListener('message', onMessage);
|
|
630
|
+
// Signal to the host that we're ready
|
|
631
|
+
window.parent.postMessage({ type: 'rool:ready' }, '*');
|
|
632
|
+
});
|
|
368
633
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rool-dev/extension",
|
|
3
|
-
"version": "0.3.7-dev.
|
|
3
|
+
"version": "0.3.7-dev.d9f341e",
|
|
4
4
|
"description": "Svelte-first extension SDK for Rool — reactive channel over iframe bridge",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"svelte": "./dist/index.js",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"archiver": "^7.0.1",
|
|
58
58
|
"tailwindcss": "^4.2.2",
|
|
59
59
|
"vite": "^8.0.0",
|
|
60
|
-
"@rool-dev/sdk": "0.3.7-dev.
|
|
60
|
+
"@rool-dev/sdk": "0.3.7-dev.d9f341e"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@sveltejs/package": "^2.5.7",
|