diff --git a/packages/adapters/opencode-local/src/server/execute.ts b/packages/adapters/opencode-local/src/server/execute.ts index 2b50c23f..c3d5d591 100644 --- a/packages/adapters/opencode-local/src/server/execute.ts +++ b/packages/adapters/opencode-local/src/server/execute.ts @@ -141,38 +141,41 @@ export async function execute(ctx: AdapterExecutionContext): Promise { - if (!instructionsFilePath) return [] as string[]; + if (!resolvedInstructionsFilePath) return [] as string[]; if (instructionsPrefix.length > 0) { return [ - `Loaded agent instructions from ${instructionsFilePath}`, + `Loaded agent instructions from ${resolvedInstructionsFilePath}`, `Prepended instructions + path directive to stdin prompt (relative references from ${instructionsDir}).`, ]; } return [ - `Configured instructionsFilePath ${instructionsFilePath}, but file could not be read; continuing without injected instructions.`, + `Configured instructionsFilePath ${resolvedInstructionsFilePath}, but file could not be read; continuing without injected instructions.`, ]; })(); diff --git a/packages/adapters/opencode-local/src/server/test.ts b/packages/adapters/opencode-local/src/server/test.ts index 6e1ac0a3..569f0d75 100644 --- a/packages/adapters/opencode-local/src/server/test.ts +++ b/packages/adapters/opencode-local/src/server/test.ts @@ -81,20 +81,30 @@ export async function testEnvironment( } const runtimeEnv = normalizeEnv(ensurePathInEnv({ ...process.env, ...env })); - try { - await ensureCommandResolvable(command, cwd, runtimeEnv); + const cwdInvalid = checks.some((check) => check.code === "opencode_cwd_invalid"); + if (cwdInvalid) { checks.push({ - code: "opencode_command_resolvable", - level: "info", - message: `Command is executable: ${command}`, - }); - } catch (err) { - checks.push({ - code: "opencode_command_unresolvable", - level: "error", - message: err instanceof Error ? err.message : "Command is not executable", + code: "opencode_command_skipped", + level: "warn", + message: "Skipped command check because working directory validation failed.", detail: command, }); + } else { + try { + await ensureCommandResolvable(command, cwd, runtimeEnv); + checks.push({ + code: "opencode_command_resolvable", + level: "info", + message: `Command is executable: ${command}`, + }); + } catch (err) { + checks.push({ + code: "opencode_command_unresolvable", + level: "error", + message: err instanceof Error ? err.message : "Command is not executable", + detail: command, + }); + } } const canRunProbe = @@ -174,61 +184,71 @@ export async function testEnvironment( if (variant) args.push("--variant", variant); if (extraArgs.length > 0) args.push(...extraArgs); - const probe = await runChildProcess( - `opencode-envtest-${Date.now()}-${Math.random().toString(16).slice(2)}`, - command, - args, - { - cwd, - env: runtimeEnv, - timeoutSec: 60, - graceSec: 5, - stdin: "Respond with hello.", - onLog: async () => {}, - }, - ); + try { + const probe = await runChildProcess( + `opencode-envtest-${Date.now()}-${Math.random().toString(16).slice(2)}`, + command, + args, + { + cwd, + env: runtimeEnv, + timeoutSec: 60, + graceSec: 5, + stdin: "Respond with hello.", + onLog: async () => {}, + }, + ); - const parsed = parseOpenCodeJsonl(probe.stdout); - const detail = summarizeProbeDetail(probe.stdout, probe.stderr, parsed.errorMessage); - const authEvidence = `${parsed.errorMessage ?? ""}\n${probe.stdout}\n${probe.stderr}`.trim(); + const parsed = parseOpenCodeJsonl(probe.stdout); + const detail = summarizeProbeDetail(probe.stdout, probe.stderr, parsed.errorMessage); + const authEvidence = `${parsed.errorMessage ?? ""}\n${probe.stdout}\n${probe.stderr}`.trim(); - if (probe.timedOut) { - checks.push({ - code: "opencode_hello_probe_timed_out", - level: "warn", - message: "OpenCode hello probe timed out.", - hint: "Retry the probe. If this persists, run OpenCode manually in this working directory.", - }); - } else if ((probe.exitCode ?? 1) === 0 && !parsed.errorMessage) { - const summary = parsed.summary.trim(); - const hasHello = /\bhello\b/i.test(summary); - checks.push({ - code: hasHello ? "opencode_hello_probe_passed" : "opencode_hello_probe_unexpected_output", - level: hasHello ? "info" : "warn", - message: hasHello - ? "OpenCode hello probe succeeded." - : "OpenCode probe ran but did not return `hello` as expected.", - ...(summary ? { detail: summary.replace(/\s+/g, " ").trim().slice(0, 240) } : {}), - ...(hasHello - ? {} - : { - hint: "Run `opencode run --format json` manually and prompt `Respond with hello` to inspect output.", - }), - }); - } else if (OPENCODE_AUTH_REQUIRED_RE.test(authEvidence)) { - checks.push({ - code: "opencode_hello_probe_auth_required", - level: "warn", - message: "OpenCode is installed, but provider authentication is not ready.", - ...(detail ? { detail } : {}), - hint: "Run `opencode auth login` or set provider credentials, then retry the probe.", - }); - } else { + if (probe.timedOut) { + checks.push({ + code: "opencode_hello_probe_timed_out", + level: "warn", + message: "OpenCode hello probe timed out.", + hint: "Retry the probe. If this persists, run OpenCode manually in this working directory.", + }); + } else if ((probe.exitCode ?? 1) === 0 && !parsed.errorMessage) { + const summary = parsed.summary.trim(); + const hasHello = /\bhello\b/i.test(summary); + checks.push({ + code: hasHello ? "opencode_hello_probe_passed" : "opencode_hello_probe_unexpected_output", + level: hasHello ? "info" : "warn", + message: hasHello + ? "OpenCode hello probe succeeded." + : "OpenCode probe ran but did not return `hello` as expected.", + ...(summary ? { detail: summary.replace(/\s+/g, " ").trim().slice(0, 240) } : {}), + ...(hasHello + ? {} + : { + hint: "Run `opencode run --format json` manually and prompt `Respond with hello` to inspect output.", + }), + }); + } else if (OPENCODE_AUTH_REQUIRED_RE.test(authEvidence)) { + checks.push({ + code: "opencode_hello_probe_auth_required", + level: "warn", + message: "OpenCode is installed, but provider authentication is not ready.", + ...(detail ? { detail } : {}), + hint: "Run `opencode auth login` or set provider credentials, then retry the probe.", + }); + } else { + checks.push({ + code: "opencode_hello_probe_failed", + level: "error", + message: "OpenCode hello probe failed.", + ...(detail ? { detail } : {}), + hint: "Run `opencode run --format json` manually in this working directory to debug.", + }); + } + } catch (err) { checks.push({ code: "opencode_hello_probe_failed", level: "error", message: "OpenCode hello probe failed.", - ...(detail ? { detail } : {}), + detail: err instanceof Error ? err.message : String(err), hint: "Run `opencode run --format json` manually in this working directory to debug.", }); } diff --git a/ui/src/components/OnboardingWizard.tsx b/ui/src/components/OnboardingWizard.tsx index 50b7a6a3..dd426e53 100644 --- a/ui/src/components/OnboardingWizard.tsx +++ b/ui/src/components/OnboardingWizard.tsx @@ -149,6 +149,8 @@ export function OnboardingWizard() { const { data: adapterModels, error: adapterModelsError, + isLoading: adapterModelsLoading, + isFetching: adapterModelsFetching, } = useQuery({ queryKey: createdCompanyId @@ -332,6 +334,10 @@ export function OnboardingWizard() { ); return; } + if (adapterModelsLoading || adapterModelsFetching) { + setError("OpenCode models are still loading. Please wait and try again."); + return; + } const discoveredModels = adapterModels ?? []; if (!discoveredModels.some((entry) => entry.id === selectedModelId)) { setError( diff --git a/ui/src/lib/model-utils.ts b/ui/src/lib/model-utils.ts index b3753e61..baa721f5 100644 --- a/ui/src/lib/model-utils.ts +++ b/ui/src/lib/model-utils.ts @@ -12,5 +12,5 @@ export function extractProviderIdWithFallback(modelId: string, fallback = "other export function extractModelName(modelId: string): string { const trimmed = modelId.trim(); if (!trimmed.includes("/")) return trimmed; - return trimmed.slice(trimmed.indexOf("/") + 1); + return trimmed.slice(trimmed.indexOf("/") + 1).trim(); }