mirror of
https://github.com/anomalyco/opencode.git
synced 2026-03-19 21:24:37 +00:00
Compare commits
7 Commits
kit/effect
...
fix-e2e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4a86db544 | ||
|
|
2dbcd79fd2 | ||
|
|
665b251ee1 | ||
|
|
48a7f0fd93 | ||
|
|
d69962b0f7 | ||
|
|
a6f23cb08e | ||
|
|
0540751897 |
@@ -2,7 +2,7 @@ import type { Page } from "@playwright/test"
|
||||
import { test, expect } from "../fixtures"
|
||||
import { openSidebar, resolveSlug, sessionIDFromUrl, setWorkspacesEnabled, waitDir, waitSlug } from "../actions"
|
||||
import { promptSelector, workspaceItemSelector, workspaceNewSessionSelector } from "../selectors"
|
||||
import { createSdk } from "../utils"
|
||||
import { createSdk, dirDecode, dirSlug, resolveDirectory } from "../utils"
|
||||
|
||||
function item(space: { slug: string; raw: string }) {
|
||||
return `${workspaceItemSelector(space.slug)}, ${workspaceItemSelector(space.raw)}`
|
||||
@@ -101,13 +101,13 @@ test("new sessions from sidebar workspace actions stay in selected workspace", a
|
||||
trackDirectory(second.directory)
|
||||
await waitWorkspaceReady(page, second)
|
||||
|
||||
const firstSession = await createSessionFromWorkspace(page, first.slug, `workspace one ${Date.now()}`)
|
||||
const firstSession = await createSessionFromWorkspace(page, first, `workspace one ${Date.now()}`)
|
||||
trackSession(firstSession.sessionID, first.directory)
|
||||
|
||||
const secondSession = await createSessionFromWorkspace(page, second.slug, `workspace two ${Date.now()}`)
|
||||
const secondSession = await createSessionFromWorkspace(page, second, `workspace two ${Date.now()}`)
|
||||
trackSession(secondSession.sessionID, second.directory)
|
||||
|
||||
const thirdSession = await createSessionFromWorkspace(page, first.slug, `workspace one again ${Date.now()}`)
|
||||
const thirdSession = await createSessionFromWorkspace(page, first, `workspace one again ${Date.now()}`)
|
||||
trackSession(thirdSession.sessionID, first.directory)
|
||||
|
||||
await expect.poll(() => sessionDirectory(first.directory, firstSession.sessionID)).toBe(first.directory)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs/promises"
|
||||
import os from "node:os"
|
||||
import path from "node:path"
|
||||
import { base64Decode } from "@opencode-ai/util/encode"
|
||||
import type { Page } from "@playwright/test"
|
||||
|
||||
import { test, expect } from "../fixtures"
|
||||
@@ -19,7 +20,7 @@ import {
|
||||
waitSlug,
|
||||
} from "../actions"
|
||||
import { dropdownMenuContentSelector, inlineInputSelector, workspaceItemSelector } from "../selectors"
|
||||
import { createSdk, dirSlug } from "../utils"
|
||||
import { createSdk, dirDecode, dirSlug } from "../utils"
|
||||
|
||||
async function setupWorkspaceTest(page: Page, project: { slug: string }) {
|
||||
const rootSlug = project.slug
|
||||
@@ -257,7 +258,7 @@ test("can delete a workspace", async ({ page, withProject }) => {
|
||||
await clickMenuItem(menu, /^Delete$/i, { force: true })
|
||||
await confirmDialog(page, /^Delete workspace$/i)
|
||||
|
||||
await expect.poll(() => base64Decode(slugFromUrl(page.url()))).toBe(project.directory)
|
||||
await expect.poll(() => dirDecode(slugFromUrl(page.url()))).toBe(project.directory)
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
|
||||
import { base64Encode, checksum } from "@opencode-ai/util/encode"
|
||||
import { base64Decode, base64Encode, checksum } from "@opencode-ai/util/encode"
|
||||
|
||||
export const serverHost = process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"
|
||||
export const serverPort = process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"
|
||||
@@ -48,6 +48,10 @@ export function dirSlug(directory: string) {
|
||||
return base64Encode(directory)
|
||||
}
|
||||
|
||||
export function dirDecode(slug: string) {
|
||||
return base64Decode(slug)
|
||||
}
|
||||
|
||||
export function dirPath(directory: string) {
|
||||
return `/${dirSlug(directory)}`
|
||||
}
|
||||
|
||||
@@ -260,7 +260,10 @@ export namespace Agent {
|
||||
return pipe(
|
||||
await state(),
|
||||
values(),
|
||||
sortBy([(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "build"), "desc"]),
|
||||
sortBy(
|
||||
[(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "build"), "desc"],
|
||||
[(x) => x.name, "asc"],
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ export namespace Bus {
|
||||
})
|
||||
const pending = []
|
||||
for (const key of [def.type, "*"]) {
|
||||
const match = state().subscriptions.get(key)
|
||||
for (const sub of match ?? []) {
|
||||
const match = [...(state().subscriptions.get(key) ?? [])]
|
||||
for (const sub of match) {
|
||||
pending.push(sub(payload))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ import { ProviderTransform } from "./transform"
|
||||
import { Installation } from "../installation"
|
||||
import { ModelID, ProviderID } from "./schema"
|
||||
|
||||
const DEFAULT_CHUNK_TIMEOUT = 300_000
|
||||
|
||||
export namespace Provider {
|
||||
const log = Log.create({ service: "provider" })
|
||||
|
||||
@@ -1130,7 +1128,7 @@ export namespace Provider {
|
||||
if (existing) return existing
|
||||
|
||||
const customFetch = options["fetch"]
|
||||
const chunkTimeout = options["chunkTimeout"] || DEFAULT_CHUNK_TIMEOUT
|
||||
const chunkTimeout = options["chunkTimeout"]
|
||||
delete options["chunkTimeout"]
|
||||
|
||||
options["fetch"] = async (input: any, init?: BunFetchRequestInit) => {
|
||||
|
||||
85
packages/opencode/src/server/routes/event.ts
Normal file
85
packages/opencode/src/server/routes/event.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Hono } from "hono"
|
||||
import { describeRoute, resolver } from "hono-openapi"
|
||||
import { streamSSE } from "hono/streaming"
|
||||
import { Log } from "@/util/log"
|
||||
import { BusEvent } from "@/bus/bus-event"
|
||||
import { Bus } from "@/bus"
|
||||
import { lazy } from "../../util/lazy"
|
||||
import { AsyncQueue } from "../../util/queue"
|
||||
import { Instance } from "@/project/instance"
|
||||
|
||||
const log = Log.create({ service: "server" })
|
||||
|
||||
export const EventRoutes = lazy(() =>
|
||||
new Hono().get(
|
||||
"/event",
|
||||
describeRoute({
|
||||
summary: "Subscribe to events",
|
||||
description: "Get events",
|
||||
operationId: "event.subscribe",
|
||||
responses: {
|
||||
200: {
|
||||
description: "Event stream",
|
||||
content: {
|
||||
"text/event-stream": {
|
||||
schema: resolver(BusEvent.payloads()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
log.info("event connected")
|
||||
c.header("X-Accel-Buffering", "no")
|
||||
c.header("X-Content-Type-Options", "nosniff")
|
||||
return streamSSE(c, async (stream) => {
|
||||
const q = new AsyncQueue<string | null>()
|
||||
let done = false
|
||||
|
||||
q.push(
|
||||
JSON.stringify({
|
||||
type: "server.connected",
|
||||
properties: {},
|
||||
}),
|
||||
)
|
||||
|
||||
// Send heartbeat every 10s to prevent stalled proxy streams.
|
||||
const heartbeat = setInterval(() => {
|
||||
q.push(
|
||||
JSON.stringify({
|
||||
type: "server.heartbeat",
|
||||
properties: {},
|
||||
}),
|
||||
)
|
||||
}, 10_000)
|
||||
|
||||
const unsub = Bus.subscribeAll((event) => {
|
||||
q.push(JSON.stringify(event))
|
||||
if (event.type === Bus.InstanceDisposed.type) {
|
||||
stop()
|
||||
}
|
||||
})
|
||||
|
||||
const stop = () => {
|
||||
if (done) return
|
||||
done = true
|
||||
clearInterval(heartbeat)
|
||||
unsub()
|
||||
q.push(null)
|
||||
log.info("event disconnected")
|
||||
}
|
||||
|
||||
stream.onAbort(stop)
|
||||
|
||||
try {
|
||||
for await (const data of q) {
|
||||
if (data === null) return
|
||||
await stream.writeSSE({ data })
|
||||
}
|
||||
} finally {
|
||||
stop()
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -4,6 +4,7 @@ import { streamSSE } from "hono/streaming"
|
||||
import z from "zod"
|
||||
import { BusEvent } from "@/bus/bus-event"
|
||||
import { GlobalBus } from "@/bus/global"
|
||||
import { AsyncQueue } from "@/util/queue"
|
||||
import { Instance } from "../../project/instance"
|
||||
import { Installation } from "@/installation"
|
||||
import { Log } from "../../util/log"
|
||||
@@ -69,41 +70,54 @@ export const GlobalRoutes = lazy(() =>
|
||||
c.header("X-Accel-Buffering", "no")
|
||||
c.header("X-Content-Type-Options", "nosniff")
|
||||
return streamSSE(c, async (stream) => {
|
||||
stream.writeSSE({
|
||||
data: JSON.stringify({
|
||||
const q = new AsyncQueue<string | null>()
|
||||
let done = false
|
||||
|
||||
q.push(
|
||||
JSON.stringify({
|
||||
payload: {
|
||||
type: "server.connected",
|
||||
properties: {},
|
||||
},
|
||||
}),
|
||||
})
|
||||
async function handler(event: any) {
|
||||
await stream.writeSSE({
|
||||
data: JSON.stringify(event),
|
||||
})
|
||||
}
|
||||
GlobalBus.on("event", handler)
|
||||
)
|
||||
|
||||
// Send heartbeat every 10s to prevent stalled proxy streams.
|
||||
const heartbeat = setInterval(() => {
|
||||
stream.writeSSE({
|
||||
data: JSON.stringify({
|
||||
q.push(
|
||||
JSON.stringify({
|
||||
payload: {
|
||||
type: "server.heartbeat",
|
||||
properties: {},
|
||||
},
|
||||
}),
|
||||
})
|
||||
)
|
||||
}, 10_000)
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
stream.onAbort(() => {
|
||||
clearInterval(heartbeat)
|
||||
GlobalBus.off("event", handler)
|
||||
resolve()
|
||||
log.info("global event disconnected")
|
||||
})
|
||||
})
|
||||
async function handler(event: any) {
|
||||
q.push(JSON.stringify(event))
|
||||
}
|
||||
GlobalBus.on("event", handler)
|
||||
|
||||
const stop = () => {
|
||||
if (done) return
|
||||
done = true
|
||||
clearInterval(heartbeat)
|
||||
GlobalBus.off("event", handler)
|
||||
q.push(null)
|
||||
log.info("event disconnected")
|
||||
}
|
||||
|
||||
stream.onAbort(stop)
|
||||
|
||||
try {
|
||||
for await (const data of q) {
|
||||
if (data === null) return
|
||||
await stream.writeSSE({ data })
|
||||
}
|
||||
} finally {
|
||||
stop()
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { BusEvent } from "@/bus/bus-event"
|
||||
import { Bus } from "@/bus"
|
||||
import { Log } from "../util/log"
|
||||
import { describeRoute, generateSpecs, validator, resolver, openAPIRouteHandler } from "hono-openapi"
|
||||
import { Hono } from "hono"
|
||||
import { cors } from "hono/cors"
|
||||
import { streamSSE } from "hono/streaming"
|
||||
import { proxy } from "hono/proxy"
|
||||
import { basicAuth } from "hono/basic-auth"
|
||||
import z from "zod"
|
||||
@@ -34,6 +31,7 @@ import { FileRoutes } from "./routes/file"
|
||||
import { ConfigRoutes } from "./routes/config"
|
||||
import { ExperimentalRoutes } from "./routes/experimental"
|
||||
import { ProviderRoutes } from "./routes/provider"
|
||||
import { EventRoutes } from "./routes/event"
|
||||
import { InstanceBootstrap } from "../project/bootstrap"
|
||||
import { NotFoundError } from "../storage/db"
|
||||
import type { ContentfulStatusCode } from "hono/utils/http-status"
|
||||
@@ -251,6 +249,7 @@ export namespace Server {
|
||||
.route("/question", QuestionRoutes())
|
||||
.route("/provider", ProviderRoutes())
|
||||
.route("/", FileRoutes())
|
||||
.route("/", EventRoutes())
|
||||
.route("/mcp", McpRoutes())
|
||||
.route("/tui", TuiRoutes())
|
||||
.post(
|
||||
@@ -498,64 +497,6 @@ export namespace Server {
|
||||
return c.json(await Format.status())
|
||||
},
|
||||
)
|
||||
.get(
|
||||
"/event",
|
||||
describeRoute({
|
||||
summary: "Subscribe to events",
|
||||
description: "Get events",
|
||||
operationId: "event.subscribe",
|
||||
responses: {
|
||||
200: {
|
||||
description: "Event stream",
|
||||
content: {
|
||||
"text/event-stream": {
|
||||
schema: resolver(BusEvent.payloads()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
log.info("event connected")
|
||||
c.header("X-Accel-Buffering", "no")
|
||||
c.header("X-Content-Type-Options", "nosniff")
|
||||
return streamSSE(c, async (stream) => {
|
||||
stream.writeSSE({
|
||||
data: JSON.stringify({
|
||||
type: "server.connected",
|
||||
properties: {},
|
||||
}),
|
||||
})
|
||||
const unsub = Bus.subscribeAll(async (event) => {
|
||||
await stream.writeSSE({
|
||||
data: JSON.stringify(event),
|
||||
})
|
||||
if (event.type === Bus.InstanceDisposed.type) {
|
||||
stream.close()
|
||||
}
|
||||
})
|
||||
|
||||
// Send heartbeat every 10s to prevent stalled proxy streams.
|
||||
const heartbeat = setInterval(() => {
|
||||
stream.writeSSE({
|
||||
data: JSON.stringify({
|
||||
type: "server.heartbeat",
|
||||
properties: {},
|
||||
}),
|
||||
})
|
||||
}, 10_000)
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
stream.onAbort(() => {
|
||||
clearInterval(heartbeat)
|
||||
unsub()
|
||||
resolve()
|
||||
log.info("event disconnected")
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
.all("/*", async (c) => {
|
||||
const path = c.req.path
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ export namespace Skill {
|
||||
|
||||
const available = Effect.fn("Skill.available")(function* (agent?: Agent.Info) {
|
||||
yield* Effect.promise(() => state.ensure())
|
||||
const list = Object.values(state.skills)
|
||||
const list = Object.values(state.skills).toSorted((a, b) => a.name.localeCompare(b.name))
|
||||
if (!agent) return list
|
||||
return list.filter((skill) => PermissionNext.evaluate("skill", skill.name, agent.permission).action !== "deny")
|
||||
})
|
||||
|
||||
@@ -33,12 +33,13 @@ export const TaskTool = Tool.define("task", async (ctx) => {
|
||||
const accessibleAgents = caller
|
||||
? agents.filter((a) => PermissionNext.evaluate("task", a.name, caller.permission).action !== "deny")
|
||||
: agents
|
||||
const list = accessibleAgents.toSorted((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
const description = DESCRIPTION.replace(
|
||||
"{agents}",
|
||||
accessibleAgents
|
||||
.map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`)
|
||||
.join("\n"),
|
||||
list.map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`).join(
|
||||
"\n",
|
||||
),
|
||||
)
|
||||
return {
|
||||
description,
|
||||
|
||||
@@ -384,6 +384,32 @@ test("multiple custom agents can be defined", async () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("Agent.list keeps the default agent first and sorts the rest by name", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
default_agent: "plan",
|
||||
agent: {
|
||||
zebra: {
|
||||
description: "Zebra",
|
||||
mode: "subagent",
|
||||
},
|
||||
alpha: {
|
||||
description: "Alpha",
|
||||
mode: "subagent",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const names = (await Agent.list()).map((a) => a.name)
|
||||
expect(names[0]).toBe("plan")
|
||||
expect(names.slice(1)).toEqual(names.slice(1).toSorted((a, b) => a.localeCompare(b)))
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("Agent.get returns undefined for non-existent agent", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
|
||||
59
packages/opencode/test/session/system.test.ts
Normal file
59
packages/opencode/test/session/system.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import path from "path"
|
||||
import { Agent } from "../../src/agent/agent"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { SystemPrompt } from "../../src/session/system"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
|
||||
describe("session.system", () => {
|
||||
test("skills output is sorted by name and stable across calls", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
git: true,
|
||||
init: async (dir) => {
|
||||
for (const [name, description] of [
|
||||
["zeta-skill", "Zeta skill."],
|
||||
["alpha-skill", "Alpha skill."],
|
||||
["middle-skill", "Middle skill."],
|
||||
]) {
|
||||
const skillDir = path.join(dir, ".opencode", "skill", name)
|
||||
await Bun.write(
|
||||
path.join(skillDir, "SKILL.md"),
|
||||
`---
|
||||
name: ${name}
|
||||
description: ${description}
|
||||
---
|
||||
|
||||
# ${name}
|
||||
`,
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const home = process.env.OPENCODE_TEST_HOME
|
||||
process.env.OPENCODE_TEST_HOME = tmp.path
|
||||
|
||||
try {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
const first = await SystemPrompt.skills(build!)
|
||||
const second = await SystemPrompt.skills(build!)
|
||||
|
||||
expect(first).toBe(second)
|
||||
|
||||
const alpha = first!.indexOf("<name>alpha-skill</name>")
|
||||
const middle = first!.indexOf("<name>middle-skill</name>")
|
||||
const zeta = first!.indexOf("<name>zeta-skill</name>")
|
||||
|
||||
expect(alpha).toBeGreaterThan(-1)
|
||||
expect(middle).toBeGreaterThan(alpha)
|
||||
expect(zeta).toBeGreaterThan(middle)
|
||||
},
|
||||
})
|
||||
} finally {
|
||||
process.env.OPENCODE_TEST_HOME = home
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -54,6 +54,56 @@ description: Skill for tool tests.
|
||||
}
|
||||
})
|
||||
|
||||
test("description sorts skills by name and is stable across calls", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
git: true,
|
||||
init: async (dir) => {
|
||||
for (const [name, description] of [
|
||||
["zeta-skill", "Zeta skill."],
|
||||
["alpha-skill", "Alpha skill."],
|
||||
["middle-skill", "Middle skill."],
|
||||
]) {
|
||||
const skillDir = path.join(dir, ".opencode", "skill", name)
|
||||
await Bun.write(
|
||||
path.join(skillDir, "SKILL.md"),
|
||||
`---
|
||||
name: ${name}
|
||||
description: ${description}
|
||||
---
|
||||
|
||||
# ${name}
|
||||
`,
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const home = process.env.OPENCODE_TEST_HOME
|
||||
process.env.OPENCODE_TEST_HOME = tmp.path
|
||||
|
||||
try {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const first = await SkillTool.init()
|
||||
const second = await SkillTool.init()
|
||||
|
||||
expect(first.description).toBe(second.description)
|
||||
|
||||
const alpha = first.description.indexOf("**alpha-skill**: Alpha skill.")
|
||||
const middle = first.description.indexOf("**middle-skill**: Middle skill.")
|
||||
const zeta = first.description.indexOf("**zeta-skill**: Zeta skill.")
|
||||
|
||||
expect(alpha).toBeGreaterThan(-1)
|
||||
expect(middle).toBeGreaterThan(alpha)
|
||||
expect(zeta).toBeGreaterThan(middle)
|
||||
},
|
||||
})
|
||||
} finally {
|
||||
process.env.OPENCODE_TEST_HOME = home
|
||||
}
|
||||
})
|
||||
|
||||
test("execute returns skill content block with files", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
git: true,
|
||||
|
||||
45
packages/opencode/test/tool/task.test.ts
Normal file
45
packages/opencode/test/tool/task.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Agent } from "../../src/agent/agent"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { TaskTool } from "../../src/tool/task"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
|
||||
describe("tool.task", () => {
|
||||
test("description sorts subagents by name and is stable across calls", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
zebra: {
|
||||
description: "Zebra agent",
|
||||
mode: "subagent",
|
||||
},
|
||||
alpha: {
|
||||
description: "Alpha agent",
|
||||
mode: "subagent",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
const first = await TaskTool.init({ agent: build })
|
||||
const second = await TaskTool.init({ agent: build })
|
||||
|
||||
expect(first.description).toBe(second.description)
|
||||
|
||||
const alpha = first.description.indexOf("- alpha: Alpha agent")
|
||||
const explore = first.description.indexOf("- explore:")
|
||||
const general = first.description.indexOf("- general:")
|
||||
const zebra = first.description.indexOf("- zebra: Zebra agent")
|
||||
|
||||
expect(alpha).toBeGreaterThan(-1)
|
||||
expect(explore).toBeGreaterThan(alpha)
|
||||
expect(general).toBeGreaterThan(explore)
|
||||
expect(zebra).toBeGreaterThan(general)
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2845,6 +2845,38 @@ export class File extends HeyApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
export class Event extends HeyApiClient {
|
||||
/**
|
||||
* Subscribe to events
|
||||
*
|
||||
* Get events
|
||||
*/
|
||||
public subscribe<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).sse.get<EventSubscribeResponses, unknown, ThrowOnError>({
|
||||
url: "/event",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Auth2 extends HeyApiClient {
|
||||
/**
|
||||
* Remove MCP OAuth
|
||||
@@ -3866,38 +3898,6 @@ export class Formatter extends HeyApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
export class Event extends HeyApiClient {
|
||||
/**
|
||||
* Subscribe to events
|
||||
*
|
||||
* Get events
|
||||
*/
|
||||
public subscribe<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).sse.get<EventSubscribeResponses, unknown, ThrowOnError>({
|
||||
url: "/event",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class OpencodeClient extends HeyApiClient {
|
||||
public static readonly __registry = new HeyApiRegistry<OpencodeClient>()
|
||||
|
||||
@@ -3981,6 +3981,11 @@ export class OpencodeClient extends HeyApiClient {
|
||||
return (this._file ??= new File({ client: this.client }))
|
||||
}
|
||||
|
||||
private _event?: Event
|
||||
get event(): Event {
|
||||
return (this._event ??= new Event({ client: this.client }))
|
||||
}
|
||||
|
||||
private _mcp?: Mcp
|
||||
get mcp(): Mcp {
|
||||
return (this._mcp ??= new Mcp({ client: this.client }))
|
||||
@@ -4025,9 +4030,4 @@ export class OpencodeClient extends HeyApiClient {
|
||||
get formatter(): Formatter {
|
||||
return (this._formatter ??= new Formatter({ client: this.client }))
|
||||
}
|
||||
|
||||
private _event?: Event
|
||||
get event(): Event {
|
||||
return (this._event ??= new Event({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4229,6 +4229,25 @@ export type FileStatusResponses = {
|
||||
|
||||
export type FileStatusResponse = FileStatusResponses[keyof FileStatusResponses]
|
||||
|
||||
export type EventSubscribeData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/event"
|
||||
}
|
||||
|
||||
export type EventSubscribeResponses = {
|
||||
/**
|
||||
* Event stream
|
||||
*/
|
||||
200: Event
|
||||
}
|
||||
|
||||
export type EventSubscribeResponse = EventSubscribeResponses[keyof EventSubscribeResponses]
|
||||
|
||||
export type McpStatusData = {
|
||||
body?: never
|
||||
path?: never
|
||||
@@ -4979,22 +4998,3 @@ export type FormatterStatusResponses = {
|
||||
}
|
||||
|
||||
export type FormatterStatusResponse = FormatterStatusResponses[keyof FormatterStatusResponses]
|
||||
|
||||
export type EventSubscribeData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/event"
|
||||
}
|
||||
|
||||
export type EventSubscribeResponses = {
|
||||
/**
|
||||
* Event stream
|
||||
*/
|
||||
200: Event
|
||||
}
|
||||
|
||||
export type EventSubscribeResponse = EventSubscribeResponses[keyof EventSubscribeResponses]
|
||||
|
||||
@@ -5243,6 +5243,47 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/event": {
|
||||
"get": {
|
||||
"operationId": "event.subscribe",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "directory",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "workspace",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "Subscribe to events",
|
||||
"description": "Get events",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Event stream",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Event"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.event.subscribe({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/mcp": {
|
||||
"get": {
|
||||
"operationId": "mcp.status",
|
||||
@@ -6894,47 +6935,6 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/event": {
|
||||
"get": {
|
||||
"operationId": "event.subscribe",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "directory",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "workspace",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "Subscribe to events",
|
||||
"description": "Get events",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Event stream",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Event"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.event.subscribe({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
Reference in New Issue
Block a user