Skip to content

fix(config): use package.json name for file:// plugin identity#16200

Open
coleleavitt wants to merge 5 commits intoanomalyco:devfrom
coleleavitt:coleleavitt/dev
Open

fix(config): use package.json name for file:// plugin identity#16200
coleleavitt wants to merge 5 commits intoanomalyco:devfrom
coleleavitt:coleleavitt/dev

Conversation

@coleleavitt
Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #8759
Closes #10115
Closes #11159
Closes #12285
Closes #14304

Supersedes #8758 (my previous attempt, had conflicts)
Related to #15598 (similar fix by @cyberprophet)

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Problem: Multiple file:// plugins with the same entry filename (e.g., dist/index.js) were incorrectly deduplicated because getPluginName() only extracted the filename.

file:///path/to/plugin-a/dist/index.js -> "index"
file:///path/to/plugin-b/dist/index.js -> "index"  // COLLISION - silently dropped!

Solution: For file:// URLs, first look up the nearest package.json and use its name field. If no package.json exists, fall back to the full canonical URL (via realpathSync).

This approach:

  1. Properly deduplicates npm + file:// versions of the same plugin - both [email protected] and file:///path/to/oh-my-opencode/dist/index.js resolve to "oh-my-opencode" (from package.json)
  2. Prevents false collisions - multiple plugins with same filename but different paths get unique identities
  3. Matches Node.js ESM loader semantics - uses realpath for canonical paths
  4. Works cross-platform - handles Windows paths correctly

How did you verify your code works?

  1. bun test test/config/config.test.ts - all 65 tests pass
  2. Added new test cases:
    • file:// URL without package.json returns full canonical URL
    • file:// URL with package.json returns package name
    • file:// URL with scoped package.json returns scoped name
    • Multiple file:// plugins with same filename don't collide
    • file:// plugin with package.json dedupes with npm package of same name
  3. bun run typecheck passes for opencode package

Screenshots / recordings

N/A - non-UI change

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@cyberprophet
Copy link
Copy Markdown
Contributor

Thanks for the mention — I previously tried to address the same collision in #15598.

The approach here (use nearest package.json name for file:// plugins, and fall back to canonicalized URL via realpath) looks like a solid way to avoid the "dist/index.js" filename collision while still deduping npm vs local dev installs.

One question/concern: this PR also changes packages/opencode/src/plugin/codex.ts and packages/opencode/src/provider/provider.ts. Those look orthogonal to getPluginName()/plugin deduplication — was that intentional? If not, I’d strongly recommend splitting them into a separate PR to keep review + risk surface tight.

Minor: findPackageJsonName() currently caps the upward search at 5 dirs; might be worth confirming that’s enough for typical layouts (dist/, build/, etc.).

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

The following comment was made by an LLM, it may be inaccurate:

Related PRs found:

  1. fix(config): avoid dedup collisions for file:// plugins in node_modules #15598 - "fix(config): avoid dedup collisions for file:// plugins in node_modules"

  2. fix(opencode): prevent plugin deduplication collision for index.js entry points #11161 - "fix(opencode): prevent plugin deduplication collision for index.js entry points"

These PRs are addressing the same underlying problem of plugin deduplication collisions. PR #16200 appears to be a more comprehensive fix that builds upon or supersedes these earlier attempts (the PR notes it "Supersedes #8758" and closes multiple related issues #8759, #10115, #11159, #12285, #14304).

…ption

- Add fallback to languageModel when sdk.responses is undefined (e.g., @ai-sdk/openai-compatible)
- Create local copy of options before adding fetch wrapper to avoid mutating provider.options,
  which caused Anthropic SDK to fail after switching from Codex
file:// plugins now resolve their identity via the nearest package.json name,
allowing local development plugins to properly deduplicate against their npm
equivalents. Falls back to the full file URL if no package.json is found.
- Global cleanup registry with timeout-capped shutdown orchestration
- WeakRef-based parent-child AbortController to prevent GC leaks
- MCP subprocess signal escalation (SIGTERM → SIGKILL)
- Worker try/finally guarantee in TUI thread
- OAuth server auto-cleanup on timeout/resolve/reject
- Circuit breaker on compaction (max 3 consecutive failures)
- SSL error classification and stale connection detection
- HTML error page title extraction for proxy/WAF errors
- Anti-ptrace security hardening via prctl on Linux
…streaming fallback

- Exponential backoff with 25% jitter to prevent thundering herd
- Stale connection (ECONNRESET/EPIPE) errors now retryable
- parseTokenGap() for reactive compaction targeting
- DJB2 hash-based cache break detection with bounded LRU
- generateText() fallback wrapper for non-streaming recovery
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment