From 9417f76449da178b716ee56b13de0336d81daa19 Mon Sep 17 00:00:00 2001 From: Jeff Sheldon Date: Tue, 13 Oct 2026 12:32:24 -0400 Subject: [PATCH] fix(v2): dual-mode default export so the bare npm ref loads under V2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `.` entrypoint exported only named symbols — no default at all — so the V2 loader's bare-ref resolution schema-failed with `Missing key at ["default"]` (#67). The documented `opencode-pty/v2` subpath ref cannot install either (the V2 loader parses subpath refs as npm install specs), leaving no published-package path to load the plugin on V2. Add the dual-mode default export (the pattern opencode-mem shipped in 2.27.0 via PR #311): `{ id, server, setup }` — V1 (>= 1.18.29) calls `server()`, V2 calls `setup()`. The `./v2` subpath export stays for direct-import consumers. Correct the README's V2 example, which used the V1 `plugin` field (silently ignored by V2) and recommended the un-installable subpath ref. Refs #67. --- README.md | 11 ++++++++--- index.ts | 15 +++++++++++++++ test/dual-mode-default.test.ts | 25 +++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 test/dual-mode-default.test.ts diff --git a/README.md b/README.md index 1968c1bd..e05165b7 100644 --- a/README.md +++ b/README.md @@ -43,14 +43,14 @@ Add the plugin to your [OpenCode config](https://opencode.ai/docs/config/): ### OpenCode V2 -OpenCode V2 uses the new plugin API. You can load `opencode-pty/v2` and optionally configure options (such as a fixed web UI port): +OpenCode V2 uses the new plugin API. Load the package by its bare npm ref — the `.` entrypoint is a dual-mode export that serves both runtimes — and optionally configure options (such as a fixed web UI port): ```json { "$schema": "https://opencode.ai/config.json", - "plugin": [ + "plugins": [ { - "package": "opencode-pty/v2", + "package": "opencode-pty", "options": { "port": 4200, "hostname": "127.0.0.1", @@ -61,6 +61,11 @@ OpenCode V2 uses the new plugin API. You can load `opencode-pty/v2` and optional } ``` +Notes for V2: + +- V2 reads the `plugins` array (object form). The V1 `plugin` field is silently ignored — a V1-shaped array produces zero plugin loads. +- The previous `opencode-pty/v2` subpath ref does not install: the V2 loader parses subpath refs as npm install specs ([#67](https://github.com/shekohex/opencode-pty/issues/67)). The bare ref works via the dual-mode default export; the `./v2` subpath remains valid for direct-import consumers. + | Option | Type | Default | Description | | --- | --- | --- | --- | | `port` | `number` | `0` (ephemeral) | Fixed port for the PTY Web UI observer server | diff --git a/index.ts b/index.ts index 8799a0a1..84aece80 100644 --- a/index.ts +++ b/index.ts @@ -1,2 +1,17 @@ +import { Plugin } from './src/v2/index.ts' +import { PTYPlugin } from './src/plugin.ts' + export { PTYPlugin } from './src/plugin.ts' export { PTYPlugin as server } from './src/plugin.ts' +export { Plugin } from './src/v2/index.ts' + +// Dual-mode default export: opencode V1 (>= 1.18.29) resolves the object entrypoint and +// calls `server()`; V2 validates `{ id, setup }` and calls `setup()`. The bare npm ref +// therefore loads on both runtimes — necessary because the V2 loader parses the +// documented `opencode-pty/v2` subpath ref as an npm install spec and cannot install it +// (#67), and the previous `.` entrypoint carried no default export at all. +export default { + id: Plugin.id, + server: PTYPlugin, + setup: Plugin.setup, +} diff --git a/test/dual-mode-default.test.ts b/test/dual-mode-default.test.ts new file mode 100644 index 00000000..2f9c34fc --- /dev/null +++ b/test/dual-mode-default.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from 'bun:test' +import defaults, { PTYPlugin } from '../index.ts' +import { Plugin } from '../src/v2/index.ts' + +// The bare npm ref resolves the `.` entrypoint's default export on both runtimes: +// V1 (>= 1.18.29) calls `server()`, V2 validates `{ id, setup }` and calls `setup()`. +// Before the dual-mode export the `.` entrypoint had no default at all, so the bare +// ref schema-failed on V2 (#67) and the only working path was the `./v2` subpath — +// which the V2 loader cannot install (it parses subpath refs as npm install specs). +describe('dual-mode default export', () => { + it('carries the V2 id and setup', () => { + expect(defaults.id).toBe('opencode-pty') + expect(defaults.setup).toBe(Plugin.setup) + expect(typeof defaults.setup).toBe('function') + }) + + it('carries the V1 server entrypoint', () => { + expect(defaults.server).toBe(PTYPlugin) + expect(typeof defaults.server).toBe('function') + }) + + it('re-exports the V2 plugin by name', () => { + expect(Plugin.id).toBe('opencode-pty') + }) +})