跳到主要内容
    ↑↓ 选择↵ 打开esc 关闭
    中文English
    ADORSYS-GIS

    Otel

    v0.14.1认证与凭证
    @vymalo/opencode-otel

    OpenTelemetry plugin for OpenCode: exports developer interactions as OTLP traces, metrics and logs — real USD cost, all five token types (including cache read/write and reasoning), tool results, permission decisions, API errors and lines of code. Configur

    GitHub 星标

    4

    月装机量

    317

    近 7 天 317

    综合评分SCORE

    41.1

    生态多维模型

    最近提交

    17 小时前

    2026-08-19

    快速安装与配置

    opencode.json

    写入当前项目的 opencode.json,只对这个仓库生效。

    opencode.json

    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": ["@vymalo/opencode-otel@0.14.1"]
    }

    opencode 启动时会通过内嵌运行时自动加载 npm 依赖并缓存至本地目录,无需手动在全局环境执行安装。

    Eight small plugins that make OpenCode feel like it's running on your own infrastructure — OAuth-secured providers, rich model metadata, polite rate-limiting, hands in a real browser, local utilities, and OpenTelemetry export.

    CI license: MIT node: >=22 pnpm workspace built with TypeScript formatted with Biome

    @vymalo/opencode-oauth2 @vymalo/opencode-models-info @vymalo/opencode-ratelimit @vymalo/opencode-browser @vymalo/opencode-browser-mcp @vymalo/opencode-devtools @vymalo/opencode-devtools-mcp @vymalo/opencode-otel


    OpenCode is great at talking to providers — as long as your provider is a hosted SaaS with a static API key and a tidy /v1/models endpoint. The moment you put your models behind a corporate IdP, a self-hosted gateway, a per-window rate limit, or you want the model to actually click around a web page, you're on your own.

    The OpenCode Toolbelt is the set of @vymalo/* plugins that fill those gaps. Each one is small, auth-agnostic where it can be, independently published, and does exactly one job. Reach for one, or snap several onto the same provider — they're designed to stack.

    What's in the belt

    🔧 Tool npm What it does
    oauth2 @vymalo/opencode-oauth2 Wire up OpenAI-compatible providers sitting behind OAuth2 / OIDC — five auth flows, dynamic model discovery, a persistent token cache, and a fresh Authorization header on every request. No long-lived keys in your config.
    models-info @vymalo/opencode-models-info Enrich your model list with real metadata — context length, output limit, USD/M-token cost, modalities, and tool_call / reasoning / attachment flags — from any OpenRouter-shaped /models endpoint.
    ratelimit @vymalo/opencode-ratelimit Teach a provider to respect the rate-limit headers your gateway already sends. Reads Envoy / IETF draft-03 x-ratelimit-*, throttles before you hit the wall, and backs off + retries on 429.
    browser @vymalo/opencode-browser Give the model hands in a real browser: 34 browser_* tools (open, click, type, scroll, screenshot, snapshot, human-in-the-loop feedback…) over a localhost bridge that a companion extension drives.
    browser-mcp @vymalo/opencode-browser-mcp The same browser tools, exposed as an MCP stdio server — so Claude Code, Cursor, Cline, and any other MCP client can drive the extension too.
    devtools @vymalo/opencode-devtools A belt of everyday local utilities the model can call — math (precise eval, units, stats), codec (base64/hex/url, JWT decode, gzip), crypto (hash/hmac, uuid/ulid, keypairs), datetime (parse/format/diff, timezones, cron), convert (JSON/YAML/TOML/CSV + JSONPath), and an opt-in, SSRF-guarded http client. Deterministic, zero-auth, no server.
    devtools-mcp @vymalo/opencode-devtools-mcp The same utilities, exposed as an MCP stdio server for any MCP client.
    otel @vymalo/opencode-otel See what your agent is actually doing and costing: OTLP traces, metrics and logs — real USD cost, all five token types (cache read/write included), tool results, permission decisions, API errors, lines of code, repository metadata, and export-failure visibility. Inert until an endpoint is configured. What Claude Code and Codex export, for OpenCode.
    🧩 extension apps/browser-extension The companion Chromium + Firefox extension that the browser plugin / MCP server talk to. Private — ships as a Release asset and on the Chrome Web Store / Firefox AMO.
    flowchart LR
        OC([OpenCode / MCP client])
        subgraph belt[🧰 OpenCode Toolbelt]
            direction TB
            A[oauth2]
            M[models-info]
            R[ratelimit]
            B[browser]
        end
        OC --> belt
        A -->|Bearer token| IdP[(OAuth / OIDC server)]
        M -->|metadata| Meta[(/models endpoint)]
        R -->|x-ratelimit-*| GW[(Gateway)]
        B <-->|ws bridge| Ext[Browser extension]
        belt --> Up[(Your provider API)]
    

    [!TIP] Every plugin here works with any auth scheme — static API key, OAuth2, or none — except oauth2 itself, which is the one doing the authenticating. Enable only what you need.

    [!NOTE] The workspace also carries an experimental, private plugin — @vymalo/opencode-code-index (DuckDB + tree-sitter code indexing: code_symbol, code_callers, code_blast_radius, …). It is not published, not part of the published suite above, and may be removed. See docs/code-index.md.

    Quickstart

    Add the plugins you want to your OpenCode config and declare a provider. The smallest possible setup — an OAuth2-protected provider that discovers its own models:

    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": ["@vymalo/opencode-oauth2"],
      "provider": {
        "example-ai": {
          "name": "Example AI",
          "options": {
            "baseURL": "https://api.example.com/v1",
            "oauth2": {
              "issuer": "https://auth.example.com",
              "clientId": "opencode-client",
              "scopes": ["openid", "profile", "offline_access"],
              "syncIntervalMinutes": 60
            }
          }
        }
      }
    }
    

    On boot, oauth2 runs the auth flow, caches the token, discovers your models, and OpenCode treats example-ai like any other provider. For the full configuration reference (every flow, every field, the alternative pluginConfig.oauth2ModelSync.servers layout), see packages/opencode-oauth2/README.md.

    [!NOTE] The slickest way to ship this to a team is not an opencode.json at all — a server publishes a .well-known/opencode document and opencode auth login <url> adopts the provider + plugin setup automatically. See docs/well-known.md.


    🔐 oauth2 — bring your own OAuth-protected gateway

    Most OpenCode providers assume a static bearer key. That works for hosted SaaS, but breaks down the moment your models sit behind a corporate IdP (Keycloak, Auth0, Okta, Azure AD), a self-hosted gateway with short-lived tokens, a multi-tenant setup where each user authenticates as themselves, or a CI runner that has no business carrying a long-lived secret.

    @vymalo/opencode-oauth2 handles the OAuth dance, caches tokens, refreshes silently, and feeds OpenCode a normal-looking provider with a fresh Authorization header on every request.

    • Five auth flows — pick what matches your runtime:
      • authorization_code — interactive PKCE login (default)
      • device_code — RFC 8628, for browserless user auth
      • client_credentials — machine-to-machine with a clientSecret
      • jwt_bearer — RFC 7523 federated identity (GitHub Actions OIDC, Kubernetes SA tokens) — no long-lived secret in CI
      • token_exchange — RFC 8693 federated identity with explicit audience targeting
    • Dynamic model discovery from /v1/models — no hand-maintained model lists
    • Display-name normalization so glm-5 shows up as GLM 5
    • Persistent token cache with automatic refresh, atomic writes, 0o600
    • chat.headers hook injects a fresh bearer per request
    • Strict refresh-token policy — a user-flow session is either fully renewable or it doesn't get cached (machine flows re-acquire on every expiry)

    Federated identity (CI / Kubernetes): use jwt_bearer or token_exchange with the platform's own short-lived OIDC token as the subject — nothing long-lived is ever cached. End-to-end recipes in docs/github-actions.md and docs/kubernetes.md; the shipped reusable workflow at .github/workflows/opencode-run.yml covers the common opencode run case.

    📊 models-info — real metadata for your model list

    A plain OpenAI-compatible /v1/models returns almost nothing useful (id, object, owned_by). @vymalo/opencode-models-info enriches each model with the rich stuff — context length, output limit, USD/M-token cost, modalities, and tool_call / reasoning / attachment flags — from any OpenRouter-shaped metadata endpoint.

    {
      "plugin": ["@vymalo/opencode-models-info"],
      "provider": {
        "my-provider": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "baseURL": "https://api.example.com/v1",
            "meta": { "modelsInfoUrl": "https://api.example.com/v1/models" }
          },
          "models": { "my-model-large": {} }
        }
      }
    }
    

    It runs as a config hook after other plugins and the merge is upstream-wins — a field you (or another plugin) already set is never clobbered, so it's safe to enable globally. Full reference: packages/opencode-models-info/README.md; composition + caching details in docs/models-info.md.

    🚦 ratelimit — cooperate with your gateway instead of fighting it

    @vymalo/opencode-ratelimit makes a provider respect the rate-limit headers your gateway already emits. It reads the IETF draft-03 triple from Envoy Gateway (x-ratelimit-limit / -remaining / -reset), proactively pauses new requests once the window is exhausted, and backs off + retries on 429 — so a burst cooperates with the gateway instead of earning a wall of errors.

    {
      "plugin": ["@vymalo/opencode-ratelimit"],
      "provider": {
        "my-provider": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "baseURL": "https://api.example.com/v1",
            "meta": { "rateLimit": { "maxWaitMs": 0, "maxRetries": 5 } }
          }
        }
      }
    }
    

    OpenCode has no post-response hook, so the only way to observe response status/headers is to wrap the provider's fetch — which is exactly what this plugin does. It supports tiers (wait through a 60s burst reset, but error fast on a multi-day budget reset) and scope: "model" (per-model cooldown buckets). Full reference: packages/opencode-ratelimit/README.md; the throttle/backoff state machine in docs/ratelimit.md.

    🖐️ browser — give the model hands

    @vymalo/opencode-browser registers browser_* tools and hosts a localhost WebSocket bridge that a companion Chromium/Firefox extension dials. The model drives real tabs — organized into named groups — with 34 tools across four groups: page (observe), control (drive), debug (powerful, off by default), and interactive (human-in-the-loop feedback, off by default).

    {
      "plugin": [
        ["@vymalo/opencode-browser", { "port": 4517 }]
      ]
    }
    

    Because an extension can't host a server, the plugin is the server and the extension connects out to it. On first run the plugin logs a generated token — paste it (and ws://127.0.0.1:4517) into the extension dashboard, hit Save & reconnect, and you're live. Build/load the extension from apps/browser-extension.

    Targeting is via stable browser_snapshot refs or CSS selectors / coordinates. On Chromium it uses the trusted CDP executor (chrome.debugger, with the visible "being debugged" banner); on Firefox or when the debugger is unavailable it falls back to a content-script executor with scroll-and-stitch full-page capture. Screenshots are written to disk (tool output is text-only) — view them with the read tool.

    Beyond OpenCode@vymalo/opencode-browser-mcp hosts the same bridge and exposes the same tools over the Model Context Protocol, so any MCP client (Claude Code, Cursor, Cline, …) can drive the extension; screenshots come back as inline images. The plugin and MCP server share one tool catalog, so they never drift.

    [!WARNING] The bridge binds 127.0.0.1 + token only, but it grants control of a real browser profile. Use a dedicated / throwaway Chrome profile. Security model for the whole suite: docs/security.md.


    🧰 Devtools — local utilities

    @vymalo/opencode-devtools gives the model a belt of everyday, deterministic tools that run in-process — no bridge, no auth, no network (except the opt-in http group). They're organized into groups (like the browser plugin), with the five offline groups on by default:

    {
      "plugin": [
        "@vymalo/opencode-devtools",
        // or enable the http group (network egress, off by default):
        ["@vymalo/opencode-devtools", { "groups": ["math", "codec", "crypto", "datetime", "convert", "http"] }]
      ]
    }
    

    math (precise eval, unit & base conversion, stats) · codec (base64/hex/url, JWT decode, gzip) · crypto (hash/hmac, uuid v4/v7, ulid, random, keypairs) · datetime (parse/format/diff, timezones, cron explain + next-runs) · convert (JSON/YAML/TOML/CSV + JSONPath) · http (request + GraphQL, SSRF-guarded, opt-in).

    Beyond OpenCode@vymalo/opencode-devtools-mcp exposes the same catalog over MCP for any client. Full reference: docs/devtools.md.

    Adopt, don't rebuild. We deliberately did not build plugins for memory, Android/iOS, or databases — mature MCP servers already cover those. See docs/recommended-mcps.md for the ones we recommend wiring in instead.


    🧱 Stacking the belt

    The plugins are decoupled at the import level but meet at the shared config object, so you can snap several onto one provider. Order matters for the config hooks — list them oauth2 → models-info → ratelimit so the bearer is in place before models-info fetches metadata with it:

    {
      "plugin": [
        "@vymalo/opencode-oauth2",
        "@vymalo/opencode-models-info",
        "@vymalo/opencode-ratelimit"
      ],
      "provider": {
        "my-provider": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "baseURL": "https://api.example.com/v1",
            "oauth2": {
              "issuer": "https://auth.example.com",
              "clientId": "opencode-client",
              "scopes": ["openid", "profile", "offline_access"]
            },
            "meta": {
              "modelsInfoUrl": "https://api.example.com/v1/models",
              "rateLimit": { "maxWaitMs": 0, "maxRetries": 5 }
            }
          }
        }
      }
    }
    

    On boot: oauth2 authenticates, discovers models from /v1/models, and stamps the access token onto the provider headers → models-info fetches modelsInfoUrl with that token and merges richer metadata → ratelimit wraps the fetch so every call honors the gateway's window. No models block to maintain, no Authorization header to manage — it's all automatic.

    📚 Documentation

    Full index: docs/README.md. The highlights:

    Page When you need it
    docs/architecture.md Hooks, token lifecycle per flow, cache layout, sync scheduler, logging
    docs/well-known.md How .well-known/opencode distributes a provider + plugin setup to clients
    docs/models-info.md Metadata enrichment — composition with any auth scheme, caching, failure modes
    docs/ratelimit.md Reading x-ratelimit-*, the throttle/backoff state machine, tiers, the timeout caveat
    docs/browser.md Browser automation — topology, wire protocol, full tool reference, executors, multi-client routing, store publishing
    docs/devtools.md Devtools utilities — tool groups, full reference, the math/http security model, plugin + MCP config
    docs/otel.md OpenTelemetry export — config precedence, every metric/log/span, privacy & cardinality
    docs/recommended-mcps.md Mature third-party MCP servers to adopt (memory, android/iOS, database) instead of rebuilding
    docs/security.md Consolidated security model across all plugins — token cache, the browser bridge, blast radius
    docs/github-actions.md CI without stored secrets — Keycloak/Auth0/Okta setup, reusable workflow, matrix, fork-PR limits
    docs/kubernetes.md CronJob / Job / Deployment with projected SA tokens, multi-provider pods, RBAC
    docs/local-development.md Sandbox setup, the plugin re-export trick, forcing re-auth, dev-only env subject token
    docs/troubleshooting.md Symptom-keyed fixes — redirect_uri_mismatch, model discovery 403, invalid_client, token rotation
    docs/adr/ Architecture Decision Records — load-bearing choices and why (e.g. ws, not Bun.serve/socket.io)

    🗂️ Workspace layout

    This is a pnpm monorepo. Eight packages publish to npm under @vymalo; four are private.

    Package Published as
    packages/opencode-oauth2 @vymalo/opencode-oauth2 OAuth2/OIDC auth + model discovery
    packages/opencode-models-info @vymalo/opencode-models-info Auth-agnostic model metadata enrichment
    packages/opencode-ratelimit @vymalo/opencode-ratelimit Auth-agnostic rate-limit awareness
    packages/opencode-browser @vymalo/opencode-browser Auth-agnostic browser automation + localhost bridge
    packages/opencode-browser-mcp @vymalo/opencode-browser-mcp MCP server exposing the browser tools to any MCP client
    packages/opencode-devtools @vymalo/opencode-devtools Auth-agnostic local developer utilities
    packages/opencode-devtools-mcp @vymalo/opencode-devtools-mcp MCP server exposing the devtools utilities
    packages/opencode-otel @vymalo/opencode-otel Auth-agnostic OpenTelemetry export (traces, metrics, logs)
    apps/browser-extension private Companion Chromium/Firefox extension
    packages/opencode-code-index private Experimental DuckDB + tree-sitter code index
    packages/plugin-bundle private Rolldown single-file distribution of oauth2

    🛠️ Development

    pnpm install          # bootstrap workspace
    pnpm -r build         # emit all packages to dist/ (oxc, no type checking)
    pnpm -r typecheck     # tsc --noEmit across packages
    pnpm -r test          # fast vitest run (no coverage)
    pnpm coverage         # tests + per-package coverage thresholds (what CI runs)
    pnpm lint             # biome lint
    pnpm format:check     # biome format (no write)
    

    Pre-push gate (run all five before opening a PR):

    pnpm -r build && pnpm -r typecheck && pnpm coverage && pnpm lint && pnpm format:check
    

    [!NOTE] A green pnpm build says nothing about types — it emits with oxc and never type-checks. pnpm typecheck is the only type-safety gate. See ADR-0010.

    Per-package iteration is much faster:

    pnpm --filter @vymalo/opencode-oauth2 test
    pnpm --filter @vymalo/opencode-browser build
    

    For end-to-end usage against a local OpenCode install, see GETTING_STARTED.md. For architecture, conventions, package layout, and the release process, see CONTRIBUTING.md and CLAUDE.md.

    🌟 Status

    Early but functional, and used in anger. All eight plugins are published and on the same version line; the browser extension ships alongside as a Release asset and on the Chrome Web Store / Firefox AMO. Public API may still shift before 1.0 — roadmap in plans/prd.md.

    🤝 Contributing

    Issues and PRs are welcome — please open an issue first for substantial changes so we can align on scope. See CONTRIBUTING.md for bootstrap, the pre-push gate, conventions, package layout, and the release process.

    📄 License

    MIT © vymalo contributors