/
Alex_Ural
/
opencode
Обзор
Документация
Войти
/
Alex_Ural
/
opencode
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
packages/tui/src/util/error.ts
182 строки
6 KB
opencode-agent[bot]
chore: generate
07 июн 2026, 06:28
07 июн 2026, 06:28
155e1f2
Код
Авторство
О чём код?
import { isRecord } from "./record" type ConfigIssue = { message: string; path: string[] } export function cliErrorMessage(input: unknown): string | undefined { if (input instanceof Error && isRecord(input.cause) && "body" in input.cause) { const formatted = cliErrorMessage(input.cause.body) if (formatted) return formatted } if (tagged(input, "CliError")) { if (typeof input.exitCode === "number") process.exitCode = input.exitCode return field(input, "message") ?? "" } if (tagged(input, "AccountServiceError") || tagged(input, "AccountTransportError")) { return field(input, "message") ?? "" } const model = configData(input, "ProviderModelNotFoundError") if (model) { const suggestions = Array.isArray(model.suggestions) ? model.suggestions.filter((item): item is string => typeof item === "string") : [] return [ `Model not found: ${field(model, "providerID")}/${field(model, "modelID")}`, ...(suggestions.length ? ["Did you mean: " + suggestions.join(", ")] : []), "Try: `opencode models` to list available models", "Or check your config (opencode.json) provider/model names", ].join("\n") } const provider = configData(input, "ProviderInitError") if (provider) return `Failed to initialize provider "${field(provider, "providerID")}". Check credentials and configuration.` const json = configData(input, "ConfigJsonError") if (json) { const message = field(json, "message") return `Config file at ${field(json, "path")} is not valid JSON(C)` + (message ? `: ${message}` : "") } const directory = configData(input, "ConfigDirectoryTypoError") if (directory) { return `Directory "${field(directory, "dir")}" in ${field(directory, "path")} is not valid. Rename the directory to "${field(directory, "suggestion")}" or remove it. This is a common typo.` } const frontmatter = configData(input, "ConfigFrontmatterError") if (frontmatter) return field(frontmatter, "message") ?? "" const invalid = configData(input, "ConfigInvalidError") if (invalid) { const path = field(invalid, "path") const message = field(invalid, "message") const issues = Array.isArray(invalid.issues) ? invalid.issues.filter((issue): issue is ConfigIssue => { return ( isRecord(issue) && typeof issue.message === "string" && Array.isArray(issue.path) && issue.path.every((item) => typeof item === "string") ) }) : [] return [ `Configuration is invalid${path && path !== "config" ? ` at ${path}` : ""}` + (message ? `: ${message}` : ""), ...issues.map((issue) => "↳ " + issue.message + " " + issue.path.join(".")), ].join("\n") } if (tagged(input, "UICancelledError") || named(input, "UICancelledError")) return "" if (isRecord(input) && named(input, "MCPFailed")) { const name = isRecord(input.data) ? field(input.data, "name") : undefined return `MCP server "${name}" failed. Note, opencode does not support MCP authentication yet.` } return undefined } function tagged(input: unknown, tag: string): input is Record<string, unknown> { return isRecord(input) && input._tag === tag } function named(input: unknown, name: string) { return isRecord(input) && (input.name === name || input._tag === name) } function configData(input: unknown, tag: string) { if (!isRecord(input)) return undefined if (input.name === tag && isRecord(input.data)) return input.data if (input._tag === tag) return input return undefined } function field(input: Record<string, unknown>, key: string) { return typeof input[key] === "string" ? input[key] : undefined } export function errorFormat(error: unknown): string { if (error instanceof Error) { return error.stack ?? `${error.name}: ${error.message}` } if (typeof error === "object" && error !== null) { try { const json = JSON.stringify(error, null, 2) // Plain objects whose own properties are all non-enumerable (or empty) // serialize to "{}", which prints as a useless bare `{}` on stderr. // Fall back to a custom toString first, then to ctor name + own prop names. if (json === "{}") { const str = String(error) if (str && str !== "[object Object]") return str const ctor = error.constructor?.name const prefix = ctor && ctor !== "Object" ? ctor : "Error" const names = Object.getOwnPropertyNames(error) return names.length === 0 ? `${prefix} (no message)` : `${prefix} { ${names.join(", ")} }` } return json } catch { return "Unexpected error (unserializable)" } } return String(error) } export function errorMessage(error: unknown): string { if (error instanceof Error) { if (error.message) return error.message if (error.name) return error.name } if (isRecord(error) && typeof error.message === "string" && error.message) { return error.message } if (isRecord(error) && isRecord(error.data) && typeof error.data.message === "string" && error.data.message) { return error.data.message } const text = String(error) if (text && text !== "[object Object]") return text const formatted = errorFormat(error) if (formatted) return formatted return "unknown error" } export function errorData(error: unknown) { if (error instanceof Error) { return { type: error.name, message: errorMessage(error), stack: error.stack, cause: error.cause === undefined ? undefined : errorFormat(error.cause), formatted: errorFormat(error), } } if (!isRecord(error)) { return { type: typeof error, message: errorMessage(error), formatted: errorFormat(error), } } const data = Object.getOwnPropertyNames(error).reduce<Record<string, unknown>>((acc, key) => { const value = error[key] if (value === undefined) return acc if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { acc[key] = value return acc } // oxlint-disable-next-line no-base-to-string -- intentional coercion of arbitrary error properties acc[key] = value instanceof Error ? value.message : String(value) return acc }, {}) if (typeof data.message !== "string") data.message = errorMessage(error) if (typeof data.type !== "string") data.type = error.constructor?.name data.formatted = errorFormat(error) return data }