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

chatgpt-web-opencode

v0.9.5

Published

OpenCode plugin for ChatGPT web /backend-api/f/conversation (SSE).

Readme

chatgpt-web-opencode

OpenCode plugin that routes @ai-sdk/openai Responses streaming requests to ChatGPT web /backend-api/f/conversation (prepare → Sentinel PoW → SSE), not Codex Responses WS. Streaming is real and incremental: deltas emit live as ChatGPT SSE arrives, not buffered until EOF.

Provider id: chatgpt-web. Sibling to openai-ws-opencode (different backend).

Requires Node.js 20+. No Bun toolchain.

Install

npm install -g chatgpt-web-opencode
chatgpt-web-opencode setup --global
opencode auth login chatgpt-web

From GitHub:

npm install -g github:jaredboynton/chatgpt-web-opencode
chatgpt-web-opencode setup --global

Local checkout:

cd ~/__devlocal/chatgpt-web
npm install && npm run build
chatgpt-web-opencode setup --global --plugin "chatgpt-web-opencode@file:$(pwd)"
opencode auth login chatgpt-web

setup owns repair of OpenCode's chatgpt-web-opencode@latest cache. When that cache is missing its package metadata or contains an older version than the setup package, setup moves it aside to a timestamped .stale-<ISO timestamp> path so OpenCode can reinstall it. Use --no-cache-repair to skip this step. OpenCode launch does not perform this plugin cache repair.

Models

| OpenCode id | Wire slug | |-------------|-----------| | gpt-5.6-luna-pro | gpt-5-6-luna-pro | | gpt-5.6-terra-pro | gpt-5-6-terra-pro | | gpt-5.6-sol-pro | gpt-5-6-sol-pro | | gpt-5.5-pro | gpt-5-5-pro | | gpt-5.5-thinking | gpt-5-5-thinking | | gpt-5.5-instant | gpt-5-5-instant |

Variants map OpenCode reasoningEffort → ChatGPT thinking_effort (medium→standard, high→extended, xhigh/max→max; thinking also supports low→min).

Auth

ChatGPT / Codex OAuth only (same Auth0 client as Codex CLI). Uses OpenCode’s auth store for provider chatgpt-web. Codex bearer + Chrome-like browser headers are enough for the text path (inert oai-did cookie). No ChatGPT session cookie / UMP envelope required.

Refresh is singleflight within the plugin process and guarded across processes by $XDG_DATA_HOME/opencode/.chatgpt-web-refresh.lock (or the platform default XDG data home). Under that lock it reloads auth, persists the issuer-rotated refresh token before using the new access token, and rejects a response without a rotated token; it never falls back to the old token.

Wire notes

  • Never sends service_tierpriority returns HTTP 422 on /f/conversation.
  • Never sends a null x-conduit-token — the header is omitted when conversation prepare returns no conduit token.
  • SSE-only (no Celsius WSS handoff).
  • Multi-turn via adapter-owned previous_response_id{conversation_id, message_id} cache.
  • Function tools: OpenAI tools[] + local_function_names (bare declared names first, then api_tool.call_tool wrapper last). tool_choice is passed through on first and continuation turns (resolveToolChoice; defaults to auto). Tool results continue via encoded call_cgw_* ids. Exactly one tool output is supported per turn; turns with more than one function_call_output return HTTP 400 rather than dropping output. Hosted tools / images rejected.
  • Tool schemas over the ~24 KB body budget are progressively shrunk (mild 400-char trim → 200 → strip param descriptions → skeleton) instead of dropped wholesale (fitToolsToBudget).
  • Prompts with declared function tools get an imperative <tool_availability> trailer (buildToolAvailabilityGuard) with a budgeted, model-visible compact schema roster. The trailer requires an immediate tool call and forbids "tools unavailable" narration — no single-call ceiling. If a workspace-action turn still returns prose without a call, the bridge issues one parented continuation retry with tool_choice: required; literal JSON from that retry is validated and converted into a local call so ChatGPT's remote sandbox cannot consume it. It never loops. Luna's reliable emission channels are harness-native python and container.exec/shell; those remap onto local python3/bash via declared bash (remapNativeToolCall). Shell-shaped payloads accidentally emitted through python (for example pwd or ls -la) are healed to direct local bash instead of invalid Python. Declared OpenCode function names are re-advertised, but Luna often will not emit them directly. Tool bodies are buffered across SSE patches and emitted only after that tool message reports both status: finished_successfully and metadata.is_complete: true; assistant handoff, [DONE], and EOF discard an incomplete call, so OpenCode never executes a partial function. Connector/plugin calls remain disabled. # CODE_MODE Python bodies may enter the CodeAct route. The upstream stream is cut only after a complete remapped call is emitted. Tools stay re-advertised on tool-result continuation turns.
  • If Sentinel returns Turnstile dx, empty turnstile is tried first; hard fail if finalize rejects.
  • Conversation prepare and Sentinel admission run concurrently (Promise.all). Finalized Sentinel requirements tokens are cached per account for 60 seconds and lazily re-solved on conversation HTTP 401/403; warm turns perform no PoW solves.

Runtime (OpenCode on Bun)

OpenCode often loads plugins under Bun. Bun’s TLS fingerprint is Cloudflare-blocked on chatgpt.com, so this plugin runs the shipped bin/chatgpt-fetch.mjs --daemon helper with your system node binary for ChatGPT-host requests. The persistent daemon multiplexes requests over newline-delimited JSON frames and keeps a warm keep-alive connection pool, avoiding per-request spawns and cold TLS. shutdownHelper() tears it down when the session or server is disposed. Node-native hosts use fetch directly.

  • CHATGPT_WEB_FORCE_NODE_HELPER=1 — always use the Node helper
  • CHATGPT_WEB_FORCE_FETCH=1 — always use globalThis.fetch (will fail under Bun for ChatGPT)

Code mode

The chatgpt-web-opencode/code-mode export provides startCodeRun(): a single-use Node worker_threads + node:vm CodeAct runtime with batched tools.* waves, per-run state, capped artifacts, and local bash, read, write, and exact edit execution. node:vm supplies timeout and crash isolation; it is not a security sandbox. Under a Bun/OpenCode host, the VM is moved into the shipped bin/chatgpt-code-mode.mjs system-Node helper. Run the standalone vertical slice with:

npx tsx scripts/test-code-mode.ts

When a Responses request declares code, a // CODE_MODE request or a model-emitted code({script}) enters the runtime. Tool waves return as ordinary Responses function_call items and resume through previous_response_id; set CHATGPT_WEB_CODE_MODE_LOCAL=1 to execute supported tools inside the plugin instead. CHATGPT_WEB_CODE_MODE=1 also injects the synthetic code tool and enables // CODE_MODE / #codemode shell-marker interception.

The runtime may apply compile-verified script salvages for a trailing main() (rewritten to return await main()), expression auto-invocation, or backtick healing. When a heal fires, startCodeRun() may set result.salvage; disable these salvages with CHATGPT_WEB_CODE_SCRIPT_HEAL=0.

Environment knobs

| Variable | Purpose | |---|---| | CHATGPT_WEB_CODE_MODE | Enable code mode + synthetic code tool | | CHATGPT_WEB_CODE_MODE_LOCAL | Execute supported tools locally instead of upstream | | CHATGPT_WEB_CODE_TIMEOUT_MS | Per-run timeout | | CHATGPT_WEB_CODE_MAX_WAVES | Max tool waves per run | | CHATGPT_WEB_CODE_MAX_CALLS | Max calls per wave | | CHATGPT_WEB_CODE_MAX_OUTPUT_BYTES | Output cap | | CHATGPT_WEB_CODE_ARTIFACT_MAX_BYTES | Artifact size cap | | CHATGPT_WEB_CODE_SCRIPT_HEAL | Script salvage/heal (0 = off) | | CHATGPT_WEB_CODE_WAVE_IDLE_MS | Idle delay between waves | | CHATGPT_WEB_CODE_EXEC | Local exec (0 = disabled) | | CHATGPT_WEB_CODE_REGISTRY | Script registry (0 = disabled) | | CHATGPT_WEB_CODE_REGISTRY_DIR | Registry directory override | | CHATGPT_WEB_CODE_SKILLS | Skill store (0 = disabled) | | CHATGPT_WEB_CODE_SKILLS_DIR | Skills directory override | | CHATGPT_WEB_CODE_ANCHORS | Anchor store (0 = disabled; boot-latched: read once per process) | | CHATGPT_WEB_CODE_WARP | Warp edits (0 = disabled; boot-latched: read once per process) | | CHATGPT_WEB_CODE_POOL | Daemon worker pool (0 = single-shot) | | CHATGPT_WEB_CODE_POLL | Explicit poll/monitor (0 = disabled) | | CHATGPT_WEB_CODE_POLL_YIELD | Poll backoff yields exec permit via __codemode_sleep wave (0 = in-VM only) | | CHATGPT_WEB_CODE_BRIDGED | Code-mode bridging (0 = off; reserved / not shipped) | | CHATGPT_WEB_CODE_AUTO_MONITOR | Auto-monitor (0 = off; reserved / not shipped) | | CHATGPT_WEB_CODE_POLL_AWAIT | Poll await (0 = off; reserved / not shipped) | | CHATGPT_WEB_CODE_SKILL_LEARN | Skill learning (0 = off; reserved / not shipped; no learner shipped) |

Debugging

  • CHATGPT_WEB_DEBUG=1 appends full conversation bodies and raw SSE chunks to ~/.cache/chatgpt-web-opencode/debug.log. It does not capture HTTP headers, bearer tokens, cookies, or credentials. Payload content is otherwise unredacted; delete the log after use.

Limitations

  • Not a public OpenAI API credential path.
  • Function tools are supported; hosted tools / images are rejected. fitToolsToBudget progressively shrinks oversized tools[] (400 → 200 → strip param descriptions → skeleton) and keeps tools visible on continuation turns. Live probes: npm run live:tools, npx tsx scripts/live-tool-loop.ts.
  • The ChatGPT web backend does not surface declared OpenCode tools to the model, so indirect tools (task, code, question, todowrite, skill) are reached via a __opencode_tool__ JSON payload carried in a native container.exec call; the bridge extracts and remaps it. Evidence and ratchet harness: docs/task-tool-ratchet-evidence.md, scripts/probe-task-ratchet.ts.

Dev

npm install
npm run verify
npm run harness:contract
npm run harness:opencode
npm run harness:live
npm run harness:release
npm run harness:scan

verify runs lint, typecheck, tests, and build. The contract/OpenCode harnesses and receipt scan are separate (npm run harness:contract, npm run harness:opencode, npm run harness:scan). harness:contract runs the targeted contract tests. harness:opencode packs and installs the current artifact at an explicit local file URL and uses deterministic isolated auth against its loopback fake backend, so neither requires a live credential. harness:live packs and installs the local artifact unless CHATGPT_WEB_PLUGIN_SPEC selects an installed package; it uses OPENCODE_AUTH_JSON, or a host OpenCode auth store when that variable is absent, for real service calls. harness:release requires HARNESS_RELEASE_EXECUTE=1, an authenticated npm session (NODE_AUTH_TOKEN in CI), and live OpenCode auth. It publishes an unpublished immutable version under the canary dist-tag and promotes those exact bytes to latest only after all gates pass.

OpenCode and live harness reports are written under artifacts/harness/ as report.json, junit.xml, and report.md; run npm run harness:scan before retaining or uploading them. Exit classes are 0 (pass), 2 (product/contract failure), 3 (external dependency, credential, or authorization block), and 4 (harness/infrastructure failure). CI is .github/workflows/ci.yml; nightly live checks are .github/workflows/nightly-live.yml; the manually dispatched canary release is .github/workflows/canary-release.yml. Release performs registry, dist-tag, tag, and GitHub release writes only when explicitly authorized; do not run it for ordinary verification.

Security

Never log bearer tokens, cookies, account/device/session IDs, or raw user content in tests. See SECURITY.md.