fix(run/cli): wait for selected model catalog (#35130)
This commit is contained in:
@@ -26,7 +26,7 @@ import { createOpencodeClient, type OpencodeClient, type ToolPart } from "@openc
|
|||||||
import { FormatError, FormatUnknownError } from "../error"
|
import { FormatError, FormatUnknownError } from "../error"
|
||||||
import { INTERACTIVE_INPUT_ERROR, resolveInteractiveStdin } from "./run/runtime.stdin"
|
import { INTERACTIVE_INPUT_ERROR, resolveInteractiveStdin } from "./run/runtime.stdin"
|
||||||
import { isImageAttachment, isPdfAttachment } from "@/util/media"
|
import { isImageAttachment, isPdfAttachment } from "@/util/media"
|
||||||
import { loadRunAgents } from "./run/catalog.shared"
|
import { loadRunAgents, waitForCatalogReady } from "./run/catalog.shared"
|
||||||
|
|
||||||
type ModelInput = Parameters<OpencodeClient["session"]["prompt"]>[0]["model"]
|
type ModelInput = Parameters<OpencodeClient["session"]["prompt"]>[0]["model"]
|
||||||
|
|
||||||
@@ -931,12 +931,20 @@ export const RunCommand = effectCmd({
|
|||||||
const cwd = args.attach ? (directory ?? sess.directory ?? (await current(sdk))) : (directory ?? root)
|
const cwd = args.attach ? (directory ?? sess.directory ?? (await current(sdk))) : (directory ?? root)
|
||||||
const client = args.attach ? attachSDK(cwd) : sdk
|
const client = args.attach ? attachSDK(cwd) : sdk
|
||||||
|
|
||||||
|
// Current-session flows resolve explicit --model refs from a location
|
||||||
|
// catalog that populates asynchronously after boot. Wait only when the
|
||||||
|
// user supplied a model; default-model races.
|
||||||
|
const selectedModel = pick(args.model)
|
||||||
|
if (selectedModel && (interactive || (currentPrompt && sess.current !== false))) {
|
||||||
|
await waitForCatalogReady({ sdk: client, directory: cwd, model: selectedModel })
|
||||||
|
}
|
||||||
|
|
||||||
// Validate agent if specified
|
// Validate agent if specified
|
||||||
const agent = await pickAgent(client)
|
const agent = await pickAgent(client)
|
||||||
|
|
||||||
if (!interactive) {
|
if (!interactive) {
|
||||||
if (currentPrompt && sess.current !== false) {
|
if (currentPrompt && sess.current !== false) {
|
||||||
const model = pick(args.model)
|
const model = selectedModel
|
||||||
const { runNonInteractivePrompt } = await import("./run/noninteractive")
|
const { runNonInteractivePrompt } = await import("./run/noninteractive")
|
||||||
try {
|
try {
|
||||||
await runNonInteractivePrompt({
|
await runNonInteractivePrompt({
|
||||||
|
|||||||
@@ -84,6 +84,27 @@ export function runProviders(providers: CurrentProvider[], models: CurrentModel[
|
|||||||
return [...grouped.values()]
|
return [...grouped.values()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A location boots its plugins in a deferred background batch after the layer
|
||||||
|
// is built, so first-turn model resolution can observe empty catalog state.
|
||||||
|
// For explicit --model flows, wait for that exact ref to appear before prompt
|
||||||
|
// admission. On timeout, return and let the real execution error surface.
|
||||||
|
export async function waitForCatalogReady(input: {
|
||||||
|
sdk: OpencodeClient
|
||||||
|
directory: string
|
||||||
|
model: { providerID: string; modelID: string }
|
||||||
|
timeoutMs?: number
|
||||||
|
}) {
|
||||||
|
const deadline = Date.now() + (input.timeoutMs ?? 5_000)
|
||||||
|
while (Date.now() < deadline) {
|
||||||
|
const models = await input.sdk.v2.model
|
||||||
|
.list(location(input.directory), { throwOnError: true })
|
||||||
|
.then((result) => result.data?.data ?? [])
|
||||||
|
.catch(() => undefined)
|
||||||
|
if (models?.some((model) => model.providerID === input.model.providerID && model.id === input.model.modelID)) return
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 25))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function loadRunAgents(sdk: OpencodeClient, directory: string): Promise<RunAgent[]> {
|
export async function loadRunAgents(sdk: OpencodeClient, directory: string): Promise<RunAgent[]> {
|
||||||
const result = await sdk.v2.agent.list(location(directory), { throwOnError: true })
|
const result = await sdk.v2.agent.list(location(directory), { throwOnError: true })
|
||||||
return (result.data?.data ?? []).map(runAgent)
|
return (result.data?.data ?? []).map(runAgent)
|
||||||
|
|||||||
Reference in New Issue
Block a user