@ilivemylife/graph-sdk 1.0.0

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,143 @@
1
+ # SDK Usage Examples
2
+
3
+ Examples demonstrating how to use the iLiveMyLife Graph SDK.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ # 1. Install dependencies (from project root)
9
+ npm install
10
+
11
+ # 2. Build the SDK
12
+ npm run build
13
+
14
+ # 3. Login to get your token
15
+ node dist/cli.mjs login
16
+
17
+ # 4. Run examples
18
+ node examples/getting-started.mjs
19
+ ```
20
+
21
+ Or set token manually:
22
+ ```bash
23
+ export ILML_TOKEN="your-access-token"
24
+ node examples/getting-started.mjs
25
+ ```
26
+
27
+ ## Getting Your Token
28
+
29
+ **Option 1: CLI Login**
30
+ ```bash
31
+ npx ilml login
32
+ # Token saved automatically to ~/.ilivemylife/config.json
33
+ ```
34
+
35
+ **Option 2: Browser DevTools**
36
+ 1. Open https://app.ilivemylife.io and log in
37
+ 2. Open DevTools (F12) → Network tab
38
+ 3. Make any action (refresh page, open a node)
39
+ 4. Click any GraphQL request → Headers tab
40
+ 5. Find `access-token` in Request Headers and copy its value
41
+
42
+ ## Examples
43
+
44
+ ### 1. Getting Started (Start Here!)
45
+ ```bash
46
+ node examples/getting-started.mjs
47
+ ```
48
+ **Works without NODE_ID!** Complete walkthrough:
49
+ - Connect with your token
50
+ - Navigate your "My Life" tree
51
+ - Read node fields (title, description, tags, etc.)
52
+ - Create nodes with tags (like `private`)
53
+ - Create child nodes
54
+ - Edit nodes (partial updates)
55
+ - Reorder children
56
+ - Understand eventual consistency
57
+
58
+ ### 2. Messenger & Lifebot
59
+ ```bash
60
+ node examples/messenger-basics.mjs
61
+ ```
62
+ Working with messages and AI:
63
+ - Read messages from a node
64
+ - Send messages (without AI response)
65
+ - Ask Lifebot questions
66
+ - Ask Lifebot to create nodes
67
+ - Ask Lifebot to create structure (nested nodes)
68
+
69
+ ### 3. Ask Lifebot (Simple)
70
+ ```bash
71
+ export NODE_ID="your-node-id"
72
+ node examples/ask-lifebot.mjs
73
+ ```
74
+ Focused example of AI interaction with error handling.
75
+
76
+ ### 4. Manage Items
77
+ ```bash
78
+ export NODE_ID="your-node-id"
79
+ node examples/manage-items.mjs
80
+ ```
81
+ Creating, editing, reordering, archiving items.
82
+
83
+ ### 5. Real-time Subscriptions
84
+ ```bash
85
+ export NODE_ID="your-node-id"
86
+ node examples/subscriptions.mjs
87
+ ```
88
+ Subscribing to message and item changes in real-time.
89
+
90
+ ### 6. Error Handling
91
+ ```bash
92
+ node examples/error-handling.mjs
93
+ ```
94
+ Proper error handling patterns with typed exceptions.
95
+
96
+ ## Finding Your Node ID
97
+
98
+ Node IDs are in the URL when viewing a node:
99
+ ```
100
+ https://app.ilivemylife.io/item/00001191f13c5c81-ee4ae8284f820001
101
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
102
+ This is the node ID
103
+ ```
104
+
105
+ Or get your root node ID programmatically:
106
+ ```javascript
107
+ const me = await graph.me();
108
+ console.log(me.rootItemId); // Your "My Life" root node ID
109
+ ```
110
+
111
+ ## What is Lifebot?
112
+
113
+ Lifebot is the AI assistant that lives inside your iLiveMyLife knowledge graph.
114
+
115
+ **What Lifebot can do:**
116
+ - Answer questions about your data
117
+ - Search and find information across nodes
118
+ - Create new nodes with content
119
+ - Organize information into structure
120
+ - Execute actions on your behalf
121
+
122
+ **How to use:**
123
+ ```javascript
124
+ // Simple question
125
+ const answer = await graph.askLifebot(nodeId, 'Summarize this project');
126
+
127
+ // Ask to create something
128
+ const result = await graph.askLifebot(nodeId,
129
+ 'Create a node called "Weekly Tasks" with 3 sub-tasks inside');
130
+
131
+ // Lifebot will create the node AND the children!
132
+ ```
133
+
134
+ **Enable Lifebot:** Add `assist` tag to a node to enable Lifebot in that node.
135
+
136
+ ## Eventual Consistency
137
+
138
+ When you write data (addItem, editItem, etc.):
139
+ 1. Command is accepted immediately
140
+ 2. You get confirmation (or error)
141
+ 3. Data propagates across the system
142
+
143
+ **Important:** Reading immediately after write might return stale data. If you need real-time updates, use subscriptions.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Ask Lifebot Example
3
+ *
4
+ * Shows how to send a message and wait for AI response.
5
+ *
6
+ * Run: node examples/ask-lifebot.mjs
7
+ */
8
+
9
+ import {
10
+ createGraphClient,
11
+ LifebotTimeoutError,
12
+ LifebotRateLimitError
13
+ } from '@ilivemylife/graph-sdk';
14
+
15
+ const graph = createGraphClient({
16
+ token: process.env.ILML_TOKEN,
17
+ // Optional: enable logging
18
+ logger: console
19
+ });
20
+
21
+ const nodeId = process.env.NODE_ID;
22
+
23
+ try {
24
+ console.log('Asking Lifebot...');
25
+
26
+ const reply = await graph.askLifebot(nodeId, 'What is 2 + 2?', {
27
+ timeout: 30000 // 30 seconds timeout
28
+ });
29
+
30
+ console.log('Lifebot response:', reply.content);
31
+
32
+ } catch (error) {
33
+ if (error instanceof LifebotTimeoutError) {
34
+ console.error('Lifebot did not respond in time');
35
+ } else if (error instanceof LifebotRateLimitError) {
36
+ console.error('Rate limit exceeded, try again later');
37
+ } else {
38
+ throw error;
39
+ }
40
+ } finally {
41
+ // Free resources and close connections
42
+ graph.destroy();
43
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Error Handling Example
3
+ *
4
+ * Shows how to properly handle SDK errors.
5
+ *
6
+ * Run: node examples/error-handling.mjs
7
+ */
8
+
9
+ import {
10
+ createGraphClient,
11
+ GraphError,
12
+ GraphNetworkError,
13
+ GraphAuthError,
14
+ LifebotError,
15
+ LifebotRateLimitError,
16
+ LifebotTimeoutError,
17
+ GraphErrorCodes
18
+ } from '@ilivemylife/graph-sdk';
19
+
20
+ // Example 1: Missing token
21
+ try {
22
+ const client = createGraphClient({ token: '' });
23
+ } catch (error) {
24
+ if (error instanceof GraphError) {
25
+ console.log('Expected error - token required:', error.message);
26
+ }
27
+ }
28
+
29
+ // Example 2: Working with a valid client
30
+ const graph = createGraphClient({
31
+ token: process.env.ILML_TOKEN
32
+ });
33
+
34
+ // Example 3: Handling API errors
35
+ async function safeGetItems(nodeId) {
36
+ try {
37
+ return await graph.items(nodeId);
38
+ } catch (error) {
39
+ if (error instanceof GraphNetworkError) {
40
+ console.error('Network error - check your connection');
41
+ return [];
42
+ }
43
+ if (error instanceof GraphAuthError) {
44
+ console.error('Auth error - check your token');
45
+ return [];
46
+ }
47
+ throw error; // Re-throw unknown errors
48
+ }
49
+ }
50
+
51
+ // Example 4: Handling Lifebot errors
52
+ async function safeAskLifebot(nodeId, question) {
53
+ try {
54
+ return await graph.askLifebot(nodeId, question, { timeout: 30000 });
55
+ } catch (error) {
56
+ if (error instanceof LifebotTimeoutError) {
57
+ console.error(`Timeout after ${error.timeout}ms`);
58
+ return null;
59
+ }
60
+ if (error instanceof LifebotRateLimitError) {
61
+ console.error('Rate limited - waiting before retry');
62
+ await new Promise(r => setTimeout(r, 5000));
63
+ return safeAskLifebot(nodeId, question); // Retry
64
+ }
65
+ if (error instanceof LifebotError) {
66
+ console.error('Lifebot error:', error.message);
67
+ return null;
68
+ }
69
+ throw error;
70
+ }
71
+ }
72
+
73
+ // Example 5: Using error codes
74
+ async function handleMutationError() {
75
+ try {
76
+ await graph.addItem('invalid-node-id', { title: 'Test' });
77
+ } catch (error) {
78
+ if (error instanceof GraphError && error.code === GraphErrorCodes.MUTATION_FAILED) {
79
+ console.log('Mutation failed - check node ID');
80
+ }
81
+ }
82
+ }
83
+
84
+ // Run examples
85
+ const nodeId = process.env.NODE_ID;
86
+ if (nodeId) {
87
+ const items = await safeGetItems(nodeId);
88
+ console.log('Items:', items.length);
89
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Getting Started - Complete Example
3
+ *
4
+ * Works with just ILML_TOKEN - no NODE_ID needed!
5
+ * Shows the core flow: navigate tree, create nodes, understand structure.
6
+ *
7
+ * Getting your token:
8
+ * Option 1: npx ilml login (then token saved automatically)
9
+ * Option 2: In browser app.ilivemylife.io → DevTools → Network tab →
10
+ * click any request → Headers → find "access-token"
11
+ *
12
+ * Run:
13
+ * node examples/getting-started.mjs
14
+ */
15
+
16
+ import { createGraphClient } from '@ilivemylife/graph-sdk';
17
+
18
+ // ============================================================================
19
+ // Step 1: Create client
20
+ // ============================================================================
21
+
22
+ const graph = createGraphClient({
23
+ token: process.env.ILML_TOKEN
24
+ });
25
+
26
+ console.log('='.repeat(60));
27
+ console.log('iLiveMyLife Graph SDK - Getting Started');
28
+ console.log('='.repeat(60));
29
+
30
+ // ============================================================================
31
+ // Step 2: Get current user and root node
32
+ // ============================================================================
33
+
34
+ const me = await graph.me();
35
+ console.log('\n[Your Account]');
36
+ console.log(' Name:', me.displayName);
37
+ console.log(' Email:', me.email);
38
+ console.log(' Root Node ID:', me.rootItemId);
39
+
40
+ // ============================================================================
41
+ // Step 3: Navigate the tree - read root and children
42
+ // ============================================================================
43
+
44
+ // items() returns [node, ...children] - first element is the node itself
45
+ const rootItems = await graph.items(me.rootItemId);
46
+ const rootNode = rootItems[0];
47
+ const rootChildren = rootItems.slice(1);
48
+
49
+ console.log('\n[Your "My Life" (root) Node]');
50
+ console.log(' Title:', rootNode.title);
51
+ console.log(' Description:', rootNode.description || '(empty)');
52
+ console.log(' Tags:', rootNode.tags?.join(', ') || '(none)');
53
+ console.log(' Children:', rootChildren.length);
54
+
55
+ // ============================================================================
56
+ // Step 4: Iterate through children - show all fields
57
+ // ============================================================================
58
+
59
+ console.log('\n[Root Children]');
60
+ if (rootChildren.length === 0) {
61
+ console.log(' (no children yet)');
62
+ } else {
63
+ rootChildren.slice(0, 5).forEach((item, i) => {
64
+ console.log(`\n ${i + 1}. "${item.title}"`);
65
+ console.log(` ID: ${item.id}`);
66
+ console.log(` Description: ${item.description || '(empty)'}`);
67
+ console.log(` Tags: ${item.tags?.join(', ') || '(none)'}`);
68
+ console.log(` Order: ${item.order}`);
69
+ console.log(` Created: ${new Date(parseInt(item.createdAt)).toLocaleString()}`);
70
+ console.log(` By: ${item.createdBy}`);
71
+ if (item.refId) {
72
+ console.log(` → Link to: ${item.refId}`);
73
+ }
74
+ });
75
+ if (rootChildren.length > 5) {
76
+ console.log(`\n ... and ${rootChildren.length - 5} more`);
77
+ }
78
+ }
79
+
80
+ // ============================================================================
81
+ // Step 5: Go deeper - read grandchildren (2 levels)
82
+ // ============================================================================
83
+
84
+ if (rootChildren.length > 0) {
85
+ const firstChild = rootChildren[0];
86
+ const childItems = await graph.items(firstChild.id);
87
+ const grandchildren = childItems.slice(1);
88
+
89
+ console.log(`\n[Inside "${firstChild.title}"]`);
90
+ console.log(' Children:', grandchildren.length);
91
+ grandchildren.slice(0, 3).forEach((item, i) => {
92
+ console.log(` ${i + 1}. ${item.title}`);
93
+ });
94
+ }
95
+
96
+ // ============================================================================
97
+ // Step 6: Create a new node
98
+ // ============================================================================
99
+
100
+ console.log('\n[Creating New Node]');
101
+
102
+ const newNode = await graph.addItem(me.rootItemId, {
103
+ title: 'SDK Demo',
104
+ description: 'This node was created via iLiveMyLife Graph SDK.\n\nIt demonstrates the `addItem()` method.',
105
+ tags: ['private']
106
+ });
107
+
108
+ console.log(' Created:', newNode.title);
109
+ console.log(' ID:', newNode.id);
110
+ console.log(' URL: https://app.ilivemylife.io/item/' + newNode.id);
111
+
112
+ // ============================================================================
113
+ // Step 7: Create child nodes inside it
114
+ // ============================================================================
115
+
116
+ console.log('\n[Creating Child Nodes]');
117
+
118
+ const child1 = await graph.addItem(newNode.id, {
119
+ title: 'First Task',
120
+ description: 'This is the first task in our demo project.'
121
+ });
122
+ console.log(' Created:', child1.title);
123
+
124
+ const child2 = await graph.addItem(newNode.id, {
125
+ title: 'Second Task',
126
+ description: 'This is the second task.'
127
+ });
128
+ console.log(' Created:', child2.title);
129
+
130
+ const child3 = await graph.addItem(newNode.id, {
131
+ title: 'Third Task',
132
+ description: 'This is the third task.'
133
+ });
134
+ console.log(' Created:', child3.title);
135
+
136
+ // ============================================================================
137
+ // Note on Eventual Consistency
138
+ // ============================================================================
139
+ // When you call addItem/editItem, the command is accepted immediately.
140
+ // However, propagation across the system takes a moment.
141
+ // If you read right after write, you might get stale data.
142
+ // But once you receive confirmation (no error), data WILL propagate eventually.
143
+ //
144
+ // For real-time updates, use subscriptions (see subscriptions.mjs)
145
+
146
+ // ============================================================================
147
+ // Step 8: Edit a node
148
+ // ============================================================================
149
+
150
+ console.log('\n[Editing Node]');
151
+ // Only pass fields you want to change - omitted fields stay unchanged
152
+ const edited = await graph.editItem({
153
+ id: child1.id,
154
+ title: 'First Task (Updated)',
155
+ description: 'Description updated via SDK!'
156
+ // tags: not passed = tags stay unchanged
157
+ // dueDate: not passed = dueDate stays unchanged
158
+ });
159
+ console.log(' Updated:', edited.title);
160
+
161
+ // ============================================================================
162
+ // Step 9: Reorder children
163
+ // ============================================================================
164
+
165
+ console.log('\n[Reordering]');
166
+ // Move last child (position 3) to first position (position 1)
167
+ const reordered = await graph.reorderChild(newNode.id, 3, 1);
168
+ console.log(' Moved "' + reordered.title + '" to position 1');
169
+
170
+ // Verify new order
171
+ const afterReorder = await graph.items(newNode.id);
172
+ console.log(' New order:', afterReorder.slice(1).map(i => i.title).join(' → '));
173
+
174
+ // ============================================================================
175
+ // Done!
176
+ // ============================================================================
177
+
178
+ console.log('\n' + '='.repeat(60));
179
+ console.log('Done! Summary:');
180
+ console.log('='.repeat(60));
181
+ console.log(`
182
+ What we did:
183
+ 1. Connected with your token
184
+ 2. Read your user info
185
+ 3. Navigated the tree (root → children → grandchildren)
186
+ 4. Created a new node with title, description, tags
187
+ 5. Created child nodes inside it
188
+ 6. Edited a node
189
+ 7. Reordered children
190
+
191
+ View in app: https://app.ilivemylife.io/item/${newNode.id}
192
+
193
+ Next examples to try:
194
+ - messenger-basics.mjs - Send messages, chat in nodes
195
+ - ask-lifebot.mjs - Ask AI questions, let it create nodes
196
+ - subscriptions.mjs - Real-time updates
197
+
198
+ CLI commands:
199
+ npx ilml items ${newNode.id}
200
+ npx ilml tree ${newNode.id}
201
+ `);
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Item Management Example
3
+ *
4
+ * Shows how to create, edit, and organize items.
5
+ *
6
+ * Run: node examples/manage-items.mjs
7
+ */
8
+
9
+ import { createGraphClient } from '@ilivemylife/graph-sdk';
10
+
11
+ const graph = createGraphClient({
12
+ token: process.env.ILML_TOKEN
13
+ });
14
+
15
+ const parentNodeId = process.env.NODE_ID;
16
+
17
+ // Create a new item (all fields optional)
18
+ const newItem = await graph.addItem(parentNodeId, {
19
+ title: 'My New Task',
20
+ description: 'This is a task created via SDK',
21
+ tags: ['sdk', 'example'],
22
+ dueDate: String(Date.now() + 7 * 24 * 60 * 60 * 1000) // timestamp string (ms)
23
+ });
24
+ console.log('Created item:', newItem.id, newItem.title);
25
+
26
+ // Create a reference (shortcut) to another node
27
+ // const ref = await graph.addItem(parentNodeId, {
28
+ // refId: 'target-node-id', // ID of the original node
29
+ // title: 'Custom Title', // optional: override original title
30
+ // description: 'Custom description' // optional: override original description
31
+ // });
32
+ // console.log('Created ref:', ref.id, '→', ref.refId);
33
+
34
+ // Edit the item
35
+ const editedItem = await graph.editItem({
36
+ id: newItem.id,
37
+ title: 'My Updated Task',
38
+ description: 'Description updated via SDK'
39
+ });
40
+ console.log('Updated item:', editedItem.title);
41
+
42
+ // Get all items to see current order
43
+ const items = await graph.items(parentNodeId);
44
+ const children = items.slice(1); // First item is the parent itself
45
+ console.log('Children count:', children.length);
46
+
47
+ // Resolve a node — if it has refId, follows it to get the original node.
48
+ // Returns the node itself if it has no refId.
49
+ if (children.length > 0) {
50
+ const firstChild = children[0];
51
+ const resolved = await graph.resolveRef(firstChild.id);
52
+ if (resolved) {
53
+ console.log('Resolved:', firstChild.title, firstChild.refId ? `→ ${resolved.title}` : '(not a ref)');
54
+ }
55
+ }
56
+
57
+ if (children.length >= 2) {
58
+ // Move child from last position to position 2 (1-based)
59
+ const lastPosition = children.length;
60
+ const reordered = await graph.reorderChild(parentNodeId, lastPosition, 2);
61
+ console.log('Reordered to position 2:', reordered.title);
62
+
63
+ // Move specific node to position 1
64
+ const moved = await graph.setPosition(newItem.id, parentNodeId, 1);
65
+ console.log('Set position to 1:', moved.title);
66
+ }
67
+
68
+ // Move item to a different parent
69
+ // await graph.moveItem(newItem.id, parentNodeId, otherParentId); // to the end
70
+ // await graph.moveItemToPosition(newItem.id, parentNodeId, otherParentId, 3); // to position 3
71
+
72
+ // Archive the item
73
+ await graph.archiveItem(newItem.id);
74
+ console.log('Item archived');
75
+
76
+ // Unarchive if needed
77
+ await graph.unarchiveItem(newItem.id);
78
+ console.log('Item unarchived');