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

@timefly/opencode-plugin

v0.2.12

Published

TimeFly telemetry plugin for OpenCode — sessions, tokens, models, and tools

Downloads

1,463

Readme

@timefly/opencode-plugin

TimeFly telemetry for OpenCode. Tracks sessions, models, tokens, tool calls, and errors — not prompt content.

Quick start

# 1. Add plugin to OpenCode config
bunx @timefly/opencode-plugin setup-opencode -- --target user

# 2. Sign in with your TimeFly account (Google OAuth)
bunx @timefly/opencode-plugin login

# 3. Restart OpenCode

Note: login and setup-opencode are subcommands. If login runs the installer again, update to v0.2.1+ or use bunx timefly-opencode-login.

You need a TimeFly Supporter plan for sync. Free accounts can install the plugin but POST /ai/sync returns 403.

Installation (step by step)

Prerequisites

  • OpenCode installed and working
  • Bun (OpenCode uses it internally; you need it for bunx)
  • TimeFly account with Supporter plan (pricing)

Option A — npm (when published)

# Global plugin (all projects on this machine)
bunx @timefly/opencode-plugin setup-opencode -- --target user

# Sign in once
bunx @timefly/opencode-plugin login

# Restart OpenCode

Option B — one project only

bunx @timefly/opencode-plugin setup-opencode -- --target project --project /path/to/your/repo
bunx @timefly/opencode-plugin login
# restart OpenCode in that project

Writes to ./opencode.json in the project root.

Option C — local development (monorepo)

cd ai-integrations
bun install
bun run build

bun run --filter @timefly/opencode-plugin setup-opencode -- --target user --local
bun run --filter @timefly/opencode-plugin login
# restart OpenCode

--local adds an absolute path to dist/index.js instead of the npm package name.

What setup-opencode changes

It merges into your OpenCode config (does not overwrite other settings):

| Target | Config file | |--------|-------------| | --target user | ~/.config/opencode/opencode.json (Windows: %USERPROFILE%\.config\opencode\opencode.json) | | --target project | <project>/opencode.json |

Example result:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@timefly/opencode-plugin@latest"]
}

OpenCode loads plugins at startup. You must restart OpenCode after install or config changes.

Verify it is loaded

After restart, use OpenCode normally (send a prompt, run a tool). Check:

  1. OpenCode logs — warnings if not signed in or not Supporter
  2. TimeFly dashboard → AI Usage card (may take ~10s after backend ingest)
  3. Local queue file — if sync fails, events appear in <project>/.timefly-ai-queue.json

How auth works

OpenCode plugins run inside the OpenCode process. There is no built-in TimeFly UI inside OpenCode — auth is handled outside:

| Step | What happens | |------|----------------| | login | Opens browser → Google OAuth → saves tokens to ~/.config/opencode/timefly-auth.json | | Plugin startup | Reads auth file from login | | Each sync | Sends Authorization: Bearer <accessToken> to POST /ai/sync | | Token expiry | SDK auto-refreshes using refreshToken (~30 days) and updates the auth file | | No auth | Events queue locally in .timefly-ai-queue.json (project cwd) |

How sync works

The SDK batches OpenCode events before sending them to TimeFly. This keeps VPS load predictable as usage grows.

| Setting | Default | Behavior | |---------|---------|----------| | Debounce | 5s | Flush pending events 5 seconds after the last event in a burst | | Max batch | 50 events | Flush immediately when the in-memory buffer is full | | Immediate flush | session_end | Session totals are sent right away |

Typical flow during a coding session:

  1. You send a prompt → llm_request is queued
  2. Model responds → turn_complete, tool events, etc. join the same batch
  3. After ~5s idle (or 50 events), one gzip POST /ai/sync sends the batch
  4. session_end flushes immediately so session totals are not delayed

Compared with the old per-event sync model, one active agent turn usually produces 1–2 HTTP requests instead of 10–15+.

Unlike the VS Code extension (timer every ~2 minutes), OpenCode still syncs in near real time — just batched within each active burst.

What each sync request does

1. flushPendingQueue()     ← retry anything in .timefly-ai-queue.json
2. gzip JSON batch         ← usually 1–3 events per request
3. POST /ai/sync           ← Bearer token from auth file
4. Gateway                 ← JWT validate + Supporter role check
5. Ingest → Redis queue    ← accepted immediately (202-style)
6. Backend worker (~5s)    ← drains Redis → ClickHouse
7. Dashboard               ← reads aggregated data from ClickHouse

End-to-end latency: ~1–10 seconds from event to dashboard (network + 5s worker poll).

Offline queue

If sync fails (no network, 401, 403), events append to:

<OpenCode project cwd>/.timefly-ai-queue.json

On the next successful sync, the SDK flushes this file first. Nothing is lost unless you delete the file.

Auth file location

~/.config/opencode/timefly-auth.json
{
  "accessToken": "...",
  "refreshToken": "...",
  "apiBaseUrl": "https://api.timefly.dev",
  "savedAt": "2026-06-20T..."
}

Access token expires in ~15 minutes; the SDK refreshes automatically using refreshToken (~30 days).

Architecture diagram

OpenCode hooks → @timefly/opencode-plugin → @timefly/ai-sdk
  → POST /ai/sync (gzip JSON, Bearer token)
  → Gateway auth + Supporter check
  → Ingest queue (Redis)
  → Worker (every 5s) → ClickHouse activity_events (ai.* activities)
  → Dashboard GET /analytics/ai-usage

If sync fails:

  • 401 — run login again
  • 403 — upgrade to Supporter at timefly.dev/pricing
  • Network — events stay in local queue and retry on next event

Errors are logged via OpenCode's client.app.log() (service: timefly-opencode-plugin).

Install options (quick reference)

# Global (all projects)
bunx @timefly/opencode-plugin setup-opencode -- --target user

# Single project
bunx @timefly/opencode-plugin setup-opencode -- --target project --project /path/to/repo

# Local dev build
bun run --filter @timefly/opencode-plugin setup-opencode -- --target user --local
bun run --filter @timefly/opencode-plugin login

Manual config

Add to opencode.json or ~/.config/opencode/opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@timefly/opencode-plugin@latest"]
}

OpenCode installs npm plugins automatically at startup via Bun. Re-run setup-opencode to refresh @latest and clear the plugin cache.

Events captured

Based on OpenCode source (packages/opencode, @opencode-ai/plugin hooks, and @opencode-ai/sdk event types). We only emit metadata — never prompts, tool args, or file contents.

Plugin hooks we use

| Hook | When it fires (OpenCode source) | TimeFly event | |------|----------------------------------|---------------| | event | Bus events forwarded from EventV2Bridge to all plugins | see bus table below | | chat.params | Before every LLM call (LLMRequestPrep.prepare) | llm_request | | tool.execute.before | Before built-in, MCP, and registry tools run (SessionTools.resolve) | tool_call | | tool.execute.after | After tool completes (same path) | tool_result | | experimental.session.compacting | Before compaction LLM call (SessionCompaction.process) | compaction |

Bus events we handle (event hook)

| OpenCode event | Status | TimeFly event | Data captured | |----------------|--------|---------------|---------------| | session.created | Active | session_start | title, project, directory | | session.status (type: idle) | Preferred | session_end | session token/tool/request totals | | session.idle | Deprecated alias | session_end | same as above | | message.updated (assistant, completed) | Active | turn_complete + llm_response | tokens, cost, duration, finish reason | | message.updated (user) | Active | llm_request | model, agent (metadata only) | | message.part.updated (step-finish) | Active | llm_response | per-step tokens, cost | | message.part.updated (retry) | Active | error | retry attempt | | message.part.updated (compaction) | Active | compaction | auto/manual flag | | session.compacted | Active | compaction | session id | | session.error | Active | error | error name/message | | command.executed | Active | tool_call | command name (as command:*) |

Bus events we intentionally skip

| OpenCode event | Why not tracked | |----------------|-----------------| | message.part.updated (text, reasoning, tool, …) | Would expose prompt/response/tool args | | message.removed, message.part.removed | Deletion only — no usage signal | | session.updated, session.deleted, session.diff | Metadata or file diffs — not AI usage | | file.edited, file.watcher.updated | File paths/content | | permission.asked, permission.replied | No token/model signal | | todo.updated | Task list text | | lsp.*, pty.*, tui.*, installation.*, server.* | IDE/infra — not LLM usage |

Hooks we do not use (available in OpenCode, not needed for usage telemetry)

| Hook | Reason | |------|--------| | chat.message | Full user message + parts — privacy | | chat.headers | Auth headers — security | | shell.env | Environment variables — secrets risk | | permission.ask | Could track denials; not implemented yet | | command.execute.before | Parts contain prompt fragments | | experimental.chat.messages.transform | Full message history | | experimental.compaction.autocontinue | No extra telemetry beyond compaction events | | tool (custom tools) | Execution still flows through tool.execute.* |

Known limitations

| Topic | Detail | |-------|--------| | Plugin cache | OpenCode caches plugins under ~/.cache/opencode/packages/. If you upgraded but still see old errors, delete @timefly there and restart OpenCode. | | Custom providers (e.g. nan) | OpenCode runtime passes flat Provider.Info (provider.id) via LLMRequestPrep.prepare — not provider.info.id. Plugin reads both runtime and typed ProviderContext shapes. | | Token counts | Require provider to report usage. If message.updated lacks token fields, turn events are skipped (no crash). | | Provider-side tools | Tools executed inside the provider (providerExecuted) may not hit tool.execute.* — no tool events for those. | | Multi-step turns | step-finish parts can emit extra llm_response events; message-level turn_complete deduplicates by message id. | | session.idle vs session.status | OpenCode marks session.idle deprecated; we handle both. | | v2 event system | OpenCode is migrating to session.next.* internally; plugins still receive v1 bus types above via the bridge. |

Quick reference (captured signals)

| OpenCode signal | TimeFly eventType | Data | |-----------------|---------------------|------| | session.created | session_start | title, project, directory | | session.status / session.idle | session_end | session token/tool/request totals | | chat.params | llm_request | model, provider, agent, temperature | | message.updated (assistant, completed) | turn_complete + llm_response | tokens, duration, tokens/s, cost | | message.part.updated (step-finish) | llm_response | per-step tokens | | tool.execute.* | tool_call / tool_result | tool name, output length | | session.compacted / compaction | compaction | auto/manual | | session.error / retry | error | error metadata |

Privacy

Metadata and token counts only. No prompts, responses, or file contents. See docs/PRIVACY.md.