@rool-dev/sdk 0.11.3-dev.5dfa2e5 → 0.11.3-dev.7c57c94
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 +156 -105
- package/dist/auth-base.d.ts +100 -0
- package/dist/auth-base.d.ts.map +1 -0
- package/dist/auth-base.js +377 -0
- package/dist/auth-base.js.map +1 -0
- package/dist/auth-browser.d.ts +6 -77
- package/dist/auth-browser.d.ts.map +1 -1
- package/dist/auth-browser.js +32 -328
- package/dist/auth-browser.js.map +1 -1
- package/dist/auth-native.d.ts +72 -0
- package/dist/auth-native.d.ts.map +1 -0
- package/dist/auth-native.js +260 -0
- package/dist/auth-native.js.map +1 -0
- package/dist/auth.d.ts +17 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +36 -5
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +27 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +52 -26
- package/dist/client.js.map +1 -1
- package/dist/graphql.d.ts +13 -19
- package/dist/graphql.d.ts.map +1 -1
- package/dist/graphql.js +35 -70
- package/dist/graphql.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/reroute.d.ts.map +1 -1
- package/dist/reroute.js +9 -0
- package/dist/reroute.js.map +1 -1
- package/dist/{channel.d.ts → space-session.d.ts} +44 -139
- package/dist/space-session.d.ts.map +1 -0
- package/dist/{channel.js → space-session.js} +111 -325
- package/dist/space-session.js.map +1 -0
- package/dist/space.d.ts +18 -48
- package/dist/space.d.ts.map +1 -1
- package/dist/space.js +84 -223
- package/dist/space.js.map +1 -1
- package/dist/subscription.d.ts +2 -2
- package/dist/subscription.d.ts.map +1 -1
- package/dist/subscription.js +6 -30
- package/dist/subscription.js.map +1 -1
- package/dist/types.d.ts +58 -83
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/channel.d.ts.map +0 -1
- package/dist/channel.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,9 +4,8 @@ TypeScript SDK for Rool, a persistent collaborative workspace for objects, AI-as
|
|
|
4
4
|
|
|
5
5
|
Core primitives:
|
|
6
6
|
|
|
7
|
-
- **Spaces** — containers for objects, schema, metadata,
|
|
8
|
-
- **
|
|
9
|
-
- **Conversations** — independent interaction histories within a channel.
|
|
7
|
+
- **Spaces** — containers for objects, schema, metadata, conversations, collaborators, and files.
|
|
8
|
+
- **Conversations** — independent interaction histories in a space.
|
|
10
9
|
- **Objects** — JSON records addressed by object paths such as `/space/article/welcome.json`.
|
|
11
10
|
- **Files** — user-visible files stored under `/rool-drive/...` through WebDAV.
|
|
12
11
|
|
|
@@ -31,36 +30,36 @@ async function main() {
|
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
const space = await client.createSpace('Solar System');
|
|
34
|
-
const
|
|
33
|
+
const conversation = space.conversation('main');
|
|
35
34
|
|
|
36
|
-
await
|
|
35
|
+
await conversation.createCollection('body', [
|
|
37
36
|
{ name: 'name', type: { kind: 'string' } },
|
|
38
37
|
{ name: 'mass', type: { kind: 'string' } },
|
|
39
38
|
{ name: 'radius', type: { kind: 'string' } },
|
|
40
39
|
{ name: 'orbits', type: { kind: 'maybe', inner: { kind: 'ref' } } },
|
|
41
40
|
]);
|
|
42
41
|
|
|
43
|
-
const { object: sun } = await
|
|
42
|
+
const { object: sun } = await conversation.putObject('/space/body/sun.json', {
|
|
44
43
|
name: 'Sun',
|
|
45
44
|
mass: '1 solar mass',
|
|
46
45
|
radius: '696,340 km',
|
|
47
46
|
});
|
|
48
47
|
|
|
49
|
-
const { object: earth } = await
|
|
48
|
+
const { object: earth } = await conversation.putObject('/space/body/earth.json', {
|
|
50
49
|
name: 'Earth',
|
|
51
50
|
mass: '1 Earth mass',
|
|
52
51
|
radius: '6,371 km',
|
|
53
52
|
orbits: sun.path,
|
|
54
53
|
});
|
|
55
54
|
|
|
56
|
-
const { message, objects } = await
|
|
55
|
+
const { message, objects } = await conversation.prompt(
|
|
57
56
|
'Add the other planets in our solar system, each referencing the Sun.'
|
|
58
57
|
);
|
|
59
58
|
|
|
60
59
|
console.log(message);
|
|
61
60
|
console.log(`Modified ${objects.length} objects`);
|
|
62
61
|
|
|
63
|
-
const loadedEarth = await
|
|
62
|
+
const loadedEarth = await space.getObject(earth.path);
|
|
64
63
|
console.log(loadedEarth?.body.name);
|
|
65
64
|
|
|
66
65
|
space.close();
|
|
@@ -142,6 +141,74 @@ if (!authenticated) {
|
|
|
142
141
|
if (!authenticated) throw new Error('Login required');
|
|
143
142
|
```
|
|
144
143
|
|
|
144
|
+
### Native (Capacitor, Cordova, Tauri, ...)
|
|
145
|
+
|
|
146
|
+
Use the native PKCE provider for JS app shells that sign in through an external
|
|
147
|
+
system browser. `login()`/`signup()` open the auth server's `/authorize` page
|
|
148
|
+
via the `openExternal` callback you supply; when the deep link returns, feed it
|
|
149
|
+
to `client.handleAuthRedirect(url)` from your platform's deep-link handler. The
|
|
150
|
+
code is exchanged for a session at `/token` and tokens are stored like the
|
|
151
|
+
browser provider.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { RoolClient, NativePkceAuthProvider } from '@rool-dev/sdk';
|
|
155
|
+
import { Browser } from '@capacitor/browser';
|
|
156
|
+
import { App } from '@capacitor/app';
|
|
157
|
+
|
|
158
|
+
const client = new RoolClient({
|
|
159
|
+
authProvider: new NativePkceAuthProvider({
|
|
160
|
+
redirectUri: 'roolandroidauth://auth/callback', // must match the server allowlist
|
|
161
|
+
defaultProvider: 'google', // 'google' | 'apple'
|
|
162
|
+
openExternal: (url) => Browser.open({ url }),
|
|
163
|
+
}),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Complete sign-in when the OS hands the app its deep link.
|
|
167
|
+
App.addListener('appUrlOpen', async ({ url }) => {
|
|
168
|
+
if (await client.handleAuthRedirect(url)) {
|
|
169
|
+
await Browser.close();
|
|
170
|
+
// Now authenticated — refresh your UI.
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
if (!(await client.initialize())) {
|
|
175
|
+
// Opens the system browser; completion arrives via the listener above.
|
|
176
|
+
await client.login('My App'); // pass { provider: 'apple' } to override the default
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### Email + password and magic links (native)
|
|
181
|
+
|
|
182
|
+
The native provider also supports email/password sign-in and magic links —
|
|
183
|
+
no system browser, the server returns the token set as JSON directly.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Password sign-in
|
|
187
|
+
const result = await client.signInWithPassword(email, password);
|
|
188
|
+
if (result.status === 'signed_in') {
|
|
189
|
+
// authenticated — refresh your UI
|
|
190
|
+
} else {
|
|
191
|
+
// status === 'verify_required': the email isn't verified yet and the server
|
|
192
|
+
// has emailed a magic link. Tell the user to check their inbox.
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Or request a magic link explicitly
|
|
196
|
+
await client.requestMagicLink(email);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The magic link carries a `?verify=<token>` param; complete sign-in by passing
|
|
200
|
+
that token to `client.verify(token)` once the link lands back in the app.
|
|
201
|
+
|
|
202
|
+
> **⚠️ Magic links open the website, not the app, until Universal Links / App
|
|
203
|
+
> Links are configured.** The emailed link is an `https://` URL for the Rool
|
|
204
|
+
> web app. On native, an `https` link only re-opens your app if you've set up
|
|
205
|
+
> [iOS Universal Links](https://developer.apple.com/ios/universal-links/) /
|
|
206
|
+
> [Android App Links](https://developer.android.com/training/app-links) for that
|
|
207
|
+
> domain (custom-scheme deep links don't apply to email links). **Without that
|
|
208
|
+
> setup the magic link completes sign-in in the browser/website, not in the
|
|
209
|
+
> native app** — so for now treat magic links on native as a website hand-off,
|
|
210
|
+
> and prefer password or social sign-in for an in-app experience.
|
|
211
|
+
|
|
145
212
|
### Auth API
|
|
146
213
|
|
|
147
214
|
| Method | Description |
|
|
@@ -150,6 +217,9 @@ if (!authenticated) throw new Error('Login required');
|
|
|
150
217
|
| `login(appName, params?): Promise<void>` | Start login flow. |
|
|
151
218
|
| `signup(appName, params?): Promise<void>` | Start signup flow. |
|
|
152
219
|
| `verify(token): Promise<boolean>` | Complete email verification token flow; returns `false` when the active auth provider does not implement verification. |
|
|
220
|
+
| `handleAuthRedirect(url): Promise<boolean>` | Complete a native PKCE sign-in from a deep-link callback URL. Returns `false` when the active auth provider does not implement it. |
|
|
221
|
+
| `signInWithPassword(email, password): Promise<PasswordSignInResult>` | Email + password sign-in (native provider). Resolves `{ status: 'signed_in' }`, or `{ status: 'verify_required' }` when the email is unverified (a magic link was emailed). Rejects on bad credentials. |
|
|
222
|
+
| `requestMagicLink(email): Promise<void>` | Email the user a magic sign-in link (native provider). See the caveat below — on native the link opens the **website**, not the app, until Universal Links / App Links are configured. |
|
|
153
223
|
| `logout(): void` | Clear auth state and close open spaces. |
|
|
154
224
|
| `isAuthenticated(): Promise<boolean>` | Whether credentials are held locally. No network call — a server outage does not read as logged out. |
|
|
155
225
|
| `getAuthUser(): AuthUser` | Return auth identity decoded from the token. |
|
|
@@ -159,58 +229,57 @@ if (!authenticated) throw new Error('Login required');
|
|
|
159
229
|
|
|
160
230
|
A temporarily unreachable server never reads as "logged out". `initialize()` reports authentication from stored credentials, so on an offline start it can return `true` while `currentUser` is still `null` and user storage is empty — the SDK keeps reconnecting in the background and hydrates both automatically once the server is reachable, emitting `currentUserChanged`. Only an invalid or expired refresh token ends the session, via `authStateChanged(false)`.
|
|
161
231
|
|
|
162
|
-
## Spaces and
|
|
232
|
+
## Spaces and Conversations
|
|
163
233
|
|
|
164
|
-
Open a space to receive live events and manage collaborators, file storage, and
|
|
234
|
+
Open a space to receive live events and manage collaborators, file storage, and conversations. Open a conversation to work with objects, schema, metadata, and AI.
|
|
165
235
|
|
|
166
236
|
```typescript
|
|
167
237
|
const space = await client.openSpace('space-id');
|
|
168
238
|
|
|
169
|
-
space.
|
|
239
|
+
const conversation = space.conversation('main');
|
|
170
240
|
space.on('filesChanged', () => console.log('files changed'));
|
|
171
241
|
|
|
172
|
-
|
|
173
|
-
await channel.prompt('Summarize this space');
|
|
242
|
+
await conversation.prompt('Summarize this space');
|
|
174
243
|
```
|
|
175
244
|
|
|
176
|
-
|
|
245
|
+
Conversation IDs must be 1–32 characters and contain only letters, numbers, `_`, and `-`.
|
|
177
246
|
|
|
178
247
|
## Object Operations
|
|
179
248
|
|
|
180
249
|
Objects are JSON files under `/space`. Create the collection before writing objects in it.
|
|
181
250
|
|
|
182
251
|
```typescript
|
|
183
|
-
await
|
|
252
|
+
await conversation.createCollection('article', [
|
|
184
253
|
{ name: 'title', type: { kind: 'string' } },
|
|
185
254
|
{ name: 'status', type: { kind: 'string' } },
|
|
186
255
|
]);
|
|
187
256
|
|
|
188
257
|
// Create or replace an exact object path
|
|
189
|
-
const { object } = await
|
|
258
|
+
const { object } = await conversation.putObject('/space/article/welcome.json', {
|
|
190
259
|
title: 'Welcome',
|
|
191
260
|
status: 'draft',
|
|
192
261
|
});
|
|
193
262
|
|
|
194
263
|
// Patch fields; null or undefined deletes a field
|
|
195
|
-
await
|
|
264
|
+
await conversation.patchObject(object.path, {
|
|
196
265
|
data: { status: 'published', obsoleteField: null },
|
|
197
266
|
});
|
|
198
267
|
|
|
199
268
|
// Read one or many objects
|
|
200
|
-
await
|
|
201
|
-
await
|
|
269
|
+
await space.getObject('/space/article/welcome.json');
|
|
270
|
+
await space.getObjects([
|
|
202
271
|
'/space/article/welcome.json',
|
|
203
272
|
'/space/article/intro.json',
|
|
204
273
|
]);
|
|
205
274
|
|
|
206
275
|
// Rename or move an object
|
|
207
|
-
await
|
|
276
|
+
await conversation.moveObject(
|
|
208
277
|
'/space/article/welcome.json',
|
|
209
278
|
'/space/article/hello-world.json'
|
|
210
279
|
);
|
|
211
280
|
|
|
212
281
|
// Delete objects
|
|
213
|
-
await
|
|
282
|
+
await conversation.deleteObjects(['/space/article/hello-world.json']);
|
|
214
283
|
```
|
|
215
284
|
|
|
216
285
|
| Method | Description |
|
|
@@ -228,7 +297,7 @@ await channel.deleteObjects(['/space/article/hello-world.json']);
|
|
|
228
297
|
`prompt()` invokes the AI agent. The agent can inspect space context and, unless `readOnly` or a read-only effort is used, create/modify/move/delete objects.
|
|
229
298
|
|
|
230
299
|
```typescript
|
|
231
|
-
const { message, objects } = await
|
|
300
|
+
const { message, objects } = await conversation.prompt(
|
|
232
301
|
'Create a topic node for the solar system, then child nodes for each planet.'
|
|
233
302
|
);
|
|
234
303
|
|
|
@@ -251,12 +320,12 @@ console.log(objects.map((object) => object.path));
|
|
|
251
320
|
|
|
252
321
|
```typescript
|
|
253
322
|
// Read-only quick question
|
|
254
|
-
await
|
|
323
|
+
await conversation.prompt('What topics are covered?', {
|
|
255
324
|
effort: 'QUICK', // fast/read-only
|
|
256
325
|
});
|
|
257
326
|
|
|
258
327
|
// Focus on existing objects and files
|
|
259
|
-
await
|
|
328
|
+
await conversation.prompt('Compare these resources', {
|
|
260
329
|
attachments: [
|
|
261
330
|
'/space/article/intro.json',
|
|
262
331
|
'rool-machine:/rool-drive/docs/report.pdf',
|
|
@@ -264,12 +333,12 @@ await channel.prompt('Compare these resources', {
|
|
|
264
333
|
});
|
|
265
334
|
|
|
266
335
|
// Upload a local file as an attachment
|
|
267
|
-
await
|
|
336
|
+
await conversation.prompt('Describe this image', {
|
|
268
337
|
attachments: [fileInput.files![0]],
|
|
269
338
|
});
|
|
270
339
|
|
|
271
340
|
// Structured response
|
|
272
|
-
const { message } = await
|
|
341
|
+
const { message } = await conversation.prompt('Categorize these items', {
|
|
273
342
|
responseSchema: {
|
|
274
343
|
type: 'object',
|
|
275
344
|
properties: {
|
|
@@ -282,7 +351,7 @@ const result = JSON.parse(message);
|
|
|
282
351
|
|
|
283
352
|
// Stop a long prompt with a signal (when the caller holds the controller)
|
|
284
353
|
const ac = new AbortController();
|
|
285
|
-
const promptPromise =
|
|
354
|
+
const promptPromise = conversation.prompt('Do a deep analysis', {
|
|
286
355
|
effort: 'RESEARCH',
|
|
287
356
|
signal: ac.signal,
|
|
288
357
|
});
|
|
@@ -294,39 +363,33 @@ await promptPromise;
|
|
|
294
363
|
|
|
295
364
|
Use `signal` when the same call site cancels the prompt. When the Stop button
|
|
296
365
|
lives elsewhere — a different component, after a reload, or a prompt another
|
|
297
|
-
client started — stop by ID or
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
billed.
|
|
366
|
+
client started — stop by interaction ID or through a conversation handle. Both
|
|
367
|
+
are best-effort: the server halts the agent loop and closes the stream, but an
|
|
368
|
+
LLM turn already in flight keeps generating server-side and is billed.
|
|
301
369
|
|
|
302
370
|
```typescript
|
|
303
|
-
// Stop
|
|
304
|
-
//
|
|
305
|
-
await
|
|
306
|
-
|
|
307
|
-
// Stop a specific interaction by ID (e.g. from channel.activeLeafId or
|
|
308
|
-
// the interactions list). Returns whether the server stopped it.
|
|
309
|
-
await channel.stopInteraction(channel.activeLeafId!);
|
|
371
|
+
// Stop a specific interaction by ID (for example, from conversation.activeLeafId
|
|
372
|
+
// or the interactions list). Returns whether the server stopped it.
|
|
373
|
+
await space.stopInteraction(conversation.activeLeafId!);
|
|
310
374
|
|
|
311
375
|
// Conversation handles stop their own in-flight interaction.
|
|
312
|
-
const thread =
|
|
376
|
+
const thread = space.conversation('thread-42');
|
|
313
377
|
await thread.stop();
|
|
314
378
|
```
|
|
315
379
|
|
|
316
380
|
| Method | Description |
|
|
317
381
|
| --- | --- |
|
|
318
|
-
| `stop(): Promise<boolean>` | Stop the in-flight interaction on the default conversation; `false` if none. |
|
|
319
382
|
| `stopInteraction(id): Promise<boolean>` | Ask the server to stop a specific interaction by ID. |
|
|
320
383
|
| `conversation.stop(): Promise<boolean>` | Stop a specific conversation's in-flight interaction. |
|
|
321
384
|
|
|
322
385
|
## Conversations
|
|
323
386
|
|
|
324
|
-
|
|
387
|
+
Use `space.conversation(id)` for independent histories (for example, multiple chat threads). Conversations are represented as trees: interactions point at a `parentId`, and the SDK tracks an active leaf for each conversation.
|
|
325
388
|
|
|
326
389
|
```typescript
|
|
327
|
-
await
|
|
390
|
+
await conversation.prompt('Hello');
|
|
328
391
|
|
|
329
|
-
const thread =
|
|
392
|
+
const thread = space.conversation('thread-42');
|
|
330
393
|
await thread.prompt('Hello from another thread');
|
|
331
394
|
await thread.setSystemInstruction('Answer in haiku');
|
|
332
395
|
|
|
@@ -340,15 +403,14 @@ if (thread.activeLeafId) {
|
|
|
340
403
|
|
|
341
404
|
| Method/property | Description |
|
|
342
405
|
| --- | --- |
|
|
343
|
-
| `
|
|
406
|
+
| `space.conversation(id): ConversationHandle` | Get a conversation-scoped handle. |
|
|
344
407
|
| `getInteractions(): Interaction[]` | Active branch as a flat list. |
|
|
345
408
|
| `getTree(): Record<string, Interaction>` | Full interaction tree. |
|
|
346
409
|
| `activeLeafId` | Current branch tip. |
|
|
347
410
|
| `setActiveLeaf(id): void` | Switch branches. |
|
|
348
411
|
| `getSystemInstruction()` / `setSystemInstruction(value)` | Manage conversation system instruction. Pass `null` to clear. |
|
|
349
|
-
| `getConversations(): ConversationInfo[]` | List
|
|
412
|
+
| `getConversations(): ConversationInfo[]` | List conversations in the space. |
|
|
350
413
|
| `deleteConversation(id): Promise<void>` | Delete a non-active conversation. |
|
|
351
|
-
| `renameConversation(name): Promise<void>` | Rename the current/default conversation (on `RoolChannel`). |
|
|
352
414
|
| `conversation.rename(name): Promise<void>` | Rename a specific conversation handle. |
|
|
353
415
|
|
|
354
416
|
`ConversationHandle` also supports conversation-scoped `putObject`, `patchObject`, `moveObject`, `deleteObjects`, `prompt`, `stop`, collection-schema methods, and `setMetadata`.
|
|
@@ -358,7 +420,7 @@ if (thread.activeLeafId) {
|
|
|
358
420
|
Collections define the schema visible to the AI agent. Hidden body fields whose names start with `_` are useful for app/UI state that should not be considered by AI.
|
|
359
421
|
|
|
360
422
|
```typescript
|
|
361
|
-
await
|
|
423
|
+
await conversation.createCollection('article', {
|
|
362
424
|
schemaOrgType: 'Article',
|
|
363
425
|
fields: [
|
|
364
426
|
{ name: 'title', type: { kind: 'string' } },
|
|
@@ -368,15 +430,15 @@ await channel.createCollection('article', {
|
|
|
368
430
|
],
|
|
369
431
|
});
|
|
370
432
|
|
|
371
|
-
const schema =
|
|
433
|
+
const schema = space.getSchema();
|
|
372
434
|
|
|
373
|
-
await
|
|
435
|
+
await conversation.alterCollection('article', [
|
|
374
436
|
{ name: 'title', type: { kind: 'string' } },
|
|
375
437
|
{ name: 'status', type: { kind: 'string' } },
|
|
376
438
|
]);
|
|
377
439
|
|
|
378
|
-
|
|
379
|
-
const viewport =
|
|
440
|
+
conversation.setMetadata('viewport', { x: 0, y: 0, zoom: 1 });
|
|
441
|
+
const viewport = space.getMetadata('viewport');
|
|
380
442
|
```
|
|
381
443
|
|
|
382
444
|
| Method | Description |
|
|
@@ -393,14 +455,14 @@ Field kinds: `string`, `number`, `boolean`, `ref`, `enum`, `literal`, `array`, a
|
|
|
393
455
|
|
|
394
456
|
## Undo/Redo
|
|
395
457
|
|
|
396
|
-
Undo/redo uses checkpoints
|
|
458
|
+
Undo/redo uses checkpoints over the whole space. A checkpoint captures space state; call `checkpoint()` before a user action you want to make undoable.
|
|
397
459
|
|
|
398
460
|
```typescript
|
|
399
|
-
await
|
|
400
|
-
await
|
|
461
|
+
await space.checkpoint('Delete article');
|
|
462
|
+
await conversation.deleteObjects(['/space/article/welcome.json']);
|
|
401
463
|
|
|
402
|
-
if (await
|
|
403
|
-
await
|
|
464
|
+
if (await space.canUndo()) {
|
|
465
|
+
await space.undo();
|
|
404
466
|
}
|
|
405
467
|
```
|
|
406
468
|
|
|
@@ -413,7 +475,7 @@ if (await channel.canUndo()) {
|
|
|
413
475
|
| `redo(): Promise<boolean>` | Reapply undone work. |
|
|
414
476
|
| `clearHistory(): Promise<void>` | Clear checkpoint history. |
|
|
415
477
|
|
|
416
|
-
Undo/redo availability and history are scoped to the
|
|
478
|
+
Undo/redo availability and history are scoped to the space.
|
|
417
479
|
|
|
418
480
|
## File Storage and WebDAV
|
|
419
481
|
|
|
@@ -579,7 +641,7 @@ const client = new RoolClient({
|
|
|
579
641
|
| `getAllUserStorage(): Record<string, unknown>` | Copy all cached user storage. |
|
|
580
642
|
| `reportEvent(event, url?): void` | Fire-and-forget telemetry event. |
|
|
581
643
|
| `destroy(): void` | Close subscriptions, spaces, auth resources, and listeners. |
|
|
582
|
-
| `generateId(): string` | Generate a
|
|
644
|
+
| `generateId(): string` | Generate a unique ID suitable for conversation IDs. |
|
|
583
645
|
|
|
584
646
|
### Client events
|
|
585
647
|
|
|
@@ -589,9 +651,6 @@ client.on('currentUserChanged', (user) => void 0); // CurrentUser | null; null o
|
|
|
589
651
|
client.on('spaceAdded', (space) => void 0);
|
|
590
652
|
client.on('spaceRemoved', (spaceId) => void 0);
|
|
591
653
|
client.on('spaceRenamed', (spaceId, newName) => void 0);
|
|
592
|
-
client.on('channelCreated', (spaceId, channel) => void 0);
|
|
593
|
-
client.on('channelUpdated', (spaceId, channel) => void 0);
|
|
594
|
-
client.on('channelDeleted', (spaceId, channelId) => void 0);
|
|
595
654
|
client.on('userStorageChanged', ({ key, value, source }) => void 0);
|
|
596
655
|
client.on('connectionStateChanged', (state) => void 0);
|
|
597
656
|
client.on('error', (error, context) => void 0);
|
|
@@ -599,12 +658,18 @@ client.on('error', (error, context) => void 0);
|
|
|
599
658
|
|
|
600
659
|
## RoolSpace API
|
|
601
660
|
|
|
602
|
-
Properties: `id`, `name`, `role`, `memberCount`, `
|
|
661
|
+
Properties: `id`, `name`, `role`, `memberCount`, `conversations`, `route`, `webdav`.
|
|
603
662
|
|
|
604
663
|
| Method | Description |
|
|
605
664
|
| --- | --- |
|
|
606
|
-
| `
|
|
607
|
-
| `
|
|
665
|
+
| `conversation(conversationId): ConversationHandle` | Get a conversation-scoped handle. |
|
|
666
|
+
| `getObject`, `getObjects`, `stat` | Read object data and stats. |
|
|
667
|
+
| `getConversations`, `deleteConversation` | List and delete conversations. |
|
|
668
|
+
| `getMetadata`, `getAllMetadata`, `getSchema` | Read metadata and schema. |
|
|
669
|
+
| `checkpoint`, `canUndo`, `canRedo`, `undo`, `redo`, `clearHistory` | Space history controls. |
|
|
670
|
+
| `stopInteraction(interactionId): Promise<boolean>` | Stop an in-flight interaction by ID. |
|
|
671
|
+
| `fetch(url, init?): Promise<Response>` | Proxy an external HTTP request through the server to bypass browser CORS. |
|
|
672
|
+
| `close(): void` | Stop the space subscription. |
|
|
608
673
|
| `rename(newName): Promise<void>` | Rename the space. |
|
|
609
674
|
| `delete(): Promise<void>` | Permanently delete the space. |
|
|
610
675
|
| `listUsers(): Promise<SpaceMember[]>` | List collaborators. |
|
|
@@ -613,8 +678,6 @@ Properties: `id`, `name`, `role`, `memberCount`, `channels`, `route`, `webdav`.
|
|
|
613
678
|
| `createInvite(role, options?): Promise<SpaceInviteCreated>` | Mint an invite link; `options` takes `email`, `expiresInDays`, `maxUses`. |
|
|
614
679
|
| `listInvites(): Promise<SpaceInvite[]>` | List currently redeemable invites. |
|
|
615
680
|
| `revokeInvite(inviteId): Promise<boolean>` | Revoke an invite so its link stops working. |
|
|
616
|
-
| `renameChannel(channelId, name): Promise<void>` | Rename a channel. |
|
|
617
|
-
| `deleteChannel(channelId): Promise<void>` | Delete a channel and history. |
|
|
618
681
|
| `exportArchive(): Promise<Blob>` | Export a space archive. |
|
|
619
682
|
| `refresh(): Promise<void>` | Refresh cached space data. |
|
|
620
683
|
| `fetchPath(path, options?): Promise<Response>` | Fetch a `/rool-drive/...` file. |
|
|
@@ -623,42 +686,15 @@ Properties: `id`, `name`, `role`, `memberCount`, `channels`, `route`, `webdav`.
|
|
|
623
686
|
Events:
|
|
624
687
|
|
|
625
688
|
```typescript
|
|
626
|
-
space.on('
|
|
627
|
-
space.on('
|
|
628
|
-
space.on('
|
|
689
|
+
space.on('metadataUpdated', ({ metadata, source }) => void 0);
|
|
690
|
+
space.on('schemaUpdated', ({ schema, source }) => void 0);
|
|
691
|
+
space.on('conversationUpdated', ({ conversationId, source }) => void 0);
|
|
692
|
+
space.on('reset', ({ source }) => void 0);
|
|
693
|
+
space.on('syncError', (error) => void 0);
|
|
629
694
|
space.on('filesChanged', ({ spaceId, source, timestamp }) => void 0);
|
|
630
695
|
space.on('filesReset', ({ spaceId, source, timestamp }) => void 0);
|
|
631
696
|
space.on('connectionStateChanged', (state) => void 0);
|
|
632
697
|
```
|
|
633
|
-
|
|
634
|
-
## RoolChannel API
|
|
635
|
-
|
|
636
|
-
Properties: `id` (space ID), `name` (space name), `role`, `userId`, `channelId`, `channelName`, `conversationId`, `isReadOnly`, `activeLeafId`.
|
|
637
|
-
|
|
638
|
-
| Area | Methods |
|
|
639
|
-
| --- | --- |
|
|
640
|
-
| Lifecycle | `close()`, `rename(name)`, `conversation(id)` |
|
|
641
|
-
| Objects | `getObject`, `getObjects`, `stat`, `putObject`, `patchObject`, `moveObject`, `deleteObjects` |
|
|
642
|
-
| Schema | `getSchema`, `createCollection`, `alterCollection`, `dropCollection` |
|
|
643
|
-
| Metadata | `setMetadata`, `getMetadata`, `getAllMetadata` |
|
|
644
|
-
| Conversations | `getInteractions`, `getTree`, `setActiveLeaf`, `getConversations`, `deleteConversation`, `getSystemInstruction`, `setSystemInstruction`, `renameConversation` |
|
|
645
|
-
| AI | `prompt`, `stop`, `stopInteraction` |
|
|
646
|
-
| Undo/redo | `checkpoint`, `canUndo`, `canRedo`, `undo`, `redo`, `clearHistory` |
|
|
647
|
-
| Utilities | `fetch(url, init?)` server-side proxied fetch |
|
|
648
|
-
|
|
649
|
-
Channel events:
|
|
650
|
-
|
|
651
|
-
```typescript
|
|
652
|
-
channel.on('metadataUpdated', ({ metadata, source }) => void 0);
|
|
653
|
-
channel.on('schemaUpdated', ({ schema, source }) => void 0);
|
|
654
|
-
channel.on('channelUpdated', ({ channelId, source }) => void 0);
|
|
655
|
-
channel.on('conversationUpdated', ({ conversationId, channelId, source }) => void 0);
|
|
656
|
-
channel.on('reset', ({ source }) => void 0);
|
|
657
|
-
channel.on('syncError', (error) => void 0);
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
`channel.fetch(url, init?)` proxies external HTTP requests through the server to bypass browser CORS.
|
|
661
|
-
|
|
662
698
|
## Import/Export
|
|
663
699
|
|
|
664
700
|
```typescript
|
|
@@ -666,11 +702,15 @@ const archive = await space.exportArchive();
|
|
|
666
702
|
const imported = await client.importArchive('Imported Data', archive);
|
|
667
703
|
```
|
|
668
704
|
|
|
669
|
-
Archives include objects, metadata,
|
|
705
|
+
Archives include objects, metadata, conversations, and file storage.
|
|
670
706
|
|
|
671
707
|
## Data Types
|
|
672
708
|
|
|
673
709
|
```typescript
|
|
710
|
+
// Outcome of signInWithPassword. 'verify_required' means the account's email
|
|
711
|
+
// isn't verified yet and the server has emailed a magic link.
|
|
712
|
+
type PasswordSignInResult = { status: 'signed_in' | 'verify_required' };
|
|
713
|
+
|
|
674
714
|
type FieldType =
|
|
675
715
|
| { kind: 'string' }
|
|
676
716
|
| { kind: 'number' }
|
|
@@ -717,10 +757,22 @@ interface SpaceInvite {
|
|
|
717
757
|
useCount: number;
|
|
718
758
|
}
|
|
719
759
|
|
|
720
|
-
// Outcome of the invite email send
|
|
721
|
-
//
|
|
722
|
-
//
|
|
723
|
-
|
|
760
|
+
// Outcome of the invite email send. Null when no email was involved (open link).
|
|
761
|
+
// The invite is always minted and its `url` is usable regardless of this value;
|
|
762
|
+
// only the email delivery is reflected here.
|
|
763
|
+
// - 'sent': email dispatched
|
|
764
|
+
// - 'not_configured': server has no mail provider (local dev)
|
|
765
|
+
// - 'failed': provider rejected the send
|
|
766
|
+
// - 'cooldown': a recent invite to this same address was already emailed
|
|
767
|
+
// - 'rate_limited': the inviter hit their daily email-invite cap
|
|
768
|
+
// Treat unknown values as not sent.
|
|
769
|
+
type InviteEmailStatus =
|
|
770
|
+
| 'sent'
|
|
771
|
+
| 'not_configured'
|
|
772
|
+
| 'failed'
|
|
773
|
+
| 'cooldown'
|
|
774
|
+
| 'rate_limited'
|
|
775
|
+
| (string & {});
|
|
724
776
|
|
|
725
777
|
interface SpaceInviteCreated {
|
|
726
778
|
inviteId: string;
|
|
@@ -752,7 +804,6 @@ interface RoolObjectStat {
|
|
|
752
804
|
modifiedAt: number;
|
|
753
805
|
modifiedBy: string;
|
|
754
806
|
modifiedByName: string | null;
|
|
755
|
-
modifiedInChannel: string;
|
|
756
807
|
modifiedInConversation: string | null;
|
|
757
808
|
modifiedInInteraction: string | null;
|
|
758
809
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { AuthProvider, AuthUser } from './types.js';
|
|
2
|
+
import { type Logger } from './logger.js';
|
|
3
|
+
export interface BaseTokenAuthConfig {
|
|
4
|
+
/** Auth URL (e.g. https://rool.dev/auth). Injected by RoolClient if omitted. */
|
|
5
|
+
authUrl?: string;
|
|
6
|
+
/** Injected by RoolClient if omitted. */
|
|
7
|
+
logger?: Logger;
|
|
8
|
+
/** Injected by RoolClient if omitted, so auth-state reaches client events. */
|
|
9
|
+
onAuthStateChanged?: (authenticated: boolean) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare abstract class BaseTokenAuthProvider implements AuthProvider {
|
|
12
|
+
protected logger: Logger;
|
|
13
|
+
private _authUrl;
|
|
14
|
+
private _onAuthStateChanged;
|
|
15
|
+
private refreshPromise;
|
|
16
|
+
private refreshTimeoutId;
|
|
17
|
+
private boundVisibilityHandler;
|
|
18
|
+
constructor(config?: BaseTokenAuthConfig);
|
|
19
|
+
setAuthUrl(url: string): void;
|
|
20
|
+
setLogger(logger: Logger): void;
|
|
21
|
+
setAuthStateChangedHandler(handler: (authenticated: boolean) => void): void;
|
|
22
|
+
abstract initialize(): boolean;
|
|
23
|
+
abstract login(appName: string, params?: Record<string, string>): Promise<void> | void;
|
|
24
|
+
abstract signup(appName: string, params?: Record<string, string>): Promise<void> | void;
|
|
25
|
+
/**
|
|
26
|
+
* Check if user is currently authenticated.
|
|
27
|
+
*
|
|
28
|
+
* This reports identity (do we hold credentials), NOT server reachability.
|
|
29
|
+
* It deliberately does not perform a network refresh: a backend outage must
|
|
30
|
+
* not read as "logged out". A genuinely invalid/expired refresh token
|
|
31
|
+
* surfaces later as a 401 on first real use, which clears tokens and fires
|
|
32
|
+
* onAuthStateChanged(false) — the only path that ends the session.
|
|
33
|
+
*/
|
|
34
|
+
isAuthenticated(): Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Get current access token and rool token, refreshing if expired.
|
|
37
|
+
* Returns undefined if not authenticated.
|
|
38
|
+
*/
|
|
39
|
+
getTokens(): Promise<{
|
|
40
|
+
accessToken: string;
|
|
41
|
+
roolToken: string;
|
|
42
|
+
} | undefined>;
|
|
43
|
+
/**
|
|
44
|
+
* Get auth identity decoded from JWT token.
|
|
45
|
+
*/
|
|
46
|
+
getAuthUser(): AuthUser;
|
|
47
|
+
/**
|
|
48
|
+
* Complete an email verification flow. Exchanges a verify JWT (from the
|
|
49
|
+
* verification email link) for a fresh token set and signs the user in.
|
|
50
|
+
*/
|
|
51
|
+
verify(token: string): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* Logout - clear all tokens and state.
|
|
54
|
+
*/
|
|
55
|
+
logout(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Destroy auth manager - clear refresh timers and listeners.
|
|
58
|
+
*/
|
|
59
|
+
destroy(): void;
|
|
60
|
+
/** Auth URL without trailing slash */
|
|
61
|
+
protected get authBaseUrl(): string;
|
|
62
|
+
protected notifyAuthState(authenticated: boolean): void;
|
|
63
|
+
/** Persist a fresh token set and (re)arm the background refresh timer. */
|
|
64
|
+
protected acceptTokens(accessToken: string, refreshToken: string | null, roolToken: string | null, expiresAt: number): void;
|
|
65
|
+
/** Arm auto-refresh + visibility re-scheduling. Call from initialize(). */
|
|
66
|
+
protected initBase(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Overridable hook: clear subclass-owned transient state on logout/cancel
|
|
69
|
+
* (e.g. an in-flight OAuth `state` or PKCE verifier). No-op by default.
|
|
70
|
+
*/
|
|
71
|
+
protected clearTransientState(): void;
|
|
72
|
+
protected get storagePrefix(): string;
|
|
73
|
+
/** Build a storage key scoped to this auth endpoint. */
|
|
74
|
+
protected keyFor(name: string): string;
|
|
75
|
+
protected get storageKeys(): {
|
|
76
|
+
readonly access: `${string}access_token`;
|
|
77
|
+
readonly refresh: `${string}refresh_token`;
|
|
78
|
+
readonly rool: `${string}rool_token`;
|
|
79
|
+
readonly expiresAt: `${string}token_expires_at`;
|
|
80
|
+
};
|
|
81
|
+
protected readString(key: string): string | null;
|
|
82
|
+
protected writeString(key: string, value: string): void;
|
|
83
|
+
protected removeString(key: string): void;
|
|
84
|
+
protected scheduleTokenRefresh(): void;
|
|
85
|
+
protected writeTokens(accessToken: string | null, refreshToken: string | null, expiresAt: number | null): void;
|
|
86
|
+
protected writeRoolToken(token: string | null): void;
|
|
87
|
+
protected clearTokens(): void;
|
|
88
|
+
protected readAccessToken(): string | null;
|
|
89
|
+
protected readRoolToken(): string;
|
|
90
|
+
protected readExpiresAt(): number | null;
|
|
91
|
+
/**
|
|
92
|
+
* Get a short hash of the auth URL for scoping storage by endpoint.
|
|
93
|
+
*/
|
|
94
|
+
private get endpointHash();
|
|
95
|
+
private tryRefreshToken;
|
|
96
|
+
private listenForVisibility;
|
|
97
|
+
private cancelScheduledRefresh;
|
|
98
|
+
private decodeAuthUser;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=auth-base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-base.d.ts","sourceRoot":"","sources":["../src/auth-base.ts"],"names":[],"mappings":"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"}
|