feat: prune cloud providers and console API, complete Neuron branding sweep

This commit is contained in:
2026-08-21 14:59:43 -05:00
parent f37714c9fc
commit e30d80389c
43 changed files with 324 additions and 2700 deletions
@@ -21,7 +21,7 @@ describe("opencode acp initialize/auth subprocess", () => {
expect(initialized.agentCapabilities?.sessionCapabilities?.fork).toEqual({})
expect(initialized.agentCapabilities?.sessionCapabilities?.list).toEqual({})
expect(initialized.agentCapabilities?.sessionCapabilities?.resume).toEqual({})
expect(initialized.agentInfo?.name).toBe("OpenCode")
expect(initialized.agentInfo?.name).toBe("Neuron")
}),
60_000,
)
@@ -1,7 +1,12 @@
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode acp --help 1`] = `
"opencode acp
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron acp
start ACP (Agent Client Protocol) server
@@ -18,31 +23,43 @@ Options:
--mdns-domain custom domain name for mDNS service (default: opencode.local)
[string] [default: "opencode.local"]
--cors additional domains to allow for CORS [array] [default: []]
--cwd working directory [string] [default: "<HOME>"]"
--cwd working directory [string] [default: "<HOME>"]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp --help 1`] = `
"opencode mcp
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron mcp
manage MCP (Model Context Protocol) servers
Commands:
opencode mcp add [name] add an MCP server
opencode mcp list list MCP servers and their status [aliases: ls]
opencode mcp auth [name] authenticate with an OAuth-enabled MCP server
opencode mcp logout [name] remove OAuth credentials for an MCP server
opencode mcp debug <name> debug OAuth connection for an MCP server
neuron mcp add [name] add an MCP server
neuron mcp list list MCP servers and their status [aliases: ls]
neuron mcp auth [name] authenticate with an OAuth-enabled MCP server
neuron mcp logout [name] remove OAuth credentials for an MCP server
neuron mcp debug <name> debug OAuth connection for an MCP server
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode attach --help 1`] = `
"opencode attach <url>
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron attach <url>
attach to a running opencode server
@@ -64,11 +81,17 @@ Options:
[string]
--mini start the minimal interactive interface [boolean] [default: false]
--no-replay disable mini session history replay on resume and after resize [boolean]
--replay-limit cap visible mini replay to the newest N messages [number]"
--replay-limit cap visible mini replay to the newest N messages [number]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode run --help 1`] = `
"opencode run [message..]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron run [message..]
run opencode with a message
@@ -104,74 +127,98 @@ Options:
--thinking show thinking blocks [boolean]
-i, --interactive run in direct interactive split-footer mode [boolean] [default: false]
--auto auto-approve permissions that are not explicitly denied (dangerous!)
[boolean] [default: false]"
[boolean] [default: false]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode debug --help 1`] = `
"opencode debug
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron debug
debugging and troubleshooting tools
Commands:
opencode debug config show resolved configuration
opencode debug lsp LSP debugging utilities
opencode debug rg ripgrep debugging utilities
opencode debug file file system debugging utilities
opencode debug scrap list all known projects
opencode debug skill list all available skills
opencode debug snapshot snapshot debugging utilities
opencode debug startup print startup timing
opencode debug agent <name> show agent configuration details
opencode debug v2 debug v2 catalog and built-in plugins
opencode debug info show debug information
opencode debug paths show global paths (data, config, cache, state)
opencode debug wait wait indefinitely (for debugging)
neuron debug config show resolved configuration
neuron debug lsp LSP debugging utilities
neuron debug rg ripgrep debugging utilities
neuron debug file file system debugging utilities
neuron debug scrap list all known projects
neuron debug skill list all available skills
neuron debug snapshot snapshot debugging utilities
neuron debug startup print startup timing
neuron debug agent <name> show agent configuration details
neuron debug v2 debug v2 catalog and built-in plugins
neuron debug info show debug information
neuron debug paths show global paths (data, config, cache, state)
neuron debug wait wait indefinitely (for debugging)
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode providers --help 1`] = `
"opencode providers
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron providers
manage AI providers and credentials
Commands:
opencode providers list list providers and credentials [aliases: ls]
opencode providers login [url] log in to a provider
opencode providers logout [provider] log out from a configured provider
neuron providers list list providers and credentials [aliases: ls]
neuron providers login [url] log in to a provider
neuron providers logout [provider] log out from a configured provider
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode agent --help 1`] = `
"opencode agent
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron agent
manage agents
Commands:
opencode agent create create a new agent
opencode agent list list all available agents
neuron agent create create a new agent
neuron agent list list all available agents
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode upgrade --help 1`] = `
"opencode upgrade [target]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron upgrade [target]
upgrade opencode to the latest or a specific version
@@ -185,11 +232,17 @@ Options:
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
-m, --method installation method to use
[string] [choices: "curl", "npm", "pnpm", "bun", "brew", "choco", "scoop"]"
[string] [choices: "curl", "npm", "pnpm", "bun", "brew", "choco", "scoop"]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode uninstall --help 1`] = `
"opencode uninstall
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron uninstall
uninstall opencode and remove all related files
@@ -202,11 +255,17 @@ Options:
-c, --keep-config keep configuration files [boolean] [default: false]
-d, --keep-data keep session data and snapshots [boolean] [default: false]
--dry-run show what would be removed without removing [boolean] [default: false]
-f, --force skip confirmation prompts [boolean] [default: false]"
-f, --force skip confirmation prompts [boolean] [default: false]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode serve --help 1`] = `
"opencode serve
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron serve
starts a headless opencode server
@@ -222,11 +281,17 @@ Options:
[boolean] [default: false]
--mdns-domain custom domain name for mDNS service (default: opencode.local)
[string] [default: "opencode.local"]
--cors additional domains to allow for CORS [array] [default: []]"
--cors additional domains to allow for CORS [array] [default: []]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode web --help 1`] = `
"opencode web
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron web
start opencode server and open web interface
@@ -242,11 +307,17 @@ Options:
[boolean] [default: false]
--mdns-domain custom domain name for mDNS service (default: opencode.local)
[string] [default: "opencode.local"]
--cors additional domains to allow for CORS [array] [default: []]"
--cors additional domains to allow for CORS [array] [default: []]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode models --help 1`] = `
"opencode models [provider]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron models [provider]
list all available models
@@ -260,11 +331,17 @@ Options:
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
--verbose use more verbose model output (includes metadata like costs) [boolean]
--refresh refresh the models cache from models.dev [boolean]"
--refresh refresh the models cache from models.dev [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode stats --help 1`] = `
"opencode stats
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron stats
show token usage and cost statistics
@@ -278,11 +355,17 @@ Options:
--tools number of tools to show (default: all) [number]
--models show model statistics (default: hidden). Pass a number to show top N, otherwise
shows all
--project filter by project (default: all projects, empty string: current project)[string]"
--project filter by project (default: all projects, empty string: current project)[string]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode export --help 1`] = `
"opencode export [sessionID]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron export [sessionID]
export session data as JSON
@@ -295,11 +378,17 @@ Options:
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
--sanitize redact sensitive transcript and file data [boolean]"
--sanitize redact sensitive transcript and file data [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode import --help 1`] = `
"opencode import <file>
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron import <file>
import session data from JSON file or URL
@@ -311,11 +400,17 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode pr --help 1`] = `
"opencode pr <number>
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron pr <number>
fetch and checkout a GitHub PR branch, then run opencode
@@ -327,28 +422,40 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode session --help 1`] = `
"opencode session
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron session
manage sessions
Commands:
opencode session list list sessions
opencode session delete <sessionID> delete a session
neuron session list list sessions
neuron session delete <sessionID> delete a session
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode plugin --help 1`] = `
"opencode plugin <module>
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron plugin <module>
install plugin and update config
@@ -362,17 +469,23 @@ Options:
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
-g, --global install in global config [boolean] [default: false]
-f, --force replace existing plugin version [boolean] [default: false]"
-f, --force replace existing plugin version [boolean] [default: false]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode db --help 1`] = `
"opencode db
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron db
database tools
Commands:
opencode db [query] open an interactive sqlite3 shell or run a query [default]
opencode db path print the database path
neuron db [query] open an interactive sqlite3 shell or run a query [default]
neuron db path print the database path
Positionals:
query SQL query to execute [string]
@@ -383,11 +496,17 @@ Options:
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
--format Output format [string] [choices: "json", "tsv"] [default: "tsv"]"
--format Output format [string] [choices: "json", "tsv"] [default: "tsv"]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp list --help 1`] = `
"opencode mcp list
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron mcp list
list MCP servers and their status
@@ -396,11 +515,17 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp add --help 1`] = `
"opencode mcp add [name]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron mcp add [name]
add an MCP server
@@ -415,16 +540,22 @@ Options:
--pure run without external plugins [boolean]
--url URL for a remote MCP server [string]
--env environment variable for a local MCP server (KEY=VALUE) [array]
--header HTTP header for a remote MCP server (KEY=VALUE) [array]"
--header HTTP header for a remote MCP server (KEY=VALUE) [array]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp auth --help 1`] = `
"opencode mcp auth [name]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron mcp auth [name]
authenticate with an OAuth-enabled MCP server
Commands:
opencode mcp auth list list OAuth-capable MCP servers and their auth status [aliases: ls]
neuron mcp auth list list OAuth-capable MCP servers and their auth status [aliases: ls]
Positionals:
name name of the MCP server [string]
@@ -434,11 +565,17 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp logout --help 1`] = `
"opencode mcp logout [name]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron mcp logout [name]
remove OAuth credentials for an MCP server
@@ -450,11 +587,17 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode providers list --help 1`] = `
"opencode providers list
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron providers list
list providers and credentials
@@ -463,11 +606,17 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode providers login --help 1`] = `
"opencode providers login [url]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron providers login [url]
log in to a provider
@@ -481,11 +630,17 @@ Options:
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
-p, --provider provider id or name to log in to (skips provider selection) [string]
-m, --method login method label (skips method selection) [string]"
-m, --method login method label (skips method selection) [string]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode providers logout --help 1`] = `
"opencode providers logout [provider]
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron providers logout [provider]
log out from a configured provider
@@ -497,11 +652,17 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode agent create --help 1`] = `
"opencode agent create
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron agent create
create a new agent
@@ -517,11 +678,17 @@ Options:
--permissions, --tools comma-separated list of permissions to allow (default: all).
Available: "bash, read, edit, glob, grep, webfetch, task, todowrite,
websearch, lsp, skill" [string]
-m, --model model to use in the format of provider/model [string]"
-m, --model model to use in the format of provider/model [string]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode agent list --help 1`] = `
"opencode agent list
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron agent list
list all available agents
@@ -530,11 +697,17 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode session list --help 1`] = `
"opencode session list
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron session list
list sessions
@@ -545,11 +718,17 @@ Options:
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
-n, --max-count limit to N most recent sessions [number]
--format output format [string] [choices: "table", "json"] [default: "table"]"
--format output format [string] [choices: "table", "json"] [default: "table"]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode session delete --help 1`] = `
"opencode session delete <sessionID>
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron session delete <sessionID>
delete a session
@@ -561,11 +740,17 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode db path --help 1`] = `
"opencode db path
"
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
neuron db path
print the database path
@@ -574,5 +759,6 @@ Options:
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
--pure run without external plugins [boolean]
"
`;
@@ -132,11 +132,11 @@ describe("run permission shared", () => {
test("formats always-allow copy for wildcard and explicit patterns", () => {
expect(permissionAlwaysLines(req({ permission: "bash", always: ["*"] }))).toEqual([
"This will allow bash until OpenCode is restarted.",
"This will allow bash until Neuron is restarted.",
])
expect(permissionAlwaysLines(req({ always: ["src/**/*.ts", "src/**/*.tsx"] }))).toEqual([
"This will allow the following patterns until OpenCode is restarted.",
"This will allow the following patterns until Neuron is restarted.",
"- src/**/*.ts",
"- src/**/*.tsx",
])
@@ -164,22 +164,7 @@ describe("installation", () => {
const brewInfoJson = JSON.stringify({
formulae: [{ versions: { stable: "2.1.0" } }],
})
testEffect(
testLayer(
() => jsonResponse({}), // HTTP not used for tap formula
(cmd, args) => {
if (cmd === "brew" && args.includes("anomalyco/tap/opencode") && args.includes("--formula")) return "opencode"
if (cmd === "brew" && args.includes("--json=v2")) return brewInfoJson
return ""
},
),
).effect("reads brew tap info JSON via CLI", () =>
Effect.gen(function* () {
const result = yield* Installation.use.latest("brew")
expect(result).toBe("2.1.0")
}),
)
})
})
describe("upgrade", () => {
testEffect(
@@ -1,25 +0,0 @@
import { expect, test } from "bun:test"
import { CloudflareAIGatewayAuthPlugin } from "@/plugin/cloudflare"
const pluginInput = {
client: {} as never,
project: {} as never,
directory: "",
worktree: "",
experimental_workspace: {
register() {},
},
serverUrl: new URL("https://example.com"),
$: {} as never,
}
test("registers the cloudflare-ai-gateway auth method", async () => {
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
expect(hooks.auth?.provider).toBe("cloudflare-ai-gateway")
expect(hooks.auth?.methods).toHaveLength(1)
})
test("no longer drops maxOutputTokens; OpenAI models ride the Responses API passthrough", async () => {
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
expect(hooks["chat.params"]).toBeUndefined()
})
@@ -1,198 +0,0 @@
import { expect, test } from "bun:test"
import type { Model, Provider } from "@opencode-ai/sdk/v2"
import { ModalPlugin } from "@/plugin/modal/modal"
const BASE_MODEL_ID = "thinkingmachines/Inkling-NVFP4"
const RUNTIME_MODEL_ID = "workspace--inkling.us-west.modal.direct"
const FALLBACK_RUNTIME_MODEL_ID = "workspace--inkling-fallback.us-west.modal.direct"
function makeProvider(baseURL: string): Provider {
const template: Model = {
id: BASE_MODEL_ID,
providerID: "modal",
name: "Inkling",
family: "ling",
api: {
id: BASE_MODEL_ID,
url: baseURL,
npm: "@ai-sdk/openai-compatible",
},
status: "active",
headers: {},
options: {},
cost: {
input: 1,
output: 4,
cache: {
read: 0.2,
write: 0,
},
},
limit: {
context: 128_000,
output: 8_192,
},
capabilities: {
temperature: true,
reasoning: true,
attachment: true,
toolcall: true,
input: {
text: true,
audio: true,
image: true,
video: false,
pdf: false,
},
output: {
text: true,
audio: false,
image: false,
video: false,
pdf: false,
},
interleaved: {
field: "reasoning_content",
},
},
release_date: "2026-07-15",
variants: {
fallback: {
reasoningEffort: "fallback",
},
},
}
return {
id: "modal",
name: "Modal",
source: "api",
env: ["MODAL_PROXY_TOKEN"],
options: {},
models: {
[template.id]: template,
},
}
}
test("discovers Modal workspace models", async () => {
const requests: Array<{ authorization: string | null; path: string }> = []
using server = Bun.serve({
port: 0,
fetch(request) {
requests.push({
authorization: request.headers.get("authorization"),
path: new URL(request.url).pathname,
})
return Response.json({
data: [
{
id: RUNTIME_MODEL_ID,
base_model_id: BASE_MODEL_ID,
name: "Thinking Machines: Inkling",
input_modalities: ["text", "image", "audio"],
output_modalities: ["text"],
context_length: 1_048_576,
max_output_length: 262_144,
pricing: {
prompt: "0.0000012",
completion: "0.000005",
input_cache_read: "0.00000027",
},
supported_sampling_parameters: ["temperature"],
supported_features: ["tools", "reasoning"],
reasoning_options: [
{
type: "effort",
values: ["none", "low", "medium", "high", "xhigh", "max"],
},
],
interleaved: {
field: "reasoning_content",
},
},
{
id: FALLBACK_RUNTIME_MODEL_ID,
base_model_id: BASE_MODEL_ID,
},
],
})
},
})
const provider = makeProvider(`${server.url}v1`)
const plugin = await ModalPlugin()
const models = await plugin.provider!.models!(provider, {
auth: {
type: "api",
key: "test-token",
},
})
const model = models[RUNTIME_MODEL_ID]
expect(requests).toEqual([
{
authorization: "Bearer test-token",
path: "/v1/models",
},
])
expect(Object.keys(models)).toEqual([RUNTIME_MODEL_ID, FALLBACK_RUNTIME_MODEL_ID])
expect(model.api).toEqual({
id: RUNTIME_MODEL_ID,
url: `${server.url}v1`,
npm: "@ai-sdk/openai-compatible",
})
expect(model.family).toBe("ling")
expect(model.capabilities.interleaved).toEqual({ field: "reasoning_content" })
expect(model.capabilities.input).toEqual({
text: true,
audio: true,
image: true,
video: false,
pdf: false,
})
expect(model.cost).toEqual({
input: 1.2,
output: 5,
cache: {
read: 0.27,
write: 0,
},
})
expect(model.limit).toEqual({
context: 1_048_576,
output: 262_144,
})
expect(model.variants).toEqual({
none: { reasoningEffort: "none" },
low: { reasoningEffort: "low" },
medium: { reasoningEffort: "medium" },
high: { reasoningEffort: "high" },
xhigh: { reasoningEffort: "xhigh" },
max: { reasoningEffort: "max" },
})
expect(models[FALLBACK_RUNTIME_MODEL_ID].variants).toEqual({
fallback: {
reasoningEffort: "fallback",
},
})
})
test("hides Modal models when discovery fails", async () => {
using server = Bun.serve({
port: 0,
fetch() {
return new Response(null, { status: 503 })
},
})
const plugin = await ModalPlugin()
const models = await plugin.provider!.models!(makeProvider(`${server.url}v1`), {
auth: {
type: "api",
key: "test-token",
},
})
expect(models).toEqual({})
})
@@ -1,278 +0,0 @@
import { describe, expect, test } from "bun:test"
import { OAUTH_DUMMY_KEY } from "../../src/auth"
import { oauthScope, SnowflakeCortexAuthPlugin } from "../../src/plugin/snowflake-cortex"
function makeInput() {
let auth: any = {
type: "oauth",
access: "access-old",
refresh: "refresh-old",
expires: Date.now() + 3600_000,
accountId: "myorg-myaccount",
}
const setCalls: Array<Record<string, unknown>> = []
return {
getAuth: async () => auth,
setAuth: (next: any) => {
auth = next
},
input: {
client: {
auth: {
set: async (request: any) => {
setCalls.push(request)
auth = request.body
},
},
},
} as any,
setCalls,
}
}
describe("plugin.snowflake-cortex", () => {
test("oauthScope uses Snowflake-compatible scope values", () => {
expect(oauthScope(undefined)).toBe("refresh_token")
expect(oauthScope("PUBLIC")).toBe("refresh_token session:role:PUBLIC")
expect(oauthScope("AUTH SNOWFLAKE")).toBe("refresh_token session:role-encoded:AUTH%20SNOWFLAKE")
})
test("loader returns empty options when auth is not oauth", async () => {
const hooks = await SnowflakeCortexAuthPlugin({} as any)
const options = await hooks.auth!.loader!(async () => ({ type: "api", key: "token" }) as any, {} as any)
expect(options).toEqual({})
})
test("loader injects bearer header and preserves custom headers", async () => {
const { input, getAuth, setAuth } = makeInput()
setAuth({
type: "oauth",
access: "access-live",
refresh: "refresh-live",
expires: Date.now() + 60 * 60 * 1000,
accountId: "myorg-myaccount",
})
const hooks = await SnowflakeCortexAuthPlugin(input)
const options = await hooks.auth!.loader!(getAuth as any, {} as any)
expect(options.apiKey).toBe(OAUTH_DUMMY_KEY)
const originalFetch = globalThis.fetch
const captured: Headers[] = []
globalThis.fetch = (async (_request, init) => {
captured.push(new Headers(init?.headers))
return new Response("{}", { status: 200, headers: { "content-type": "application/json" } })
}) as typeof fetch
try {
await options.fetch("https://example.test/v1/chat", {
headers: { Authorization: `Bearer ${OAUTH_DUMMY_KEY}`, "x-keep": "yes" },
})
} finally {
globalThis.fetch = originalFetch
}
expect(captured).toHaveLength(1)
expect(captured[0].get("authorization")).toBe("Bearer access-live")
expect(captured[0].get("x-keep")).toBe("yes")
expect(captured[0].get("user-agent")).toMatch(/^opencode\//)
})
test("loader refreshes expired token with single-flight and persists refreshed oauth", async () => {
const { input, getAuth, setCalls } = makeInput()
let refreshCalls = 0
const apiAuthHeaders: string[] = []
// Must mock fetch before calling loader because startup refresh triggers for expires: 0
const originalFetch = globalThis.fetch
globalThis.fetch = (async (request, init) => {
const url =
typeof request === "string" ? request : request instanceof URL ? request.toString() : String(request.url)
if (url.includes("/oauth/token-request")) {
refreshCalls += 1
const body = new URLSearchParams(String(init?.body ?? ""))
expect(body.get("grant_type")).toBe("refresh_token")
expect(body.get("refresh_token")).toBe("refresh-old")
expect(new Headers(init?.headers).get("authorization")).toMatch(/^Basic /)
await new Promise((resolve) => setTimeout(resolve, 20))
return Response.json({ access_token: "access-new", refresh_token: "refresh-new", expires_in: 3600 })
}
apiAuthHeaders.push(new Headers(init?.headers).get("authorization") || "")
return new Response("{}", { status: 200, headers: { "content-type": "application/json" } })
}) as typeof fetch
try {
const hooks = await SnowflakeCortexAuthPlugin(input)
const options = await hooks.auth!.loader!(
async () =>
({
type: "oauth",
access: "access-expired",
refresh: "refresh-old",
expires: 0,
accountId: "myorg-myaccount",
}) as any,
{} as any,
)
await Promise.all([
options.fetch("https://example.test/v1/chat", { headers: {} }),
options.fetch("https://example.test/v1/chat", { headers: {} }),
])
} finally {
globalThis.fetch = originalFetch
}
expect(refreshCalls).toBe(1)
expect(apiAuthHeaders).toEqual(["Bearer access-new", "Bearer access-new"])
expect(setCalls).toHaveLength(1)
expect((setCalls[0] as any).body).toMatchObject({
type: "oauth",
access: "access-new",
refresh: "refresh-new",
accountId: "myorg-myaccount",
})
})
test("loader retries once after 401 by refreshing token", async () => {
const { input, getAuth, setCalls } = makeInput()
const hooks = await SnowflakeCortexAuthPlugin(input)
const options = await hooks.auth!.loader!(
async () =>
({
type: "oauth",
access: "access-stale",
refresh: "refresh-old",
expires: Date.now() + 60 * 60 * 1000,
accountId: "myorg-myaccount",
}) as any,
{} as any,
)
let apiCalls = 0
const seenAuth: string[] = []
const originalFetch = globalThis.fetch
globalThis.fetch = (async (request, init) => {
const url =
typeof request === "string" ? request : request instanceof URL ? request.toString() : String(request.url)
if (url.includes("/oauth/token-request")) {
return Response.json({ access_token: "access-fresh", refresh_token: "refresh-fresh", expires_in: 3600 })
}
apiCalls += 1
seenAuth.push(new Headers(init?.headers).get("authorization") || "")
if (apiCalls === 1) return new Response("unauthorized", { status: 401 })
return new Response("{}", { status: 200, headers: { "content-type": "application/json" } })
}) as typeof fetch
try {
const response = await options.fetch("https://example.test/v1/chat", { headers: {} })
expect(response.status).toBe(200)
} finally {
globalThis.fetch = originalFetch
}
expect(apiCalls).toBe(2)
expect(seenAuth).toEqual(["Bearer access-stale", "Bearer access-fresh"])
expect(setCalls).toHaveLength(1)
expect((setCalls[0] as any).body).toMatchObject({
type: "oauth",
access: "access-fresh",
refresh: "refresh-fresh",
accountId: "myorg-myaccount",
})
})
test("loader converts max_tokens to max_completion_tokens in request body", async () => {
const { input, getAuth } = makeInput()
const hooks = await SnowflakeCortexAuthPlugin(input)
const options = await hooks.auth!.loader!(getAuth as any, {} as any)
let sentBody: string | undefined
const originalFetch = globalThis.fetch
globalThis.fetch = (async (request, init) => {
sentBody = typeof init?.body === "string" ? init.body : undefined
return new Response("{}", { status: 200, headers: { "content-type": "application/json" } })
}) as typeof fetch
try {
await options.fetch("https://example.test/v1/chat", {
method: "POST",
body: JSON.stringify({ model: "claude-sonnet-4-5", max_tokens: 4096, messages: [] }),
})
} finally {
globalThis.fetch = originalFetch
}
expect(sentBody).toBeDefined()
const parsed = JSON.parse(sentBody!)
expect(parsed.max_completion_tokens).toBe(4096)
expect(parsed.max_tokens).toBeUndefined()
expect(parsed.model).toBe("claude-sonnet-4-5")
})
test("loader maps 400 'conversation complete' to 200 stop", async () => {
const { input, getAuth } = makeInput()
const hooks = await SnowflakeCortexAuthPlugin(input)
const options = await hooks.auth!.loader!(getAuth as any, {} as any)
const originalFetch = globalThis.fetch
globalThis.fetch = (async () => {
return new Response(JSON.stringify({ message: "Conversation complete" }), {
status: 400,
headers: { "content-type": "application/json" },
})
}) as unknown as typeof fetch
try {
const response = await options.fetch("https://example.test/v1/chat", {
method: "POST",
body: JSON.stringify({ model: "test", messages: [] }),
})
expect(response.status).toBe(200)
const body = await response.json()
expect(body.choices[0].finish_reason).toBe("stop")
} finally {
globalThis.fetch = originalFetch
}
})
test("loader fixes empty role in SSE stream", async () => {
const { input, getAuth } = makeInput()
const hooks = await SnowflakeCortexAuthPlugin(input)
const options = await hooks.auth!.loader!(getAuth as any, {} as any)
const originalFetch = globalThis.fetch
const sseChunk = `data: {"choices":[{"delta":{"role":"","content":"hello"}}]}\n\n`
globalThis.fetch = (async () => {
const stream = new ReadableStream({
start(ctrl) {
ctrl.enqueue(new TextEncoder().encode(sseChunk))
ctrl.close()
},
})
return new Response(stream, {
status: 200,
headers: { "content-type": "text/event-stream" },
})
}) as unknown as typeof fetch
try {
const response = await options.fetch("https://example.test/v1/chat", {
method: "POST",
body: JSON.stringify({ model: "test", messages: [], stream: true }),
})
expect(response.status).toBe(200)
const reader = response.body!.getReader()
const { value } = await reader.read()
const text = new TextDecoder().decode(value)
expect(text).not.toContain('"role":""')
expect(text).toContain('"role":"assistant"')
} finally {
globalThis.fetch = originalFetch
}
})
})
@@ -1,361 +0,0 @@
import { afterEach, describe, expect, test } from "bun:test"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Effect } from "effect"
import path from "path"
import { unlink } from "fs/promises"
import { Global } from "@opencode-ai/core/global"
import { Filesystem } from "@/util/filesystem"
import { Env } from "../../src/env"
import { Provider } from "@/provider/provider"
import { disposeAllInstances } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model"
const it = testEffect(LayerNode.compile(LayerNode.group([Provider.node, Env.node])))
const originalEnv = new Map<string, string | undefined>()
const set = (k: string, v: string) =>
Effect.gen(function* () {
if (!originalEnv.has(k)) originalEnv.set(k, process.env[k])
process.env[k] = v
yield* Env.use.set(k, v)
})
afterEach(async () => {
for (const [key, value] of originalEnv) {
if (value === undefined) delete process.env[key]
else process.env[key] = value
}
originalEnv.clear()
await disposeAllInstances()
})
const list = Provider.use.list()
const mantleModelConfig = {
provider: { npm: "@ai-sdk/amazon-bedrock/mantle" },
limit: { context: 272_000, output: 32_000 },
modalities: {
input: ["text", "image", "pdf"] as Array<"text" | "image" | "pdf">,
output: ["text"] as Array<"text">,
},
}
const withAuthJson = (contents: string) =>
Effect.acquireRelease(
Effect.promise(async () => {
const authPath = path.join(Global.Path.data, "auth.json")
let original: string | undefined
try {
original = await Filesystem.readText(authPath)
} catch {
original = undefined
}
await Filesystem.write(authPath, contents)
return { authPath, original }
}),
({ authPath, original }) =>
Effect.promise(async () => {
if (original !== undefined) {
await Filesystem.write(authPath, original)
return
}
await unlink(authPath).catch(() => undefined)
}),
)
it.instance(
"Bedrock: config region takes precedence over AWS_REGION env var",
() =>
Effect.gen(function* () {
yield* set("AWS_REGION", "us-east-1")
yield* set("AWS_PROFILE", "default")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].options?.region).toBe("eu-west-1")
}),
{ config: { provider: { "amazon-bedrock": { options: { region: "eu-west-1" } } } } },
)
it.instance("Bedrock: falls back to AWS_REGION env var when no config region", () =>
Effect.gen(function* () {
yield* set("AWS_REGION", "eu-west-1")
yield* set("AWS_PROFILE", "default")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].options?.region).toBe("eu-west-1")
}),
)
it.instance(
"Bedrock: loads when bearer token from auth.json is present",
() =>
Effect.gen(function* () {
yield* withAuthJson(JSON.stringify({ "amazon-bedrock": { type: "api", key: "test-bearer-token" } }))
yield* set("AWS_PROFILE", "")
yield* set("AWS_ACCESS_KEY_ID", "")
yield* set("AWS_BEARER_TOKEN_BEDROCK", "")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].options?.region).toBe("eu-west-1")
}),
{ config: { provider: { "amazon-bedrock": { options: { region: "eu-west-1" } } } } },
)
it.instance(
"Bedrock Mantle: GPT-5.5 uses Responses API and OpenAI base path",
() =>
Effect.gen(function* () {
yield* set("AWS_REGION", "")
yield* set("AWS_PROFILE", "")
yield* set("AWS_ACCESS_KEY_ID", "")
yield* set("AWS_BEARER_TOKEN_BEDROCK", "")
const model = yield* Provider.use.getModel(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-5.5"))
const language = yield* Provider.use.getLanguage(model)
expect((language as { provider: string }).provider).toBe("bedrock-mantle.responses")
expect((language as { modelId: string }).modelId).toBe("openai.gpt-5.5")
expect(
(language as unknown as { config: { url: (input: { path: string; modelId: string }) => string } }).config.url({
path: "/responses",
modelId: "openai.gpt-5.5",
}),
).toBe("https://bedrock-mantle.us-east-2.api.aws/openai/v1/responses")
}),
{
config: {
provider: {
"amazon-bedrock": {
options: { region: "us-east-2", apiKey: "test-bearer-token" },
models: {
"openai.gpt-5.5": {
...mantleModelConfig,
provider: {
npm: "@ai-sdk/amazon-bedrock/mantle",
api: "https://bedrock-mantle.${AWS_REGION}.api.aws/openai/v1",
},
},
},
},
},
},
},
)
it.instance(
"Bedrock Mantle: GPT OSS safeguard uses Chat Completions and Mantle base path",
() =>
Effect.gen(function* () {
yield* set("AWS_BEARER_TOKEN_BEDROCK", "test-bearer-token")
const model = yield* Provider.use.getModel(
ProviderV2.ID.amazonBedrock,
ModelV2.ID.make("openai.gpt-oss-safeguard-120b"),
)
const language = yield* Provider.use.getLanguage(model)
expect((language as { provider: string }).provider).toBe("bedrock-mantle.chat")
expect((language as { modelId: string }).modelId).toBe("openai.gpt-oss-safeguard-120b")
expect(
(language as unknown as { config: { url: (input: { path: string; modelId: string }) => string } }).config.url({
path: "/chat/completions",
modelId: "openai.gpt-oss-safeguard-120b",
}),
).toBe("https://bedrock-mantle.us-east-1.api.aws/v1/chat/completions")
}),
{
config: {
provider: {
"amazon-bedrock": {
options: { region: "us-east-1" },
models: { "openai.gpt-oss-safeguard-120b": mantleModelConfig },
},
},
},
},
)
it.instance(
"Bedrock: config profile takes precedence over AWS_PROFILE env var",
() =>
Effect.gen(function* () {
yield* set("AWS_PROFILE", "default")
yield* set("AWS_ACCESS_KEY_ID", "test-key-id")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].options?.region).toBe("us-east-1")
}),
{
config: {
provider: { "amazon-bedrock": { options: { profile: "my-custom-profile", region: "us-east-1" } } },
},
},
)
it.instance(
"Bedrock: includes custom endpoint in options when specified",
() =>
Effect.gen(function* () {
yield* set("AWS_PROFILE", "default")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].options?.endpoint).toBe(
"https://bedrock-runtime.us-east-1.vpce-xxxxx.amazonaws.com",
)
}),
{
config: {
provider: {
"amazon-bedrock": {
options: { endpoint: "https://bedrock-runtime.us-east-1.vpce-xxxxx.amazonaws.com" },
},
},
},
},
)
it.instance(
"Bedrock: autoloads when AWS_WEB_IDENTITY_TOKEN_FILE is present",
() =>
Effect.gen(function* () {
yield* set("AWS_WEB_IDENTITY_TOKEN_FILE", "/var/run/secrets/eks.amazonaws.com/serviceaccount/token")
yield* set("AWS_ROLE_ARN", "arn:aws:iam::123456789012:role/my-eks-role")
yield* set("AWS_PROFILE", "")
yield* set("AWS_ACCESS_KEY_ID", "")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].options?.region).toBe("us-east-1")
}),
{ config: { provider: { "amazon-bedrock": { options: { region: "us-east-1" } } } } },
)
// Cross-region inference profile prefix handling.
// Models from models.dev may come with prefixes already (e.g. us., eu., global.).
// These should NOT be double-prefixed when passed to the SDK.
it.instance(
"Bedrock: model with us. prefix should not be double-prefixed",
() =>
Effect.gen(function* () {
yield* set("AWS_PROFILE", "default")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].models["us.anthropic.claude-opus-4-5-20251101-v1:0"]).toBeDefined()
}),
{
config: {
provider: {
"amazon-bedrock": {
options: { region: "us-east-1" },
models: { "us.anthropic.claude-opus-4-5-20251101-v1:0": { name: "Claude Opus 4.5 (US)" } },
},
},
},
},
)
it.instance(
"Bedrock: model with global. prefix should not be prefixed",
() =>
Effect.gen(function* () {
yield* set("AWS_PROFILE", "default")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(
providers[ProviderV2.ID.amazonBedrock].models["global.anthropic.claude-opus-4-5-20251101-v1:0"],
).toBeDefined()
}),
{
config: {
provider: {
"amazon-bedrock": {
options: { region: "us-east-1" },
models: { "global.anthropic.claude-opus-4-5-20251101-v1:0": { name: "Claude Opus 4.5 (Global)" } },
},
},
},
},
)
it.instance(
"Bedrock: model with eu. prefix should not be double-prefixed",
() =>
Effect.gen(function* () {
yield* set("AWS_PROFILE", "default")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].models["eu.anthropic.claude-opus-4-5-20251101-v1:0"]).toBeDefined()
}),
{
config: {
provider: {
"amazon-bedrock": {
options: { region: "eu-west-1" },
models: { "eu.anthropic.claude-opus-4-5-20251101-v1:0": { name: "Claude Opus 4.5 (EU)" } },
},
},
},
},
)
it.instance(
"Bedrock: model without prefix in US region should get us. prefix added",
() =>
Effect.gen(function* () {
yield* set("AWS_PROFILE", "default")
const providers = yield* list
expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
expect(providers[ProviderV2.ID.amazonBedrock].models["anthropic.claude-opus-4-5-20251101-v1:0"]).toBeDefined()
}),
{
config: {
provider: {
"amazon-bedrock": {
options: { region: "us-east-1" },
models: { "anthropic.claude-opus-4-5-20251101-v1:0": { name: "Claude Opus 4.5" } },
},
},
},
},
)
// Direct unit tests for cross-region inference profile prefix detection.
describe("Bedrock cross-region prefix detection", () => {
const crossRegionPrefixes = ["global.", "us.", "eu.", "jp.", "apac.", "au."]
test("should detect global. prefix", () => {
expect(crossRegionPrefixes.some((p) => "global.anthropic.claude-opus-4-5-20251101-v1:0".startsWith(p))).toBe(true)
})
test("should detect us. prefix", () => {
expect(crossRegionPrefixes.some((p) => "us.anthropic.claude-opus-4-5-20251101-v1:0".startsWith(p))).toBe(true)
})
test("should detect eu. prefix", () => {
expect(crossRegionPrefixes.some((p) => "eu.anthropic.claude-opus-4-5-20251101-v1:0".startsWith(p))).toBe(true)
})
test("should detect jp. prefix", () => {
expect(crossRegionPrefixes.some((p) => "jp.anthropic.claude-sonnet-4-20250514-v1:0".startsWith(p))).toBe(true)
})
test("should detect apac. prefix", () => {
expect(crossRegionPrefixes.some((p) => "apac.anthropic.claude-sonnet-4-20250514-v1:0".startsWith(p))).toBe(true)
})
test("should detect au. prefix", () => {
expect(crossRegionPrefixes.some((p) => "au.anthropic.claude-sonnet-4-5-20250929-v1:0".startsWith(p))).toBe(true)
})
test("should NOT detect prefix for non-prefixed model", () => {
expect(crossRegionPrefixes.some((p) => "anthropic.claude-opus-4-5-20251101-v1:0".startsWith(p))).toBe(false)
})
test("should NOT detect prefix for amazon nova models", () => {
expect(crossRegionPrefixes.some((p) => "amazon.nova-pro-v1:0".startsWith(p))).toBe(false)
})
test("should NOT detect prefix for cohere models", () => {
expect(crossRegionPrefixes.some((p) => "cohere.command-r-plus-v1:0".startsWith(p))).toBe(false)
})
})
@@ -713,14 +713,6 @@ it.instance("getSmallModel returns appropriate small model", () =>
}),
)
it.instance("getSmallModel prefers Gemini for Google Vertex", () =>
Effect.gen(function* () {
yield* set("GOOGLE_VERTEX_PROJECT", "test-project")
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.googleVertex)
expect(model).toBeDefined()
expect(model?.id).toContain("gemini")
}),
)
it.instance(
"getSmallModel selects the latest model in the preferred family",
@@ -1282,7 +1274,7 @@ it.instance(
expect(providers[ProviderV2.ID.make("nvidia")].options.headers).toEqual({
"HTTP-Referer": "https://opencode.ai/",
"X-Title": "opencode",
"X-BILLING-INVOKE-ORIGIN": "OpenCode",
"X-BILLING-INVOKE-ORIGIN": "Neuron",
})
}),
{ config: { provider: { nvidia: { options: { apiKey: "test-api-key" } } } } },
@@ -1295,7 +1287,7 @@ it.instance(
expect(providers[ProviderV2.ID.make("nvidia")].options.headers).toEqual({
"HTTP-Referer": "https://opencode.ai/",
"X-Title": "opencode",
"X-BILLING-INVOKE-ORIGIN": "OpenCode",
"X-BILLING-INVOKE-ORIGIN": "Neuron",
})
}),
{ config: { provider: { nvidia: { options: { apiKey: "test-api-key", baseURL: "http://localhost:8000/v1" } } } } },
@@ -1804,136 +1796,12 @@ it.instance(
},
)
it.instance(
"Google Vertex: retains baseURL for custom proxy",
Effect.gen(function* () {
yield* set("GOOGLE_APPLICATION_CREDENTIALS", "test-creds")
const providers = yield* list
expect(providers[ProviderV2.ID.make("vertex-proxy")]).toBeDefined()
expect(providers[ProviderV2.ID.make("vertex-proxy")].options.baseURL).toBe("https://my-proxy.com/v1")
}),
{
config: {
provider: {
"vertex-proxy": {
name: "Vertex Proxy",
npm: "@ai-sdk/google-vertex",
api: "https://my-proxy.com/v1",
env: ["GOOGLE_APPLICATION_CREDENTIALS"],
models: { "gemini-pro": { name: "Gemini Pro", tool_call: true } },
options: {
project: "test-project",
location: "us-central1",
baseURL: "https://my-proxy.com/v1",
},
},
},
},
},
)
it.instance(
"Google Vertex: supports OpenAI compatible models",
Effect.gen(function* () {
yield* set("GOOGLE_APPLICATION_CREDENTIALS", "test-creds")
const providers = yield* list
const model = providers[ProviderV2.ID.make("vertex-openai")].models["gpt-4"]
expect(model).toBeDefined()
expect(model.api.npm).toBe("@ai-sdk/openai-compatible")
}),
{
config: {
provider: {
"vertex-openai": {
name: "Vertex OpenAI",
npm: "@ai-sdk/google-vertex",
env: ["GOOGLE_APPLICATION_CREDENTIALS"],
models: {
"gpt-4": {
name: "GPT-4",
provider: { npm: "@ai-sdk/openai-compatible", api: "https://api.openai.com/v1" },
},
},
options: { project: "test-project", location: "us-central1" },
},
},
},
},
)
it.instance("Google Vertex: uses REP endpoint for Claude continental multi-regions", () =>
Effect.gen(function* () {
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
yield* set("VERTEX_LOCATION", "eu")
const provider = yield* Provider.Service
const model = yield* provider.getModel(
ProviderV2.ID.make("google-vertex"),
ModelV2.ID.make("claude-sonnet-4-6@default"),
)
const language = yield* provider.getLanguage(model)
expect(languageBaseURL(language)).toBe(
"https://aiplatform.eu.rep.googleapis.com/v1/projects/test-project/locations/eu/publishers/anthropic/models",
)
}),
)
it.instance("Google Vertex Anthropic: uses REP endpoint for continental multi-regions", () =>
Effect.gen(function* () {
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
yield* set("VERTEX_LOCATION", "us")
const provider = yield* Provider.Service
const model = yield* provider.getModel(
ProviderV2.ID.make("google-vertex-anthropic"),
ModelV2.ID.make("claude-sonnet-4-6@default"),
)
const language = yield* provider.getLanguage(model)
expect(languageBaseURL(language)).toBe(
"https://aiplatform.us.rep.googleapis.com/v1/projects/test-project/locations/us/publishers/anthropic/models",
)
}),
)
it.instance("Google Vertex: keeps regional Claude endpoints unchanged", () =>
Effect.gen(function* () {
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
yield* set("VERTEX_LOCATION", "europe-west1")
const provider = yield* Provider.Service
const model = yield* provider.getModel(
ProviderV2.ID.make("google-vertex"),
ModelV2.ID.make("claude-sonnet-4-6@default"),
)
const language = yield* provider.getLanguage(model)
expect(languageBaseURL(language)).toBe(
"https://europe-west1-aiplatform.googleapis.com/v1/projects/test-project/locations/europe-west1/publishers/anthropic/models",
)
}),
)
it.instance("Google Vertex: uses REP endpoint for Gemini continental multi-regions", () =>
Effect.gen(function* () {
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
yield* set("VERTEX_LOCATION", "eu")
const provider = yield* Provider.Service
const model = yield* provider.getModel(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini-3.5-flash"))
const language = yield* provider.getLanguage(model)
expect(languageBaseURL(language)).toBe(
"https://aiplatform.eu.rep.googleapis.com/v1beta1/projects/test-project/locations/eu/publishers/google",
)
}),
)
it.instance("Google Vertex: keeps regional Gemini endpoints unchanged", () =>
Effect.gen(function* () {
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
yield* set("VERTEX_LOCATION", "europe-west1")
const provider = yield* Provider.Service
const model = yield* provider.getModel(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini-3.5-flash"))
const language = yield* provider.getLanguage(model)
expect(languageBaseURL(language)).toBe(
"https://europe-west1-aiplatform.googleapis.com/v1beta1/projects/test-project/locations/europe-west1/publishers/google",
)
}),
)
it.instance("cloudflare-ai-gateway loads with env variables", () =>
Effect.gen(function* () {
@@ -1,63 +0,0 @@
import { NodeHttpServer } from "@effect/platform-node"
import { describe, expect } from "bun:test"
import { Context, Effect, Layer, Option, Ref } from "effect"
import { HttpBody, HttpClient, HttpClientRequest, HttpRouter } from "effect/unstable/http"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { MoveSession } from "@opencode-ai/core/control-plane/move-session"
import { AbsolutePath } from "@opencode-ai/core/schema"
import { SessionV2 } from "@opencode-ai/core/session"
import { Auth } from "../../src/auth"
import { Config } from "../../src/config/config"
import { Installation } from "../../src/installation"
import { ServerAuth } from "../../src/server/auth"
import { RootHttpApi } from "../../src/server/routes/instance/httpapi/api"
import { controlHandlers } from "../../src/server/routes/instance/httpapi/handlers/control"
import { controlPlaneHandlers } from "../../src/server/routes/instance/httpapi/handlers/control-plane"
import { globalHandlers } from "../../src/server/routes/instance/httpapi/handlers/global"
import { authorizationLayer } from "../../src/server/routes/instance/httpapi/middleware/authorization"
import { schemaErrorLayer } from "../../src/server/routes/instance/httpapi/middleware/schema-error"
import { testEffect } from "../lib/effect"
const input = MoveSession.Input.make({
sessionID: SessionV2.ID.make("ses_move"),
destination: { directory: AbsolutePath.make("/destination") },
moveChanges: true,
})
const called = Ref.makeUnsafe<MoveSession.Input | undefined>(undefined)
const apiLayer = HttpRouter.serve(
HttpApiBuilder.layer(RootHttpApi).pipe(
Layer.provide([controlHandlers, controlPlaneHandlers, globalHandlers]),
Layer.provide([authorizationLayer, schemaErrorLayer]),
// Raw HttpApi routes expose an opaque handler context at the request boundary.
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion
HttpRouter.provideRequest(Layer.succeedContext(Context.empty() as Context.Context<unknown>)),
),
{ disableListenLog: true, disableLogger: true },
).pipe(
Layer.provideMerge(NodeHttpServer.layerTest),
Layer.provide(Layer.mock(Auth.Service)({})),
Layer.provide(Layer.mock(Config.Service)({})),
Layer.provide(Layer.mock(Installation.Service)({})),
Layer.provide(
Layer.mock(MoveSession.Service)({
moveSession: (value) => Ref.set(called, value),
}),
),
Layer.provide(ServerAuth.Config.configLayer({ password: Option.none(), username: "opencode" })),
)
const it = testEffect(apiLayer)
describe("control-plane HttpApi", () => {
it.live("moves a session through the root control-plane route", () =>
Effect.gen(function* () {
const response = yield* HttpClientRequest.post("/experimental/control-plane/move-session").pipe(
HttpClientRequest.setBody(HttpBody.jsonUnsafe(input)),
HttpClient.execute,
)
expect(response.status).toBe(204)
expect(yield* Ref.get(called)).toEqual(input)
}),
)
})
@@ -1,66 +0,0 @@
import { NodeHttpServer } from "@effect/platform-node"
import { describe, expect } from "bun:test"
import { Context, Effect, Layer, Option } from "effect"
import { HttpBody, HttpClient, HttpClientRequest, HttpRouter } from "effect/unstable/http"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { Auth } from "../../src/auth"
import { Config } from "../../src/config/config"
import { Installation } from "../../src/installation"
import { MoveSession } from "@opencode-ai/core/control-plane/move-session"
import { ServerAuth } from "../../src/server/auth"
import { RootHttpApi } from "../../src/server/routes/instance/httpapi/api"
import { GlobalPaths } from "../../src/server/routes/instance/httpapi/groups/global"
import { controlHandlers } from "../../src/server/routes/instance/httpapi/handlers/control"
import { controlPlaneHandlers } from "../../src/server/routes/instance/httpapi/handlers/control-plane"
import { globalHandlers } from "../../src/server/routes/instance/httpapi/handlers/global"
import { authorizationLayer } from "../../src/server/routes/instance/httpapi/middleware/authorization"
import { schemaErrorLayer } from "../../src/server/routes/instance/httpapi/middleware/schema-error"
import { testEffect } from "../lib/effect"
const apiLayer = HttpRouter.serve(
HttpApiBuilder.layer(RootHttpApi).pipe(
Layer.provide([controlHandlers, controlPlaneHandlers, globalHandlers]),
Layer.provide([authorizationLayer, schemaErrorLayer]),
// Raw HttpApi routes expose an opaque handler context at the request boundary.
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion
HttpRouter.provideRequest(Layer.succeedContext(Context.empty() as Context.Context<unknown>)),
),
{ disableListenLog: true, disableLogger: true },
).pipe(
Layer.provideMerge(NodeHttpServer.layerTest),
Layer.provide(Layer.mock(Auth.Service)({})),
Layer.provide(Layer.mock(Config.Service)({})),
Layer.provide(Layer.mock(MoveSession.Service)({})),
Layer.provide(
Layer.mock(Installation.Service)({
method: () => Effect.succeed("npm"),
latest: () => Effect.succeed("9.9.9"),
upgrade: () => Effect.void,
}),
),
Layer.provide(ServerAuth.Config.configLayer({ password: Option.none(), username: "opencode" })),
)
const it = testEffect(apiLayer)
describe("global HttpApi", () => {
it.live("upgrades to latest when the request body is omitted", () =>
Effect.gen(function* () {
const response = yield* HttpClient.post(GlobalPaths.upgrade)
expect(response.status).toBe(200)
expect(yield* response.json).toEqual({ success: true, version: "9.9.9" })
}),
)
it.live("rejects malformed upgrade payloads", () =>
Effect.gen(function* () {
const response = yield* HttpClientRequest.post(GlobalPaths.upgrade).pipe(
HttpClientRequest.setBody(HttpBody.text("{", "application/json")),
HttpClient.execute,
)
expect(response.status).toBe(400)
expect(yield* response.json).toEqual({ success: false, error: "Invalid request body" })
}),
)
})
+2 -2
View File
@@ -335,7 +335,7 @@ describe("session.retry.retryable", () => {
expect(retryable).toEqual({ message: "Response decompression failed" })
})
test("maps free limits to Go upsell action", () => {
test("maps free limits to managed-provider guidance", () => {
const error = Schema.decodeUnknownSync(SessionV1.APIError.Schema)(
new SessionV1.APIError({
message: "Free usage exceeded",
@@ -354,7 +354,7 @@ describe("session.retry.retryable", () => {
reason: "free_tier_limit",
provider: "opencode",
title: "Free limit reached",
message: "Subscribe to OpenCode Go for reliable access to the best open-source models, starting at $5/month.",
message: "Free limit reached on the managed provider. Configure your own provider API key for unlimited use.",
label: "subscribe",
link: SessionRetry.GO_UPSELL_URL,
},