Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487import type { AgentInfo, CommandInfo, FormCancelInput, FormInfo, FormReplyInput, IntegrationInfo, LocationRef, McpResource, McpServer, ModelInfo, OpenCodeClient, OpenCodeEvent, PermissionSavedInfo, PermissionRequest, Project, ProviderInfo, ReferenceInfo, SessionInfo, SessionMessageInfo, SessionInboxInfo, ShellInfo, SkillInfo, VcsInfo,} from "@opencode-ai/client"import type { ResolvedTheme } from "@opencode-ai/theme/tui"import type { CliRenderer, KeyEvent, MarkdownCodeBlockRenderer, Renderable } from "@opentui/core"import type { JSX } from "@opentui/solid"import type { Store } from "solid-js/store"
export interface Storage { /** * Durable JSON state: persisted to disk, survives hot reloads and TUI * restarts, and stays live-synced across running TUI instances. */ store<Value extends object>( key: string, options: { readonly initial: Value }, ): readonly [Store<Value>, (mutation: (draft: Value) => void) => Promise<void>] /** * Ephemeral in-memory state: survives plugin hot reloads (old and new * generations share the same live store) and is gone when the TUI exits. * Updates are synchronous and values need not be JSON-serializable. */ memory<Value extends object>( key: string, options: { readonly initial: Value }, ): readonly [Store<Value>, (mutation: (draft: Value) => void) => void]}
interface LocationCollection<Value> { list(location?: LocationRef): Value[] | undefined sync(location?: LocationRef): Promise<void> invalidate(location?: LocationRef): void}
export interface Data { readonly on: <Type extends OpenCodeEvent["type"]>( type: Type, handler: (event: Extract<OpenCodeEvent, { type: Type }>) => void, ) => () => void readonly listen: (handler: (event: { details: OpenCodeEvent }) => void) => () => void readonly session: { list(): SessionInfo[] get(sessionID: string): SessionInfo | undefined root(sessionID: string): string family(sessionID: string): string[] cost(sessionID: string): number status(sessionID: string): "idle" | "running" readonly pending: { list(sessionID: string): SessionInboxInfo[] sync(sessionID: string): Promise<void> invalidate(sessionID: string): void } sync(sessionID: string): Promise<void> invalidate(sessionID: string): void readonly message: { list(sessionID: string): SessionMessageInfo[] get(sessionID: string, messageID: string): SessionMessageInfo | undefined sync(sessionID: string): Promise<void> invalidate(sessionID: string): void } readonly permission: { list(sessionID: string): PermissionRequest[] | undefined sync(sessionID: string): Promise<void> invalidate(sessionID: string): void } readonly form: { list(sessionID: string, location?: LocationRef): Array<FormInfo & { readonly location?: LocationRef }> | undefined sync(sessionID: string, location?: LocationRef): Promise<void> invalidate(sessionID: string, location?: LocationRef): void reply(input: FormReplyInput, location?: LocationRef): Promise<void> cancel(input: FormCancelInput, location?: LocationRef): Promise<void> } } readonly project: { list(): Project[] get(projectID: string): Project | undefined sync(): Promise<void> invalidate(): void readonly permission: { list(projectID: string): PermissionSavedInfo[] | undefined sync(projectID: string): Promise<void> invalidate(projectID: string): void } } readonly shell: { list(location?: LocationRef): ShellInfo[] get(id: string): ShellInfo | undefined sync(location?: LocationRef): Promise<void> invalidate(location?: LocationRef): void } readonly location: { default(): LocationRef sync(location?: LocationRef): Promise<void> invalidate(location?: LocationRef): void readonly vcs: { info(location?: LocationRef): VcsInfo | undefined sync(location?: LocationRef): Promise<void> invalidate(location?: LocationRef): void } readonly agent: LocationCollection<AgentInfo> readonly command: LocationCollection<CommandInfo> readonly integration: LocationCollection<IntegrationInfo> readonly mcp: { readonly server: LocationCollection<McpServer> readonly resource: LocationCollection<McpResource> } readonly model: LocationCollection<ModelInfo> readonly provider: LocationCollection<ProviderInfo> readonly reference: LocationCollection<ReferenceInfo> readonly skill: LocationCollection<SkillInfo> }}
export type Route = | { readonly type: "home" } | { readonly type: "session"; readonly sessionID: string } | { readonly type: "plugin" readonly id: string readonly name: string readonly data?: Record<string, any> }
export type Destination = Route | Omit<Extract<Route, { readonly type: "plugin" }>, "id">
export interface Page { readonly name: string readonly render: (input: { readonly data?: Record<string, any> }) => JSX.Element}
type PromptFooterInput = { readonly sessionID?: string; readonly mode: "normal" | "shell" }
/** * The host UI's slot tree. Every path is one slot: a named boundary a plugin * may render around, inside, or take over. Paths are absolute and * dot-separated, and a path contains every path it prefixes — replacing * `prompt.footer` owns everything under `prompt.footer.*`. * * Each slot publishes an input: reactive props passed to every claim render * targeting it. Inputs carry only what the SDK cannot answer — instance * identity and client-local state. Paths and their inputs are documented * API: coarse, few, and kept stable across host refactors. */export interface SlotMap { readonly app: Readonly<Record<string, never>> readonly "home.footer": Readonly<Record<string, never>> readonly "prompt.footer": PromptFooterInput readonly "prompt.footer.status": PromptFooterInput readonly "prompt.footer.file": PromptFooterInput readonly "session.composer.top": { readonly sessionID: string } readonly "sidebar.content": { readonly sessionID: string } readonly "sidebar.footer": { readonly sessionID: string }}export type SlotPath = keyof SlotMap
/** * One contribution to the slot tree. Exactly one placement key names an * absolute target path: * - `prepend` / `append`: first/last inside the target's boundary * - `before` / `after`: siblings adjacent to the target, outside its boundary * - `replace`: take over the target. The boundary itself survives — siblings * anchored `before`/`after` it still compose — but the original content and * every claim inside the boundary are suppressed and recorded, never * silently dropped. At the same target the last-enabled claim wins; an * ancestor replacement beats a descendant one regardless of enable order. * * A claim aimed at a path the host no longer publishes degrades: additive * claims append to the nearest surviving ancestor, replacements are * suppressed. Several claims at one anchor coexist in plugin enable order. * * `render` receives the target slot's input, reactively. The `?: never` * fields make the variants mutually exclusive: a claim with two placement * keys is a type error, not a silent priority pick. */export type SlotClaim<Path extends SlotPath = SlotPath> = Path extends SlotPath ? { readonly render: (input: SlotMap[Path]) => JSX.Element } & ( | { readonly prepend: Path readonly append?: never readonly before?: never readonly after?: never readonly replace?: never } | { readonly append: Path readonly prepend?: never readonly before?: never readonly after?: never readonly replace?: never } | { readonly before: Path readonly prepend?: never readonly append?: never readonly after?: never readonly replace?: never } | { readonly after: Path readonly prepend?: never readonly append?: never readonly before?: never readonly replace?: never } | { readonly replace: Path readonly prepend?: never readonly append?: never readonly before?: never readonly after?: never } ) : never
export interface App { readonly version: string readonly channel: string}
export type ToastVariant = "info" | "success" | "warning" | "error"
export interface ToastOptions { readonly title?: string readonly message: string readonly variant?: ToastVariant readonly duration?: number}
export interface Toast { show(options: ToastOptions): void}
export type AttentionWhen = "always" | "focused" | "blurred"export type AttentionSoundName = "default" | "question" | "permission" | "error" | "done" | "subagent_done"
export type AttentionNotification = | boolean | { readonly when?: AttentionWhen }
export type AttentionSound = | boolean | { readonly name?: AttentionSoundName readonly volume?: number readonly when?: AttentionWhen }
export interface AttentionNotifyOptions { readonly title?: string readonly message: string readonly notification?: AttentionNotification readonly sound?: AttentionSound}
export type AttentionNotifySkipReason = | "attention_disabled" | "empty_message" | "blurred" | "focused" | "focus_unknown" | "renderer_destroyed"
export interface AttentionNotifyResult { readonly ok: boolean readonly notification: boolean readonly sound: boolean readonly skipped?: AttentionNotifySkipReason}
export interface Attention { notify(options: AttentionNotifyOptions): Promise<AttentionNotifyResult>}
export type DialogSize = "medium" | "large" | "xlarge"
export interface DialogOptions { readonly size?: DialogSize readonly centered?: boolean}
export interface DialogAlertOptions { readonly title: string readonly message: string}
export interface DialogConfirmOptions { readonly title: string readonly message: string readonly label?: { readonly confirm?: string readonly cancel?: string }}
export interface DialogPromptOptions { readonly title: string readonly description?: string readonly placeholder?: string readonly value?: string}
export interface DialogSelectOption<Value> { readonly title: string readonly value: Value readonly description?: string readonly category?: string readonly disabled?: boolean}
export interface DialogSelectOptions<Value> { readonly title: string readonly placeholder?: string readonly options: readonly DialogSelectOption<Value>[] readonly current?: Value}
export interface Dialog { /** Shows a dialog. */ show(render: () => JSX.Element, onClose?: () => void): void /** Sets the active dialog's presentation options. */ set(options: DialogOptions): void /** Closes the active dialog. */ clear(): void alert(options: DialogAlertOptions): Promise<void> confirm(options: DialogConfirmOptions): Promise<boolean | undefined> prompt(options: DialogPromptOptions): Promise<string | undefined> select<Value>(options: DialogSelectOptions<Value>): Promise<Value | undefined>}
export interface KeymapCommand { /** Stable command and config keybind identifier. Omit for an inline command. */ readonly id?: string /** Optional label used by command discovery and keyboard-help UI. */ readonly title?: string /** Optional longer description. */ readonly description?: string /** Groups the command in discovery and keyboard-help UI. */ readonly group?: string /** Enables or disables the command. */ readonly enabled?: boolean | (() => boolean) /** Configures automatic binding, or disables it for a named command. */ readonly bind?: false | string /** Adds a named command to the command palette. */ readonly palette?: true /** Adds a named command to prompt slash completion. */ readonly slash?: { readonly name: string readonly aliases?: string[] /** Keeps the slash command in the prompt and passes its raw input to run. */ readonly arguments?: true } /** Promotes the command in discovery UI. */ readonly suggested?: boolean | (() => boolean) /** Executes the command. Keyboard dispatch includes its event; programmatic dispatch does not. Return false to continue. */ readonly run: (input?: string, event?: KeyEvent) => void | false | Promise<void>}
export interface KeymapLayer { /** Limits the layer to one OpenCode input mode. Use global to opt out; defaults to base. */ readonly mode?: string /** Enables or disables the complete layer. */ readonly enabled?: boolean | (() => boolean) /** Limits the layer to a focused renderable. */ readonly target?: () => Renderable | null | undefined /** Resolves conflicts with other active layers. */ readonly priority?: number /** Commands owned by this layer. */ readonly commands?: readonly KeymapCommand[] /** IDs of commands whose configured bindings should be active in this layer. */ readonly bindings?: readonly string[]}
export interface KeymapPending { readonly key: string readonly token?: string}
export interface KeymapActive { readonly key: string readonly title?: string readonly description?: string readonly group?: string readonly continues: boolean}
export interface Keymap { /** Creates a reactive keymap layer owned by the calling component. */ layer(input: () => KeymapLayer): void /** Dispatches a reachable command by ID. */ dispatch(id: string, input?: string): void /** Returns every formatted shortcut for a registered command. */ shortcuts(id: string): readonly string[] /** Returns the currently reachable commands. Reactive when read in a Solid computation. */ commands(): readonly KeymapCommand[] /** Returns the pending key sequence. Reactive when read in a Solid computation. */ pending(): readonly KeymapPending[] /** Returns bindings reachable from the pending key sequence. Reactive when read in a Solid computation. */ active(): readonly KeymapActive[] /** Controls mutually exclusive OpenCode input modes. */ readonly mode: { /** Returns the active mode. */ current(): string /** Pushes a mode until the returned cleanup is called. */ push(mode: string): () => void }}
export interface UI { readonly dialog: Dialog readonly toast: Toast readonly format: { path(value: string): string } readonly router: { register(page: Page): () => void navigate(destination: Destination): void current(): Route } readonly tabs: { /** Returns whether session tabs are enabled for this TUI. */ enabled(): boolean /** Returns the currently open root-session tabs. Reactive when read in a Solid computation. */ list(): readonly { readonly sessionID: string readonly title?: string readonly active: boolean readonly busy: boolean readonly attention: boolean readonly unread?: "activity" | "error" }[] /** Opens (or focuses) a tab for a session, adding it when not already open. Returns false when tabs are disabled. */ open(sessionID: string): boolean /** Focuses an already-open tab and returns false when it is not open. */ focus(sessionID: string): boolean /** Closes an open tab, or the active tab when omitted, and returns false when no tab matched. */ close(sessionID?: string): boolean } /** Claims a place in the slot tree; see SlotClaim. */ readonly slot: (claim: SlotClaim) => () => void}
export interface Context { readonly options: Readonly<Record<string, any>> readonly location: LocationRef | undefined readonly app: App readonly renderer: CliRenderer readonly client: OpenCodeClient readonly data: Data readonly attention: Attention readonly theme: ResolvedTheme readonly themeMode: "dark" | "light" readonly markdown: { registerCodeBlockRenderer(language: string, render: MarkdownCodeBlockRenderer): () => void } readonly keymap: Keymap readonly storage: Storage readonly ui: UI}