npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ilivemylife/graph-sdk

v1.0.9

Published

SDK and CLI for iLiveMyLife Knowledge Graph

Readme

@ilivemylife/graph-sdk

SDK for iLiveMyLife Knowledge Graph.

What is iLiveMyLife?

iLiveMyLife is your personal informational wallet — a unified platform to organize everything in your life.

Key Features

  • Knowledge Graph — Structure your information as interconnected nodes, not just files and folders
  • Cross-platform — Apps for iOS, Android, Web, Desktop (macOS, Windows, Linux)
  • Real-time Sync — Changes sync instantly across all your devices
  • Push Notifications — Stay informed with smart notifications
  • Team Collaboration — Share nodes with team members, control privacy with tags (private/wallet/kyc/etc.)
  • AI Assistants (Lifebot) — Built-in AI that understands your knowledge graph context
  • Offline Support — Your data is synced on your device for offline access if not connected
  • Powerful Tags System — Customize node behavior with tags (privacy, AI assistance, colors, markers, and more)
  • Markers (Status System) — Track item status with customizable markers (like task states)
  • Rich Content — Support for text, images, files, links, checklists, due dates, and more
  • Robust API & SDK — Full-featured TypeScript SDK and GraphQL API for developers
  • CLI Tool — Command-line interface for scripting and automation
  • MCP Server — Integrate with AI assistants like Claude, Cursor, Windsurf via Model Context Protocol

Use Cases

  • Personal knowledge management
  • Team project coordination
  • Task and goal tracking
  • Note-taking with AI assistance
  • Information organization for any domain

Get the app: https://iLiveMyLife.io


Prerequisites

Getting Started

1. Install

npm install -g @ilivemylife/graph-sdk

2. Login

ilml login
ilml doctor  # verify installation

Token saved to ~/.ilivemylife/config.json. For per-project accounts, see Token Priority.

3. Add to AI tools (optional)

# Claude Code (global — available in all projects)
claude mcp add --scope user ilml -- npx -y @ilivemylife/graph-sdk

# Claude Code (current project only)
claude mcp add ilml -- npx -y @ilivemylife/graph-sdk

For Cursor, Windsurf, Claude Desktop — see MCP Setup.


Authentication Details

Config File

After ilml login, config is saved as JSON:

// ~/.ilivemylife/config.json
{
  "token": "your-access-token",
  "user": {
    "id": "user-id",
    "email": "[email protected]",
    "displayName": "Your Name"
  },
  "rootItemId": "your-root-node-id"
}

Token Priority

The SDK, CLI, and MCP server resolve tokens in this order (first found wins):

| Priority | Source | How to set | |----------|--------|------------| | 1 | Local config | ilml login --local.ilivemylife/config.json in project dir | | 2 | .env file | ILML_TOKEN=your-token in project dir | | 3 | Global config | ilml login~/.ilivemylife/config.json | | 4 | Environment variable | export ILML_TOKEN=your-token |

Use different accounts per project:

# Global account (default)
ilml login

# Project-specific account (overrides global)
cd my-project
ilml login --local

Other Authentication Methods

Programmatic login (for bots and automations):

const response = await fetch('https://api.ilivemylife.io/api/v1/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email: '[email protected]', password: '...' })
});
const token = response.headers.get('access-token');
const graph = createGraphClient({ token });

Browser DevTools (manual):

  1. Open https://app.ilivemylife.io and log in
  2. Open DevTools (F12) → Network tab
  3. Click any GraphQL request → Headers tab
  4. Copy access-token value

Finding Node IDs

Node IDs are visible in the URL when viewing a node:

https://app.ilivemylife.io/item/00001191f13c5c81-ee4ae8284f820001
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                 This is the node ID

Or get your root node ("My Life") programmatically:

const me = await graph.me();
console.log(me.rootItemId);  // Your root node ID

Quick Start

import { createGraphClient } from '@ilivemylife/graph-sdk';

// Create client (token from ilml login or env variable)
const graph = createGraphClient({
    token: process.env.ILML_TOKEN
});

// Get your root node
const me = await graph.me();
const rootId = me.rootItemId;

// See what's in your graph
const items = await graph.items(rootId);
console.log('My Life:', items[0].title);
console.log('Children:', items.slice(1).map(i => i.title));

// Create a new node
const project = await graph.addItem(rootId, { title: 'My Project' });

// Add children to it
await graph.addItem(project.id, { title: 'First Task' });
await graph.addItem(project.id, { title: 'Second Task' });

// Ask AI about it (requires 'assist' tag on the node)
const reply = await graph.askLifebot(project.id, 'What tasks do I have?');
console.log(reply.content);

For a complete walkthrough, run: node examples/getting-started.mjs

Features

  • TypeScript — Full type definitions included
  • Dual Format — Works with both ESM (import) and CommonJS (require)
  • Typed ErrorsGraphError, LifebotTimeoutError, LifebotRateLimitError
  • Injectable Logger — Pass your own logger or use silent mode
  • Real-time Subscriptions — Subscribe to message and item changes

Examples

See examples/ directory for complete usage examples:


API Reference

Creating a Client

import { createGraphClient } from '@ilivemylife/graph-sdk';

const graph = createGraphClient({
    token: 'your-access-token',      // Required
    logger: console                   // Optional: enable logging
});

Queries (Read Operations)

// Get node and its children
const items = await graph.items(nodeId);
// items[0] = node itself, items[1...n] = children

// Get messages
const messages = await graph.messages(nodeId, 20);

// Get change history
const history = await graph.itemHistory(nodeId);

// Get users with access
const users = await graph.itemUsers(nodeId);

// Get child count
const { count } = await graph.subItemCount(nodeId);

// Get node settings
const settings = await graph.itemSettings(nodeId);

// Edit node settings (AI provider, notifications, etc)
import { AI_PROVIDER_TYPES, TOGGLE_VALUES } from '@ilivemylife/graph-sdk';

await graph.editItemSettings({
    itemId: nodeId,
    artificialIntelligenceProvider: AI_PROVIDER_TYPES.openai,  // switch AI to OpenAI
    intelligence: TOGGLE_VALUES.enabled                         // use smart model
});

// Reset to app defaults (user's global settings)
await graph.editItemSettings({
    itemId: nodeId,
    artificialIntelligenceProvider: null,  // null = use user's app default
    intelligence: null
});

// Search messages
const found = await graph.searchMessages(nodeId, 'keyword');

Mutations (Write Operations)

// Create item (all fields optional, node added to end of list by default)
const item = await graph.addItem(parentId, {
    title: 'New Item',
    description: 'Description',
    tags: ['tag1', 'tag2'],
    dueDate: '1735689600000'  // timestamp string (ms)
});

// Create link/shortcut to another node
const link = await graph.addItem(parentId, {
    refId: originalNodeId,        // reference to original node
    title: 'Custom Title',        // override original title
    description: 'Custom Desc'    // override original description
});

// Edit item
await graph.editItem({
    id: itemId,
    title: 'Updated Title'
});

// Archive/unarchive
await graph.archiveItem(itemId);
await graph.unarchiveItem(itemId);

// Move item to different parent
await graph.moveItem(itemId, fromParentId, toParentId);

// Reorder within parent (positions are 1-based)
await graph.reorderChild(parentId, fromPosition, toPosition);
// Example: move child from position 4 to position 1
await graph.reorderChild(parentId, 4, 1);

// Move specific node to position (positions are 1-based)
await graph.setPosition(nodeId, parentId, position);
// Example: move node to position 2
await graph.setPosition(nodeId, parentId, 2);

Messages

import { LIFEBOT_MESSAGE_TYPES, MESSAGE_TYPES } from '@ilivemylife/graph-sdk';

// Simple message (no AI)
await graph.addMessage(nodeId, 'Hello!', {
    lifebot: LIFEBOT_MESSAGE_TYPES.off
});

// Message that triggers AI response
await graph.addMessage(nodeId, 'Help me with this', {
    lifebot: LIFEBOT_MESSAGE_TYPES.on
});

// Reply to a message
await graph.addMessage(nodeId, 'Thanks!', {
    type: MESSAGE_TYPES.reply,
    refId: originalMessageId
});

// Edit/archive messages
await graph.editMessage({ id: msgId, content: 'Updated' });
await graph.archiveMessage(msgId);

Real-time Subscriptions

import { MESSAGE_CHANGE_TYPES, ITEM_CHANGE_TYPES } from '@ilivemylife/graph-sdk';

// Subscribe to messages
const unsubscribeMessages = await graph.subscribeToMessages(nodeId, (change, message) => {
    if (change === MESSAGE_CHANGE_TYPES.added) {
        console.log('New message:', message.content);
    } else if (change === MESSAGE_CHANGE_TYPES.edited) {
        console.log('Message edited:', message.content);
    } else if (change === MESSAGE_CHANGE_TYPES.deleted) {
        console.log('Message deleted:', message.id);
    }
});

// Subscribe to item changes
const unsubscribeItems = await graph.subscribeToItems(nodeId, (change, item) => {
    if (change === ITEM_CHANGE_TYPES.added) {
        console.log('New item:', item.title);
    } else if (change === ITEM_CHANGE_TYPES.movedIn) {
        console.log('Item moved here:', item.title);
    } else if (change === ITEM_CHANGE_TYPES.reorder) {
        console.log('Item reordered:', item.title);
    }
});

// Stop listening
unsubscribeMessages();
unsubscribeItems();

// Wait for specific message
const msg = await graph.waitForMessage(nodeId,
    (change, msg) => change === MESSAGE_CHANGE_TYPES.added && msg.createdBy === 'Lifebot',
    30000  // timeout
);

// Wait for any reply after your message
const reply = await graph.waitForReply(nodeId, myMessageId);

// Wait for a reply that directly references your message (via refId)
const exactReply = await graph.waitForReply(nodeId, myMessageId, { exactMatch: true, timeout: 60000 });

AI Interactions

import { LifebotTimeoutError, LifebotRateLimitError } from '@ilivemylife/graph-sdk';

try {
    const response = await graph.askLifebot(nodeId, 'What is 2 + 2?', {
        timeout: 60000  // 60 seconds
    });
    console.log(response.content);
} catch (error) {
    if (error instanceof LifebotTimeoutError) {
        console.error('AI did not respond in time');
    } else if (error instanceof LifebotRateLimitError) {
        console.error('Rate limit exceeded');
    }
}

What is Lifebot?

Lifebot is the AI assistant that lives inside your iLiveMyLife knowledge graph. It can:

  • Answer questions about your data and context
  • Search and find information across your nodes
  • Create new nodes with titles and descriptions
  • Organize information by creating nested structure
  • Execute actions on your behalf
// Ask Lifebot to create a project with sub-tasks
await graph.askLifebot(nodeId,
    'Create a node "Weekly Plan" with 3 tasks inside: Monday review, Wednesday sync, Friday summary'
);
// Lifebot will create the parent node AND all children!

Enable Lifebot: Add assist tag to a node to enable Lifebot in that context.

Helper Methods

// Get single item
const item = await graph.getItem(nodeId);

// Resolve reference (shortcut) to actual node
const actual = await graph.resolveRef(nodeId);

// Get items with references resolved
const items = await graph.itemsResolved(nodeId);

Cleanup

// Close all connections and free resources when done
graph.destroy();

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).

Validation

import { isValidNodeId, parseNodeId, validateNodeId } from '@ilivemylife/graph-sdk';

// Check if string is valid node ID
isValidNodeId('00001191f13c5c81-ee4ae8284f820001') // true
isValidNodeId('invalid') // false

// Parse node ID from raw ID or full URL
parseNodeId('00001191f13c5c81-ee4ae8284f820001')
// → '00001191f13c5c81-ee4ae8284f820001'

parseNodeId('https://app.ilivemylife.io/item/00001191f13c5c81-ee4ae8284f820001')
// → '00001191f13c5c81-ee4ae8284f820001'

parseNodeId('invalid')
// → null

// Validate and throw error if invalid
validateNodeId(userInput); // throws GraphError if invalid

REST API

// Login
const { user, accessToken } = await graph.login(email, password);

// Get current user
const me = await graph.me();

Error Handling

import {
    GraphError,           // Base error
    GraphNetworkError,    // Network/connection issues
    GraphAuthError,       // Authentication failed
    LifebotError,         // AI error base
    LifebotTimeoutError,  // AI timeout
    LifebotRateLimitError // AI rate limit
} from '@ilivemylife/graph-sdk';

try {
    await graph.addItem(nodeId, { title: 'Test' });
} catch (error) {
    if (error instanceof GraphAuthError) {
        console.error('Check your token');
    } else if (error instanceof GraphError) {
        console.error('Operation failed:', error.code);
    }
}

Constants

import {
    // Message options
    MESSAGE_TYPES,         // normal, reply, forward
    LIFEBOT_MESSAGE_TYPES, // on, off
    NOTIFY_MESSAGE_TYPES,  // on, off, postpone

    // Subscription change types
    MESSAGE_CHANGE_TYPES,  // added ('+1'), edited ('edit'), deleted ('-1')
    ITEM_CHANGE_TYPES,     // added ('+1'), edited ('edit'), deleted ('-1'), movedIn ('movedIn'), reorder ('reorder')

    // Access and settings
    ITEM_ACCESS_TYPES,     // human, bot, request
    AI_PROVIDER_TYPES,     // openai, claude, gemini
    TOGGLE_VALUES,         // enabled, disabled (for intelligence, lifebotRootAccess)
    NODE_SPECIAL_TYPES     // user_settings, user_system, non_drop
} from '@ilivemylife/graph-sdk';

// Subscription change values:
// MESSAGE_CHANGE_TYPES.added   === '+1'
// MESSAGE_CHANGE_TYPES.edited  === 'edit'
// MESSAGE_CHANGE_TYPES.deleted === '-1'
//
// ITEM_CHANGE_TYPES.added      === '+1'
// ITEM_CHANGE_TYPES.edited     === 'edit'
// ITEM_CHANGE_TYPES.deleted    === '-1'
// ITEM_CHANGE_TYPES.movedIn    === 'movedIn'
// ITEM_CHANGE_TYPES.reorder    === 'reorder'

Tags & Markers

Tags control node behavior. Format: #tagname#value or just tagname.

Special Tags

| Tag | Description | |-----|-------------| | 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 | | 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 | | assist | Enables Lifebot AI in this node | | #color#r,g,b,a | Node background color | | #markerList#<listId> | Enables status dropdown for children | | #marker#<statusId> | Sets item status from marker list |

Markers (Status System)

Markers let you track status of items (like task states). It's a pattern, not a built-in feature:

  1. Create a marker list — a node with status options as children (emoji titles)
  2. Enable on parent — add #markerList#<listId> tag
  3. Set status — add #marker#<statusId> tag to items
// Enable default markers on a parent node
await graph.editItem({
    id: parentId,
    tags: ['#markerList#0000018190aaf813-a61a6f263e6a0000']
});

// Mark item as completed (default marker)
await graph.editItem({
    id: taskId,
    tags: ['#marker#0000018195a2d792-a61a6f263e6a0000']  // ☑️ Completed
});

See CLAUDE.md for full marker documentation and default marker IDs.


CLI Usage

# Install globally
npm install -g @ilivemylife/graph-sdk

# Login (saves token to ~/.ilivemylife/config.json)
ilml login

# Queries (read operations) - all support --json for scripting
ilml items <nodeId>              # List node and children (short)
ilml items <nodeId> --single     # Node only, no children
ilml items <nodeId> --long       # Full details (all fields)
ilml items <nodeId> --json       # JSON output (for scripting)
ilml items <nodeId> --ids        # IDs only (for piping)
ilml messages <nodeId> [count]   # Show messages (short)
ilml messages <nodeId> --long    # Full message details
ilml messages <nodeId> --json    # JSON output
ilml itemHistory <nodeId>        # Show change history
ilml itemHistory <nodeId> --json # JSON output
ilml itemUsers <nodeId> --json   # Users with access
ilml subItemCount <nodeId>       # Get children count
ilml itemSettings <nodeId>       # Show node settings
ilml search <nodeId> "text"      # Search messages
ilml tree <nodeId> [depth]       # Show tree structure (truncated)
ilml tree <nodeId> --long        # Full titles, no truncation

# Item mutations
ilml addItem <parentId> --title "Task" --desc "Description" --tags work,urgent
ilml addItem <parentId> --ref <nodeId>  # Create link to another node
ilml editItem <nodeId> --title "New Title" --tags updated
ilml archiveItem <nodeId>
ilml unarchiveItem <nodeId>
ilml moveItem <nodeId> <fromParentId> <toParentId>
ilml reorderChild <parentId> <from> <to>  # Reorder child (1-based positions)
ilml setPosition <nodeId> <parentId> <pos>  # Move to position (1-based)

# Messaging
ilml send <nodeId> "message"     # Send message (no AI)
ilml ask <nodeId> "question"     # Ask AI and wait for response
ilml editMessage <msgId> "new content"
ilml archiveMessage <msgId>

# Management
ilml config                      # Show configuration
ilml me                          # Current user info

Token Configuration

Token is resolved in order:

  1. .ilivemylife/config.json in current directory (or parent directories)
  2. .env file with ILML_TOKEN in current directory
  3. ~/.ilivemylife/config.json (global, set by ilml login)
  4. ILML_TOKEN environment variable

This allows per-project tokens:

# Project A uses user A's token
project-a/.ilivemylife/config.json   # via: ilml login --local

# Or use .env file
project-b/.env                        # ILML_TOKEN=xxx

MCP Server (AI Assistant Integration)

MCP (Model Context Protocol) 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.

Setup

Prerequisites: Run ilml login first (see Getting Started).

Claude Code:

# Global (available in all projects)
claude mcp add --scope user ilml -- ilml-mcp

# Or current project only
claude mcp add ilml -- ilml-mcp

If you didn't install globally (npm install -g), use npx:

claude mcp add --scope user ilml -- npx -y @ilivemylife/graph-sdk

Other AI tools — add to config file:

{
  "mcpServers": {
    "ilml": {
      "command": "npx",
      "args": ["-y", "@ilivemylife/graph-sdk"]
    }
  }
}

Or if installed globally:

{
  "mcpServers": {
    "ilml": {
      "command": "ilml-mcp"
    }
  }
}

| AI Tool | Config Path | |---------|------------| | Claude Code | claude mcp add (recommended) or ~/.claude/settings.json | | Claude Desktop | macOS: ~/Library/Application Support/Claude/claude_desktop_config.json | | | Windows: %APPDATA%\Claude\claude_desktop_config.json | | Cursor | ~/.cursor/mcp.json | | Windsurf | ~/.codeium/windsurf/mcp_config.json | | IntelliJ IDEA | Settings → Tools → AI Assistant → Model Context Protocol (MCP) |

Token is resolved automatically: local config > .env > global config > ILML_TOKEN env var. Restart your AI tool after adding the config.

Available Tools

Read Operations (no confirmation needed):

  • graph_items — Get node and children
  • graph_messages — Get messages from node
  • graph_search_messages — Search messages
  • graph_item_history — Get change history
  • graph_item_users — Get users with access
  • graph_item_settings — Get node settings
  • graph_me — Get current user info
  • graph_info — Get documentation about graph features

Write Operations (require confirmation):

  • graph_add_item — Create new node
  • graph_edit_item — Edit node (title, description, tags)
  • graph_edit_settings — Edit node settings (AI provider, notifications)
  • graph_archive_item / graph_unarchive_item — Archive/restore
  • graph_move_item — Move to different parent
  • graph_reorder_child / graph_set_position — Reorder
  • graph_add_message — Send message
  • graph_ask_lifebot — Ask AI and wait for response

Example Prompts

"Show me items in node abc123"
"Create a task called 'Review PR' under node xyz789"
"What messages are in my root node?"
"Ask Lifebot to summarize this project"
"Switch AI to OpenAI for node xyz789"
"Use cheaper AI model for this node to save costs"

TypeScript

Full type definitions included:

import type {
    // Data types
    Item, Message, User, ItemSettings,

    // Client types
    GraphClient, GraphClientOptions,
    AddItemOptions, EditItemInput, EditItemSettingsInput, AddMessageOptions,

    // Subscription change types (for type-safe comparisons)
    MessageChangeType,  // '+1' | 'edit' | '-1'
    ItemChangeType      // '+1' | 'edit' | '-1' | 'movedIn' | 'reorder'
} from '@ilivemylife/graph-sdk';

License

© 2026 Ilya Sorokin. All rights reserved.

This package is proprietary software. For licensing inquiries, business partnerships, or enterprise usage, please contact the author.


Author

Created by Ilya Sorokin — founder and developer of iLiveMyLife.