chore: initialize qiming workspace repository
This commit is contained in:
34
qimingcode/packages/plugin/src/example-workspace.ts
Normal file
34
qimingcode/packages/plugin/src/example-workspace.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { Plugin } from "@opencode-ai/plugin"
|
||||
import { mkdir, rm } from "node:fs/promises"
|
||||
|
||||
export const FolderWorkspacePlugin: Plugin = async ({ experimental_workspace }) => {
|
||||
experimental_workspace.register("folder", {
|
||||
name: "Folder",
|
||||
description: "Create a blank folder",
|
||||
configure(config) {
|
||||
const rand = "" + Math.random()
|
||||
|
||||
return {
|
||||
...config,
|
||||
directory: `/tmp/folder/folder-${rand}`,
|
||||
}
|
||||
},
|
||||
async create(config) {
|
||||
if (!config.directory) return
|
||||
await mkdir(config.directory, { recursive: true })
|
||||
},
|
||||
async remove(config) {
|
||||
await rm(config.directory!, { recursive: true, force: true })
|
||||
},
|
||||
target(config) {
|
||||
return {
|
||||
type: "local",
|
||||
directory: config.directory!,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
export default FolderWorkspacePlugin
|
||||
18
qimingcode/packages/plugin/src/example.ts
Normal file
18
qimingcode/packages/plugin/src/example.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Plugin } from "./index.js"
|
||||
import { tool } from "./tool.js"
|
||||
|
||||
export const ExamplePlugin: Plugin = async (_ctx) => {
|
||||
return {
|
||||
tool: {
|
||||
mytool: tool({
|
||||
description: "This is a custom tool",
|
||||
args: {
|
||||
foo: tool.schema.string().describe("foo"),
|
||||
},
|
||||
async execute(args) {
|
||||
return `Hello ${args.foo}!`
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
333
qimingcode/packages/plugin/src/index.ts
Normal file
333
qimingcode/packages/plugin/src/index.ts
Normal file
@@ -0,0 +1,333 @@
|
||||
import type {
|
||||
Event,
|
||||
createOpencodeClient,
|
||||
Project,
|
||||
Model,
|
||||
Provider,
|
||||
Permission,
|
||||
UserMessage,
|
||||
Message,
|
||||
Part,
|
||||
Auth,
|
||||
Config as SDKConfig,
|
||||
} from "@opencode-ai/sdk"
|
||||
import type { Provider as ProviderV2, Model as ModelV2 } from "@opencode-ai/sdk/v2"
|
||||
|
||||
import type { BunShell } from "./shell.js"
|
||||
import { type ToolDefinition } from "./tool.js"
|
||||
|
||||
export * from "./tool.js"
|
||||
|
||||
export type ProviderContext = {
|
||||
source: "env" | "config" | "custom" | "api"
|
||||
info: Provider
|
||||
options: Record<string, any>
|
||||
}
|
||||
|
||||
export type WorkspaceInfo = {
|
||||
id: string
|
||||
type: string
|
||||
name: string
|
||||
branch: string | null
|
||||
directory: string | null
|
||||
extra: unknown | null
|
||||
projectID: string
|
||||
}
|
||||
|
||||
export type WorkspaceTarget =
|
||||
| {
|
||||
type: "local"
|
||||
directory: string
|
||||
}
|
||||
| {
|
||||
type: "remote"
|
||||
url: string | URL
|
||||
headers?: HeadersInit
|
||||
}
|
||||
|
||||
export type WorkspaceAdaptor = {
|
||||
name: string
|
||||
description: string
|
||||
configure(config: WorkspaceInfo): WorkspaceInfo | Promise<WorkspaceInfo>
|
||||
create(config: WorkspaceInfo, env: Record<string, string | undefined>, from?: WorkspaceInfo): Promise<void>
|
||||
remove(config: WorkspaceInfo): Promise<void>
|
||||
target(config: WorkspaceInfo): WorkspaceTarget | Promise<WorkspaceTarget>
|
||||
}
|
||||
|
||||
export type PluginInput = {
|
||||
client: ReturnType<typeof createOpencodeClient>
|
||||
project: Project
|
||||
directory: string
|
||||
worktree: string
|
||||
experimental_workspace: {
|
||||
register(type: string, adaptor: WorkspaceAdaptor): void
|
||||
}
|
||||
serverUrl: URL
|
||||
$: BunShell
|
||||
}
|
||||
|
||||
export type PluginOptions = Record<string, unknown>
|
||||
|
||||
export type Config = Omit<SDKConfig, "plugin"> & {
|
||||
plugin?: Array<string | [string, PluginOptions]>
|
||||
}
|
||||
|
||||
export type Plugin = (input: PluginInput, options?: PluginOptions) => Promise<Hooks>
|
||||
|
||||
export type PluginModule = {
|
||||
id?: string
|
||||
server: Plugin
|
||||
tui?: never
|
||||
}
|
||||
|
||||
type Rule = {
|
||||
key: string
|
||||
op: "eq" | "neq"
|
||||
value: string
|
||||
}
|
||||
|
||||
export type AuthHook = {
|
||||
provider: string
|
||||
loader?: (auth: () => Promise<Auth>, provider: Provider) => Promise<Record<string, any>>
|
||||
methods: (
|
||||
| {
|
||||
type: "oauth"
|
||||
label: string
|
||||
prompts?: Array<
|
||||
| {
|
||||
type: "text"
|
||||
key: string
|
||||
message: string
|
||||
placeholder?: string
|
||||
validate?: (value: string) => string | undefined
|
||||
/** @deprecated Use `when` instead */
|
||||
condition?: (inputs: Record<string, string>) => boolean
|
||||
when?: Rule
|
||||
}
|
||||
| {
|
||||
type: "select"
|
||||
key: string
|
||||
message: string
|
||||
options: Array<{
|
||||
label: string
|
||||
value: string
|
||||
hint?: string
|
||||
}>
|
||||
/** @deprecated Use `when` instead */
|
||||
condition?: (inputs: Record<string, string>) => boolean
|
||||
when?: Rule
|
||||
}
|
||||
>
|
||||
authorize(inputs?: Record<string, string>): Promise<AuthOAuthResult>
|
||||
}
|
||||
| {
|
||||
type: "api"
|
||||
label: string
|
||||
prompts?: Array<
|
||||
| {
|
||||
type: "text"
|
||||
key: string
|
||||
message: string
|
||||
placeholder?: string
|
||||
validate?: (value: string) => string | undefined
|
||||
/** @deprecated Use `when` instead */
|
||||
condition?: (inputs: Record<string, string>) => boolean
|
||||
when?: Rule
|
||||
}
|
||||
| {
|
||||
type: "select"
|
||||
key: string
|
||||
message: string
|
||||
options: Array<{
|
||||
label: string
|
||||
value: string
|
||||
hint?: string
|
||||
}>
|
||||
/** @deprecated Use `when` instead */
|
||||
condition?: (inputs: Record<string, string>) => boolean
|
||||
when?: Rule
|
||||
}
|
||||
>
|
||||
authorize?(inputs?: Record<string, string>): Promise<
|
||||
| {
|
||||
type: "success"
|
||||
key: string
|
||||
provider?: string
|
||||
}
|
||||
| {
|
||||
type: "failed"
|
||||
}
|
||||
>
|
||||
}
|
||||
)[]
|
||||
}
|
||||
|
||||
export type AuthOAuthResult = { url: string; instructions: string } & (
|
||||
| {
|
||||
method: "auto"
|
||||
callback(): Promise<
|
||||
| ({
|
||||
type: "success"
|
||||
provider?: string
|
||||
} & (
|
||||
| {
|
||||
refresh: string
|
||||
access: string
|
||||
expires: number
|
||||
accountId?: string
|
||||
enterpriseUrl?: string
|
||||
}
|
||||
| { key: string }
|
||||
))
|
||||
| {
|
||||
type: "failed"
|
||||
}
|
||||
>
|
||||
}
|
||||
| {
|
||||
method: "code"
|
||||
callback(code: string): Promise<
|
||||
| ({
|
||||
type: "success"
|
||||
provider?: string
|
||||
} & (
|
||||
| {
|
||||
refresh: string
|
||||
access: string
|
||||
expires: number
|
||||
accountId?: string
|
||||
enterpriseUrl?: string
|
||||
}
|
||||
| { key: string }
|
||||
))
|
||||
| {
|
||||
type: "failed"
|
||||
}
|
||||
>
|
||||
}
|
||||
)
|
||||
|
||||
export type ProviderHookContext = {
|
||||
auth?: Auth
|
||||
}
|
||||
|
||||
export type ProviderHook = {
|
||||
id: string
|
||||
models?: (provider: ProviderV2, ctx: ProviderHookContext) => Promise<Record<string, ModelV2>>
|
||||
}
|
||||
|
||||
/** @deprecated Use AuthOAuthResult instead. */
|
||||
export type AuthOuathResult = AuthOAuthResult
|
||||
|
||||
export interface Hooks {
|
||||
event?: (input: { event: Event }) => Promise<void>
|
||||
config?: (input: Config) => Promise<void>
|
||||
tool?: {
|
||||
[key: string]: ToolDefinition
|
||||
}
|
||||
auth?: AuthHook
|
||||
provider?: ProviderHook
|
||||
/**
|
||||
* Called when a new message is received
|
||||
*/
|
||||
"chat.message"?: (
|
||||
input: {
|
||||
sessionID: string
|
||||
agent?: string
|
||||
model?: { providerID: string; modelID: string }
|
||||
messageID?: string
|
||||
variant?: string
|
||||
},
|
||||
output: { message: UserMessage; parts: Part[] },
|
||||
) => Promise<void>
|
||||
/**
|
||||
* Modify parameters sent to LLM
|
||||
*/
|
||||
"chat.params"?: (
|
||||
input: { sessionID: string; agent: string; model: Model; provider: ProviderContext; message: UserMessage },
|
||||
output: {
|
||||
temperature: number
|
||||
topP: number
|
||||
topK: number
|
||||
maxOutputTokens: number | undefined
|
||||
options: Record<string, any>
|
||||
},
|
||||
) => Promise<void>
|
||||
"chat.headers"?: (
|
||||
input: { sessionID: string; agent: string; model: Model; provider: ProviderContext; message: UserMessage },
|
||||
output: { headers: Record<string, string> },
|
||||
) => Promise<void>
|
||||
"permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise<void>
|
||||
"command.execute.before"?: (
|
||||
input: { command: string; sessionID: string; arguments: string },
|
||||
output: { parts: Part[] },
|
||||
) => Promise<void>
|
||||
"tool.execute.before"?: (
|
||||
input: { tool: string; sessionID: string; callID: string },
|
||||
output: { args: any },
|
||||
) => Promise<void>
|
||||
"shell.env"?: (
|
||||
input: { cwd: string; sessionID?: string; callID?: string },
|
||||
output: { env: Record<string, string> },
|
||||
) => Promise<void>
|
||||
"tool.execute.after"?: (
|
||||
input: { tool: string; sessionID: string; callID: string; args: any },
|
||||
output: {
|
||||
title: string
|
||||
output: string
|
||||
metadata: any
|
||||
},
|
||||
) => Promise<void>
|
||||
"experimental.chat.messages.transform"?: (
|
||||
input: {},
|
||||
output: {
|
||||
messages: {
|
||||
info: Message
|
||||
parts: Part[]
|
||||
}[]
|
||||
},
|
||||
) => Promise<void>
|
||||
"experimental.chat.system.transform"?: (
|
||||
input: { sessionID?: string; model: Model },
|
||||
output: {
|
||||
system: string[]
|
||||
},
|
||||
) => Promise<void>
|
||||
/**
|
||||
* Called before session compaction starts. Allows plugins to customize
|
||||
* the compaction prompt.
|
||||
*
|
||||
* - `context`: Additional context strings appended to the default prompt
|
||||
* - `prompt`: If set, replaces the default compaction prompt entirely
|
||||
*/
|
||||
"experimental.session.compacting"?: (
|
||||
input: { sessionID: string },
|
||||
output: { context: string[]; prompt?: string },
|
||||
) => Promise<void>
|
||||
/**
|
||||
* Called after compaction succeeds and before a synthetic user
|
||||
* auto-continue message is added.
|
||||
*
|
||||
* - `enabled`: Defaults to `true`. Set to `false` to skip the synthetic
|
||||
* user "continue" turn.
|
||||
*/
|
||||
"experimental.compaction.autocontinue"?: (
|
||||
input: {
|
||||
sessionID: string
|
||||
agent: string
|
||||
model: Model
|
||||
provider: ProviderContext
|
||||
message: UserMessage
|
||||
overflow: boolean
|
||||
},
|
||||
output: { enabled: boolean },
|
||||
) => Promise<void>
|
||||
"experimental.text.complete"?: (
|
||||
input: { sessionID: string; messageID: string; partID: string },
|
||||
output: { text: string },
|
||||
) => Promise<void>
|
||||
/**
|
||||
* Modify tool definitions (description and parameters) sent to LLM
|
||||
*/
|
||||
"tool.definition"?: (input: { toolID: string }, output: { description: string; parameters: any }) => Promise<void>
|
||||
}
|
||||
136
qimingcode/packages/plugin/src/shell.ts
Normal file
136
qimingcode/packages/plugin/src/shell.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
export type ShellFunction = (input: Uint8Array) => Uint8Array
|
||||
|
||||
export type ShellExpression =
|
||||
| { toString(): string }
|
||||
| Array<ShellExpression>
|
||||
| string
|
||||
| { raw: string }
|
||||
| ReadableStream
|
||||
|
||||
export interface BunShell {
|
||||
(strings: TemplateStringsArray, ...expressions: ShellExpression[]): BunShellPromise
|
||||
|
||||
/**
|
||||
* Perform bash-like brace expansion on the given pattern.
|
||||
* @param pattern - Brace pattern to expand
|
||||
*/
|
||||
braces(pattern: string): string[]
|
||||
|
||||
/**
|
||||
* Escape strings for input into shell commands.
|
||||
*/
|
||||
escape(input: string): string
|
||||
|
||||
/**
|
||||
* Change the default environment variables for shells created by this instance.
|
||||
*/
|
||||
env(newEnv?: Record<string, string | undefined>): BunShell
|
||||
|
||||
/**
|
||||
* Default working directory to use for shells created by this instance.
|
||||
*/
|
||||
cwd(newCwd?: string): BunShell
|
||||
|
||||
/**
|
||||
* Configure the shell to not throw an exception on non-zero exit codes.
|
||||
*/
|
||||
nothrow(): BunShell
|
||||
|
||||
/**
|
||||
* Configure whether or not the shell should throw an exception on non-zero exit codes.
|
||||
*/
|
||||
throws(shouldThrow: boolean): BunShell
|
||||
}
|
||||
|
||||
export interface BunShellPromise extends Promise<BunShellOutput> {
|
||||
readonly stdin: WritableStream
|
||||
|
||||
/**
|
||||
* Change the current working directory of the shell.
|
||||
*/
|
||||
cwd(newCwd: string): this
|
||||
|
||||
/**
|
||||
* Set environment variables for the shell.
|
||||
*/
|
||||
env(newEnv: Record<string, string> | undefined): this
|
||||
|
||||
/**
|
||||
* By default, the shell will write to the current process's stdout and stderr, as well as buffering that output.
|
||||
* This configures the shell to only buffer the output.
|
||||
*/
|
||||
quiet(): this
|
||||
|
||||
/**
|
||||
* Read from stdout as a string, line by line
|
||||
* Automatically calls quiet() to disable echoing to stdout.
|
||||
*/
|
||||
lines(): AsyncIterable<string>
|
||||
|
||||
/**
|
||||
* Read from stdout as a string.
|
||||
* Automatically calls quiet() to disable echoing to stdout.
|
||||
*/
|
||||
text(encoding?: BufferEncoding): Promise<string>
|
||||
|
||||
/**
|
||||
* Read from stdout as a JSON object
|
||||
* Automatically calls quiet()
|
||||
*/
|
||||
json(): Promise<any>
|
||||
|
||||
/**
|
||||
* Read from stdout as an ArrayBuffer
|
||||
* Automatically calls quiet()
|
||||
*/
|
||||
arrayBuffer(): Promise<ArrayBuffer>
|
||||
|
||||
/**
|
||||
* Read from stdout as a Blob
|
||||
* Automatically calls quiet()
|
||||
*/
|
||||
blob(): Promise<Blob>
|
||||
|
||||
/**
|
||||
* Configure the shell to not throw an exception on non-zero exit codes.
|
||||
*/
|
||||
nothrow(): this
|
||||
|
||||
/**
|
||||
* Configure whether or not the shell should throw an exception on non-zero exit codes.
|
||||
*/
|
||||
throws(shouldThrow: boolean): this
|
||||
}
|
||||
|
||||
export interface BunShellOutput {
|
||||
readonly stdout: Buffer
|
||||
readonly stderr: Buffer
|
||||
readonly exitCode: number
|
||||
|
||||
/**
|
||||
* Read from stdout as a string
|
||||
*/
|
||||
text(encoding?: BufferEncoding): string
|
||||
|
||||
/**
|
||||
* Read from stdout as a JSON object
|
||||
*/
|
||||
json(): any
|
||||
|
||||
/**
|
||||
* Read from stdout as an ArrayBuffer
|
||||
*/
|
||||
arrayBuffer(): ArrayBuffer
|
||||
|
||||
/**
|
||||
* Read from stdout as an Uint8Array
|
||||
*/
|
||||
bytes(): Uint8Array
|
||||
|
||||
/**
|
||||
* Read from stdout as a Blob
|
||||
*/
|
||||
blob(): Blob
|
||||
}
|
||||
|
||||
export type BunShellError = Error & BunShellOutput
|
||||
41
qimingcode/packages/plugin/src/tool.ts
Normal file
41
qimingcode/packages/plugin/src/tool.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { z } from "zod"
|
||||
import { Effect } from "effect"
|
||||
|
||||
export type ToolContext = {
|
||||
sessionID: string
|
||||
messageID: string
|
||||
agent: string
|
||||
/**
|
||||
* Current project directory for this session.
|
||||
* Prefer this over process.cwd() when resolving relative paths.
|
||||
*/
|
||||
directory: string
|
||||
/**
|
||||
* Project worktree root for this session.
|
||||
* Useful for generating stable relative paths (e.g. path.relative(worktree, absPath)).
|
||||
*/
|
||||
worktree: string
|
||||
abort: AbortSignal
|
||||
metadata(input: { title?: string; metadata?: { [key: string]: any } }): void
|
||||
ask(input: AskInput): Effect.Effect<void>
|
||||
}
|
||||
|
||||
type AskInput = {
|
||||
permission: string
|
||||
patterns: string[]
|
||||
always: string[]
|
||||
metadata: { [key: string]: any }
|
||||
}
|
||||
|
||||
export type ToolResult = string | { output: string; metadata?: { [key: string]: any } }
|
||||
|
||||
export function tool<Args extends z.ZodRawShape>(input: {
|
||||
description: string
|
||||
args: Args
|
||||
execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<ToolResult>
|
||||
}) {
|
||||
return input
|
||||
}
|
||||
tool.schema = z
|
||||
|
||||
export type ToolDefinition = ReturnType<typeof tool>
|
||||
501
qimingcode/packages/plugin/src/tui.ts
Normal file
501
qimingcode/packages/plugin/src/tui.ts
Normal file
@@ -0,0 +1,501 @@
|
||||
import type {
|
||||
AgentPart,
|
||||
OpencodeClient,
|
||||
Event,
|
||||
FilePart,
|
||||
LspStatus,
|
||||
McpStatus,
|
||||
Todo,
|
||||
Message,
|
||||
Part,
|
||||
Provider,
|
||||
PermissionRequest,
|
||||
QuestionRequest,
|
||||
SessionStatus,
|
||||
TextPart,
|
||||
Config as SdkConfig,
|
||||
} from "@opencode-ai/sdk/v2"
|
||||
import type { CliRenderer, ParsedKey, RGBA, SlotMode } from "@opentui/core"
|
||||
import type { JSX, SolidPlugin } from "@opentui/solid"
|
||||
import type { Config as PluginConfig, PluginOptions } from "./index.js"
|
||||
|
||||
export type { CliRenderer, SlotMode } from "@opentui/core"
|
||||
|
||||
export type TuiRouteCurrent =
|
||||
| {
|
||||
name: "home"
|
||||
}
|
||||
| {
|
||||
name: "session"
|
||||
params: {
|
||||
sessionID: string
|
||||
prompt?: unknown
|
||||
}
|
||||
}
|
||||
| {
|
||||
name: string
|
||||
params?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type TuiRouteDefinition = {
|
||||
name: string
|
||||
render: (input: { params?: Record<string, unknown> }) => JSX.Element
|
||||
}
|
||||
|
||||
export type TuiCommand = {
|
||||
title: string
|
||||
value: string
|
||||
description?: string
|
||||
category?: string
|
||||
keybind?: string
|
||||
suggested?: boolean
|
||||
hidden?: boolean
|
||||
enabled?: boolean
|
||||
slash?: {
|
||||
name: string
|
||||
aliases?: string[]
|
||||
}
|
||||
onSelect?: () => void
|
||||
}
|
||||
|
||||
export type TuiKeybind = {
|
||||
name: string
|
||||
ctrl: boolean
|
||||
meta: boolean
|
||||
shift: boolean
|
||||
super?: boolean
|
||||
leader: boolean
|
||||
}
|
||||
|
||||
export type TuiKeybindMap = Record<string, string>
|
||||
|
||||
export type TuiKeybindSet = {
|
||||
readonly all: TuiKeybindMap
|
||||
get: (name: string) => string
|
||||
match: (name: string, evt: ParsedKey) => boolean
|
||||
print: (name: string) => string
|
||||
}
|
||||
|
||||
export type TuiDialogProps = {
|
||||
size?: "medium" | "large" | "xlarge"
|
||||
onClose: () => void
|
||||
children?: JSX.Element
|
||||
}
|
||||
|
||||
export type TuiDialogStack = {
|
||||
replace: (render: () => JSX.Element, onClose?: () => void) => void
|
||||
clear: () => void
|
||||
setSize: (size: "medium" | "large" | "xlarge") => void
|
||||
readonly size: "medium" | "large" | "xlarge"
|
||||
readonly depth: number
|
||||
readonly open: boolean
|
||||
}
|
||||
|
||||
export type TuiDialogAlertProps = {
|
||||
title: string
|
||||
message: string
|
||||
onConfirm?: () => void
|
||||
}
|
||||
|
||||
export type TuiDialogConfirmProps = {
|
||||
title: string
|
||||
message: string
|
||||
onConfirm?: () => void
|
||||
onCancel?: () => void
|
||||
}
|
||||
|
||||
export type TuiDialogPromptProps = {
|
||||
title: string
|
||||
description?: () => JSX.Element
|
||||
placeholder?: string
|
||||
value?: string
|
||||
busy?: boolean
|
||||
busyText?: string
|
||||
onConfirm?: (value: string) => void
|
||||
onCancel?: () => void
|
||||
}
|
||||
|
||||
export type TuiDialogSelectOption<Value = unknown> = {
|
||||
title: string
|
||||
value: Value
|
||||
description?: string
|
||||
footer?: JSX.Element | string
|
||||
category?: string
|
||||
disabled?: boolean
|
||||
onSelect?: () => void
|
||||
}
|
||||
|
||||
export type TuiDialogSelectProps<Value = unknown> = {
|
||||
title: string
|
||||
placeholder?: string
|
||||
options: TuiDialogSelectOption<Value>[]
|
||||
flat?: boolean
|
||||
onMove?: (option: TuiDialogSelectOption<Value>) => void
|
||||
onFilter?: (query: string) => void
|
||||
onSelect?: (option: TuiDialogSelectOption<Value>) => void
|
||||
skipFilter?: boolean
|
||||
current?: Value
|
||||
}
|
||||
|
||||
export type TuiPromptInfo = {
|
||||
input: string
|
||||
mode?: "normal" | "shell"
|
||||
parts: (
|
||||
| Omit<FilePart, "id" | "messageID" | "sessionID">
|
||||
| Omit<AgentPart, "id" | "messageID" | "sessionID">
|
||||
| (Omit<TextPart, "id" | "messageID" | "sessionID"> & {
|
||||
source?: {
|
||||
text: {
|
||||
start: number
|
||||
end: number
|
||||
value: string
|
||||
}
|
||||
}
|
||||
})
|
||||
)[]
|
||||
}
|
||||
|
||||
export type TuiPromptRef = {
|
||||
focused: boolean
|
||||
current: TuiPromptInfo
|
||||
set(prompt: TuiPromptInfo): void
|
||||
reset(): void
|
||||
blur(): void
|
||||
focus(): void
|
||||
submit(): void
|
||||
}
|
||||
|
||||
export type TuiPromptProps = {
|
||||
sessionID?: string
|
||||
workspaceID?: string
|
||||
visible?: boolean
|
||||
disabled?: boolean
|
||||
onSubmit?: () => void
|
||||
ref?: (ref: TuiPromptRef | undefined) => void
|
||||
hint?: JSX.Element
|
||||
right?: JSX.Element
|
||||
showPlaceholder?: boolean
|
||||
placeholders?: {
|
||||
normal?: string[]
|
||||
shell?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export type TuiToast = {
|
||||
variant?: "info" | "success" | "warning" | "error"
|
||||
title?: string
|
||||
message: string
|
||||
duration?: number
|
||||
}
|
||||
|
||||
export type TuiThemeCurrent = {
|
||||
readonly primary: RGBA
|
||||
readonly secondary: RGBA
|
||||
readonly accent: RGBA
|
||||
readonly error: RGBA
|
||||
readonly warning: RGBA
|
||||
readonly success: RGBA
|
||||
readonly info: RGBA
|
||||
readonly text: RGBA
|
||||
readonly textMuted: RGBA
|
||||
readonly selectedListItemText: RGBA
|
||||
readonly background: RGBA
|
||||
readonly backgroundPanel: RGBA
|
||||
readonly backgroundElement: RGBA
|
||||
readonly backgroundMenu: RGBA
|
||||
readonly border: RGBA
|
||||
readonly borderActive: RGBA
|
||||
readonly borderSubtle: RGBA
|
||||
readonly diffAdded: RGBA
|
||||
readonly diffRemoved: RGBA
|
||||
readonly diffContext: RGBA
|
||||
readonly diffHunkHeader: RGBA
|
||||
readonly diffHighlightAdded: RGBA
|
||||
readonly diffHighlightRemoved: RGBA
|
||||
readonly diffAddedBg: RGBA
|
||||
readonly diffRemovedBg: RGBA
|
||||
readonly diffContextBg: RGBA
|
||||
readonly diffLineNumber: RGBA
|
||||
readonly diffAddedLineNumberBg: RGBA
|
||||
readonly diffRemovedLineNumberBg: RGBA
|
||||
readonly markdownText: RGBA
|
||||
readonly markdownHeading: RGBA
|
||||
readonly markdownLink: RGBA
|
||||
readonly markdownLinkText: RGBA
|
||||
readonly markdownCode: RGBA
|
||||
readonly markdownBlockQuote: RGBA
|
||||
readonly markdownEmph: RGBA
|
||||
readonly markdownStrong: RGBA
|
||||
readonly markdownHorizontalRule: RGBA
|
||||
readonly markdownListItem: RGBA
|
||||
readonly markdownListEnumeration: RGBA
|
||||
readonly markdownImage: RGBA
|
||||
readonly markdownImageText: RGBA
|
||||
readonly markdownCodeBlock: RGBA
|
||||
readonly syntaxComment: RGBA
|
||||
readonly syntaxKeyword: RGBA
|
||||
readonly syntaxFunction: RGBA
|
||||
readonly syntaxVariable: RGBA
|
||||
readonly syntaxString: RGBA
|
||||
readonly syntaxNumber: RGBA
|
||||
readonly syntaxType: RGBA
|
||||
readonly syntaxOperator: RGBA
|
||||
readonly syntaxPunctuation: RGBA
|
||||
readonly thinkingOpacity: number
|
||||
}
|
||||
|
||||
export type TuiTheme = {
|
||||
readonly current: TuiThemeCurrent
|
||||
readonly selected: string
|
||||
has: (name: string) => boolean
|
||||
set: (name: string) => boolean
|
||||
install: (jsonPath: string) => Promise<void>
|
||||
mode: () => "dark" | "light"
|
||||
readonly ready: boolean
|
||||
}
|
||||
|
||||
export type TuiKV = {
|
||||
get: <Value = unknown>(key: string, fallback?: Value) => Value
|
||||
set: (key: string, value: unknown) => void
|
||||
readonly ready: boolean
|
||||
}
|
||||
|
||||
export type TuiState = {
|
||||
readonly ready: boolean
|
||||
readonly config: SdkConfig
|
||||
readonly provider: ReadonlyArray<Provider>
|
||||
readonly path: {
|
||||
state: string
|
||||
config: string
|
||||
worktree: string
|
||||
directory: string
|
||||
}
|
||||
readonly vcs: { branch?: string } | undefined
|
||||
session: {
|
||||
count: () => number
|
||||
diff: (sessionID: string) => ReadonlyArray<TuiSidebarFileItem>
|
||||
todo: (sessionID: string) => ReadonlyArray<TuiSidebarTodoItem>
|
||||
messages: (sessionID: string) => ReadonlyArray<Message>
|
||||
status: (sessionID: string) => SessionStatus | undefined
|
||||
permission: (sessionID: string) => ReadonlyArray<PermissionRequest>
|
||||
question: (sessionID: string) => ReadonlyArray<QuestionRequest>
|
||||
}
|
||||
part: (messageID: string) => ReadonlyArray<Part>
|
||||
lsp: () => ReadonlyArray<TuiSidebarLspItem>
|
||||
mcp: () => ReadonlyArray<TuiSidebarMcpItem>
|
||||
}
|
||||
|
||||
type TuiConfigView = Pick<PluginConfig, "$schema" | "theme" | "keybinds" | "plugin"> &
|
||||
NonNullable<PluginConfig["tui"]> & {
|
||||
plugin_enabled?: Record<string, boolean>
|
||||
}
|
||||
|
||||
export type TuiApp = {
|
||||
readonly version: string
|
||||
}
|
||||
|
||||
type Frozen<Value> = Value extends (...args: never[]) => unknown
|
||||
? Value
|
||||
: Value extends ReadonlyArray<infer Item>
|
||||
? ReadonlyArray<Frozen<Item>>
|
||||
: Value extends object
|
||||
? { readonly [Key in keyof Value]: Frozen<Value[Key]> }
|
||||
: Value
|
||||
|
||||
export type TuiSidebarMcpItem = {
|
||||
name: string
|
||||
status: McpStatus["status"]
|
||||
error?: string
|
||||
}
|
||||
|
||||
export type TuiSidebarLspItem = Pick<LspStatus, "id" | "root" | "status">
|
||||
|
||||
export type TuiSidebarTodoItem = Pick<Todo, "content" | "status">
|
||||
|
||||
export type TuiSidebarFileItem = {
|
||||
file: string
|
||||
additions: number
|
||||
deletions: number
|
||||
}
|
||||
|
||||
export type TuiHostSlotMap = {
|
||||
app: {}
|
||||
home_logo: {}
|
||||
home_prompt: {
|
||||
workspace_id?: string
|
||||
ref?: (ref: TuiPromptRef | undefined) => void
|
||||
}
|
||||
home_prompt_right: {
|
||||
workspace_id?: string
|
||||
}
|
||||
session_prompt: {
|
||||
session_id: string
|
||||
visible?: boolean
|
||||
disabled?: boolean
|
||||
on_submit?: () => void
|
||||
ref?: (ref: TuiPromptRef | undefined) => void
|
||||
}
|
||||
session_prompt_right: {
|
||||
session_id: string
|
||||
}
|
||||
home_bottom: {}
|
||||
home_footer: {}
|
||||
sidebar_title: {
|
||||
session_id: string
|
||||
title: string
|
||||
share_url?: string
|
||||
}
|
||||
sidebar_content: {
|
||||
session_id: string
|
||||
}
|
||||
sidebar_footer: {
|
||||
session_id: string
|
||||
}
|
||||
}
|
||||
|
||||
export type TuiSlotMap<Slots extends Record<string, object> = {}> = TuiHostSlotMap & Slots
|
||||
|
||||
type TuiSlotShape<Name extends string, Slots extends Record<string, object>> = Name extends keyof TuiHostSlotMap
|
||||
? TuiHostSlotMap[Name]
|
||||
: Name extends keyof Slots
|
||||
? Slots[Name]
|
||||
: Record<string, unknown>
|
||||
|
||||
export type TuiSlotProps<Name extends string = string, Slots extends Record<string, object> = {}> = {
|
||||
name: Name
|
||||
mode?: SlotMode
|
||||
children?: JSX.Element
|
||||
} & TuiSlotShape<Name, Slots>
|
||||
|
||||
export type TuiSlotContext = {
|
||||
theme: TuiTheme
|
||||
}
|
||||
|
||||
type SlotCore<Slots extends Record<string, object> = {}> = SolidPlugin<TuiSlotMap<Slots>, TuiSlotContext>
|
||||
|
||||
export type TuiSlotPlugin<Slots extends Record<string, object> = {}> = Omit<SlotCore<Slots>, "id"> & {
|
||||
id?: never
|
||||
}
|
||||
|
||||
export type TuiSlots = {
|
||||
register: {
|
||||
(plugin: TuiSlotPlugin): string
|
||||
<Slots extends Record<string, object>>(plugin: TuiSlotPlugin<Slots>): string
|
||||
}
|
||||
}
|
||||
|
||||
export type TuiEventBus = {
|
||||
on: <Type extends Event["type"]>(type: Type, handler: (event: Extract<Event, { type: Type }>) => void) => () => void
|
||||
}
|
||||
|
||||
export type TuiDispose = () => void | Promise<void>
|
||||
|
||||
export type TuiLifecycle = {
|
||||
readonly signal: AbortSignal
|
||||
onDispose: (fn: TuiDispose) => () => void
|
||||
}
|
||||
|
||||
export type TuiPluginState = "first" | "updated" | "same"
|
||||
|
||||
export type TuiPluginEntry = {
|
||||
id: string
|
||||
source: "file" | "npm" | "internal"
|
||||
spec: string
|
||||
target: string
|
||||
requested?: string
|
||||
version?: string
|
||||
modified?: number
|
||||
first_time: number
|
||||
last_time: number
|
||||
time_changed: number
|
||||
load_count: number
|
||||
fingerprint: string
|
||||
}
|
||||
|
||||
export type TuiPluginMeta = TuiPluginEntry & {
|
||||
state: TuiPluginState
|
||||
}
|
||||
|
||||
export type TuiPluginStatus = {
|
||||
id: string
|
||||
source: TuiPluginEntry["source"]
|
||||
spec: string
|
||||
target: string
|
||||
enabled: boolean
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export type TuiPluginInstallOptions = {
|
||||
global?: boolean
|
||||
}
|
||||
|
||||
export type TuiPluginInstallResult =
|
||||
| {
|
||||
ok: true
|
||||
dir: string
|
||||
tui: boolean
|
||||
}
|
||||
| {
|
||||
ok: false
|
||||
message: string
|
||||
missing?: boolean
|
||||
}
|
||||
|
||||
export type TuiWorkspace = {
|
||||
current: () => string | undefined
|
||||
set: (workspaceID?: string) => void
|
||||
}
|
||||
|
||||
export type TuiPluginApi = {
|
||||
app: TuiApp
|
||||
command: {
|
||||
register: (cb: () => TuiCommand[]) => () => void
|
||||
trigger: (value: string) => void
|
||||
show: () => void
|
||||
}
|
||||
route: {
|
||||
register: (routes: TuiRouteDefinition[]) => () => void
|
||||
navigate: (name: string, params?: Record<string, unknown>) => void
|
||||
readonly current: TuiRouteCurrent
|
||||
}
|
||||
ui: {
|
||||
Dialog: (props: TuiDialogProps) => JSX.Element
|
||||
DialogAlert: (props: TuiDialogAlertProps) => JSX.Element
|
||||
DialogConfirm: (props: TuiDialogConfirmProps) => JSX.Element
|
||||
DialogPrompt: (props: TuiDialogPromptProps) => JSX.Element
|
||||
DialogSelect: <Value = unknown>(props: TuiDialogSelectProps<Value>) => JSX.Element
|
||||
Slot: <Name extends string>(props: TuiSlotProps<Name>) => JSX.Element | null
|
||||
Prompt: (props: TuiPromptProps) => JSX.Element
|
||||
toast: (input: TuiToast) => void
|
||||
dialog: TuiDialogStack
|
||||
}
|
||||
keybind: {
|
||||
match: (key: string, evt: ParsedKey) => boolean
|
||||
print: (key: string) => string
|
||||
create: (defaults: TuiKeybindMap, overrides?: Record<string, unknown>) => TuiKeybindSet
|
||||
}
|
||||
readonly tuiConfig: Frozen<TuiConfigView>
|
||||
kv: TuiKV
|
||||
state: TuiState
|
||||
theme: TuiTheme
|
||||
client: OpencodeClient
|
||||
event: TuiEventBus
|
||||
renderer: CliRenderer
|
||||
slots: TuiSlots
|
||||
plugins: {
|
||||
list: () => ReadonlyArray<TuiPluginStatus>
|
||||
activate: (id: string) => Promise<boolean>
|
||||
deactivate: (id: string) => Promise<boolean>
|
||||
add: (spec: string) => Promise<boolean>
|
||||
install: (spec: string, options?: TuiPluginInstallOptions) => Promise<TuiPluginInstallResult>
|
||||
}
|
||||
lifecycle: TuiLifecycle
|
||||
}
|
||||
|
||||
export type TuiPlugin = (api: TuiPluginApi, options: PluginOptions | undefined, meta: TuiPluginMeta) => Promise<void>
|
||||
|
||||
export type TuiPluginModule = {
|
||||
id?: string
|
||||
tui: TuiPlugin
|
||||
server?: never
|
||||
}
|
||||
Reference in New Issue
Block a user