Use system Claude binary for model listing
The bundled SDK binary had stale model definitions. Resolve the system `claude` path once and pass it via pathToClaudeCodeExecutable so both agent sessions and supportedModels() use the up-to-date binary. Replace the CLI's hardcoded static model list with a live call to the daemon's listProviderModels endpoint.
This commit is contained in:
@@ -1,39 +1,23 @@
|
|||||||
import type { Command } from 'commander'
|
import type { Command } from 'commander'
|
||||||
import type { CommandOptions, ListResult, OutputSchema, CommandError } from '../../output/index.js'
|
import { connectToDaemon } from '../../utils/client.js'
|
||||||
|
import type { CommandOptions, ListResult, OutputSchema } from '../../output/index.js'
|
||||||
|
|
||||||
/** Model list item for display */
|
/** Model list item for display */
|
||||||
export interface ModelListItem {
|
export interface ModelListItem {
|
||||||
model: string
|
model: string
|
||||||
id: string
|
id: string
|
||||||
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Static model data by provider */
|
const VALID_PROVIDERS = ['claude', 'codex', 'opencode']
|
||||||
const MODELS_BY_PROVIDER: Record<string, ModelListItem[]> = {
|
|
||||||
claude: [
|
|
||||||
{ model: 'Claude Sonnet 4', id: 'claude-sonnet-4-20250514' },
|
|
||||||
{ model: 'Claude Opus 4', id: 'claude-opus-4-20250514' },
|
|
||||||
{ model: 'Claude Haiku 3.5', id: 'claude-3-5-haiku-20241022' },
|
|
||||||
],
|
|
||||||
codex: [
|
|
||||||
{ model: 'o3-mini', id: 'o3-mini' },
|
|
||||||
{ model: 'o4-mini', id: 'o4-mini' },
|
|
||||||
],
|
|
||||||
opencode: [
|
|
||||||
// opencode uses claude or codex under the hood
|
|
||||||
{ model: 'Claude Sonnet 4', id: 'claude-sonnet-4-20250514' },
|
|
||||||
{ model: 'Claude Opus 4', id: 'claude-opus-4-20250514' },
|
|
||||||
{ model: 'Claude Haiku 3.5', id: 'claude-3-5-haiku-20241022' },
|
|
||||||
{ model: 'o3-mini', id: 'o3-mini' },
|
|
||||||
{ model: 'o4-mini', id: 'o4-mini' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Schema for provider models output */
|
/** Schema for provider models output */
|
||||||
export const providerModelsSchema: OutputSchema<ModelListItem> = {
|
export const providerModelsSchema: OutputSchema<ModelListItem> = {
|
||||||
idField: 'id',
|
idField: 'id',
|
||||||
columns: [
|
columns: [
|
||||||
{ header: 'MODEL', field: 'model', width: 30 },
|
|
||||||
{ header: 'ID', field: 'id', width: 30 },
|
{ header: 'ID', field: 'id', width: 30 },
|
||||||
|
{ header: 'MODEL', field: 'model', width: 30 },
|
||||||
|
{ header: 'DESCRIPTION', field: 'description', width: 40 },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,25 +29,42 @@ export interface ProviderModelsOptions extends CommandOptions {
|
|||||||
|
|
||||||
export async function runModelsCommand(
|
export async function runModelsCommand(
|
||||||
provider: string,
|
provider: string,
|
||||||
_options: ProviderModelsOptions,
|
options: ProviderModelsOptions,
|
||||||
_command: Command
|
_command: Command
|
||||||
): Promise<ProviderModelsResult> {
|
): Promise<ProviderModelsResult> {
|
||||||
const normalizedProvider = provider.toLowerCase()
|
const normalizedProvider = provider.toLowerCase()
|
||||||
const models = MODELS_BY_PROVIDER[normalizedProvider]
|
|
||||||
|
|
||||||
if (!models) {
|
if (!VALID_PROVIDERS.includes(normalizedProvider)) {
|
||||||
const validProviders = Object.keys(MODELS_BY_PROVIDER).join(', ')
|
throw {
|
||||||
const error: CommandError = {
|
|
||||||
code: 'UNKNOWN_PROVIDER',
|
code: 'UNKNOWN_PROVIDER',
|
||||||
message: `Unknown provider: ${provider}`,
|
message: `Unknown provider: ${provider}`,
|
||||||
details: `Valid providers: ${validProviders}`,
|
details: `Valid providers: ${VALID_PROVIDERS.join(', ')}`,
|
||||||
}
|
}
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const client = await connectToDaemon({ host: options.host })
|
||||||
type: 'list',
|
try {
|
||||||
data: models,
|
const result = await client.listProviderModels(normalizedProvider)
|
||||||
schema: providerModelsSchema,
|
|
||||||
|
if (result.error) {
|
||||||
|
throw {
|
||||||
|
code: 'PROVIDER_ERROR',
|
||||||
|
message: `Failed to fetch models for ${provider}: ${result.error}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const models: ModelListItem[] = (result.models ?? []).map((m) => ({
|
||||||
|
model: m.label,
|
||||||
|
id: m.id,
|
||||||
|
description: m.description ?? '',
|
||||||
|
}))
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'list',
|
||||||
|
data: models,
|
||||||
|
schema: providerModelsSchema,
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await client.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ type ClaudeAgentClientOptions = {
|
|||||||
|
|
||||||
type ClaudeAgentSessionOptions = {
|
type ClaudeAgentSessionOptions = {
|
||||||
defaults?: { agents?: Record<string, AgentDefinition> };
|
defaults?: { agents?: Record<string, AgentDefinition> };
|
||||||
|
claudePath: string | null;
|
||||||
handle?: AgentPersistenceHandle;
|
handle?: AgentPersistenceHandle;
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
};
|
};
|
||||||
@@ -315,16 +316,23 @@ export class ClaudeAgentClient implements AgentClient {
|
|||||||
|
|
||||||
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
|
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
|
||||||
private readonly logger: Logger;
|
private readonly logger: Logger;
|
||||||
|
private readonly claudePath: string | null;
|
||||||
|
|
||||||
constructor(options: ClaudeAgentClientOptions) {
|
constructor(options: ClaudeAgentClientOptions) {
|
||||||
this.defaults = options.defaults;
|
this.defaults = options.defaults;
|
||||||
this.logger = options.logger.child({ module: "agent", provider: "claude" });
|
this.logger = options.logger.child({ module: "agent", provider: "claude" });
|
||||||
|
try {
|
||||||
|
this.claudePath = execSync("which claude", { encoding: "utf8" }).trim() || null;
|
||||||
|
} catch {
|
||||||
|
this.claudePath = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSession(config: AgentSessionConfig): Promise<AgentSession> {
|
async createSession(config: AgentSessionConfig): Promise<AgentSession> {
|
||||||
const claudeConfig = this.assertConfig(config);
|
const claudeConfig = this.assertConfig(config);
|
||||||
return new ClaudeAgentSession(claudeConfig, {
|
return new ClaudeAgentSession(claudeConfig, {
|
||||||
defaults: this.defaults,
|
defaults: this.defaults,
|
||||||
|
claudePath: this.claudePath,
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -342,6 +350,7 @@ export class ClaudeAgentClient implements AgentClient {
|
|||||||
const claudeConfig = this.assertConfig(mergedConfig);
|
const claudeConfig = this.assertConfig(mergedConfig);
|
||||||
return new ClaudeAgentSession(claudeConfig, {
|
return new ClaudeAgentSession(claudeConfig, {
|
||||||
defaults: this.defaults,
|
defaults: this.defaults,
|
||||||
|
claudePath: this.claudePath,
|
||||||
handle,
|
handle,
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
});
|
});
|
||||||
@@ -353,6 +362,7 @@ export class ClaudeAgentClient implements AgentClient {
|
|||||||
cwd: options?.cwd ?? process.cwd(),
|
cwd: options?.cwd ?? process.cwd(),
|
||||||
permissionMode: "plan",
|
permissionMode: "plan",
|
||||||
includePartialMessages: false,
|
includePartialMessages: false,
|
||||||
|
...(this.claudePath ? { pathToClaudeCodeExecutable: this.claudePath } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const claudeQuery = query({ prompt, options: claudeOptions });
|
const claudeQuery = query({ prompt, options: claudeOptions });
|
||||||
@@ -407,12 +417,7 @@ export class ClaudeAgentClient implements AgentClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async isAvailable(): Promise<boolean> {
|
async isAvailable(): Promise<boolean> {
|
||||||
try {
|
return this.claudePath !== null;
|
||||||
const claudePath = execSync("which claude", { encoding: "utf8" }).trim();
|
|
||||||
return Boolean(claudePath);
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertConfig(config: AgentSessionConfig): ClaudeAgentConfig {
|
private assertConfig(config: AgentSessionConfig): ClaudeAgentConfig {
|
||||||
@@ -429,6 +434,7 @@ class ClaudeAgentSession implements AgentSession {
|
|||||||
|
|
||||||
private readonly config: ClaudeAgentConfig;
|
private readonly config: ClaudeAgentConfig;
|
||||||
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
|
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
|
||||||
|
private readonly claudePath: string | null;
|
||||||
private readonly logger: Logger;
|
private readonly logger: Logger;
|
||||||
private query: Query | null = null;
|
private query: Query | null = null;
|
||||||
private input: Pushable<SDKUserMessage> | null = null;
|
private input: Pushable<SDKUserMessage> | null = null;
|
||||||
@@ -464,6 +470,7 @@ class ClaudeAgentSession implements AgentSession {
|
|||||||
) {
|
) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.defaults = options.defaults;
|
this.defaults = options.defaults;
|
||||||
|
this.claudePath = options.claudePath;
|
||||||
this.logger = options.logger;
|
this.logger = options.logger;
|
||||||
const handle = options.handle;
|
const handle = options.handle;
|
||||||
|
|
||||||
@@ -880,6 +887,7 @@ class ClaudeAgentSession implements AgentSession {
|
|||||||
permissionMode: this.currentMode,
|
permissionMode: this.currentMode,
|
||||||
agents: this.defaults?.agents,
|
agents: this.defaults?.agents,
|
||||||
canUseTool: this.handlePermissionRequest,
|
canUseTool: this.handlePermissionRequest,
|
||||||
|
...(this.claudePath ? { pathToClaudeCodeExecutable: this.claudePath } : {}),
|
||||||
// Use Claude Code preset system prompt and load CLAUDE.md files
|
// Use Claude Code preset system prompt and load CLAUDE.md files
|
||||||
// Append orchestrator mode instructions for agents
|
// Append orchestrator mode instructions for agents
|
||||||
systemPrompt: {
|
systemPrompt: {
|
||||||
|
|||||||
Reference in New Issue
Block a user