@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.
- package/README.md +725 -0
- package/bin/ilml +5 -0
- package/dist/cli.mjs +2890 -0
- package/dist/client.cjs +1212 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +607 -0
- package/dist/client.d.ts +607 -0
- package/dist/client.mjs +1138 -0
- package/dist/client.mjs.map +1 -0
- package/dist/mcp-server.mjs +1957 -0
- package/dist/queries.cjs +443 -0
- package/dist/queries.cjs.map +1 -0
- package/dist/queries.d.cts +201 -0
- package/dist/queries.d.ts +201 -0
- package/dist/queries.mjs +383 -0
- package/dist/queries.mjs.map +1 -0
- package/examples/README.md +143 -0
- package/examples/ask-lifebot.mjs +43 -0
- package/examples/error-handling.mjs +89 -0
- package/examples/getting-started.mjs +201 -0
- package/examples/manage-items.mjs +78 -0
- package/examples/messenger-basics.mjs +189 -0
- package/examples/subscriptions.mjs +60 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
# @ilivemylife/graph-sdk
|
|
2
|
+
|
|
3
|
+
SDK for [iLiveMyLife](https://iLiveMyLife.io) Knowledge Graph.
|
|
4
|
+
|
|
5
|
+
## What is iLiveMyLife?
|
|
6
|
+
|
|
7
|
+
**iLiveMyLife** is your personal **informational wallet** — a unified platform to organize everything in your life.
|
|
8
|
+
|
|
9
|
+
### Key Features
|
|
10
|
+
|
|
11
|
+
- **Knowledge Graph** — Structure your information as interconnected nodes, not just files and folders
|
|
12
|
+
- **Cross-platform** — Apps for iOS, Android, Web, Desktop (macOS, Windows, Linux)
|
|
13
|
+
- **Real-time Sync** — Changes sync instantly across all your devices
|
|
14
|
+
- **Push Notifications** — Stay informed with smart notifications
|
|
15
|
+
- **Team Collaboration** — Share nodes with team members, control privacy with tags (private/wallet/kyc/etc.)
|
|
16
|
+
- **AI Assistants (Lifebot)** — Built-in AI that understands your knowledge graph context
|
|
17
|
+
- **Offline Support** — Your data is synced on your device for offline access if not connected
|
|
18
|
+
- **Powerful Tags System** — Customize node behavior with tags (privacy, AI assistance, colors, markers, and more)
|
|
19
|
+
- **Markers (Status System)** — Track item status with customizable markers (like task states)
|
|
20
|
+
- **Rich Content** — Support for text, images, files, links, checklists, due dates, and more
|
|
21
|
+
- **Robust API & SDK** — Full-featured TypeScript SDK and GraphQL API for developers
|
|
22
|
+
- **CLI Tool** — Command-line interface for scripting and automation
|
|
23
|
+
- **MCP Server** — Integrate with AI assistants like Claude, Cursor, Windsurf via Model Context Protocol
|
|
24
|
+
|
|
25
|
+
### Use Cases
|
|
26
|
+
|
|
27
|
+
- Personal knowledge management
|
|
28
|
+
- Team project coordination
|
|
29
|
+
- Task and goal tracking
|
|
30
|
+
- Note-taking with AI assistance
|
|
31
|
+
- Information organization for any domain
|
|
32
|
+
|
|
33
|
+
**Get the app:** [https://iLiveMyLife.io](https://iLiveMyLife.io)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
- **Node.js 18+** — [download](https://nodejs.org/)
|
|
40
|
+
- **iLiveMyLife account** — sign up at [iLiveMyLife.io](https://iLiveMyLife.io)
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install @ilivemylife/graph-sdk
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Authentication
|
|
49
|
+
|
|
50
|
+
You need an access token to use the SDK.
|
|
51
|
+
|
|
52
|
+
**Option 1: CLI Login (recommended)**
|
|
53
|
+
```bash
|
|
54
|
+
npx ilml login
|
|
55
|
+
# Prompts for email and password in terminal
|
|
56
|
+
# Token saved to ~/.ilivemylife/config.json
|
|
57
|
+
# After this, CLI commands work automatically
|
|
58
|
+
# For SDK, pass token explicitly: createGraphClient({ token })
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Option 2: Programmatic login (for bots and automations)**
|
|
62
|
+
```javascript
|
|
63
|
+
const response = await fetch('https://api.ilivemylife.io/api/v1/login', {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: { 'Content-Type': 'application/json' },
|
|
66
|
+
body: JSON.stringify({ email: 'you@example.com', password: '...' })
|
|
67
|
+
});
|
|
68
|
+
const token = response.headers.get('access-token');
|
|
69
|
+
|
|
70
|
+
const graph = createGraphClient({ token });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Option 3: Browser DevTools (manual)**
|
|
74
|
+
1. Open https://app.ilivemylife.io and log in
|
|
75
|
+
2. Open DevTools (F12) → Network tab
|
|
76
|
+
3. Make any action (refresh page, open a node)
|
|
77
|
+
4. Click any GraphQL request → Headers tab
|
|
78
|
+
5. Copy `access-token` value, use it as: `createGraphClient({ token: 'copied-value' })`
|
|
79
|
+
|
|
80
|
+
### Config File
|
|
81
|
+
|
|
82
|
+
After `ilml login`, config is saved as JSON:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
// ~/.ilivemylife/config.json
|
|
86
|
+
{
|
|
87
|
+
"token": "your-access-token",
|
|
88
|
+
"user": {
|
|
89
|
+
"id": "user-id",
|
|
90
|
+
"email": "you@example.com",
|
|
91
|
+
"displayName": "Your Name"
|
|
92
|
+
},
|
|
93
|
+
"rootItemId": "your-root-node-id"
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- `token` — access token for API requests
|
|
98
|
+
- `user` — your account info (cached for display in CLI)
|
|
99
|
+
- `rootItemId` — your "My Life" root node ID
|
|
100
|
+
|
|
101
|
+
### Token Priority
|
|
102
|
+
|
|
103
|
+
The SDK and CLI resolve tokens in this order (first found wins):
|
|
104
|
+
|
|
105
|
+
| Priority | Source | How to set |
|
|
106
|
+
|----------|--------|------------|
|
|
107
|
+
| 1 | Local config | `ilml login --local` → `.ilivemylife/config.json` in project dir |
|
|
108
|
+
| 2 | `.env` file | Create `.env` with `ILML_TOKEN=your-token` in project dir |
|
|
109
|
+
| 3 | Global config | `ilml login` → `~/.ilivemylife/config.json` |
|
|
110
|
+
| 4 | Environment variable | `export ILML_TOKEN=your-token` |
|
|
111
|
+
|
|
112
|
+
This lets you use different accounts per project:
|
|
113
|
+
```bash
|
|
114
|
+
# Global account (default)
|
|
115
|
+
ilml login
|
|
116
|
+
|
|
117
|
+
# Project-specific account (overrides global)
|
|
118
|
+
cd my-project
|
|
119
|
+
ilml login --local
|
|
120
|
+
|
|
121
|
+
# Or via .env file
|
|
122
|
+
echo "ILML_TOKEN=project-specific-token" > .env
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Finding Node IDs
|
|
126
|
+
|
|
127
|
+
Node IDs are visible in the URL when viewing a node:
|
|
128
|
+
```
|
|
129
|
+
https://app.ilivemylife.io/item/00001191f13c5c81-ee4ae8284f820001
|
|
130
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
131
|
+
This is the node ID
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Or get your root node ("My Life") programmatically:
|
|
135
|
+
```javascript
|
|
136
|
+
const me = await graph.me();
|
|
137
|
+
console.log(me.rootItemId); // Your root node ID
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Quick Start
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
import { createGraphClient } from '@ilivemylife/graph-sdk';
|
|
144
|
+
|
|
145
|
+
// Create client (token from ilml login or env variable)
|
|
146
|
+
const graph = createGraphClient({
|
|
147
|
+
token: process.env.ILML_TOKEN
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Get your root node
|
|
151
|
+
const me = await graph.me();
|
|
152
|
+
const rootId = me.rootItemId;
|
|
153
|
+
|
|
154
|
+
// See what's in your graph
|
|
155
|
+
const items = await graph.items(rootId);
|
|
156
|
+
console.log('My Life:', items[0].title);
|
|
157
|
+
console.log('Children:', items.slice(1).map(i => i.title));
|
|
158
|
+
|
|
159
|
+
// Create a new node
|
|
160
|
+
const project = await graph.addItem(rootId, { title: 'My Project' });
|
|
161
|
+
|
|
162
|
+
// Add children to it
|
|
163
|
+
await graph.addItem(project.id, { title: 'First Task' });
|
|
164
|
+
await graph.addItem(project.id, { title: 'Second Task' });
|
|
165
|
+
|
|
166
|
+
// Ask AI about it (requires 'assist' tag on the node)
|
|
167
|
+
const reply = await graph.askLifebot(project.id, 'What tasks do I have?');
|
|
168
|
+
console.log(reply.content);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
For a complete walkthrough, run: `node examples/getting-started.mjs`
|
|
172
|
+
|
|
173
|
+
## Features
|
|
174
|
+
|
|
175
|
+
- **TypeScript** — Full type definitions included
|
|
176
|
+
- **Dual Format** — Works with both ESM (`import`) and CommonJS (`require`)
|
|
177
|
+
- **Typed Errors** — `GraphError`, `LifebotTimeoutError`, `LifebotRateLimitError`
|
|
178
|
+
- **Injectable Logger** — Pass your own logger or use silent mode
|
|
179
|
+
- **Real-time Subscriptions** — Subscribe to message and item changes
|
|
180
|
+
|
|
181
|
+
## Examples
|
|
182
|
+
|
|
183
|
+
See [examples/](./examples/) directory for complete usage examples:
|
|
184
|
+
|
|
185
|
+
- [getting-started.mjs](./examples/getting-started.mjs) — **Start here!** Complete walkthrough (no NODE_ID needed)
|
|
186
|
+
- [messenger-basics.mjs](./examples/messenger-basics.mjs) — Messages and Lifebot AI
|
|
187
|
+
- [ask-lifebot.mjs](./examples/ask-lifebot.mjs) — AI interactions with error handling
|
|
188
|
+
- [manage-items.mjs](./examples/manage-items.mjs) — Create, edit, reorder items
|
|
189
|
+
- [subscriptions.mjs](./examples/subscriptions.mjs) — Real-time updates
|
|
190
|
+
- [error-handling.mjs](./examples/error-handling.mjs) — Error handling patterns
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## API Reference
|
|
195
|
+
|
|
196
|
+
### Creating a Client
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
import { createGraphClient } from '@ilivemylife/graph-sdk';
|
|
200
|
+
|
|
201
|
+
const graph = createGraphClient({
|
|
202
|
+
token: 'your-access-token', // Required
|
|
203
|
+
logger: console // Optional: enable logging
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Queries (Read Operations)
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
// Get node and its children
|
|
211
|
+
const items = await graph.items(nodeId);
|
|
212
|
+
// items[0] = node itself, items[1...n] = children
|
|
213
|
+
|
|
214
|
+
// Get messages
|
|
215
|
+
const messages = await graph.messages(nodeId, 20);
|
|
216
|
+
|
|
217
|
+
// Get change history
|
|
218
|
+
const history = await graph.itemHistory(nodeId);
|
|
219
|
+
|
|
220
|
+
// Get users with access
|
|
221
|
+
const users = await graph.itemUsers(nodeId);
|
|
222
|
+
|
|
223
|
+
// Get child count
|
|
224
|
+
const { count } = await graph.subItemCount(nodeId);
|
|
225
|
+
|
|
226
|
+
// Get node settings
|
|
227
|
+
const settings = await graph.itemSettings(nodeId);
|
|
228
|
+
|
|
229
|
+
// Search messages
|
|
230
|
+
const found = await graph.searchMessages(nodeId, 'keyword');
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Mutations (Write Operations)
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
// Create item (all fields optional, node added to end of list by default)
|
|
237
|
+
const item = await graph.addItem(parentId, {
|
|
238
|
+
title: 'New Item',
|
|
239
|
+
description: 'Description',
|
|
240
|
+
tags: ['tag1', 'tag2'],
|
|
241
|
+
dueDate: '1735689600000' // timestamp string (ms)
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Create link/shortcut to another node
|
|
245
|
+
const link = await graph.addItem(parentId, {
|
|
246
|
+
refId: originalNodeId, // reference to original node
|
|
247
|
+
title: 'Custom Title', // override original title
|
|
248
|
+
description: 'Custom Desc' // override original description
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Edit item
|
|
252
|
+
await graph.editItem({
|
|
253
|
+
id: itemId,
|
|
254
|
+
title: 'Updated Title'
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Archive/unarchive
|
|
258
|
+
await graph.archiveItem(itemId);
|
|
259
|
+
await graph.unarchiveItem(itemId);
|
|
260
|
+
|
|
261
|
+
// Move item to different parent
|
|
262
|
+
await graph.moveItem(itemId, fromParentId, toParentId);
|
|
263
|
+
|
|
264
|
+
// Reorder within parent (positions are 1-based)
|
|
265
|
+
await graph.reorderChild(parentId, fromPosition, toPosition);
|
|
266
|
+
// Example: move child from position 4 to position 1
|
|
267
|
+
await graph.reorderChild(parentId, 4, 1);
|
|
268
|
+
|
|
269
|
+
// Move specific node to position (positions are 1-based)
|
|
270
|
+
await graph.setPosition(nodeId, parentId, position);
|
|
271
|
+
// Example: move node to position 2
|
|
272
|
+
await graph.setPosition(nodeId, parentId, 2);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Messages
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
import { LIFEBOT_MESSAGE_TYPES, MESSAGE_TYPES } from '@ilivemylife/graph-sdk';
|
|
279
|
+
|
|
280
|
+
// Simple message (no AI)
|
|
281
|
+
await graph.addMessage(nodeId, 'Hello!', {
|
|
282
|
+
lifebot: LIFEBOT_MESSAGE_TYPES.off
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Message that triggers AI response
|
|
286
|
+
await graph.addMessage(nodeId, 'Help me with this', {
|
|
287
|
+
lifebot: LIFEBOT_MESSAGE_TYPES.on
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Reply to a message
|
|
291
|
+
await graph.addMessage(nodeId, 'Thanks!', {
|
|
292
|
+
type: MESSAGE_TYPES.reply,
|
|
293
|
+
refId: originalMessageId
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Edit/archive messages
|
|
297
|
+
await graph.editMessage({ id: msgId, content: 'Updated' });
|
|
298
|
+
await graph.archiveMessage(msgId);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Real-time Subscriptions
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
import { MESSAGE_CHANGE_TYPES, ITEM_CHANGE_TYPES } from '@ilivemylife/graph-sdk';
|
|
305
|
+
|
|
306
|
+
// Subscribe to messages
|
|
307
|
+
const unsubscribeMessages = await graph.subscribeToMessages(nodeId, (change, message) => {
|
|
308
|
+
if (change === MESSAGE_CHANGE_TYPES.added) {
|
|
309
|
+
console.log('New message:', message.content);
|
|
310
|
+
} else if (change === MESSAGE_CHANGE_TYPES.edited) {
|
|
311
|
+
console.log('Message edited:', message.content);
|
|
312
|
+
} else if (change === MESSAGE_CHANGE_TYPES.deleted) {
|
|
313
|
+
console.log('Message deleted:', message.id);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Subscribe to item changes
|
|
318
|
+
const unsubscribeItems = await graph.subscribeToItems(nodeId, (change, item) => {
|
|
319
|
+
if (change === ITEM_CHANGE_TYPES.added) {
|
|
320
|
+
console.log('New item:', item.title);
|
|
321
|
+
} else if (change === ITEM_CHANGE_TYPES.movedIn) {
|
|
322
|
+
console.log('Item moved here:', item.title);
|
|
323
|
+
} else if (change === ITEM_CHANGE_TYPES.reorder) {
|
|
324
|
+
console.log('Item reordered:', item.title);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Stop listening
|
|
329
|
+
unsubscribeMessages();
|
|
330
|
+
unsubscribeItems();
|
|
331
|
+
|
|
332
|
+
// Wait for specific message
|
|
333
|
+
const msg = await graph.waitForMessage(nodeId,
|
|
334
|
+
(change, msg) => change === MESSAGE_CHANGE_TYPES.added && msg.createdBy === 'Lifebot',
|
|
335
|
+
30000 // timeout
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// Wait for any reply after your message
|
|
339
|
+
const reply = await graph.waitForReply(nodeId, myMessageId);
|
|
340
|
+
|
|
341
|
+
// Wait for a reply that directly references your message (via refId)
|
|
342
|
+
const exactReply = await graph.waitForReply(nodeId, myMessageId, { exactMatch: true, timeout: 60000 });
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### AI Interactions
|
|
346
|
+
|
|
347
|
+
```javascript
|
|
348
|
+
import { LifebotTimeoutError, LifebotRateLimitError } from '@ilivemylife/graph-sdk';
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const response = await graph.askLifebot(nodeId, 'What is 2 + 2?', {
|
|
352
|
+
timeout: 60000 // 60 seconds
|
|
353
|
+
});
|
|
354
|
+
console.log(response.content);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
if (error instanceof LifebotTimeoutError) {
|
|
357
|
+
console.error('AI did not respond in time');
|
|
358
|
+
} else if (error instanceof LifebotRateLimitError) {
|
|
359
|
+
console.error('Rate limit exceeded');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**What is Lifebot?**
|
|
365
|
+
|
|
366
|
+
Lifebot is the AI assistant that lives inside your iLiveMyLife knowledge graph. It can:
|
|
367
|
+
- Answer questions about your data and context
|
|
368
|
+
- Search and find information across your nodes
|
|
369
|
+
- **Create new nodes** with titles and descriptions
|
|
370
|
+
- **Organize information** by creating nested structure
|
|
371
|
+
- Execute actions on your behalf
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
// Ask Lifebot to create a project with sub-tasks
|
|
375
|
+
await graph.askLifebot(nodeId,
|
|
376
|
+
'Create a node "Weekly Plan" with 3 tasks inside: Monday review, Wednesday sync, Friday summary'
|
|
377
|
+
);
|
|
378
|
+
// Lifebot will create the parent node AND all children!
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Enable Lifebot:** Add `assist` tag to a node to enable Lifebot in that context.
|
|
382
|
+
|
|
383
|
+
### Helper Methods
|
|
384
|
+
|
|
385
|
+
```javascript
|
|
386
|
+
// Get single item
|
|
387
|
+
const item = await graph.getItem(nodeId);
|
|
388
|
+
|
|
389
|
+
// Resolve reference (shortcut) to actual node
|
|
390
|
+
const actual = await graph.resolveRef(nodeId);
|
|
391
|
+
|
|
392
|
+
// Get items with references resolved
|
|
393
|
+
const items = await graph.itemsResolved(nodeId);
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Cleanup
|
|
397
|
+
|
|
398
|
+
```javascript
|
|
399
|
+
// Close all connections and free resources when done
|
|
400
|
+
graph.destroy();
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Call `destroy()` at the end of scripts to close WebSocket connections and let the process exit. Safe to call multiple times. Not needed for HTTP-only usage (queries/mutations without subscriptions).
|
|
404
|
+
|
|
405
|
+
### Validation
|
|
406
|
+
|
|
407
|
+
```javascript
|
|
408
|
+
import { isValidNodeId, parseNodeId, validateNodeId } from '@ilivemylife/graph-sdk';
|
|
409
|
+
|
|
410
|
+
// Check if string is valid node ID
|
|
411
|
+
isValidNodeId('00001191f13c5c81-ee4ae8284f820001') // true
|
|
412
|
+
isValidNodeId('invalid') // false
|
|
413
|
+
|
|
414
|
+
// Parse node ID from raw ID or full URL
|
|
415
|
+
parseNodeId('00001191f13c5c81-ee4ae8284f820001')
|
|
416
|
+
// → '00001191f13c5c81-ee4ae8284f820001'
|
|
417
|
+
|
|
418
|
+
parseNodeId('https://app.ilivemylife.io/item/00001191f13c5c81-ee4ae8284f820001')
|
|
419
|
+
// → '00001191f13c5c81-ee4ae8284f820001'
|
|
420
|
+
|
|
421
|
+
parseNodeId('invalid')
|
|
422
|
+
// → null
|
|
423
|
+
|
|
424
|
+
// Validate and throw error if invalid
|
|
425
|
+
validateNodeId(userInput); // throws GraphError if invalid
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### REST API
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
// Login
|
|
432
|
+
const { user, accessToken } = await graph.login(email, password);
|
|
433
|
+
|
|
434
|
+
// Get current user
|
|
435
|
+
const me = await graph.me();
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## Error Handling
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
import {
|
|
444
|
+
GraphError, // Base error
|
|
445
|
+
GraphNetworkError, // Network/connection issues
|
|
446
|
+
GraphAuthError, // Authentication failed
|
|
447
|
+
LifebotError, // AI error base
|
|
448
|
+
LifebotTimeoutError, // AI timeout
|
|
449
|
+
LifebotRateLimitError // AI rate limit
|
|
450
|
+
} from '@ilivemylife/graph-sdk';
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
await graph.addItem(nodeId, { title: 'Test' });
|
|
454
|
+
} catch (error) {
|
|
455
|
+
if (error instanceof GraphAuthError) {
|
|
456
|
+
console.error('Check your token');
|
|
457
|
+
} else if (error instanceof GraphError) {
|
|
458
|
+
console.error('Operation failed:', error.code);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Constants
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
import {
|
|
469
|
+
// Message options
|
|
470
|
+
MESSAGE_TYPES, // normal, reply, forward
|
|
471
|
+
LIFEBOT_MESSAGE_TYPES, // on, off
|
|
472
|
+
NOTIFY_MESSAGE_TYPES, // on, off, postpone
|
|
473
|
+
|
|
474
|
+
// Subscription change types
|
|
475
|
+
MESSAGE_CHANGE_TYPES, // added ('+1'), edited ('edit'), deleted ('-1')
|
|
476
|
+
ITEM_CHANGE_TYPES, // added ('+1'), edited ('edit'), deleted ('-1'), movedIn ('movedIn'), reorder ('reorder')
|
|
477
|
+
|
|
478
|
+
// Access and settings
|
|
479
|
+
ITEM_ACCESS_TYPES, // human, bot, request
|
|
480
|
+
AI_PROVIDER_TYPES, // openai, claude, gemini
|
|
481
|
+
NODE_SPECIAL_TYPES // user_settings, user_system, non_drop
|
|
482
|
+
} from '@ilivemylife/graph-sdk';
|
|
483
|
+
|
|
484
|
+
// Subscription change values:
|
|
485
|
+
// MESSAGE_CHANGE_TYPES.added === '+1'
|
|
486
|
+
// MESSAGE_CHANGE_TYPES.edited === 'edit'
|
|
487
|
+
// MESSAGE_CHANGE_TYPES.deleted === '-1'
|
|
488
|
+
//
|
|
489
|
+
// ITEM_CHANGE_TYPES.added === '+1'
|
|
490
|
+
// ITEM_CHANGE_TYPES.edited === 'edit'
|
|
491
|
+
// ITEM_CHANGE_TYPES.deleted === '-1'
|
|
492
|
+
// ITEM_CHANGE_TYPES.movedIn === 'movedIn'
|
|
493
|
+
// ITEM_CHANGE_TYPES.reorder === 'reorder'
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## Tags & Markers
|
|
499
|
+
|
|
500
|
+
Tags control node behavior. Format: `#tagname#value` or just `tagname`.
|
|
501
|
+
|
|
502
|
+
### Special Tags
|
|
503
|
+
|
|
504
|
+
| Tag | Description |
|
|
505
|
+
|-----|-------------|
|
|
506
|
+
| `wallet` | High privacy — entering the node requires access. Hidden from AI completely (invisible in search, context, and responses). Can be shared between users (e.g. corporate wallet between CEO and accountant) by granting access. **Known limitation:** currently wallet nodes are still visible (title, description) in parent's children list and in messenger link previews to users without access. This will be fixed — wallet nodes should be completely invisible to unauthorized users |
|
|
507
|
+
| `private` | Lower privacy — node itself is visible but entering (seeing children) requires access. Users with access can use AI to search inside if `allowAIPrivateSearch` setting is enabled |
|
|
508
|
+
| `assist` | Enables Lifebot AI in this node |
|
|
509
|
+
| `#color#r,g,b,a` | Node background color |
|
|
510
|
+
| `#markerList#<listId>` | Enables status dropdown for children |
|
|
511
|
+
| `#marker#<statusId>` | Sets item status from marker list |
|
|
512
|
+
|
|
513
|
+
### Markers (Status System)
|
|
514
|
+
|
|
515
|
+
Markers let you track status of items (like task states). It's a pattern, not a built-in feature:
|
|
516
|
+
|
|
517
|
+
1. **Create a marker list** — a node with status options as children (emoji titles)
|
|
518
|
+
2. **Enable on parent** — add `#markerList#<listId>` tag
|
|
519
|
+
3. **Set status** — add `#marker#<statusId>` tag to items
|
|
520
|
+
|
|
521
|
+
```javascript
|
|
522
|
+
// Enable default markers on a parent node
|
|
523
|
+
await graph.editItem({
|
|
524
|
+
id: parentId,
|
|
525
|
+
tags: ['#markerList#0000018190aaf813-a61a6f263e6a0000']
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// Mark item as completed (default marker)
|
|
529
|
+
await graph.editItem({
|
|
530
|
+
id: taskId,
|
|
531
|
+
tags: ['#marker#0000018195a2d792-a61a6f263e6a0000'] // ☑️ Completed
|
|
532
|
+
});
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
See `CLAUDE.md` for full marker documentation and default marker IDs.
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## CLI Usage
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
# Install globally
|
|
543
|
+
npm install -g @ilivemylife/graph-sdk
|
|
544
|
+
|
|
545
|
+
# Login (saves token to ~/.ilivemylife/config.json)
|
|
546
|
+
ilml login
|
|
547
|
+
|
|
548
|
+
# Queries (read operations) - all support --json for scripting
|
|
549
|
+
ilml items <nodeId> # List node and children (short)
|
|
550
|
+
ilml items <nodeId> --single # Node only, no children
|
|
551
|
+
ilml items <nodeId> --long # Full details (all fields)
|
|
552
|
+
ilml items <nodeId> --json # JSON output (for scripting)
|
|
553
|
+
ilml items <nodeId> --ids # IDs only (for piping)
|
|
554
|
+
ilml messages <nodeId> [count] # Show messages (short)
|
|
555
|
+
ilml messages <nodeId> --long # Full message details
|
|
556
|
+
ilml messages <nodeId> --json # JSON output
|
|
557
|
+
ilml itemHistory <nodeId> # Show change history
|
|
558
|
+
ilml itemHistory <nodeId> --json # JSON output
|
|
559
|
+
ilml itemUsers <nodeId> --json # Users with access
|
|
560
|
+
ilml subItemCount <nodeId> # Get children count
|
|
561
|
+
ilml itemSettings <nodeId> # Show node settings
|
|
562
|
+
ilml search <nodeId> "text" # Search messages
|
|
563
|
+
ilml tree <nodeId> [depth] # Show tree structure (truncated)
|
|
564
|
+
ilml tree <nodeId> --long # Full titles, no truncation
|
|
565
|
+
|
|
566
|
+
# Item mutations
|
|
567
|
+
ilml addItem <parentId> --title "Task" --desc "Description" --tags work,urgent
|
|
568
|
+
ilml addItem <parentId> --ref <nodeId> # Create link to another node
|
|
569
|
+
ilml editItem <nodeId> --title "New Title" --tags updated
|
|
570
|
+
ilml archiveItem <nodeId>
|
|
571
|
+
ilml unarchiveItem <nodeId>
|
|
572
|
+
ilml moveItem <nodeId> <fromParentId> <toParentId>
|
|
573
|
+
ilml reorderChild <parentId> <from> <to> # Reorder child (1-based positions)
|
|
574
|
+
ilml setPosition <nodeId> <parentId> <pos> # Move to position (1-based)
|
|
575
|
+
|
|
576
|
+
# Messaging
|
|
577
|
+
ilml send <nodeId> "message" # Send message (no AI)
|
|
578
|
+
ilml ask <nodeId> "question" # Ask AI and wait for response
|
|
579
|
+
ilml editMessage <msgId> "new content"
|
|
580
|
+
ilml archiveMessage <msgId>
|
|
581
|
+
|
|
582
|
+
# Management
|
|
583
|
+
ilml config # Show configuration
|
|
584
|
+
ilml me # Current user info
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Token Configuration
|
|
588
|
+
|
|
589
|
+
Token is resolved in order:
|
|
590
|
+
1. `.ilivemylife/config.json` in current directory (or parent directories)
|
|
591
|
+
2. `.env` file with `ILML_TOKEN` in current directory
|
|
592
|
+
3. `~/.ilivemylife/config.json` (global, set by `ilml login`)
|
|
593
|
+
4. `ILML_TOKEN` environment variable
|
|
594
|
+
|
|
595
|
+
This allows per-project tokens:
|
|
596
|
+
```bash
|
|
597
|
+
# Project A uses user A's token
|
|
598
|
+
project-a/.ilivemylife/config.json # via: ilml login --local
|
|
599
|
+
|
|
600
|
+
# Or use .env file
|
|
601
|
+
project-b/.env # ILML_TOKEN=xxx
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
## MCP Server (AI Assistant Integration)
|
|
607
|
+
[MCP (Model Context Protocol)](https://modelcontextprotocol.io/) allows AI assistants to interact with external tools. This SDK includes an MCP server that exposes graph operations to Claude, Cursor, Windsurf and other MCP-compatible tools.
|
|
608
|
+
|
|
609
|
+
### Setup
|
|
610
|
+
|
|
611
|
+
**Claude Code (quick):**
|
|
612
|
+
```bash
|
|
613
|
+
claude mcp add --env ILML_TOKEN=your-token ilml -- ilml-mcp
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**Claude Code (manual)** — add to `~/.claude/settings.json`:
|
|
617
|
+
|
|
618
|
+
```json
|
|
619
|
+
{
|
|
620
|
+
"mcpServers": {
|
|
621
|
+
"ilml": {
|
|
622
|
+
"command": "ilml-mcp",
|
|
623
|
+
"env": { "ILML_TOKEN": "your-token" }
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
**Other AI tools** — same JSON content, different config file:
|
|
630
|
+
|
|
631
|
+
| AI Tool | Config Path |
|
|
632
|
+
|---------|------------|
|
|
633
|
+
| Claude Code | `~/.claude/settings.json` or `claude mcp add` |
|
|
634
|
+
| Claude Desktop | macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
635
|
+
| | Windows: `%APPDATA%\Claude\claude_desktop_config.json` |
|
|
636
|
+
| Cursor | `~/.cursor/mcp.json` |
|
|
637
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
638
|
+
|
|
639
|
+
Restart your AI tool after adding the config.
|
|
640
|
+
|
|
641
|
+
### Available Tools
|
|
642
|
+
|
|
643
|
+
**Read Operations** (no confirmation needed):
|
|
644
|
+
- `graph_items` — Get node and children
|
|
645
|
+
- `graph_messages` — Get messages from node
|
|
646
|
+
- `graph_search_messages` — Search messages
|
|
647
|
+
- `graph_item_history` — Get change history
|
|
648
|
+
- `graph_item_users` — Get users with access
|
|
649
|
+
- `graph_item_settings` — Get node settings
|
|
650
|
+
- `graph_me` — Get current user info
|
|
651
|
+
- `graph_info` — Get documentation about graph features
|
|
652
|
+
|
|
653
|
+
**Write Operations** (require confirmation):
|
|
654
|
+
- `graph_add_item` — Create new node
|
|
655
|
+
- `graph_edit_item` — Edit node (title, description, tags)
|
|
656
|
+
- `graph_archive_item` / `graph_unarchive_item` — Archive/restore
|
|
657
|
+
- `graph_move_item` — Move to different parent
|
|
658
|
+
- `graph_reorder_child` / `graph_set_position` — Reorder
|
|
659
|
+
- `graph_add_message` — Send message
|
|
660
|
+
- `graph_ask_lifebot` — Ask AI and wait for response
|
|
661
|
+
|
|
662
|
+
### Example Prompts
|
|
663
|
+
|
|
664
|
+
```
|
|
665
|
+
"Show me items in node abc123"
|
|
666
|
+
"Create a task called 'Review PR' under node xyz789"
|
|
667
|
+
"What messages are in my root node?"
|
|
668
|
+
"Ask Lifebot to summarize this project"
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
---
|
|
672
|
+
|
|
673
|
+
## TypeScript
|
|
674
|
+
|
|
675
|
+
Full type definitions included:
|
|
676
|
+
|
|
677
|
+
```typescript
|
|
678
|
+
import type {
|
|
679
|
+
// Data types
|
|
680
|
+
Item, Message, User, ItemSettings,
|
|
681
|
+
|
|
682
|
+
// Client types
|
|
683
|
+
GraphClient, GraphClientOptions,
|
|
684
|
+
AddItemOptions, EditItemInput, AddMessageOptions,
|
|
685
|
+
|
|
686
|
+
// Subscription change types (for type-safe comparisons)
|
|
687
|
+
MessageChangeType, // '+1' | 'edit' | '-1'
|
|
688
|
+
ItemChangeType // '+1' | 'edit' | '-1' | 'movedIn' | 'reorder'
|
|
689
|
+
} from '@ilivemylife/graph-sdk';
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
---
|
|
693
|
+
|
|
694
|
+
## Publishing New Versions
|
|
695
|
+
|
|
696
|
+
```bash
|
|
697
|
+
# First time: login to npm
|
|
698
|
+
npm login
|
|
699
|
+
|
|
700
|
+
# First publish
|
|
701
|
+
npm publish --access public
|
|
702
|
+
|
|
703
|
+
# Update versions:
|
|
704
|
+
npm run release:patch # 1.0.0 → 1.0.1 (bug fixes)
|
|
705
|
+
npm run release:minor # 1.0.0 → 1.1.0 (new features)
|
|
706
|
+
npm run release:major # 1.0.0 → 2.0.0 (breaking changes)
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## License
|
|
712
|
+
|
|
713
|
+
© 2026 Ilya Sorokin. All rights reserved.
|
|
714
|
+
|
|
715
|
+
This package is proprietary software. For licensing inquiries, business partnerships, or enterprise usage, please contact the author.
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
## Author
|
|
720
|
+
|
|
721
|
+
Created by **Ilya Sorokin** — founder and developer of iLiveMyLife.
|
|
722
|
+
|
|
723
|
+
- **LinkedIn:** [linkedin.com/in/ilyasorokin](https://www.linkedin.com/in/ilyasorokin/)
|
|
724
|
+
- **Email:** info@ilivemylife.io
|
|
725
|
+
- **App:** [https://iLiveMyLife.io](https://iLiveMyLife.io)
|