Skip to content

fix: Windows compatibility — bypass wrapper, disable MCP bridge, CLI mode default, .qodersec support - #3

Open
zperel-2nd-account wants to merge 2 commits into
zxcrf:mainfrom
zperel-2nd-account:fix/windows-spawn-hang
Open

fix: Windows compatibility — bypass wrapper, disable MCP bridge, CLI mode default, .qodersec support#3
zperel-2nd-account wants to merge 2 commits into
zxcrf:mainfrom
zperel-2nd-account:fix/windows-spawn-hang

Conversation

@zperel-2nd-account

@zperel-2nd-account zperel-2nd-account commented Aug 5, 2026

Copy link
Copy Markdown

Problem

When using the opencode-qoder-auth plugin on Windows, selecting any Qoder model causes OpenCode to hang indefinitely with no error output.

Root Causes & Fixes

1. Windows compat wrapper spawn hang

The plugin's resolveQoderCLIForSDK() unconditionally creates a Unix-style shebang script (#!/usr/bin/env node) and passes it to the vendored SDK as the CLI path. On Windows, child_process.spawn() cannot execute shebang scripts — it needs a real executable or .cmd file. The process silently fails to start.

Fix: On Windows, bypass the wrapper and use the real qodercli.exe directly. The wrapper only existed to strip --verbose, --storage-dir, and --resource-dir — but the vendored SDK's buildCommand() already omits these flags (line 565), so the wrapper serves no purpose on any platform.

2. MCP bridge hangs qodercli

The plugin bridges OpenCode's config.mcp servers to qodercli via --mcp-config. The CLI process attempts to connect to each server during startup. If any server is unreachable (e.g. a Docker container not running), qodercli blocks on the connection and never produces output beyond hook_started/ hook_response. OpenCode waits forever.

Fix: Disable the MCP bridge in both mcp-bridge.ts (return empty) and index.ts (don't add bridged servers to provider options). OpenCode already handles MCP tools itself via the functionTools layer — qodercli doesn't need them.

3. --include-partial-messages incompatibility

qodercli 1.1.15 rejects --include-partial-messages unless --print is also passed (Error: --include-partial-messages requires --print and --output-format=stream-json). The plugin hardcodes includePartialMessages: true.

Fix: Remove it. This option only affects stream-json mode and provides minor benefit — dropping it avoids flag incompatibilities across CLI versions.

4. Qoder CLI directory migration (.qodersec)

Recent Qoder versions moved the CLI from ~/.qoder to ~/.qodersec. The plugin's resolveQoderCLI() only checked the old paths, causing "Qoder CLI not found" errors.

Fix: Updated resolveQoderCLI() to search .qodersec first (new location), then fall back to .qoder (legacy).

5. SDK streaming broken on Windows — use CLI mode by default

The vendored SDK's streaming mode (--output-format stream-json --input-format stream-json) only produces hook_started + hook_response then exits on Windows, never emitting init or assistant messages. The root cause appears to be a stdin/stdout pipe issue in the SDK's SubprocessTransport.

Fix: On Windows, default to CLI mode (mode: 'cli') which calls qodercli --print -p <prompt> and returns stdout directly. This works reliably. Users can still force SDK mode with provider.qoder.options.mode: 'sdk' if needed.

Testing

Tested on Windows 10 with qodercli 1.1.15 and OpenCode 1.18.11:

  • Before fix: opencode run -m qoder/lite 'hello' → hangs forever at > build · lite
  • After fix: Same command → immediate response from qoder/lite and qoder/auto
  • Linux/macOS: Compat wrapper path unchanged (bypass is win32-only); SDK mode still works

Configuration

No additional config needed beyond adding the plugin:

{
  "plugin": ["opencode-qoder-auth"]
}

Optional: force SDK mode on Windows (not recommended):

{
  "plugin": ["opencode-qoder-auth"],
  "provider": {
    "qoder": {
      "options": { "mode": "sdk" }
    }
  }
}

Files Changed

  • src/qoder-language-model.ts: Windows wrapper bypass, CLI mode default, .qodersec paths
  • src/mcp-bridge.ts: Disable MCP bridge
  • src/index.ts: Don't add bridged MCP to provider options
  • src/mcp-bridge.ts: Return empty from getMcpBridgeServers()

The ensureQoderCLICompatWrapper creates a Unix-style shebang script
(#!/usr/bin/env node) and passes it to the vendored SDK as the CLI path.
On Windows, child_process.spawn() cannot execute shebang scripts directly,
causing the Qoder CLI to never start — OpenCode hangs indefinitely waiting
for a response with no error message.

Since the vendored SDK's buildCommand() already omits --verbose,
--storage-dir, and --resource-dir (the only flags the wrapper stripped),
the compat wrapper is unnecessary on any platform. On Windows it actively
breaks the spawn. Bypass it for win32 and use the real qodercli.exe.
Copilot AI lite review requested due to automatic review settings August 5, 2026 21:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

On Windows, resolveQoderCLI() can still return a .cmd shim which the vendored SDK cannot spawn with shell:false, so the hang can persist unless the SDK path is guaranteed to be a real .exe (and tests should cover the new win32 branch).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR addresses a Windows-specific hang when using the Qoder SDK transport by avoiding the Unix shebang-based compat wrapper and passing a spawnable CLI path to the vendored SDK.

Changes:

  • Add a Windows (win32) branch in resolveQoderCLIForSDK() to bypass the compat wrapper and use the real CLI path.
  • Document in-code why the wrapper is incompatible with Windows spawning and why it’s no longer needed for argument stripping.
File summaries
File Description
src/qoder-language-model.ts Skips the shebang compat wrapper on Windows when resolving the CLI path for the vendored SDK to prevent spawn() hangs.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +142 to +144
if (process.platform === 'win32') {
return cliPath
}
Comment on lines 137 to +141
function resolveQoderCLIForSDK(cliPath: string): string {
// On Windows, use the real exe directly — the compat wrapper (shebang script
// `#!/usr/bin/env node`) can't be spawned by child_process on Windows.
// The wrapper only existed to strip --verbose/--storage-dir/--resource-dir,
// which are already removed in the vendored SDK's buildCommand().
…dePartialMessages

Three fixes for OpenCode plugin stability on Windows:

1. Windows spawn hang (qoder-language-model.ts):
   Bypass the Unix shebang compat wrapper on Windows — child_process.spawn()
   cannot execute '#!/usr/bin/env node' scripts. Use the real qodercli.exe
   directly since the vendored SDK already omits the flags the wrapper stripped.

2. MCP bridge hang (mcp-bridge.ts):
   Don't bridge OpenCode config.mcp servers to qodercli. The CLI process
   receives --mcp-config and blocks trying to connect to each server (e.g.
   Docker containers). If any server is unreachable, qodercli never produces
   output beyond hook_started/hook_response, and OpenCode hangs indefinitely.
   OpenCode already handles MCP tools via the functionTools layer.

3. Remove includePartialMessages (qoder-language-model.ts):
   qodercli 1.1.15 rejects --include-partial-messages unless --print is also
   passed. Since this option only affects stream-json mode and offers minor
   benefit, drop it to avoid the flag incompatibility across CLI versions.
@zperel-2nd-account zperel-2nd-account changed the title fix: bypass compat wrapper on Windows — prevent spawn hang fix: Windows compatibility — bypass wrapper, disable MCP bridge, CLI mode default, .qodersec support Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants