fix(opencode): prefix provider id with opencode- for auth login command (#8830)

The bundled @omniroute/opencode-plugin registers its provider under
'opencode-omniroute' (the 'opencode-' prefix is required by OpenCode
>=1.17.8's native-adapter gate on model providerID). But the CLI
instructed 'opencode auth login --provider omniroute' — the unprefixed
id — so OpenCode reported 'Unknown provider "omniroute"' because it
resolves --provider against the exact provider id the plugin registered.

Add resolveOpenCodeAuthProviderId() helper that idempotently adds the
'opencode-' prefix when absent, and use it everywhere the CLI builds
or prints the --provider argument: resolveOpenCodeAuthSpawn args,
runOpenCodeAuth ENOENT message, and runSetupOpenCodeCommand 'Run
manually'/'Next step' messages. Update the plugin README and test
assertions to match.

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-07 18:07:30 -03:00
committed by GitHub
parent 7fce2d55a4
commit 904e8af09a
4 changed files with 58 additions and 12 deletions

View File

@@ -30,7 +30,7 @@ omniroute setup opencode --auth
# 3. Restart OpenCode — /models lists the full live catalog
```
The `--auth` flag runs `opencode auth login --provider omniroute` automatically.
The `--auth` flag runs `opencode auth login --provider opencode-omniroute` automatically.
Use `--base-url` to point at a non-default OmniRoute address:
```sh
@@ -84,7 +84,7 @@ Peer dep: `@opencode-ai/plugin` (managed by your OpenCode install).
```
```sh
opencode auth login --provider omniroute
opencode auth login --provider opencode-omniroute
# prompts for the OmniRoute API key, writes to ~/.local/share/opencode/auth.json
```
@@ -164,8 +164,8 @@ Then in `~/.config/opencode/opencode.json` reference each directory by absolute
Paths are relative to `~/.config/opencode/`. Each entry now resolves to a distinct module file, so OC loads them as two separate plugin instances. Authenticate each:
```sh
opencode auth login --provider omniroute
opencode auth login --provider omniroute-preprod
opencode auth login --provider opencode-omniroute
opencode auth login --provider opencode-omniroute-preprod
```
Each entry gets its own provider id, its own model picker entry, its own slot in `auth.json`, and its own TTL cache. Closures are isolated per plugin instance — no cross-talk.

View File

@@ -218,6 +218,26 @@ function registerPluginInOpenCodeConfig({
* a clear "could not run opencode" message instead of a hard import
* failure.
*/
/**
* Resolve the provider id used for `opencode auth login --provider <id>`.
*
* The bundled @omniroute/opencode-plugin registers its provider under
* `opencode-<id>` (the `opencode-` prefix is required by OpenCode >=1.17.8's
* native-adapter gate). The auth login command must use the prefixed form
* because OpenCode resolves `--provider <id>` against the provider id the
* plugin actually registered.
*
* Idempotent: if the id already starts with `opencode-`, it passes through
* unchanged. This protects users who manually worked around the bug with
* `--provider opencode-omniroute`.
*
* @param {string} providerId
* @returns {string}
*/
export function resolveOpenCodeAuthProviderId(providerId) {
return providerId.startsWith("opencode-") ? providerId : `opencode-${providerId}`;
}
/**
* Pure resolver for the `opencode auth login` spawn descriptor. Extracted so the
* platform-branching logic is unit-testable without mocking child_process or
@@ -231,21 +251,23 @@ function registerPluginInOpenCodeConfig({
*/
export function resolveOpenCodeAuthSpawn(providerId, platform = process.platform) {
const isWin = platform === "win32";
const authProviderId = resolveOpenCodeAuthProviderId(providerId);
return {
command: isWin ? "opencode.cmd" : "opencode",
args: ["auth", "login", "--provider", providerId],
args: ["auth", "login", "--provider", authProviderId],
options: { stdio: "inherit", shell: isWin },
};
}
export function runOpenCodeAuth(providerId) {
const authProviderId = resolveOpenCodeAuthProviderId(providerId);
const { command, args, options } = resolveOpenCodeAuthSpawn(providerId);
const res = spawnSync(command, args, options);
if (res.error) {
// ENOENT = opencode is not on PATH
if (res.error.code === "ENOENT") {
printInfo(
`opencode CLI not found on PATH. Run \`opencode auth login --provider ${providerId}\` manually after installing OpenCode.`
`opencode CLI not found on PATH. Run \`opencode auth login --provider ${authProviderId}\` manually after installing OpenCode.`
);
return 1;
}
@@ -343,7 +365,8 @@ export async function runSetupOpenCodeCommand(opts = {}) {
if (wantsAuth) {
if (nonInteractive) {
printInfo(`Skipping \`opencode auth login\` (non-interactive mode).`);
printInfo(`Run manually: opencode auth login --provider ${providerId}`);
const authProviderId = resolveOpenCodeAuthProviderId(providerId);
printInfo(`Run manually: opencode auth login --provider ${authProviderId}`);
} else {
printHeading("Authenticating with OpenCode");
const authExit = runOpenCodeAuth(providerId);
@@ -352,8 +375,9 @@ export async function runSetupOpenCodeCommand(opts = {}) {
}
}
} else {
const authProviderId = resolveOpenCodeAuthProviderId(providerId);
printInfo(
`Next step: opencode auth login --provider ${providerId} (pass --auth to do this automatically)`
`Next step: opencode auth login --provider ${authProviderId} (pass --auth to do this automatically)`
);
}

View File

@@ -0,0 +1 @@
- fix(opencode): prefix provider id with "opencode-" for auth login command (#8830)

View File

@@ -11,7 +11,10 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { resolveOpenCodeAuthSpawn } from "../../bin/cli/commands/setup-open-code.mjs";
import {
resolveOpenCodeAuthSpawn,
resolveOpenCodeAuthProviderId,
} from "../../bin/cli/commands/setup-open-code.mjs";
test("resolveOpenCodeAuthSpawn: win32 spawns opencode.cmd with shell:true (repro #7913)", () => {
const spawn = resolveOpenCodeAuthSpawn("omniroute", "win32");
@@ -21,7 +24,7 @@ test("resolveOpenCodeAuthSpawn: win32 spawns opencode.cmd with shell:true (repro
true,
`expected shell:true on win32 (the EINVAL fix), got shell:${spawn.options.shell}`
);
assert.deepEqual(spawn.args, ["auth", "login", "--provider", "omniroute"]);
assert.deepEqual(spawn.args, ["auth", "login", "--provider", "opencode-omniroute"]);
});
test("resolveOpenCodeAuthSpawn: linux/darwin spawn bare opencode with shell:false (no regression)", () => {
@@ -36,7 +39,25 @@ test("resolveOpenCodeAuthSpawn: linux/darwin spawn bare opencode with shell:fals
}
});
test("resolveOpenCodeAuthSpawn: forwards the provider id into the args", () => {
test("resolveOpenCodeAuthSpawn: prefixes provider id for auth login (#8830)", () => {
const spawn = resolveOpenCodeAuthSpawn("anthropic", "linux");
assert.deepEqual(spawn.args, ["auth", "login", "--provider", "anthropic"]);
assert.deepEqual(spawn.args, ["auth", "login", "--provider", "opencode-anthropic"]);
});
test("resolveOpenCodeAuthProviderId: adds opencode- prefix when absent (#8830)", () => {
assert.equal(resolveOpenCodeAuthProviderId("omniroute"), "opencode-omniroute");
assert.equal(resolveOpenCodeAuthProviderId("omniroute-preprod"), "opencode-omniroute-preprod");
assert.equal(resolveOpenCodeAuthProviderId("anthropic"), "opencode-anthropic");
});
test("resolveOpenCodeAuthProviderId: idempotent — passes through already-prefixed ids (#8830)", () => {
assert.equal(resolveOpenCodeAuthProviderId("opencode-omniroute"), "opencode-omniroute");
assert.equal(
resolveOpenCodeAuthProviderId("opencode-omniroute-preprod"),
"opencode-omniroute-preprod"
);
assert.equal(
resolveOpenCodeAuthProviderId("opencode-anthropic"),
"opencode-anthropic"
);
});