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

@egradman/jio

v1.0.0

Published

Personal bookmark manager - extract, index, and search vault links

Readme

jio

I keep my brain in Obsidian. Throughout the day, as I collect useful links either for work or for personal projects or fun, I jam them in Obsidian. Hence the name.

Jio is designed for people who keep notes in Markdown files. It scans your vault for links, builds a full-text search index, and lets you find any bookmark instantly -- from the terminal, a browser search bar, or any HTTP client.

It extracts URLs from your Markdown vault, indexes them with SQLite FTS5, and provides fast search through both a CLI and an HTTP API. It has a Cloudflare Worker, and a tool to sync your database to that worker, enabling you to search your bookmarks using an OpenSearch compatible API (Chrome custom search engine, Raycast Quicklink, etc).

I primarily interact with it using Openclaw. My HEARTBEAT.md includes a directive to rebuild and sync my bookmarks. I can also ask Openclaw to find any "forgotten links," which have been added but not navigated in the first week, as a weak proxy for things I added but did not follow up on.

Because I don't like to mix business and business and business and personal, you can scope your bookmarks by name and create tokens that restrict the search engine to individual scopes: so the Chrome custom search engine on my Dayjob Mac only has access to Dayjob links.

Jio is distributed as a skill. Install it in OpenClaw, ask OpenClaw to configure it for your Cloudflare Workers, assign a master token, deploy it, and enjoy!

Features

  • Vault scanning -- Extracts Markdown links ([title](url)) and bare URLs from .md files
  • Full-text search -- SQLite FTS5 with Porter stemming and Unicode support
  • Scopes -- Organize bookmarks by project or category using directory-level config or YAML frontmatter
  • HTTP API -- Token-authenticated search with OpenSearch-compatible JSON responses
  • Browser integration -- Add as a custom search engine in Chrome, Firefox, or any browser
  • Click tracking -- Tracks which links you actually use and when
  • Forgotten links -- Surface bookmarks you saved but never visited
  • CLI-first -- Full functionality available from the command line

Installation

Run directly with npx (no install)

npx @egradman/jio refresh --vault ~/notes
npx @egradman/jio search "kubernetes deploy"

Install globally

npm install -g @egradman/jio

Then use jio directly:

jio refresh --vault ~/notes
jio search "kubernetes deploy"

Install from source

git clone https://github.com/egradman/jio.git
cd jio
npm install
npm link

Requirements

  • Node.js 18+
  • A directory of Markdown files (your "vault")

Quick Start

# Import bookmarks from your vault
jio refresh --vault ~/notes

# Search from the terminal
jio search "kubernetes deploy"

# Open the first matching result in your browser
jio open "github actions"

All commands also work with npx @egradman/jio if you haven't installed globally.

Configuration

Config File (jio_conf.json)

The CLI looks for a jio_conf.json config file in three locations (first found wins):

  1. ./jio_conf.json (project directory)
  2. ~/.config/jio_conf.json (XDG config)
  3. ~/.jio_conf.json (home directory)
cp jio_conf.example.json jio_conf.json
# Edit jio_conf.json with your values
{
  "masterToken": "your-secret-here",
  "syncServer": "https://jio.example.com"
}

This is the recommended way to configure jio sync. The file is gitignored. For per-project config, place it in the project directory. For global config, place it in your home directory as ~/.jio_conf.json.

Environment Variables

Create a .env file in the project root for CLI settings:

# Path to SQLite database (default: ./bookmarks.db)
DB_PATH=./bookmarks.db

# Path to your Markdown vault (default: ~/vault)
VAULT_PATH=~/vault

Environment variables MASTER_TOKEN and SYNC_SERVER are also supported as a fallback for jio sync, but jio_conf.json is preferred.

Scopes

Scopes let you organize bookmarks into categories. There are two ways to assign scopes:

Directory-level -- Place a .jio.json file in any vault directory:

{
  "scope": "work"
}

All links discovered in that directory (and subdirectories) inherit the scope.

File-level -- Add YAML frontmatter to any Markdown file:

---
jio_scope: personal
---

# My notes
...

File-level scopes override directory-level scopes.

CLI Reference

jio refresh

Scan your vault and import/update bookmarks.

jio refresh [--vault <path>] [--quiet]

| Option | Description | |--------|-------------| | --vault <path> | Path to vault directory (default: $VAULT_PATH or ~/vault) | | --quiet | Suppress output |

jio search <query>

Search bookmarks from the terminal.

jio search "docker compose" [--scope <scope>] [--limit <n>] [--json]

| Option | Description | |--------|-------------| | --scope <scope> | Filter results to a specific scope | | --limit <n> | Maximum number of results | | --json | Output as JSON |

jio open <query>

Search and open the first matching result in your default browser.

jio open "grafana dashboards"

jio forgotten

Find bookmarks you saved but never clicked. Queries the remote Cloudflare Worker (not the local database).

jio forgotten [--days <n>] [--scope <scope>] [--limit <n>] [--all] [--json] [--server <url>] [--token <token>]

| Option | Description | |--------|-------------| | --days <n> | Minimum age in days (default: 14) | | --scope <scope> | Filter by scope | | --limit <n> | Maximum number of results (default: 20) | | --all | Show all forgotten links (sets limit to 1000) | | --json | Output as JSON | | --server <url> | Worker URL (overrides jio_conf.json and SYNC_SERVER) | | --token <token> | Master token (overrides jio_conf.json and MASTER_TOKEN) |

jio stats

Display bookmark statistics. Queries the remote Cloudflare Worker (not the local database).

jio stats [--json] [--server <url>] [--token <token>]

| Option | Description | |--------|-------------| | --json | Output as JSON | | --server <url> | Worker URL (overrides jio_conf.json and SYNC_SERVER) | | --token <token> | Master token (overrides jio_conf.json and MASTER_TOKEN) |

jio tokens

Manage API tokens for HTTP access.

# List all tokens
jio tokens list [--json]

# Create a new token
jio tokens add --name "chrome" [--scope <scope>]

# Delete a token
jio tokens delete --id <id>

Tokens are read-only and used for search. Tokens with a --scope can only search within that scope. Tokens without a scope have access to all bookmarks. Write operations (sync, token management on the Worker) use the MASTER_TOKEN env var instead.

jio sync

Push local bookmarks to a remote Cloudflare Worker.

jio sync [--server <url>] [--token <master-token>]

| Option | Description | |--------|-------------| | --server <url> | Worker URL | | --token <token> | Master token |

Resolution order for both values: CLI flags > jio_conf.json > environment variables (SYNC_SERVER, MASTER_TOKEN).

If you have jio_conf.json configured, just run jio sync with no flags.

HTTP API (Cloudflare Worker)

These endpoints are served by the Cloudflare Worker. Search endpoints require a read-only API token. Write endpoints require the master token.

GET /

Returns an HTML page with usage instructions for the API.

GET /search/:token?q=<query>

Search bookmarks.

Parameters:

| Parameter | Description | |-----------|-------------| | q | Search query string |

Behavior:

  • Returns up to 10 results ranked by FTS5 relevance
  • If the query ends with a number (e.g., "grafana 2"), that number selects a specific result from the list
  • Scoped tokens only return results within their scope
  • Accepts Accept: application/json for JSON responses

JSON response format (OpenSearch Suggestions):

[
  "query",
  ["Title 1", "Title 2"],
  ["Description 1", "Description 2"],
  ["https://url1.com", "https://url2.com"]
]

HTML response: An interactive results page with keyboard navigation (press 1-9 to open a result).

GET /go/:token?url=<url>

Redirect to a URL while tracking the click. Increments the followed_count and updates last_followed for the bookmark.

POST /import/:masterToken

Import links into the database. Requires the master token.

Request body:

{
  "links": [
    { "url": "https://example.com", "title": "Example", "description": "...", "scope": "work" }
  ]
}

Response:

{ "imported": 42 }

Links are upserted -- existing URLs have their title, description, and scope updated.

GET /tokens/:masterToken

List all API tokens. Requires the master token.

POST /tokens/:masterToken

Create a new API token. Requires the master token.

Request body:

{ "name": "chrome", "scope": "work" }

Response:

{ "token": "abc123...", "name": "chrome", "scope": "work" }

DELETE /tokens/:masterToken/:id

Delete an API token by ID. Requires the master token.

GET /stats/:masterToken

Returns bookmark statistics. Requires the master token.

Response:

{
  "total": 500,
  "withClicks": 42,
  "totalClicks": 128,
  "forgottenCount": 200,
  "byScope": [{ "scope": "work", "count": 300 }, { "scope": "personal", "count": 200 }],
  "topClicked": [{ "url": "https://example.com", "title": "Example", "scope": "work", "followed_count": 15 }]
}

GET /forgotten/:masterToken

Returns links that were saved but never clicked. Requires the master token.

| Parameter | Description | |-----------|-------------| | days | Minimum age in days (default: 14) | | scope | Filter by scope | | limit | Maximum number of results (default: 20) |

Response:

{
  "forgotten": [{ "url": "https://example.com", "title": "Example", "scope": "work", "discovered_at": "2025-01-01", "followed_count": 0 }],
  "days": 14,
  "count": 1
}

Browser Search Engine Setup

After deploying the Worker and creating a token, add jio as a custom search engine in your browser:

URL: https://jio.<your-subdomain>.workers.dev/search/YOUR_TOKEN?q=%s

In Chrome: Settings > Search engine > Manage search engines > Add.

Database

Jio uses SQLite with WAL mode for concurrent reads. The database is created automatically on first run and contains:

  • links -- Bookmark records with URL, title, description, scope, and usage stats
  • links_fts -- FTS5 virtual table for full-text search (Porter stemmer, Unicode61 tokenizer)
  • api_tokens -- Token-based API authentication

The database file is stored at DB_PATH (default: ./bookmarks.db).

How Link Extraction Works

During jio refresh, Jio scans all .md files in your vault and extracts:

  1. Markdown links -- [link text](https://example.com) -- the link text becomes the title
  2. Bare URLs -- https://example.com found anywhere in the text

It automatically filters out:

  • Localhost/loopback URLs
  • Asset files (.png, .jpg, .css, .js, .woff, etc.)
  • Duplicate URLs (only the first occurrence is stored)

Links are upserted -- existing URLs are updated with new metadata, and new URLs are inserted.

Cloudflare Workers Deployment

Jio runs on Cloudflare Workers with D1 for globally distributed, serverless bookmark search.

Authenticate with Cloudflare

If deploying from a headless/SSH machine, wrangler login can't open a browser directly. Two options:

Port-forward (recommended for one-off setup):

# On your local machine
ssh -L 8976:localhost:8976 your-server

# On the remote machine
npx wrangler login --browser false
# Open the printed URL in your local browser to authorize

API token (better for CI or persistent remote access):

  1. Go to https://dash.cloudflare.com/profile/api-tokens
  2. Create a token using the "Edit Cloudflare Workers" template
  3. Export it:
    export CLOUDFLARE_API_TOKEN="your-token"

Setup and Deploy

# 1. Install dependencies
npm install

# 2. Create the D1 database
npx wrangler d1 create jio-db
# Copy the database_id from the output into wrangler.toml

# 3. Run schema migrations against remote D1
npx wrangler d1 migrations apply jio-db --remote

# 4. Generate a master token and set it everywhere
MASTER_TOKEN=$(openssl rand -hex 32)
echo "$MASTER_TOKEN" | npx wrangler secret put MASTER_TOKEN

# Write CLI config so `jio sync` works
mkdir -p ~/.config
cat > ~/.config/jio_conf.json <<EOF
{
  "masterToken": "$MASTER_TOKEN",
  "syncServer": "https://jio.<your-subdomain>.workers.dev"
}
EOF

# 5. Deploy the Worker
npx wrangler deploy

# 6. Sync local bookmarks to the Worker
jio refresh --vault ~/vault
jio sync

Managing Search Tokens

Create and manage search tokens on the Worker using the master token:

# Create a search token
curl -X POST https://jio.example.com/tokens/<master-token> \
  -H 'Content-Type: application/json' \
  -d '{"name": "chrome", "scope": "work"}'

# List tokens
curl https://jio.example.com/tokens/<master-token>

# Delete a token
curl -X DELETE https://jio.example.com/tokens/<master-token>/1

Alternative: SQL dump (for initial setup or bulk replace):

sqlite3 bookmarks.db .dump > d1-import.sql
npx wrangler d1 execute jio-db --file=d1-import.sql

Local Development

# Copy the example and set your local master token
cp .dev.vars.example .dev.vars

# Start the local Worker
npm run worker:dev

This starts a local Worker with a local D1 database at http://localhost:8787. Wrangler automatically loads .dev.vars as environment variables during local development. The .dev.vars file is gitignored.

Environment Variables

Set BASE_URL in wrangler.toml under [vars] for non-secret config:

[vars]
BASE_URL = "https://jio.example.com"

MASTER_TOKEN should never go in wrangler.toml (it would be committed to git). Instead:

  • Local dev: Put it in .dev.vars (gitignored, loaded automatically by wrangler dev)
  • Production: Set it with npx wrangler secret put MASTER_TOKEN (encrypted, stored by Cloudflare)

OpenClaw Skill

Jio is distributed as an OpenClaw skill. The SKILL.md file contains the skill definition that tells OpenClaw how to configure and use jio. You can install this skill in OpenClaw to enable your AI agent to manage and search your bookmarks automatically, including refreshing your vault, querying forgotten links, and managing tokens.

License

ISC