chore: generate

This commit is contained in:
opencode-agent[bot] 2026-06-07 03:28:52 +00:00
parent fe0c4f8c74
commit 155e1f20d6
25 changed files with 571 additions and 408 deletions

View file

@ -115,7 +115,8 @@ export const layer = Layer.effect(
if (found) yield* stopProcess(found).pipe(Effect.ignore)
const entrypoint = compiled ? undefined : process.argv[1]
if (!compiled && entrypoint === undefined) return yield* Effect.fail(new Error("Failed to resolve CLI entrypoint"))
if (!compiled && entrypoint === undefined)
return yield* Effect.fail(new Error("Failed to resolve CLI entrypoint"))
yield* Effect.try({
try: () => {
spawn(process.execPath, [...(entrypoint ? [entrypoint] : []), "serve", "--register"], {

View file

@ -106,10 +106,13 @@ const legacyDefaults: Record<string, unknown> = {
"/config": {},
}
const gracefulFetch = Object.assign(async (input: RequestInfo | URL, init?: RequestInit) => {
const response = await fetch(input, init)
if (response.status !== 404) return response
const fallback = legacyDefaults[new URL(input instanceof Request ? input.url : input).pathname]
if (fallback === undefined) return response
return Response.json(fallback)
}, { preconnect: fetch.preconnect })
const gracefulFetch = Object.assign(
async (input: RequestInfo | URL, init?: RequestInit) => {
const response = await fetch(input, init)
if (response.status !== 404) return response
const fallback = legacyDefaults[new URL(input instanceof Request ? input.url : input).pathname]
if (fallback === undefined) return response
return Response.json(fallback)
},
{ preconnect: fetch.preconnect },
)

View file

@ -100,9 +100,7 @@ function normalizePack(pack: TuiAttentionSoundPack): RegisteredSoundPack | undef
sounds: Object.fromEntries(
Object.entries(pack.sounds).filter(
(item): item is [TuiAttentionSoundName, string] =>
Schema.is(AttentionSoundName)(item[0]) &&
typeof item[1] === "string" &&
item[1].trim().length > 0,
Schema.is(AttentionSoundName)(item[0]) && typeof item[1] === "string" && item[1].trim().length > 0,
),
),
}
@ -200,9 +198,7 @@ export function createTuiAttention(input: {
const requestedSound = typeof request.sound === "object" ? request.sound : undefined
const soundSkip = volume === undefined ? undefined : focusSkip(requestedSound?.when ?? "always", focus)
const soundName =
requestedSound?.name && Schema.is(AttentionSoundName)(requestedSound.name)
? requestedSound.name
: "default"
requestedSound?.name && Schema.is(AttentionSoundName)(requestedSound.name) ? requestedSound.name : "default"
const sound = volume === undefined || soundSkip ? false : await playSound(soundName, volume)
if (!notification && !sound) {

View file

@ -86,13 +86,15 @@ export function discoverEditorConnection(directory: string) {
: []
const score = Math.max(0, ...folders.map(contains))
if (!score) return []
return [{
url: `ws://127.0.0.1:${port}`,
authToken: typeof value.authToken === "string" ? value.authToken : undefined,
source: `lock:${port}`,
score,
mtime: statSync(file).mtimeMs,
}]
return [
{
url: `ws://127.0.0.1:${port}`,
authToken: typeof value.authToken === "string" ? value.authToken : undefined,
source: `lock:${port}`,
score,
mtime: statSync(file).mtimeMs,
},
]
} catch {
return []
}
@ -110,11 +112,13 @@ function resolveZedDbPath() {
path.join(os.homedir(), "Library", "Application Support", "Zed", "db", "0-stable", "db.sqlite"),
path.join(os.homedir(), ".local", "share", "zed", "db", "0-stable", "db.sqlite"),
].filter((item): item is string => Boolean(item))
return candidates.find((item) => {
try {
return statSync(item).isFile()
} catch {
return false
}
}) ?? ""
return (
candidates.find((item) => {
try {
return statSync(item).isFile()
} catch {
return false
}
}) ?? ""
)
}

View file

@ -4,7 +4,10 @@ import { HttpEffect, HttpRouter, HttpServerRequest, HttpServerResponse } from "e
import { HttpApiError, HttpApiMiddleware } from "effect/unstable/httpapi"
import { hasPtyConnectTicketURL } from "@/server/shared/pty-ticket"
import { isPublicUIPath } from "@/server/shared/public-ui"
export { Authorization as ServerAuthorization, authorizationLayer as serverAuthorizationLayer } from "@opencode-ai/server/middleware/authorization"
export {
Authorization as ServerAuthorization,
authorizationLayer as serverAuthorizationLayer,
} from "@opencode-ai/server/middleware/authorization"
const AUTH_TOKEN_QUERY = "auth_token"
const UNAUTHORIZED = 401

View file

@ -3,12 +3,7 @@ import { mkdir, symlink } from "node:fs/promises"
import os from "node:os"
import path from "node:path"
import { afterEach, expect, spyOn, test } from "bun:test"
import {
isZedTerminal,
offsetToPosition,
resolveZedDbPath,
resolveZedSelection,
} from "../../../src/cli/tui/editor-zed"
import { isZedTerminal, offsetToPosition, resolveZedDbPath, resolveZedSelection } from "../../../src/cli/tui/editor-zed"
import { tmpdir } from "../../fixture/fixture"
const originalZedTerm = process.env.ZED_TERM

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,14 @@
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
export const HealthGroup = HttpApiGroup.make("server.health")
.add(
HttpApiEndpoint.get("health.get", "/api/health", {
success: Schema.Struct({ healthy: Schema.Literal(true) }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.health.get",
summary: "Check server health",
description: "Check whether the API server is ready to accept requests.",
}),
),
)
export const HealthGroup = HttpApiGroup.make("server.health").add(
HttpApiEndpoint.get("health.get", "/api/health", {
success: Schema.Struct({ healthy: Schema.Literal(true) }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.health.get",
summary: "Check server health",
description: "Check whether the API server is ready to accept requests.",
}),
),
)

View file

@ -83,6 +83,4 @@ export const PermissionGroup = HttpApiGroup.make("server.permission")
}),
),
)
.annotateMerge(
OpenApi.annotations({ title: "permissions", description: "Experimental permission routes." }),
)
.annotateMerge(OpenApi.annotations({ title: "permissions", description: "Experimental permission routes." }))

View file

@ -33,8 +33,7 @@ export const ProviderGroup = HttpApiGroup.make("server.provider")
OpenApi.annotations({
identifier: "v2.provider.get",
summary: "Get provider",
description:
"Retrieve a single AI provider so clients can inspect its availability and endpoint settings.",
description: "Retrieve a single AI provider so clients can inspect its availability and endpoint settings.",
}),
),
)

View file

@ -43,7 +43,11 @@ export const PermissionHandler = HttpApiBuilder.group(Api, "server.permission",
"permission.saved.list",
Effect.fn(function* (ctx) {
const location = yield* Location.Service
return { data: yield* (yield* PermissionSaved.Service).list({ projectID: ctx.query.projectID ?? location.project.id }) }
return {
data: yield* (yield* PermissionSaved.Service).list({
projectID: ctx.query.projectID ?? location.project.id,
}),
}
}),
)
.handle(

View file

@ -7,12 +7,9 @@ import { HttpApiMiddleware } from "effect/unstable/httpapi"
const AUTH_TOKEN_QUERY = "auth_token"
const WWW_AUTHENTICATE = 'Basic realm="Secure Area"'
export class Authorization extends HttpApiMiddleware.Service<Authorization>()(
"@opencode/HttpApiAuthorization",
{
error: UnauthorizedError,
},
) {}
export class Authorization extends HttpApiMiddleware.Service<Authorization>()("@opencode/HttpApiAuthorization", {
error: UnauthorizedError,
}) {}
function emptyCredential() {
return { username: "", password: Redacted.make("") }

View file

@ -480,30 +480,33 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
const attention = props.host.attention({ renderer, config: tuiConfig, kv })
const platform = useTuiPlatform()
const api = createTuiApi(createTuiApiAdapters({
version: build.version,
tuiConfig,
dialog,
keymap,
kv,
route,
routes: pluginRuntime.routes,
event,
sdk,
sync,
theme: themeState,
toast,
renderer,
attention,
Slot: pluginRuntime.Slot,
}))
const api = createTuiApi(
createTuiApiAdapters({
version: build.version,
tuiConfig,
dialog,
keymap,
kv,
route,
routes: pluginRuntime.routes,
event,
sdk,
sync,
theme: themeState,
toast,
renderer,
attention,
Slot: pluginRuntime.Slot,
}),
)
const [ready, setReady] = createSignal(false)
props.pluginHost.start({
api,
config: tuiConfig,
runtime: pluginRuntime,
dispose: () => attention.dispose(),
})
props.pluginHost
.start({
api,
config: tuiConfig,
runtime: pluginRuntime,
dispose: () => attention.dispose(),
})
.catch((error) => {
console.error("Failed to load TUI plugins", error)
})
@ -529,7 +532,8 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
renderer.console.onCopySelection = async (text: string) => {
if (!text || text.length === 0) return
await platform.clipboard?.write?.(text)
await platform.clipboard
?.write?.(text)
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
.catch(toast.error)
@ -691,7 +695,8 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
run: async () => {
const workspace = currentWorktreeWorkspace()
if (!workspace?.directory) return
await platform.clipboard?.write?.(workspace.directory)
await platform.clipboard
?.write?.(workspace.directory)
.then(() => toast.show({ message: "Copied worktree path", variant: "info" }))
.catch(toast.error)
dialog.clear()

View file

@ -16,10 +16,7 @@ export const AttentionSoundName = Schema.Literals([
export type AttentionSoundName = Schema.Schema.Type<typeof AttentionSoundName>
export const PluginOptions = Schema.Record(Schema.String, Schema.Unknown)
export const PluginSpec = Schema.Union([
Schema.String,
Schema.mutable(Schema.Tuple([Schema.String, PluginOptions])),
])
export const PluginSpec = Schema.Union([Schema.String, Schema.mutable(Schema.Tuple([Schema.String, PluginOptions]))])
export const LeaderTimeoutDefault = 2000
export const LeaderTimeout = Schema.Int.check(Schema.isGreaterThan(0)).annotate({

View file

@ -55,7 +55,11 @@ export type SyncDependencies = {
logger: { error(message: string, extra?: Record<string, unknown>): void }
}
export const { context: SyncContext, use: useSync, provider: SyncProvider } = createSimpleContext({
export const {
context: SyncContext,
use: useSync,
provider: SyncProvider,
} = createSimpleContext({
name: "Sync",
init: (dependencies: SyncDependencies) => {
const environment = useTuiEnvironment()

View file

@ -96,14 +96,15 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
})
function syncCustomThemes() {
return (platform?.themes?.discover() ?? Promise.resolve({})).then((themes) => {
setCustomThemes(
Object.entries(themes).reduce<Record<string, ThemeJson>>((result, [name, theme]) => {
if (isTheme(theme)) result[name] = theme
return result
}, {}),
)
})
return (platform?.themes?.discover() ?? Promise.resolve({}))
.then((themes) => {
setCustomThemes(
Object.entries(themes).reduce<Record<string, ThemeJson>>((result, [name, theme]) => {
if (isTheme(theme)) result[name] = theme
return result
}, {}),
)
})
.catch(() => setStore("active", "opencode"))
}

View file

@ -136,10 +136,12 @@ export function Tips(props: { api: TuiPluginApi; connected?: boolean }) {
}
const tip = createMemo(() => {
if (props.connected === false) return NO_MODELS_TIP
const tips = [...TIPS, environment.capabilities.terminalSuspend ? TERMINAL_SUSPEND_TIP : INPUT_UNDO_TIP].flatMap((item) => {
const value = typeof item === "string" ? item : item(shortcuts)
return value ? [value] : []
})
const tips = [...TIPS, environment.capabilities.terminalSuspend ? TERMINAL_SUSPEND_TIP : INPUT_UNDO_TIP].flatMap(
(item) => {
const value = typeof item === "string" ? item : item(shortcuts)
return value ? [value] : []
},
)
return tips[Math.floor(tipOffset * tips.length)] ?? NO_MODELS_TIP
}, NO_MODELS_TIP)
// Solid can expose a memo's initial value while a pure computation is pending.

View file

@ -211,11 +211,7 @@ export function formatKeyBindings(bindings: Parameters<typeof formatCommandBindi
return formatCommandBindingsExtra(bindings, formatOptions(config))
}
export function registerOpencodeKeymap(
keymap: OpenTuiKeymap,
renderer: CliRenderer,
config: ResolvedKeymapConfig,
) {
export function registerOpencodeKeymap(keymap: OpenTuiKeymap, renderer: CliRenderer, config: ResolvedKeymapConfig) {
const modeStack = createOpencodeModeStack(keymap)
const offCommaBindings = registerCommaBindings(keymap)
const offAliasExpander = registerKeyAliases(keymap)

View file

@ -5,10 +5,7 @@ export function stripPromptPartIDs<Part extends { id: string; messageID: string;
return rest
}
export function expandPastedTextPlaceholders(
text: string,
parts: readonly unknown[],
) {
export function expandPastedTextPlaceholders(text: string, parts: readonly unknown[]) {
return parts.reduce<string>((result, part) => {
if (!isPastedTextPart(part)) return result
return result.replace(part.source.text.value, part.text)

View file

@ -460,7 +460,8 @@ export function Session() {
},
run: async () => {
const copy = (url: string) =>
platform.clipboard?.write?.(url)
platform.clipboard
?.write?.(url)
.then(() => toast.show({ message: "Share URL copied to clipboard!", variant: "success" }))
.catch(() => toast.show({ message: "Failed to copy URL to clipboard", variant: "error" }))
const url = session()?.share?.url
@ -895,7 +896,8 @@ export function Session() {
return
}
platform.clipboard?.write?.(text)
platform.clipboard
?.write?.(text)
.then(() => toast.show({ message: "Message copied to clipboard!", variant: "success" }))
.catch(() => toast.show({ message: "Failed to copy to clipboard", variant: "error" }))
dialog.clear()

View file

@ -30,7 +30,8 @@ export function cliErrorMessage(input: unknown): string | undefined {
}
const provider = configData(input, "ProviderInitError")
if (provider) return `Failed to initialize provider "${field(provider, "providerID")}". Check credentials and configuration.`
if (provider)
return `Failed to initialize provider "${field(provider, "providerID")}". Check credentials and configuration.`
const json = configData(input, "ConfigJsonError")
if (json) {

View file

@ -20,14 +20,7 @@ test("defines package-owned plugin specs and attention sound names", () => {
expect(decodePlugin("example-plugin")).toBe("example-plugin")
expect(decodePlugin(["example-plugin", { enabled: true }])).toEqual(["example-plugin", { enabled: true }])
expect(() => decodePlugin(["example-plugin"])).toThrow()
expect(AttentionSoundName.literals).toEqual([
"default",
"question",
"permission",
"error",
"done",
"subagent_done",
])
expect(AttentionSoundName.literals).toEqual(["default", "question", "permission", "error", "done", "subagent_done"])
})
test("validates config constraints", () => {

View file

@ -43,8 +43,19 @@ export function createFetch(override?: FetchHandler) {
const overridden = await override?.(url)
if (overridden) return overridden
if (["/agent", "/command", "/experimental/workspace", "/experimental/workspace/status", "/formatter", "/lsp"].includes(url.pathname)) return json([])
if (["/config", "/experimental/resource", "/mcp", "/provider/auth", "/session/status"].includes(url.pathname)) return json({})
if (
[
"/agent",
"/command",
"/experimental/workspace",
"/experimental/workspace/status",
"/formatter",
"/lsp",
].includes(url.pathname)
)
return json([])
if (["/config", "/experimental/resource", "/mcp", "/provider/auth", "/session/status"].includes(url.pathname))
return json({})
if (url.pathname === "/config/providers") return json({ providers: {}, default: {} })
if (url.pathname === "/experimental/console") return json({ consoleManagedProviders: [], switchableOrgCount: 0 })
if (url.pathname === "/path") return json({ home: "", state: "", config: "", worktree, directory })

View file

@ -5,12 +5,7 @@ import { testRender, useRenderer } from "@opentui/solid"
import { expect, test } from "bun:test"
import { onCleanup } from "solid-js"
import { TuiKeybind } from "../src/config/keybind"
import {
getOpencodeModeStack,
OPENCODE_BASE_MODE,
OpencodeKeymapProvider,
registerOpencodeKeymap,
} from "../src/keymap"
import { getOpencodeModeStack, OPENCODE_BASE_MODE, OpencodeKeymapProvider, registerOpencodeKeymap } from "../src/keymap"
function createResolvedKeymapConfig(input: TuiKeybind.KeybindOverrides = {}) {
const keybinds = TuiKeybind.parse(input)

View file

@ -1,10 +1,5 @@
import { describe, expect, test } from "bun:test"
import {
formatAssistantHeader,
formatMessage,
formatPart,
formatTranscript,
} from "../../src/util/transcript"
import { formatAssistantHeader, formatMessage, formatPart, formatTranscript } from "../../src/util/transcript"
import type { AssistantMessage, Part, Provider, UserMessage } from "@opencode-ai/sdk/v2"
const providers: Provider[] = [