mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
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 { 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 */
|
||||
export interface ModelListItem {
|
||||
model: string
|
||||
id: string
|
||||
description: string
|
||||
}
|
||||
|
||||
/** Static model data by provider */
|
||||
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' },
|
||||
],
|
||||
}
|
||||
const VALID_PROVIDERS = ['claude', 'codex', 'opencode']
|
||||
|
||||
/** Schema for provider models output */
|
||||
export const providerModelsSchema: OutputSchema<ModelListItem> = {
|
||||
idField: 'id',
|
||||
columns: [
|
||||
{ header: 'MODEL', field: 'model', 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(
|
||||
provider: string,
|
||||
_options: ProviderModelsOptions,
|
||||
options: ProviderModelsOptions,
|
||||
_command: Command
|
||||
): Promise<ProviderModelsResult> {
|
||||
const normalizedProvider = provider.toLowerCase()
|
||||
const models = MODELS_BY_PROVIDER[normalizedProvider]
|
||||
|
||||
if (!models) {
|
||||
const validProviders = Object.keys(MODELS_BY_PROVIDER).join(', ')
|
||||
const error: CommandError = {
|
||||
if (!VALID_PROVIDERS.includes(normalizedProvider)) {
|
||||
throw {
|
||||
code: 'UNKNOWN_PROVIDER',
|
||||
message: `Unknown provider: ${provider}`,
|
||||
details: `Valid providers: ${validProviders}`,
|
||||
details: `Valid providers: ${VALID_PROVIDERS.join(', ')}`,
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'list',
|
||||
data: models,
|
||||
schema: providerModelsSchema,
|
||||
const client = await connectToDaemon({ host: options.host })
|
||||
try {
|
||||
const result = await client.listProviderModels(normalizedProvider)
|
||||
|
||||
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 = {
|
||||
defaults?: { agents?: Record<string, AgentDefinition> };
|
||||
claudePath: string | null;
|
||||
handle?: AgentPersistenceHandle;
|
||||
logger: Logger;
|
||||
};
|
||||
@@ -315,16 +316,23 @@ export class ClaudeAgentClient implements AgentClient {
|
||||
|
||||
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
|
||||
private readonly logger: Logger;
|
||||
private readonly claudePath: string | null;
|
||||
|
||||
constructor(options: ClaudeAgentClientOptions) {
|
||||
this.defaults = options.defaults;
|
||||
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> {
|
||||
const claudeConfig = this.assertConfig(config);
|
||||
return new ClaudeAgentSession(claudeConfig, {
|
||||
defaults: this.defaults,
|
||||
claudePath: this.claudePath,
|
||||
logger: this.logger,
|
||||
});
|
||||
}
|
||||
@@ -342,6 +350,7 @@ export class ClaudeAgentClient implements AgentClient {
|
||||
const claudeConfig = this.assertConfig(mergedConfig);
|
||||
return new ClaudeAgentSession(claudeConfig, {
|
||||
defaults: this.defaults,
|
||||
claudePath: this.claudePath,
|
||||
handle,
|
||||
logger: this.logger,
|
||||
});
|
||||
@@ -353,6 +362,7 @@ export class ClaudeAgentClient implements AgentClient {
|
||||
cwd: options?.cwd ?? process.cwd(),
|
||||
permissionMode: "plan",
|
||||
includePartialMessages: false,
|
||||
...(this.claudePath ? { pathToClaudeCodeExecutable: this.claudePath } : {}),
|
||||
};
|
||||
|
||||
const claudeQuery = query({ prompt, options: claudeOptions });
|
||||
@@ -407,12 +417,7 @@ export class ClaudeAgentClient implements AgentClient {
|
||||
}
|
||||
|
||||
async isAvailable(): Promise<boolean> {
|
||||
try {
|
||||
const claudePath = execSync("which claude", { encoding: "utf8" }).trim();
|
||||
return Boolean(claudePath);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return this.claudePath !== null;
|
||||
}
|
||||
|
||||
private assertConfig(config: AgentSessionConfig): ClaudeAgentConfig {
|
||||
@@ -429,6 +434,7 @@ class ClaudeAgentSession implements AgentSession {
|
||||
|
||||
private readonly config: ClaudeAgentConfig;
|
||||
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
|
||||
private readonly claudePath: string | null;
|
||||
private readonly logger: Logger;
|
||||
private query: Query | null = null;
|
||||
private input: Pushable<SDKUserMessage> | null = null;
|
||||
@@ -464,6 +470,7 @@ class ClaudeAgentSession implements AgentSession {
|
||||
) {
|
||||
this.config = config;
|
||||
this.defaults = options.defaults;
|
||||
this.claudePath = options.claudePath;
|
||||
this.logger = options.logger;
|
||||
const handle = options.handle;
|
||||
|
||||
@@ -880,6 +887,7 @@ class ClaudeAgentSession implements AgentSession {
|
||||
permissionMode: this.currentMode,
|
||||
agents: this.defaults?.agents,
|
||||
canUseTool: this.handlePermissionRequest,
|
||||
...(this.claudePath ? { pathToClaudeCodeExecutable: this.claudePath } : {}),
|
||||
// Use Claude Code preset system prompt and load CLAUDE.md files
|
||||
// Append orchestrator mode instructions for agents
|
||||
systemPrompt: {
|
||||
|
||||
Reference in New Issue
Block a user