mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
opencode support
This commit is contained in:
@@ -96,6 +96,9 @@ export {
|
||||
getCodexCliPaths,
|
||||
getCodexConfigDir,
|
||||
getCodexAuthPath,
|
||||
getOpenCodeCliPaths,
|
||||
getOpenCodeConfigDir,
|
||||
getOpenCodeAuthPath,
|
||||
getShellPaths,
|
||||
getExtendedPath,
|
||||
// Node.js paths
|
||||
@@ -126,6 +129,9 @@ export {
|
||||
findCodexCliPath,
|
||||
getCodexAuthIndicators,
|
||||
type CodexAuthIndicators,
|
||||
findOpenCodeCliPath,
|
||||
getOpenCodeAuthIndicators,
|
||||
type OpenCodeAuthIndicators,
|
||||
// Electron userData operations
|
||||
setElectronUserDataPath,
|
||||
getElectronUserDataPath,
|
||||
|
||||
@@ -543,6 +543,11 @@ function getAllAllowedSystemPaths(): string[] {
|
||||
// Codex config directory and files
|
||||
getCodexConfigDir(),
|
||||
getCodexAuthPath(),
|
||||
// OpenCode CLI paths
|
||||
...getOpenCodeCliPaths(),
|
||||
// OpenCode config directory and files
|
||||
getOpenCodeConfigDir(),
|
||||
getOpenCodeAuthPath(),
|
||||
// Shell paths
|
||||
...getShellPaths(),
|
||||
// Node.js system paths
|
||||
@@ -564,6 +569,8 @@ function getAllAllowedSystemDirs(): string[] {
|
||||
getClaudeProjectsDir(),
|
||||
// Codex config
|
||||
getCodexConfigDir(),
|
||||
// OpenCode config
|
||||
getOpenCodeConfigDir(),
|
||||
// Version managers (need recursive access for version directories)
|
||||
...getNvmPaths(),
|
||||
...getFnmPaths(),
|
||||
@@ -1007,3 +1014,148 @@ export async function getCodexAuthIndicators(): Promise<CodexAuthIndicators> {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// OpenCode CLI Detection
|
||||
// =============================================================================
|
||||
|
||||
const OPENCODE_CONFIG_DIR_NAME = '.opencode';
|
||||
const OPENCODE_AUTH_FILENAME = 'auth.json';
|
||||
const OPENCODE_TOKENS_KEY = 'tokens';
|
||||
|
||||
/**
|
||||
* Get common paths where OpenCode CLI might be installed
|
||||
*/
|
||||
export function getOpenCodeCliPaths(): string[] {
|
||||
const isWindows = process.platform === 'win32';
|
||||
const homeDir = os.homedir();
|
||||
|
||||
if (isWindows) {
|
||||
const appData = process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming');
|
||||
const localAppData = process.env.LOCALAPPDATA || path.join(homeDir, 'AppData', 'Local');
|
||||
return [
|
||||
path.join(homeDir, '.local', 'bin', 'opencode.exe'),
|
||||
path.join(appData, 'npm', 'opencode.cmd'),
|
||||
path.join(appData, 'npm', 'opencode'),
|
||||
path.join(appData, '.npm-global', 'bin', 'opencode.cmd'),
|
||||
path.join(appData, '.npm-global', 'bin', 'opencode'),
|
||||
// Volta on Windows
|
||||
path.join(homeDir, '.volta', 'bin', 'opencode.exe'),
|
||||
// pnpm on Windows
|
||||
path.join(localAppData, 'pnpm', 'opencode.cmd'),
|
||||
path.join(localAppData, 'pnpm', 'opencode'),
|
||||
// Go installation (if OpenCode is a Go binary)
|
||||
path.join(homeDir, 'go', 'bin', 'opencode.exe'),
|
||||
path.join(process.env.GOPATH || path.join(homeDir, 'go'), 'bin', 'opencode.exe'),
|
||||
];
|
||||
}
|
||||
|
||||
// Include NVM bin paths for opencode installed via npm global under NVM
|
||||
const nvmBinPaths = getNvmBinPaths().map((binPath) => path.join(binPath, 'opencode'));
|
||||
|
||||
// Include fnm bin paths
|
||||
const fnmBinPaths = getFnmBinPaths().map((binPath) => path.join(binPath, 'opencode'));
|
||||
|
||||
// pnpm global bin path
|
||||
const pnpmHome = process.env.PNPM_HOME || path.join(homeDir, '.local', 'share', 'pnpm');
|
||||
|
||||
return [
|
||||
// Standard locations
|
||||
path.join(homeDir, '.local', 'bin', 'opencode'),
|
||||
'/opt/homebrew/bin/opencode',
|
||||
'/usr/local/bin/opencode',
|
||||
'/usr/bin/opencode',
|
||||
path.join(homeDir, '.npm-global', 'bin', 'opencode'),
|
||||
// Linuxbrew
|
||||
'/home/linuxbrew/.linuxbrew/bin/opencode',
|
||||
// Volta
|
||||
path.join(homeDir, '.volta', 'bin', 'opencode'),
|
||||
// pnpm global
|
||||
path.join(pnpmHome, 'opencode'),
|
||||
// Yarn global
|
||||
path.join(homeDir, '.yarn', 'bin', 'opencode'),
|
||||
path.join(homeDir, '.config', 'yarn', 'global', 'node_modules', '.bin', 'opencode'),
|
||||
// Go installation (if OpenCode is a Go binary)
|
||||
path.join(homeDir, 'go', 'bin', 'opencode'),
|
||||
path.join(process.env.GOPATH || path.join(homeDir, 'go'), 'bin', 'opencode'),
|
||||
// Snap packages
|
||||
'/snap/bin/opencode',
|
||||
// NVM paths
|
||||
...nvmBinPaths,
|
||||
// fnm paths
|
||||
...fnmBinPaths,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OpenCode configuration directory path
|
||||
*/
|
||||
export function getOpenCodeConfigDir(): string {
|
||||
return path.join(os.homedir(), OPENCODE_CONFIG_DIR_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to OpenCode auth file
|
||||
*/
|
||||
export function getOpenCodeAuthPath(): string {
|
||||
return path.join(getOpenCodeConfigDir(), OPENCODE_AUTH_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if OpenCode CLI is installed and return its path
|
||||
*/
|
||||
export async function findOpenCodeCliPath(): Promise<string | null> {
|
||||
return findFirstExistingPath(getOpenCodeCliPaths());
|
||||
}
|
||||
|
||||
export interface OpenCodeAuthIndicators {
|
||||
hasAuthFile: boolean;
|
||||
hasOAuthToken: boolean;
|
||||
hasApiKey: boolean;
|
||||
}
|
||||
|
||||
const OPENCODE_OAUTH_KEYS = ['access_token', 'oauth_token'] as const;
|
||||
const OPENCODE_API_KEY_KEYS = ['api_key', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY'] as const;
|
||||
|
||||
function getOpenCodeNestedTokens(record: Record<string, unknown>): Record<string, unknown> | null {
|
||||
const tokens = record[OPENCODE_TOKENS_KEY];
|
||||
if (tokens && typeof tokens === 'object' && !Array.isArray(tokens)) {
|
||||
return tokens as Record<string, unknown>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenCode authentication status by checking auth file indicators
|
||||
*/
|
||||
export async function getOpenCodeAuthIndicators(): Promise<OpenCodeAuthIndicators> {
|
||||
const result: OpenCodeAuthIndicators = {
|
||||
hasAuthFile: false,
|
||||
hasOAuthToken: false,
|
||||
hasApiKey: false,
|
||||
};
|
||||
|
||||
try {
|
||||
const authContent = await systemPathReadFile(getOpenCodeAuthPath());
|
||||
result.hasAuthFile = true;
|
||||
|
||||
try {
|
||||
const authJson = JSON.parse(authContent) as Record<string, unknown>;
|
||||
result.hasOAuthToken = hasNonEmptyStringField(authJson, OPENCODE_OAUTH_KEYS);
|
||||
result.hasApiKey = hasNonEmptyStringField(authJson, OPENCODE_API_KEY_KEYS);
|
||||
const nestedTokens = getOpenCodeNestedTokens(authJson);
|
||||
if (nestedTokens) {
|
||||
result.hasOAuthToken =
|
||||
result.hasOAuthToken || hasNonEmptyStringField(nestedTokens, OPENCODE_OAUTH_KEYS);
|
||||
result.hasApiKey =
|
||||
result.hasApiKey || hasNonEmptyStringField(nestedTokens, OPENCODE_API_KEY_KEYS);
|
||||
}
|
||||
} catch {
|
||||
// Ignore parse errors; file exists but contents are unreadable
|
||||
}
|
||||
} catch {
|
||||
// Auth file not found or inaccessible
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -174,12 +174,16 @@ export type {
|
||||
export * from './cursor-models.js';
|
||||
export * from './cursor-cli.js';
|
||||
|
||||
// OpenCode types
|
||||
export * from './opencode-models.js';
|
||||
|
||||
// Provider utilities
|
||||
export {
|
||||
PROVIDER_PREFIXES,
|
||||
isCursorModel,
|
||||
isClaudeModel,
|
||||
isCodexModel,
|
||||
isOpencodeModel,
|
||||
getModelProvider,
|
||||
stripProviderPrefix,
|
||||
addProviderPrefix,
|
||||
|
||||
397
libs/types/src/opencode-models.ts
Normal file
397
libs/types/src/opencode-models.ts
Normal file
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* OpenCode Model IDs
|
||||
* Models available via OpenCode CLI (opencode models command)
|
||||
*/
|
||||
export type OpencodeModelId =
|
||||
// OpenCode Free Tier Models
|
||||
| 'opencode/big-pickle'
|
||||
| 'opencode/glm-4.7-free'
|
||||
| 'opencode/gpt-5-nano'
|
||||
| 'opencode/grok-code'
|
||||
| 'opencode/minimax-m2.1-free'
|
||||
// Amazon Bedrock - Claude Models
|
||||
| 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0'
|
||||
| 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0'
|
||||
| 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0'
|
||||
| 'amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0'
|
||||
| 'amazon-bedrock/anthropic.claude-opus-4-20250514-v1:0'
|
||||
| 'amazon-bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0'
|
||||
| 'amazon-bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0'
|
||||
| 'amazon-bedrock/anthropic.claude-3-opus-20240229-v1:0'
|
||||
// Amazon Bedrock - DeepSeek Models
|
||||
| 'amazon-bedrock/deepseek.r1-v1:0'
|
||||
| 'amazon-bedrock/deepseek.v3-v1:0'
|
||||
// Amazon Bedrock - Amazon Nova Models
|
||||
| 'amazon-bedrock/amazon.nova-premier-v1:0'
|
||||
| 'amazon-bedrock/amazon.nova-pro-v1:0'
|
||||
| 'amazon-bedrock/amazon.nova-lite-v1:0'
|
||||
// Amazon Bedrock - Meta Llama Models
|
||||
| 'amazon-bedrock/meta.llama4-maverick-17b-instruct-v1:0'
|
||||
| 'amazon-bedrock/meta.llama3-3-70b-instruct-v1:0'
|
||||
// Amazon Bedrock - Mistral Models
|
||||
| 'amazon-bedrock/mistral.mistral-large-2402-v1:0'
|
||||
// Amazon Bedrock - Qwen Models
|
||||
| 'amazon-bedrock/qwen.qwen3-coder-480b-a35b-v1:0'
|
||||
| 'amazon-bedrock/qwen.qwen3-235b-a22b-2507-v1:0';
|
||||
|
||||
/**
|
||||
* Provider type for OpenCode models
|
||||
*/
|
||||
export type OpencodeProvider =
|
||||
| 'opencode'
|
||||
| 'amazon-bedrock-anthropic'
|
||||
| 'amazon-bedrock-deepseek'
|
||||
| 'amazon-bedrock-amazon'
|
||||
| 'amazon-bedrock-meta'
|
||||
| 'amazon-bedrock-mistral'
|
||||
| 'amazon-bedrock-qwen';
|
||||
|
||||
/**
|
||||
* Friendly aliases mapped to full model IDs
|
||||
*/
|
||||
export const OPENCODE_MODEL_MAP: Record<string, OpencodeModelId> = {
|
||||
// OpenCode free tier aliases
|
||||
'big-pickle': 'opencode/big-pickle',
|
||||
pickle: 'opencode/big-pickle',
|
||||
'glm-free': 'opencode/glm-4.7-free',
|
||||
'gpt-nano': 'opencode/gpt-5-nano',
|
||||
nano: 'opencode/gpt-5-nano',
|
||||
'grok-code': 'opencode/grok-code',
|
||||
grok: 'opencode/grok-code',
|
||||
minimax: 'opencode/minimax-m2.1-free',
|
||||
|
||||
// Claude aliases (via Bedrock)
|
||||
'claude-sonnet-4.5': 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
|
||||
'sonnet-4.5': 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
|
||||
sonnet: 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
|
||||
'claude-opus-4.5': 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0',
|
||||
'opus-4.5': 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0',
|
||||
opus: 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0',
|
||||
'claude-haiku-4.5': 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0',
|
||||
'haiku-4.5': 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0',
|
||||
haiku: 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0',
|
||||
|
||||
// DeepSeek aliases
|
||||
'deepseek-r1': 'amazon-bedrock/deepseek.r1-v1:0',
|
||||
r1: 'amazon-bedrock/deepseek.r1-v1:0',
|
||||
'deepseek-v3': 'amazon-bedrock/deepseek.v3-v1:0',
|
||||
|
||||
// Nova aliases
|
||||
'nova-premier': 'amazon-bedrock/amazon.nova-premier-v1:0',
|
||||
'nova-pro': 'amazon-bedrock/amazon.nova-pro-v1:0',
|
||||
nova: 'amazon-bedrock/amazon.nova-pro-v1:0',
|
||||
|
||||
// Llama aliases
|
||||
llama4: 'amazon-bedrock/meta.llama4-maverick-17b-instruct-v1:0',
|
||||
'llama-4': 'amazon-bedrock/meta.llama4-maverick-17b-instruct-v1:0',
|
||||
llama3: 'amazon-bedrock/meta.llama3-3-70b-instruct-v1:0',
|
||||
|
||||
// Qwen aliases
|
||||
qwen: 'amazon-bedrock/qwen.qwen3-coder-480b-a35b-v1:0',
|
||||
'qwen-coder': 'amazon-bedrock/qwen.qwen3-coder-480b-a35b-v1:0',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* OpenCode model metadata
|
||||
*/
|
||||
export interface OpencodeModelConfig {
|
||||
id: OpencodeModelId;
|
||||
label: string;
|
||||
description: string;
|
||||
supportsVision: boolean;
|
||||
provider: OpencodeProvider;
|
||||
tier: 'free' | 'standard' | 'premium';
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete list of OpenCode model configurations
|
||||
*/
|
||||
export const OPENCODE_MODELS: OpencodeModelConfig[] = [
|
||||
// OpenCode Free Tier Models
|
||||
{
|
||||
id: 'opencode/big-pickle',
|
||||
label: 'Big Pickle',
|
||||
description: 'OpenCode free tier model - great for general coding',
|
||||
supportsVision: false,
|
||||
provider: 'opencode',
|
||||
tier: 'free',
|
||||
},
|
||||
{
|
||||
id: 'opencode/glm-4.7-free',
|
||||
label: 'GLM 4.7 Free',
|
||||
description: 'OpenCode free tier GLM model',
|
||||
supportsVision: false,
|
||||
provider: 'opencode',
|
||||
tier: 'free',
|
||||
},
|
||||
{
|
||||
id: 'opencode/gpt-5-nano',
|
||||
label: 'GPT-5 Nano',
|
||||
description: 'OpenCode free tier nano model - fast and lightweight',
|
||||
supportsVision: false,
|
||||
provider: 'opencode',
|
||||
tier: 'free',
|
||||
},
|
||||
{
|
||||
id: 'opencode/grok-code',
|
||||
label: 'Grok Code',
|
||||
description: 'OpenCode free tier Grok model for coding',
|
||||
supportsVision: false,
|
||||
provider: 'opencode',
|
||||
tier: 'free',
|
||||
},
|
||||
{
|
||||
id: 'opencode/minimax-m2.1-free',
|
||||
label: 'MiniMax M2.1 Free',
|
||||
description: 'OpenCode free tier MiniMax model',
|
||||
supportsVision: false,
|
||||
provider: 'opencode',
|
||||
tier: 'free',
|
||||
},
|
||||
|
||||
// Amazon Bedrock - Claude Models
|
||||
{
|
||||
id: 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
|
||||
label: 'Claude Sonnet 4.5 (Bedrock)',
|
||||
description: 'Latest Claude Sonnet via AWS Bedrock - fast and intelligent (default)',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-anthropic',
|
||||
tier: 'premium',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0',
|
||||
label: 'Claude Opus 4.5 (Bedrock)',
|
||||
description: 'Most capable Claude model via AWS Bedrock',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-anthropic',
|
||||
tier: 'premium',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0',
|
||||
label: 'Claude Haiku 4.5 (Bedrock)',
|
||||
description: 'Fastest Claude model via AWS Bedrock',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-anthropic',
|
||||
tier: 'standard',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0',
|
||||
label: 'Claude Sonnet 4 (Bedrock)',
|
||||
description: 'Claude Sonnet 4 via AWS Bedrock',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-anthropic',
|
||||
tier: 'premium',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/anthropic.claude-opus-4-20250514-v1:0',
|
||||
label: 'Claude Opus 4 (Bedrock)',
|
||||
description: 'Claude Opus 4 via AWS Bedrock',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-anthropic',
|
||||
tier: 'premium',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0',
|
||||
label: 'Claude 3.7 Sonnet (Bedrock)',
|
||||
description: 'Claude 3.7 Sonnet via AWS Bedrock',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-anthropic',
|
||||
tier: 'standard',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0',
|
||||
label: 'Claude 3.5 Sonnet (Bedrock)',
|
||||
description: 'Claude 3.5 Sonnet v2 via AWS Bedrock',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-anthropic',
|
||||
tier: 'standard',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/anthropic.claude-3-opus-20240229-v1:0',
|
||||
label: 'Claude 3 Opus (Bedrock)',
|
||||
description: 'Claude 3 Opus via AWS Bedrock',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-anthropic',
|
||||
tier: 'premium',
|
||||
},
|
||||
|
||||
// Amazon Bedrock - DeepSeek Models
|
||||
{
|
||||
id: 'amazon-bedrock/deepseek.r1-v1:0',
|
||||
label: 'DeepSeek R1 (Bedrock)',
|
||||
description: 'DeepSeek R1 reasoning model via AWS Bedrock - excellent for coding',
|
||||
supportsVision: false,
|
||||
provider: 'amazon-bedrock-deepseek',
|
||||
tier: 'premium',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/deepseek.v3-v1:0',
|
||||
label: 'DeepSeek V3 (Bedrock)',
|
||||
description: 'DeepSeek V3 via AWS Bedrock',
|
||||
supportsVision: false,
|
||||
provider: 'amazon-bedrock-deepseek',
|
||||
tier: 'standard',
|
||||
},
|
||||
|
||||
// Amazon Bedrock - Amazon Nova Models
|
||||
{
|
||||
id: 'amazon-bedrock/amazon.nova-premier-v1:0',
|
||||
label: 'Amazon Nova Premier (Bedrock)',
|
||||
description: 'Amazon Nova Premier - most capable Nova model',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-amazon',
|
||||
tier: 'premium',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/amazon.nova-pro-v1:0',
|
||||
label: 'Amazon Nova Pro (Bedrock)',
|
||||
description: 'Amazon Nova Pro - balanced performance',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-amazon',
|
||||
tier: 'standard',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/amazon.nova-lite-v1:0',
|
||||
label: 'Amazon Nova Lite (Bedrock)',
|
||||
description: 'Amazon Nova Lite - fast and efficient',
|
||||
supportsVision: true,
|
||||
provider: 'amazon-bedrock-amazon',
|
||||
tier: 'standard',
|
||||
},
|
||||
|
||||
// Amazon Bedrock - Meta Llama Models
|
||||
{
|
||||
id: 'amazon-bedrock/meta.llama4-maverick-17b-instruct-v1:0',
|
||||
label: 'Llama 4 Maverick 17B (Bedrock)',
|
||||
description: 'Meta Llama 4 Maverick via AWS Bedrock',
|
||||
supportsVision: false,
|
||||
provider: 'amazon-bedrock-meta',
|
||||
tier: 'standard',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/meta.llama3-3-70b-instruct-v1:0',
|
||||
label: 'Llama 3.3 70B (Bedrock)',
|
||||
description: 'Meta Llama 3.3 70B via AWS Bedrock',
|
||||
supportsVision: false,
|
||||
provider: 'amazon-bedrock-meta',
|
||||
tier: 'standard',
|
||||
},
|
||||
|
||||
// Amazon Bedrock - Mistral Models
|
||||
{
|
||||
id: 'amazon-bedrock/mistral.mistral-large-2402-v1:0',
|
||||
label: 'Mistral Large (Bedrock)',
|
||||
description: 'Mistral Large via AWS Bedrock',
|
||||
supportsVision: false,
|
||||
provider: 'amazon-bedrock-mistral',
|
||||
tier: 'standard',
|
||||
},
|
||||
|
||||
// Amazon Bedrock - Qwen Models
|
||||
{
|
||||
id: 'amazon-bedrock/qwen.qwen3-coder-480b-a35b-v1:0',
|
||||
label: 'Qwen3 Coder 480B (Bedrock)',
|
||||
description: 'Qwen3 Coder 480B via AWS Bedrock - excellent for coding',
|
||||
supportsVision: false,
|
||||
provider: 'amazon-bedrock-qwen',
|
||||
tier: 'premium',
|
||||
},
|
||||
{
|
||||
id: 'amazon-bedrock/qwen.qwen3-235b-a22b-2507-v1:0',
|
||||
label: 'Qwen3 235B (Bedrock)',
|
||||
description: 'Qwen3 235B via AWS Bedrock',
|
||||
supportsVision: false,
|
||||
provider: 'amazon-bedrock-qwen',
|
||||
tier: 'premium',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Complete model configuration map indexed by model ID
|
||||
*/
|
||||
export const OPENCODE_MODEL_CONFIG_MAP: Record<OpencodeModelId, OpencodeModelConfig> =
|
||||
OPENCODE_MODELS.reduce(
|
||||
(acc, config) => {
|
||||
acc[config.id] = config;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<OpencodeModelId, OpencodeModelConfig>
|
||||
);
|
||||
|
||||
/**
|
||||
* Default OpenCode model - Claude Sonnet 4.5 via Bedrock
|
||||
*/
|
||||
export const DEFAULT_OPENCODE_MODEL: OpencodeModelId =
|
||||
'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0';
|
||||
|
||||
/**
|
||||
* Helper: Get display name for model
|
||||
*/
|
||||
export function getOpencodeModelLabel(modelId: OpencodeModelId): string {
|
||||
return OPENCODE_MODEL_CONFIG_MAP[modelId]?.label ?? modelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get all OpenCode model IDs
|
||||
*/
|
||||
export function getAllOpencodeModelIds(): OpencodeModelId[] {
|
||||
return OPENCODE_MODELS.map((config) => config.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Check if OpenCode model supports vision
|
||||
*/
|
||||
export function opencodeModelSupportsVision(modelId: OpencodeModelId): boolean {
|
||||
return OPENCODE_MODEL_CONFIG_MAP[modelId]?.supportsVision ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get the provider for a model
|
||||
*/
|
||||
export function getOpencodeModelProvider(modelId: OpencodeModelId): OpencodeProvider {
|
||||
return OPENCODE_MODEL_CONFIG_MAP[modelId]?.provider ?? 'opencode';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Resolve an alias or partial model ID to a full model ID
|
||||
*/
|
||||
export function resolveOpencodeModelId(input: string): OpencodeModelId | undefined {
|
||||
// Check if it's already a valid model ID
|
||||
if (OPENCODE_MODEL_CONFIG_MAP[input as OpencodeModelId]) {
|
||||
return input as OpencodeModelId;
|
||||
}
|
||||
|
||||
// Check alias map
|
||||
const normalized = input.toLowerCase();
|
||||
return OPENCODE_MODEL_MAP[normalized];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Check if a string is a valid OpenCode model ID
|
||||
*/
|
||||
export function isOpencodeModelId(value: string): value is OpencodeModelId {
|
||||
return value in OPENCODE_MODEL_CONFIG_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get models filtered by provider
|
||||
*/
|
||||
export function getOpencodeModelsByProvider(provider: OpencodeProvider): OpencodeModelConfig[] {
|
||||
return OPENCODE_MODELS.filter((config) => config.provider === provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get models filtered by tier
|
||||
*/
|
||||
export function getOpencodeModelsByTier(
|
||||
tier: 'free' | 'standard' | 'premium'
|
||||
): OpencodeModelConfig[] {
|
||||
return OPENCODE_MODELS.filter((config) => config.tier === tier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get free tier models
|
||||
*/
|
||||
export function getOpencodeFreeModels(): OpencodeModelConfig[] {
|
||||
return getOpencodeModelsByTier('free');
|
||||
}
|
||||
@@ -9,11 +9,13 @@
|
||||
import type { ModelProvider } from './settings.js';
|
||||
import { CURSOR_MODEL_MAP, type CursorModelId } from './cursor-models.js';
|
||||
import { CLAUDE_MODEL_MAP, CODEX_MODEL_MAP, type CodexModelId } from './model.js';
|
||||
import { OPENCODE_MODEL_CONFIG_MAP } from './opencode-models.js';
|
||||
|
||||
/** Provider prefix constants */
|
||||
export const PROVIDER_PREFIXES = {
|
||||
cursor: 'cursor-',
|
||||
codex: 'codex-',
|
||||
opencode: 'opencode-',
|
||||
// Add new provider prefixes here
|
||||
} as const;
|
||||
|
||||
@@ -82,6 +84,41 @@ export function isCodexModel(model: string | undefined | null): boolean {
|
||||
return modelValues.includes(model as CodexModelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a model string represents an OpenCode model
|
||||
*
|
||||
* OpenCode models can be identified by:
|
||||
* - Explicit 'opencode-' prefix (for routing in Automaker)
|
||||
* - 'opencode/' prefix (OpenCode free tier models)
|
||||
* - 'amazon-bedrock/' prefix (AWS Bedrock models via OpenCode)
|
||||
* - Full model ID from OPENCODE_MODEL_CONFIG_MAP
|
||||
*
|
||||
* @param model - Model string to check (e.g., "opencode-sonnet", "opencode/big-pickle", "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0")
|
||||
* @returns true if the model is an OpenCode model
|
||||
*/
|
||||
export function isOpencodeModel(model: string | undefined | null): boolean {
|
||||
if (!model || typeof model !== 'string') return false;
|
||||
|
||||
// Check for explicit opencode- prefix (Automaker routing prefix)
|
||||
if (model.startsWith(PROVIDER_PREFIXES.opencode)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it's a known OpenCode model ID
|
||||
if (model in OPENCODE_MODEL_CONFIG_MAP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for OpenCode native model prefixes
|
||||
// - opencode/ = OpenCode free tier models (e.g., opencode/big-pickle)
|
||||
// - amazon-bedrock/ = AWS Bedrock models (e.g., amazon-bedrock/anthropic.claude-*)
|
||||
if (model.startsWith('opencode/') || model.startsWith('amazon-bedrock/')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the provider for a model string
|
||||
*
|
||||
@@ -89,7 +126,11 @@ export function isCodexModel(model: string | undefined | null): boolean {
|
||||
* @returns The provider type, defaults to 'claude' for unknown models
|
||||
*/
|
||||
export function getModelProvider(model: string | undefined | null): ModelProvider {
|
||||
// Check Codex first before Cursor, since Cursor also supports gpt models
|
||||
// Check OpenCode first since it uses provider-prefixed formats that could conflict
|
||||
if (isOpencodeModel(model)) {
|
||||
return 'opencode';
|
||||
}
|
||||
// Check Codex before Cursor, since Cursor also supports gpt models
|
||||
// but bare gpt-* should route to Codex
|
||||
if (isCodexModel(model)) {
|
||||
return 'codex';
|
||||
@@ -145,6 +186,10 @@ export function addProviderPrefix(model: string, provider: ModelProvider): strin
|
||||
if (!model.startsWith(PROVIDER_PREFIXES.codex)) {
|
||||
return `${PROVIDER_PREFIXES.codex}${model}`;
|
||||
}
|
||||
} else if (provider === 'opencode') {
|
||||
if (!model.startsWith(PROVIDER_PREFIXES.opencode)) {
|
||||
return `${PROVIDER_PREFIXES.opencode}${model}`;
|
||||
}
|
||||
}
|
||||
// Claude models don't use prefixes
|
||||
return model;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
import type { ModelAlias, AgentModel, CodexModelId } from './model.js';
|
||||
import type { CursorModelId } from './cursor-models.js';
|
||||
import { CURSOR_MODEL_MAP, getAllCursorModelIds } from './cursor-models.js';
|
||||
import type { OpencodeModelId } from './opencode-models.js';
|
||||
import { getAllOpencodeModelIds, DEFAULT_OPENCODE_MODEL } from './opencode-models.js';
|
||||
import type { PromptCustomization } from './prompts.js';
|
||||
import type { CodexSandboxMode, CodexApprovalPolicy } from './codex.js';
|
||||
|
||||
@@ -96,7 +98,7 @@ export function getThinkingTokenBudget(level: ThinkingLevel | undefined): number
|
||||
}
|
||||
|
||||
/** ModelProvider - AI model provider for credentials and API key management */
|
||||
export type ModelProvider = 'claude' | 'cursor' | 'codex';
|
||||
export type ModelProvider = 'claude' | 'cursor' | 'codex' | 'opencode';
|
||||
|
||||
const DEFAULT_CODEX_AUTO_LOAD_AGENTS = false;
|
||||
const DEFAULT_CODEX_SANDBOX_MODE: CodexSandboxMode = 'workspace-write';
|
||||
@@ -257,6 +259,10 @@ export interface AIProfile {
|
||||
// Codex-specific settings
|
||||
/** Which Codex/GPT model to use - only for Codex provider */
|
||||
codexModel?: CodexModelId;
|
||||
|
||||
// OpenCode-specific settings
|
||||
/** Which OpenCode model to use - only for OpenCode provider */
|
||||
opencodeModel?: OpencodeModelId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,6 +286,11 @@ export function profileHasThinking(profile: AIProfile): boolean {
|
||||
return model.startsWith('o');
|
||||
}
|
||||
|
||||
if (profile.provider === 'opencode') {
|
||||
// OpenCode models don't expose thinking configuration
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -295,6 +306,10 @@ export function getProfileModelString(profile: AIProfile): string {
|
||||
return `codex:${profile.codexModel || 'gpt-5.2'}`;
|
||||
}
|
||||
|
||||
if (profile.provider === 'opencode') {
|
||||
return `opencode:${profile.opencodeModel || DEFAULT_OPENCODE_MODEL}`;
|
||||
}
|
||||
|
||||
// Claude
|
||||
return profile.model || 'sonnet';
|
||||
}
|
||||
@@ -473,6 +488,12 @@ export interface GlobalSettings {
|
||||
/** Default Cursor model selection when switching to Cursor CLI */
|
||||
cursorDefaultModel: CursorModelId;
|
||||
|
||||
// OpenCode CLI Settings (global)
|
||||
/** Which OpenCode models are available in feature modal (empty = all) */
|
||||
enabledOpencodeModels?: OpencodeModelId[];
|
||||
/** Default OpenCode model selection when switching to OpenCode CLI */
|
||||
opencodeDefaultModel?: OpencodeModelId;
|
||||
|
||||
// Input Configuration
|
||||
/** User's keyboard shortcut bindings */
|
||||
keyboardShortcuts: KeyboardShortcuts;
|
||||
@@ -717,6 +738,8 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
|
||||
validationModel: 'opus',
|
||||
enabledCursorModels: getAllCursorModelIds(),
|
||||
cursorDefaultModel: 'auto',
|
||||
enabledOpencodeModels: getAllOpencodeModelIds(),
|
||||
opencodeDefaultModel: DEFAULT_OPENCODE_MODEL,
|
||||
keyboardShortcuts: DEFAULT_KEYBOARD_SHORTCUTS,
|
||||
aiProfiles: [],
|
||||
projects: [],
|
||||
|
||||
Reference in New Issue
Block a user