Compare commits

...

5 Commits

Author SHA1 Message Date
Aiden Cline 111f5858d9 refactor(tui): simplify rpc failure propagation 2026-06-04 11:45:42 -05:00
Aiden Cline 45efcc893c fix(tui): reject worker rpc failures 2026-06-04 11:42:13 -05:00
Dax Raad 94c49b20ba make scripts executable 2026-06-04 11:19:36 -04:00
opencode-agent[bot] 9f3a0fe1d0 chore: update nix node_modules hashes 2026-06-04 15:15:12 +00:00
Dax Raad 7f54b1bfb8 fix build 2026-06-04 11:11:23 -04:00
9 changed files with 68 additions and 19 deletions
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-VJyM9J6fjpFjlUGDl4AKPZB2IpV+nN8Tduj4n1dPSFw=",
"aarch64-linux": "sha256-L8X/W7XbAUwwz+4luj4qVZbrnnjM2+DGuEL1aSNO2wM=",
"aarch64-darwin": "sha256-oA2xSHcs/axmGH1kcXZpELrv5DlB6DRDW3ybD3s4z1E=",
"x86_64-darwin": "sha256-mG7z3K9IOdX/XdiHLvCyLfVvXwsgMw1LIc2XbJUdRRI="
"x86_64-linux": "sha256-dvFu5Cbs8MFoSBQXwv4HN2vyh5p20dh6QC5zZiFr0qs=",
"aarch64-linux": "sha256-l0xO7Nocl6enQxLQlLB71mG+NuT6I1eQQ1FgLtYGQOg=",
"aarch64-darwin": "sha256-WY6Lstxt4n4n63kYZUX09birHx7sNvl0Pegc6L13mGE=",
"x86_64-darwin": "sha256-sZdG40TSE9KhrmLQyQMPRugGo6R7AS3wgHiEGYtcXtc="
}
}
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
+4
View File
@@ -0,0 +1,4 @@
declare module "*.md" {
const content: string
export default content
}
+6 -4
View File
@@ -1,18 +1,20 @@
/// <reference path="../markdown.d.ts" />
export * as SkillPlugin from "./skill"
import { Effect } from "effect"
import { PluginV2 } from "../plugin"
import { AbsolutePath } from "../schema"
import { SkillV2 } from "../skill"
import customizeOpencodeContent from "./skill/customize-opencode.md" with { type: "text" }
export const CustomizeOpencodeContent = customizeOpencodeContent
export const Plugin = PluginV2.define({
id: PluginV2.ID.make("skill"),
effect: Effect.gen(function* () {
const skill = yield* SkillV2.Service
const transform = yield* skill.transform()
const content = yield* Effect.promise(() =>
Bun.file(new URL("./skill/customize-opencode.md", import.meta.url)).text(),
)
yield* transform((editor) => {
editor.source(
@@ -23,7 +25,7 @@ export const Plugin = PluginV2.define({
description:
"Use ONLY when the user is editing or creating opencode's own configuration: opencode.json, opencode.jsonc, files under .opencode/, or files under ~/.config/opencode/. Also use when creating or fixing opencode agents, subagents, skills, plugins, MCP servers, or permission rules. Do not use for the user's own application code, or for any project that is not configuring opencode itself.",
location: AbsolutePath.make("/builtin/customize-opencode.md"),
content,
content: CustomizeOpencodeContent,
}),
}),
)
+2 -3
View File
@@ -6,6 +6,7 @@ import type { Agent } from "@/agent/agent"
import { EventV2Bridge } from "@/event-v2-bridge"
import { InstanceState } from "@/effect/instance-state"
import { Global } from "@opencode-ai/core/global"
import { SkillPlugin } from "@opencode-ai/core/plugin/skill"
import { Permission } from "@/permission"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { Config } from "@/config/config"
@@ -32,9 +33,7 @@ const SKILL_PATTERN = "**/SKILL.md"
const CUSTOMIZE_OPENCODE_SKILL_NAME = "customize-opencode"
const CUSTOMIZE_OPENCODE_SKILL_DESCRIPTION =
"Use ONLY when the user is editing or creating opencode's own configuration: opencode.json, opencode.jsonc, files under .opencode/, or files under ~/.config/opencode/. Also use when creating or fixing opencode agents, subagents, skills, plugins, MCP servers, or permission rules. Do not use for the user's own application code, or for any project that is not configuring opencode itself."
const CUSTOMIZE_OPENCODE_SKILL_BODY = await Bun.file(
new URL("../../../core/src/plugin/skill/customize-opencode.md", import.meta.url),
).text()
const CUSTOMIZE_OPENCODE_SKILL_BODY = SkillPlugin.CustomizeOpencodeContent
export const Info = Schema.Struct({
name: Schema.String,
+21 -8
View File
@@ -6,8 +6,14 @@ export function listen(rpc: Definition) {
onmessage = async (evt) => {
const parsed = JSON.parse(evt.data)
if (parsed.type === "rpc.request") {
const result = await rpc[parsed.method](parsed.input)
postMessage(JSON.stringify({ type: "rpc.result", result, id: parsed.id }))
try {
const result = await rpc[parsed.method](parsed.input)
postMessage(JSON.stringify({ type: "rpc.result", result, id: parsed.id }))
} catch (error) {
postMessage(
JSON.stringify({ type: "rpc.error", error: error instanceof Error ? error.message : String(error), id: parsed.id }),
)
}
}
}
}
@@ -20,15 +26,22 @@ export function client<T extends Definition>(target: {
postMessage: (data: string) => void | null
onmessage: ((this: Worker, ev: MessageEvent<any>) => any) | null
}) {
const pending = new Map<number, (result: any) => void>()
const pending = new Map<number, { resolve: (result: any) => void; reject: (error: any) => void }>()
const listeners = new Map<string, Set<(data: any) => void>>()
let id = 0
target.onmessage = async (evt) => {
const parsed = JSON.parse(evt.data)
if (parsed.type === "rpc.result") {
const resolve = pending.get(parsed.id)
if (resolve) {
resolve(parsed.result)
const request = pending.get(parsed.id)
if (request) {
request.resolve(parsed.result)
pending.delete(parsed.id)
}
}
if (parsed.type === "rpc.error") {
const request = pending.get(parsed.id)
if (request) {
request.reject(new Error(parsed.error))
pending.delete(parsed.id)
}
}
@@ -44,8 +57,8 @@ export function client<T extends Definition>(target: {
return {
call<Method extends keyof T>(method: Method, input: Parameters<T[Method]>[0]): Promise<ReturnType<T[Method]>> {
const requestId = id++
return new Promise((resolve) => {
pending.set(requestId, resolve)
return new Promise((resolve, reject) => {
pending.set(requestId, { resolve, reject })
target.postMessage(JSON.stringify({ type: "rpc.request", method, input, id: requestId }))
})
},
+31
View File
@@ -0,0 +1,31 @@
import { describe, expect, test } from "bun:test"
import { Rpc } from "@/util/rpc"
type TestRpc = {
fail(input: undefined): Promise<void>
}
type Target = Parameters<typeof Rpc.client<TestRpc>>[0]
describe("Rpc", () => {
test("rejects pending calls when the worker reports an error", async () => {
const target: Target = {
postMessage(data) {
const request = JSON.parse(data)
target.onmessage?.call(
{} as Worker,
{
data: JSON.stringify({
type: "rpc.error",
id: request.id,
error: "boom",
}),
} as MessageEvent<any>,
)
},
onmessage: null,
}
await expect(Rpc.client<TestRpc>(target).call("fail", undefined)).rejects.toThrow("boom")
})
})