@rool-dev/client 0.3.0-dev.f9b91b8 → 0.3.1-dev.34258b6

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 CHANGED
@@ -13,11 +13,11 @@ npm install @rool-dev/client
13
13
  ```typescript
14
14
  import { RoolClient } from '@rool-dev/client';
15
15
 
16
- const client = new RoolClient({ baseUrl: 'https://use.rool.dev/rool-server' });
17
- client.initialize();
16
+ const client = new RoolClient({ baseUrl: 'https://use.rool.dev' });
17
+ client.initialize(); // Process auth callback if returning from login
18
18
 
19
19
  if (!client.isAuthenticated()) {
20
- client.login(); // Redirects to auth page
20
+ client.login(); // Redirects to auth page
21
21
  }
22
22
 
23
23
  // Subscribe to real-time events
@@ -27,16 +27,16 @@ await client.subscribe();
27
27
  const graph = await client.openGraph('my-graph-id');
28
28
  graph.subscribe(); // Listen for real-time updates
29
29
 
30
- // Add a node
31
- graph.checkpoint('Add node');
32
- graph.addNode('node1', { type: 'text', content: 'Hello' });
30
+ // Create an object
31
+ graph.checkpoint('Add object');
32
+ const { id } = await graph.createObject({ type: 'text', fields: { content: 'Hello' } });
33
33
 
34
- // Link nodes
35
- graph.linkNodes('node1', 'node2', 'connection');
34
+ // Create a link between objects
35
+ await graph.link(id, 'object2', 'connection');
36
36
 
37
37
  // Undo/redo support
38
- graph.undo();
39
- graph.redo();
38
+ await graph.undo();
39
+ await graph.redo();
40
40
 
41
41
  // Clean up
42
42
  graph.close();
@@ -46,18 +46,64 @@ graph.close();
46
46
 
47
47
  ```typescript
48
48
  interface RoolClientConfig {
49
- baseUrl: string; // Server URL (see below)
50
- storagePrefix?: string; // localStorage prefix (default: 'rool_')
51
- authProvider?: AuthProvider; // Custom auth (e.g., for Electron)
49
+ baseUrl: string; // Base URL (e.g., 'https://use.rool.dev')
50
+ graphqlUrl?: string; // Override GraphQL endpoint (default: {baseUrl}/graphql)
51
+ mediaUrl?: string; // Override media endpoint (default: {baseUrl}/media)
52
+ authUrl?: string; // Override auth endpoint (default: {baseUrl}/auth)
53
+ authProvider?: AuthProvider; // Optional, defaults to browser auth
52
54
  }
53
55
  ```
54
56
 
55
- ### Server URLs
57
+ ### Base URLs
56
58
 
57
59
  | Environment | URL |
58
60
  |-------------|-----|
59
- | Development | `https://use.rool.dev/rool-server` |
60
- | Production | `https://use.rool.app/rool-server` |
61
+ | Development | `https://use.rool.dev` |
62
+ | Production | `https://use.rool.app` |
63
+
64
+ ### Auth Providers
65
+
66
+ **Browser (default)** — No configuration needed. Uses localStorage for tokens, redirects to login page.
67
+
68
+ ```typescript
69
+ const client = new RoolClient({ baseUrl: 'https://use.rool.dev' });
70
+ client.initialize(); // Process auth callbacks if this is a callback from the auth page
71
+
72
+ if (!client.isAuthenticated()) {
73
+ client.login(); // Redirect to the auth page
74
+ }
75
+ ```
76
+
77
+ **Node.js** — For CLI tools and scripts. Stores credentials in `~/.config/rool/`, opens browser for login.
78
+
79
+ ```typescript
80
+ import { NodeAuthProvider } from '@rool-dev/client/node';
81
+
82
+ const client = new RoolClient({
83
+ baseUrl: 'https://use.rool.dev',
84
+ authProvider: new NodeAuthProvider()
85
+ });
86
+
87
+ if (!client.isAuthenticated()) {
88
+ await client.login(); // Open auth page in system browser, await callback
89
+ }
90
+ ```
91
+
92
+ **Custom** — Implement the `AuthProvider` interface for full control.
93
+
94
+ ```typescript
95
+ const client = new RoolClient({
96
+ baseUrl: 'https://use.rool.dev',
97
+ authProvider: {
98
+ initialize: () => false,
99
+ getToken: async () => myStore.getAccessToken(),
100
+ getUser: () => ({ email: 'user@example.com', name: 'User' }),
101
+ isAuthenticated: () => myStore.hasValidToken(),
102
+ login: async () => { /* your login flow */ },
103
+ logout: () => myStore.clear(),
104
+ }
105
+ });
106
+ ```
61
107
 
62
108
 
63
109
  ---
@@ -68,7 +114,7 @@ interface RoolClientConfig {
68
114
 
69
115
  | Method | Description |
70
116
  |--------|-------------|
71
- | `initialize(): boolean` | **Call on app startup.** Processes auth callback from URL, sets up token refresh. |
117
+ | `initialize(): boolean` | **Call on app startup if running in browser.** Processes auth callback from URL, sets up token refresh. |
72
118
  | `login(): void` | Redirect to login page |
73
119
  | `logout(): void` | Clear tokens and state |
74
120
  | `isAuthenticated(): boolean` | Check auth status |
@@ -155,34 +201,70 @@ Graphs are first-class objects with built-in undo/redo, event emission, and real
155
201
  | `redo(): Promise<boolean>` | Redo undone action |
156
202
  | `clearHistory(): void` | Clear undo/redo stack |
157
203
 
158
- ### Node Operations
204
+ ### Object Operations
159
205
 
160
206
  | Method | Description |
161
207
  |--------|-------------|
162
- | `getNode(nodeId): RoolNode` | Get node (throws if not found) |
163
- | `getNodeOrUndefined(nodeId): RoolNode \| undefined` | Get node or undefined |
164
- | `getNodesByType(type): RoolNode[]` | Get all nodes of type |
165
- | `getNodeIds(): string[]` | Get all node IDs |
166
- | `addNode(nodeId, node): void` | Add a node |
167
- | `updateNode(nodeId, updates): void` | Update node fields |
168
- | `deleteNodes(nodeIds): void` | Delete nodes (and connected edges) |
208
+ | `getObject(objectId): RoolObject` | Get object data. Throws if not found. |
209
+ | `getObjectOrUndefined(objectId): RoolObject \| undefined` | Get object data, or undefined if not found. |
210
+ | `getObjectsByType(type): RoolObject[]` | Get all objects matching the given type. |
211
+ | `getObjectIds(): string[]` | Get all object IDs in the graph. |
212
+ | `getObjectMeta(objectId): Record<string, unknown>` | Get object metadata (position, UI state, etc.) Hidden from AI. |
213
+ | `createObject(options): Promise<{ id, message }>` | Create a new object. Returns the generated ID and AI message. |
214
+ | `updateObject(objectId, options): Promise<string>` | Update an existing object. Returns AI message. |
215
+ | `deleteObjects(objectIds): Promise<void>` | Delete objects. Outbound links are removed automatically. |
216
+
217
+ #### createObject / updateObject Options
218
+
219
+ Both methods accept an options object:
220
+
221
+ | Option | Description |
222
+ |--------|-------------|
223
+ | `type` | Object type (required for create, optional for update). |
224
+ | `fields` | Key-value pairs for object data. Use `{{placeholder}}` for AI-generated content. |
225
+ | `meta` | Client-private metadata (e.g., position). Hidden from AI operations. |
226
+ | `prompt` | Natural language instruction for AI to generate or modify content. |
227
+
228
+ **AI Placeholder Pattern**: Use `{{description}}` in field values to have AI generate content:
229
+
230
+ ```typescript
231
+ // Create with AI-generated content
232
+ await graph.createObject({
233
+ type: 'article',
234
+ fields: {
235
+ headline: '{{catchy headline about coffee}}',
236
+ body: '{{informative paragraph}}'
237
+ },
238
+ prompt: 'Write about specialty coffee brewing'
239
+ });
169
240
 
170
- ### Edge Operations
241
+ // Update existing content with AI
242
+ await graph.updateObject('abc123', {
243
+ prompt: 'Make the body shorter and more casual'
244
+ });
245
+
246
+ // Add new AI-generated field to existing object
247
+ await graph.updateObject('abc123', {
248
+ fields: { summary: '{{one-sentence summary}}' }
249
+ });
250
+ ```
251
+
252
+ ### Link Operations
253
+
254
+ Links connect objects directionally. They are stored on the source object and identified by `sourceId + linkType + targetId`.
171
255
 
172
256
  | Method | Description |
173
257
  |--------|-------------|
174
- | `linkNodes(sourceId, targetId, edgeType): string` | Create edge, returns edge ID |
175
- | `unlinkNodes(sourceId, targetId): boolean` | Remove edges between nodes |
176
- | `getParents(nodeId, edgeType?): string[]` | Get nodes pointing to this node |
177
- | `getChildren(nodeId, edgeType?): string[]` | Get nodes this node points to |
178
- | `getEdge(edgeId): RoolEdge \| undefined` | Get edge by ID |
179
- | `getEdgeIds(): string[]` | Get all edge IDs |
258
+ | `link(sourceId, targetId, linkType): Promise<void>` | Create a link from source to target with the given type. |
259
+ | `unlink(sourceId, targetId, linkType?): Promise<boolean>` | Remove link(s). If `linkType` provided, removes only that type; otherwise removes all links between the objects. |
260
+ | `getParents(objectId, linkType?): string[]` | Get IDs of objects that link TO this object. |
261
+ | `getChildren(objectId, linkType?): string[]` | Get IDs of objects this object links TO. |
180
262
 
181
263
  ### Metadata
182
264
 
183
265
  | Method | Description |
184
266
  |--------|-------------|
185
- | `setMetadata(key, value): void` | Set metadata (hidden from AI) |
267
+ | `setMetadata(key, value): void` | Set graph-level metadata (hidden from AI) |
186
268
  | `getMetadata(key): unknown` | Get metadata value |
187
269
  | `getAllMetadata(): Record<string, unknown>` | Get all metadata |
188
270
 
@@ -191,8 +273,32 @@ Graphs are first-class objects with built-in undo/redo, event emission, and real
191
273
  | Method | Description |
192
274
  |--------|-------------|
193
275
  | `prompt(prompt, options?): Promise<string \| null>` | AI graph manipulation |
194
- | `generateImage(prompt, aspectRatio?): Promise<ImageResult>` | Generate AI image |
195
- | `editImage(prompt, url): Promise<ImageResult>` | Edit image with AI |
276
+
277
+ The `prompt` method lets you invoke the AI agent to manipulate the graph, perform research, or query information.
278
+
279
+ **Capabilities:**
280
+ - **Creation**: "Create 3 markdown nodes with haikus about nature"
281
+ - **Modification**: "Make the selected node's text more formal"
282
+ - **Research**: "Create a knowledge graph about the exoplanets orbiting PSR B1257+12" (Agent will search Wikipedia)
283
+ - **Structure**: "Connect the topic node to all created child nodes"
284
+
285
+ **Options:**
286
+ - `objectIds`: Limit context to specific objects ("Update *these* objects")
287
+ - `responseSchema`: Request a structured JSON response instead of a text summary
288
+
289
+ ```typescript
290
+ // Example: Research and expansion
291
+ await client.prompt(
292
+ "Create a topic node for the solar system, then child nodes for each planet.",
293
+ { responseSchema: null } // Returns a text summary
294
+ );
295
+
296
+ // Example: Focused update
297
+ await client.prompt(
298
+ "Summarize this article in the 'summary' field",
299
+ { objectIds: ['article-node-id'] }
300
+ );
301
+ ```
196
302
 
197
303
  ### Collaboration
198
304
 
@@ -216,15 +322,30 @@ Graphs are first-class objects with built-in undo/redo, event emission, and real
216
322
 
217
323
  | Method | Description |
218
324
  |--------|-------------|
219
- | `getData(): RoolGraphData` | Get full graph data |
220
- | `patch(operations): void` | Apply JSON patch |
325
+ | `getData(): RoolGraphData` | Get full graph data (internal format) |
221
326
 
222
327
  ### Graph Events
223
328
 
329
+ Semantic events describe what changed. Events fire for both local changes and remote changes.
330
+
224
331
  ```typescript
225
- // Incremental change - ops is the raw JSON patch
226
- // source indicates origin: 'local' | 'remote_user' | 'remote_agent' | 'system'
227
- graph.on('patch', ({ ops, source }) => void)
332
+ // source indicates origin:
333
+ // - 'local_user': This client made the change
334
+ // - 'remote_user': Another user/client made the change
335
+ // - 'remote_agent': AI agent made the change
336
+ // - 'system': Resync after error
337
+
338
+ // Object events
339
+ graph.on('objectCreated', ({ objectId, object, source }) => void)
340
+ graph.on('objectUpdated', ({ objectId, object, source }) => void)
341
+ graph.on('objectDeleted', ({ objectId, source }) => void)
342
+
343
+ // Link events
344
+ graph.on('linked', ({ sourceId, targetId, linkType, source }) => void)
345
+ graph.on('unlinked', ({ sourceId, targetId, linkType, source }) => void)
346
+
347
+ // Graph metadata
348
+ graph.on('metaUpdated', ({ meta, source }) => void)
228
349
 
229
350
  // Full state replacement (undo/redo, resync after error)
230
351
  graph.on('reset', ({ source }) => void)
@@ -233,9 +354,19 @@ graph.on('reset', ({ source }) => void)
233
354
  graph.on('syncError', (error: Error) => void)
234
355
  ```
235
356
 
236
- **Usage patterns:**
237
- - **Simple (full re-render)**: Listen to both `patch` and `reset`, call `graph.getData()` and re-render
238
- - **Power user (incremental)**: Parse `ops` in `patch` for granular updates, full re-render on `reset`
357
+ **Usage pattern:**
358
+ ```typescript
359
+ // All UI updates happen in one place, regardless of change source
360
+ graph.on('objectUpdated', ({ objectId, object, source }) => {
361
+ renderObject(objectId, object);
362
+ if (source === 'remote_agent') {
363
+ doLayout(); // AI might have added content
364
+ }
365
+ });
366
+
367
+ // Caller just makes the change - event handler does the UI work
368
+ graph.updateObject(objectId, { prompt: 'expand this' });
369
+ ```
239
370
 
240
371
  ---
241
372
 
@@ -244,24 +375,23 @@ graph.on('syncError', (error: Error) => void)
244
375
  ### Graph Data
245
376
 
246
377
  ```typescript
247
- interface RoolGraphData {
248
- nodes: Record<string, RoolNode>;
249
- edges: Record<string, RoolEdge>;
250
- _meta: Record<string, unknown>; // Hidden from AI
251
- }
252
-
253
- interface RoolNode {
378
+ // RoolObject represents the object data you work with
379
+ interface RoolObject {
254
380
  type: string;
255
- _meta: Record<string, unknown>;
256
381
  [key: string]: unknown;
257
382
  }
258
383
 
259
- interface RoolEdge {
260
- sources: string[];
261
- targets: string[];
262
- type: string;
263
- _meta: Record<string, unknown>;
264
- [key: string]: unknown;
384
+ // Internal graph data structure
385
+ interface RoolGraphData {
386
+ objects: Record<string, ObjectEntry>;
387
+ meta: Record<string, unknown>; // Graph-level metadata, hidden from AI
388
+ }
389
+
390
+ // Internal: Full stored object structure (you typically don't use this directly)
391
+ interface ObjectEntry {
392
+ meta: Record<string, unknown>; // Object-level metadata
393
+ links: Record<string, Record<string, Record<string, unknown>>>; // linkType -> targetId -> linkData
394
+ data: RoolObject; // The actual object data
265
395
  }
266
396
  ```
267
397
 
@@ -275,43 +405,20 @@ interface RoolGraphUser { id: string; email: string; role: RoolGraphRole; }
275
405
  interface UserResult { id: string; email: string; }
276
406
  interface Account { id: string; email: string; plan: string; creditsBalance: number; }
277
407
  interface MediaItem { uuid: string; url: string; contentType: string; size: number; }
278
- interface ImageResult { url: string; }
279
- type ImageAspectRatio = '1:1' | '3:4' | '4:3' | '9:16' | '16:9';
280
- type GraphPatchSource = 'local' | 'remote_user' | 'remote_agent' | 'system';
408
+ type ChangeSource = 'local_user' | 'remote_user' | 'remote_agent' | 'system';
281
409
  ```
282
410
 
283
411
  ### Prompt Options
284
412
 
285
413
  ```typescript
286
414
  interface PromptOptions {
287
- nodeIds?: string[]; // Scope to specific nodes
288
- edgeIds?: string[]; // Scope to specific edges
289
- metadata?: Record<string, unknown>;
415
+ objectIds?: string[]; // Scope to specific objects
290
416
  responseSchema?: Record<string, unknown>;
291
417
  }
292
418
  ```
293
419
 
294
420
  ---
295
421
 
296
- ## Custom Auth Provider
297
-
298
- For Electron or custom auth flows:
299
-
300
- ```typescript
301
- const client = new RoolClient({
302
- baseUrl: 'https://use.rool.dev',
303
- authProvider: {
304
- getToken: async () => myTokenStore.getAccessToken(),
305
- getUser: () => ({ email: 'user@example.com', name: 'User' }),
306
- isAuthenticated: () => myTokenStore.hasValidToken(),
307
- login: () => myAuthFlow.startLogin(),
308
- logout: () => myTokenStore.clear(),
309
- }
310
- });
311
- ```
312
-
313
- ---
314
-
315
422
  ## License
316
423
 
317
424
  Proprietary - © Lightpost One. All rights reserved.
@@ -0,0 +1,71 @@
1
+ import type { AuthProvider, UserInfo } from './types.js';
2
+ export interface BrowserAuthConfig {
3
+ /** Auth service URL (e.g. https://use.rool.dev/auth) */
4
+ authUrl: string;
5
+ storagePrefix?: string;
6
+ onAuthStateChanged: (authenticated: boolean) => void;
7
+ }
8
+ export declare class BrowserAuthProvider implements AuthProvider {
9
+ private config;
10
+ private apiKey;
11
+ private apiKeyFetchPromise;
12
+ private refreshPromise;
13
+ private refreshTimeoutId;
14
+ private get storageKeys();
15
+ /** Auth URL without trailing slash */
16
+ private get authBaseUrl();
17
+ constructor(config: BrowserAuthConfig);
18
+ /**
19
+ * Initialize auth manager - should be called on app startup.
20
+ * Processes any auth callback in the URL and sets up auto-refresh.
21
+ */
22
+ initialize(): boolean;
23
+ /**
24
+ * Check if user is currently authenticated (has valid token).
25
+ */
26
+ isAuthenticated(): boolean;
27
+ /**
28
+ * Get current access token, refreshing if expired.
29
+ * Returns undefined if not authenticated.
30
+ */
31
+ getToken(): Promise<string | undefined>;
32
+ /**
33
+ * Get user info decoded from JWT token.
34
+ */
35
+ getUser(): UserInfo;
36
+ /**
37
+ * Initiate login by redirecting to auth page.
38
+ */
39
+ login(): void;
40
+ /**
41
+ * Logout - clear all tokens and state.
42
+ */
43
+ logout(): void;
44
+ /**
45
+ * Process auth callback from URL fragment.
46
+ * Should be called on page load.
47
+ * @returns true if callback was processed
48
+ */
49
+ processCallback(): boolean;
50
+ /**
51
+ * Destroy auth manager - clear refresh timers.
52
+ */
53
+ destroy(): void;
54
+ /**
55
+ * Get the API key, fetching from server if not provided in config.
56
+ */
57
+ private getApiKey;
58
+ private tryRefreshToken;
59
+ private scheduleTokenRefresh;
60
+ private cancelScheduledRefresh;
61
+ private readAccessToken;
62
+ private readExpiresAt;
63
+ private writeTokens;
64
+ private clearTokens;
65
+ private storeState;
66
+ private readState;
67
+ private clearState;
68
+ private generateState;
69
+ private decodeUserInfo;
70
+ }
71
+ //# sourceMappingURL=auth-browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-browser.d.ts","sourceRoot":"","sources":["../src/auth-browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOzD,MAAM,WAAW,iBAAiB;IAC9B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,CAAC,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;CACxD;AAED,qBAAa,mBAAoB,YAAW,YAAY;IACpD,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,kBAAkB,CAAuC;IACjE,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,gBAAgB,CAA8C;IAEtE,OAAO,KAAK,WAAW,GAQtB;IAED,sCAAsC;IACtC,OAAO,KAAK,WAAW,GAEtB;gBAEW,MAAM,EAAE,iBAAiB;IAIrC;;;OAGG;IACH,UAAU,IAAI,OAAO;IAMrB;;OAEG;IACH,eAAe,IAAI,OAAO;IAO1B;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAgB7C;;OAEG;IACH,OAAO,IAAI,QAAQ;IAMnB;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;OAEG;IACH,MAAM,IAAI,IAAI;IAOd;;;;OAIG;IACH,eAAe,IAAI,OAAO;IAyC1B;;OAEG;IACH,OAAO,IAAI,IAAI;IAQf;;OAEG;YACW,SAAS;YAgCT,eAAe;IA4E7B,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,WAAW;IAwBnB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,cAAc;CAYzB"}