refactor(tui): remove legacy sdk client
This commit is contained in:
parent
4c45a5cf23
commit
36f8cb7054
36 changed files with 316 additions and 510 deletions
|
|
@ -1,14 +1,19 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import Notifications from "../../../../src/feature-plugins/system/notifications"
|
||||
import type { OpenCodeEvent, PermissionAsked, QuestionAsked } from "@opencode-ai/client"
|
||||
import type { Session } from "@opencode-ai/sdk/v2"
|
||||
import type { TuiAttentionNotifyInput } from "@opencode-ai/plugin/tui"
|
||||
import type { TuiAttentionNotifyInput, TuiPluginApi } from "@opencode-ai/plugin/tui"
|
||||
import { createTuiPluginApi } from "../../../fixture/tui-plugin"
|
||||
|
||||
type Session = NonNullable<ReturnType<TuiPluginApi["state"]["session"]["get"]>>
|
||||
|
||||
async function setup() {
|
||||
const notifications: TuiAttentionNotifyInput[] = []
|
||||
const handlers = new Map<OpenCodeEvent["type"], ((event: OpenCodeEvent) => void)[]>()
|
||||
const session = (id: string, title: string, parentID?: string): Session => ({
|
||||
const session = (
|
||||
id: string,
|
||||
title: string,
|
||||
parentID?: string,
|
||||
): Session => ({
|
||||
id,
|
||||
title,
|
||||
slug: id,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import { SessionMessage } from "@opencode-ai/core/session/message"
|
|||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { onMount } from "solid-js"
|
||||
import { ProjectProvider } from "../../../src/context/project"
|
||||
import { SDKProvider, useSDK } from "../../../src/context/sdk"
|
||||
import { ClientProvider, useClient } from "../../../src/context/client"
|
||||
import { DataProvider, useData } from "../../../src/context/data"
|
||||
import { createSessionRows, type SessionRow } from "../../../src/routes/session/rows"
|
||||
import { createApi, createClient, createEventStream, createFetch, directory, json } from "../../fixture/tui-sdk"
|
||||
import { createApi, createEventStream, createFetch, directory, json } from "../../fixture/tui-client"
|
||||
import { TestTuiContexts } from "../../fixture/tui-environment"
|
||||
|
||||
const formFields = [{ key: "authorization", type: "external", url: "https://example.com" }] satisfies [
|
||||
|
|
@ -90,13 +90,13 @@ test("refreshes resources into reactive getters", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -148,13 +148,13 @@ test("applies absolute usage events to session info", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -233,13 +233,13 @@ test("truncates committed revert messages without changing lifetime usage", asyn
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -371,13 +371,13 @@ test("updates session location when moved", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -430,13 +430,13 @@ test("restores running manual compaction before applying live deltas", async ()
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -514,23 +514,23 @@ test("reconnects the event stream and bootstraps fresh data", async () => {
|
|||
})
|
||||
}, events)
|
||||
let data!: ReturnType<typeof useData>
|
||||
let sdk!: ReturnType<typeof useSDK>
|
||||
let client!: ReturnType<typeof useClient>
|
||||
|
||||
function Probe() {
|
||||
data = useData()
|
||||
sdk = useSDK()
|
||||
client = useClient()
|
||||
return <box />
|
||||
}
|
||||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -539,15 +539,15 @@ test("reconnects the event stream and bootstraps fresh data", async () => {
|
|||
await wait(() => data.session.status("session-stale") === "running")
|
||||
await data.session.message.refresh("session-stale")
|
||||
expect(data.session.message.get("session-stale", "message-stale")?.id).toBe("message-stale")
|
||||
expect(sdk.connection.status()).toBe("connected")
|
||||
expect(sdk.connection.attempt()).toBe(0)
|
||||
expect(client.connection.status()).toBe("connected")
|
||||
expect(client.connection.attempt()).toBe(0)
|
||||
|
||||
events.disconnect()
|
||||
await wait(() => sdk.connection.status() === "reconnecting")
|
||||
expect(sdk.connection.attempt()).toBe(1)
|
||||
expect(sdk.connection.error()).toBe("Event stream disconnected")
|
||||
await wait(() => client.connection.status() === "reconnecting")
|
||||
expect(client.connection.attempt()).toBe(1)
|
||||
expect(client.connection.error()).toBe("Event stream disconnected")
|
||||
|
||||
await wait(() => requests.active === 2 && sdk.connection.status() === "connected", 4000)
|
||||
await wait(() => requests.active === 2 && client.connection.status() === "connected", 4000)
|
||||
resolveActive(json({ data: { "session-new": { type: "running" } } }))
|
||||
|
||||
await wait(() => data.location.model.list()?.[0]?.id === "model-2", 4000)
|
||||
|
|
@ -565,9 +565,9 @@ test("reconnects the event stream and bootstraps fresh data", async () => {
|
|||
await wait(() => data.session.status("session-new") === "running")
|
||||
expect(requests.event).toBe(2)
|
||||
expect(requests.message).toBe(2)
|
||||
expect(sdk.connection.status()).toBe("connected")
|
||||
expect(sdk.connection.attempt()).toBe(0)
|
||||
expect(sdk.connection.error()).toBeUndefined()
|
||||
expect(client.connection.status()).toBe("connected")
|
||||
expect(client.connection.attempt()).toBe(0)
|
||||
expect(client.connection.error()).toBeUndefined()
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
|
|
@ -588,13 +588,13 @@ test("completes exploration when a queued prompt is promoted", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -668,13 +668,13 @@ test("removes committed revert messages from local state", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -733,34 +733,34 @@ test("distinguishes initial connection from reconnection", async () => {
|
|||
const calls = createFetch((url) => {
|
||||
if (url.pathname === "/api/event") return eventResponse()
|
||||
})
|
||||
let sdk!: ReturnType<typeof useSDK>
|
||||
let client!: ReturnType<typeof useClient>
|
||||
|
||||
function Probe() {
|
||||
sdk = useSDK()
|
||||
client = useClient()
|
||||
return <box />
|
||||
}
|
||||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
try {
|
||||
await wait(() => stream !== undefined)
|
||||
expect(sdk.connection.status()).toBe("connecting")
|
||||
expect(client.connection.status()).toBe("connecting")
|
||||
|
||||
connect()
|
||||
await wait(() => sdk.connection.status() === "connected")
|
||||
await wait(() => client.connection.status() === "connected")
|
||||
|
||||
disconnect()
|
||||
await wait(() => sdk.connection.status() === "reconnecting")
|
||||
await wait(() => client.connection.status() === "reconnecting")
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
|
|
@ -811,13 +811,13 @@ test("tracks session status from active sessions and execution events", async ()
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1180,13 +1180,13 @@ test("restores queued compaction from durable pending input", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1295,13 +1295,13 @@ test("refreshes integrations after integration updates", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1351,13 +1351,13 @@ test("refreshes MCP resources after catalog updates", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1400,13 +1400,13 @@ test("refreshes effective catalog data after catalog updates", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<box />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1448,13 +1448,13 @@ test("refreshes agents after agent updates", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1492,13 +1492,13 @@ test("refreshes references after updates", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1547,13 +1547,13 @@ test("keeps shell state scoped to location", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1593,28 +1593,28 @@ test("adds and dismisses permission requests from live events", async () => {
|
|||
const events = createEventStream()
|
||||
const calls = createFetch(undefined, events)
|
||||
let data!: ReturnType<typeof useData>
|
||||
let sdk!: ReturnType<typeof useSDK>
|
||||
let client!: ReturnType<typeof useClient>
|
||||
|
||||
function Probe() {
|
||||
data = useData()
|
||||
sdk = useSDK()
|
||||
client = useClient()
|
||||
return <box />
|
||||
}
|
||||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
try {
|
||||
await wait(() => sdk.connection.status() === "connected")
|
||||
await wait(() => client.connection.status() === "connected")
|
||||
emitEvent(events, {
|
||||
id: "evt_permission_asked_1",
|
||||
created: 0,
|
||||
|
|
@ -1681,13 +1681,13 @@ test("reconciles all pending permission requests when the event stream reconnect
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(fetch.fetch)} api={createApi(fetch.fetch)}>
|
||||
<ClientProvider api={createApi(fetch.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -1715,28 +1715,28 @@ test("adds, dismisses, and refreshes form requests", async () => {
|
|||
})
|
||||
}, events)
|
||||
let data!: ReturnType<typeof useData>
|
||||
let sdk!: ReturnType<typeof useSDK>
|
||||
let client!: ReturnType<typeof useClient>
|
||||
|
||||
function Probe() {
|
||||
data = useData()
|
||||
sdk = useSDK()
|
||||
client = useClient()
|
||||
return <box />
|
||||
}
|
||||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
try {
|
||||
await wait(() => sdk.connection.status() === "connected")
|
||||
await wait(() => client.connection.status() === "connected")
|
||||
emitEvent(events, {
|
||||
id: "evt_form_created_1",
|
||||
created: 0,
|
||||
|
|
@ -1785,28 +1785,28 @@ test("tracks global forms by location", async () => {
|
|||
const calls = createFetch(undefined, events)
|
||||
const other = { directory: "/tmp/opencode-other", workspaceID: "wrk_other" }
|
||||
let data!: ReturnType<typeof useData>
|
||||
let sdk!: ReturnType<typeof useSDK>
|
||||
let client!: ReturnType<typeof useClient>
|
||||
|
||||
function Probe() {
|
||||
data = useData()
|
||||
sdk = useSDK()
|
||||
client = useClient()
|
||||
return <box />
|
||||
}
|
||||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
try {
|
||||
await wait(() => sdk.connection.status() === "connected")
|
||||
await wait(() => client.connection.status() === "connected")
|
||||
events.emit({
|
||||
id: "evt_form_created_global_other",
|
||||
created: 0,
|
||||
|
|
@ -1871,28 +1871,28 @@ test("refreshes global forms for the requested location", async () => {
|
|||
})
|
||||
}, events)
|
||||
let data!: ReturnType<typeof useData>
|
||||
let sdk!: ReturnType<typeof useSDK>
|
||||
let client!: ReturnType<typeof useClient>
|
||||
|
||||
function Probe() {
|
||||
data = useData()
|
||||
sdk = useSDK()
|
||||
client = useClient()
|
||||
return <box />
|
||||
}
|
||||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
try {
|
||||
await wait(() => sdk.connection.status() === "connected" && requests.length > 0)
|
||||
await wait(() => client.connection.status() === "connected" && requests.length > 0)
|
||||
requests.length = 0
|
||||
|
||||
await data.session.form.refresh("global", { directory })
|
||||
|
|
@ -1957,13 +1957,13 @@ test("refreshes global forms once per loaded location after reconnect", async ()
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -2025,13 +2025,13 @@ test("reconciles all pending form requests when the event stream reconnects", as
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(fetch.fetch)} api={createApi(fetch.fetch)}>
|
||||
<ClientProvider api={createApi(fetch.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -2078,13 +2078,13 @@ test("settles pending tools when a live failure arrives", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -2225,13 +2225,13 @@ test("renders admitted prompts immediately and tracks them until promoted", asyn
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -2318,13 +2318,13 @@ test("projects live instruction updates with their message ID", async () => {
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
|
|
@ -2387,13 +2387,13 @@ async function mountData(parents: Record<string, string>, costs: Record<string,
|
|||
}
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ClientProvider api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
await mounted
|
||||
|
|
|
|||
|
|
@ -4,17 +4,16 @@ import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui"
|
|||
import { DiffRenderable, type Renderable, ScrollBoxRenderable } from "@opentui/core"
|
||||
import { testRender, useRenderer } from "@opentui/solid"
|
||||
import type { TuiPluginApi, TuiPluginMeta, TuiRouteCurrent, TuiRouteDefinition } from "@opencode-ai/plugin/tui"
|
||||
import type { Session } from "@opencode-ai/sdk/v2"
|
||||
import { ThemeProvider } from "../../../src/context/theme"
|
||||
import { ConfigProvider } from "../../../src/config"
|
||||
import { SDKProvider } from "../../../src/context/sdk"
|
||||
import { ClientProvider } from "../../../src/context/client"
|
||||
import { TuiKeybind } from "../../../src/config/keybind"
|
||||
import { OpencodeKeymapProvider } from "../../../src/keymap"
|
||||
import diffViewerPlugin from "../../../src/feature-plugins/system/diff-viewer"
|
||||
import { createTuiPluginApi } from "../../fixture/tui-plugin"
|
||||
import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
|
||||
import { TestTuiContexts } from "../../fixture/tui-environment"
|
||||
import { createApi, createClient, createEventStream, createFetch, json } from "../../fixture/tui-sdk"
|
||||
import { createApi, createEventStream, createFetch, json } from "../../fixture/tui-client"
|
||||
|
||||
test("closing the diff viewer returns to the route it opened from", async () => {
|
||||
const viewer = await renderDiffViewer([])
|
||||
|
|
@ -111,7 +110,6 @@ async function renderDiffViewer(vcsDiff: unknown[], height = 20, initialRoute?:
|
|||
let current = initialRoute ?? startRoute
|
||||
let renderDiff: TuiRouteDefinition["render"] | undefined
|
||||
let vcsDiffInput: unknown
|
||||
let sessionDiffInput: unknown
|
||||
const config = createTuiResolvedConfig()
|
||||
const transport = createFetch((url) => {
|
||||
if (url.pathname !== "/api/vcs/diff") return
|
||||
|
|
@ -135,14 +133,6 @@ async function renderDiffViewer(vcsDiff: unknown[], height = 20, initialRoute?:
|
|||
}
|
||||
const base = createTuiPluginApi({
|
||||
keymap,
|
||||
client: {
|
||||
session: {
|
||||
diff: async (input: unknown) => {
|
||||
sessionDiffInput = input
|
||||
return { data: [] }
|
||||
},
|
||||
},
|
||||
} as unknown as TuiPluginApi["client"],
|
||||
state: {
|
||||
session: {
|
||||
get: () => session,
|
||||
|
|
@ -170,7 +160,7 @@ async function renderDiffViewer(vcsDiff: unknown[], height = 20, initialRoute?:
|
|||
|
||||
return (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(transport.fetch)} api={createApi(transport.fetch)}>
|
||||
<ClientProvider api={createApi(transport.fetch)}>
|
||||
<OpencodeKeymapProvider keymap={keymap}>
|
||||
<ConfigProvider config={config}>
|
||||
<ThemeProvider mode="dark">
|
||||
|
|
@ -178,7 +168,7 @@ async function renderDiffViewer(vcsDiff: unknown[], height = 20, initialRoute?:
|
|||
</ThemeProvider>
|
||||
</ConfigProvider>
|
||||
</OpencodeKeymapProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
)
|
||||
}
|
||||
|
|
@ -190,7 +180,6 @@ async function renderDiffViewer(vcsDiff: unknown[], height = 20, initialRoute?:
|
|||
commands,
|
||||
current: () => current,
|
||||
vcsDiffInput: () => vcsDiffInput,
|
||||
sessionDiffInput: () => sessionDiffInput,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +206,7 @@ const session = {
|
|||
created: 0,
|
||||
updated: 0,
|
||||
},
|
||||
} satisfies Session
|
||||
} satisfies NonNullable<ReturnType<TuiPluginApi["state"]["session"]["get"]>>
|
||||
|
||||
test("branch diff source requests branch VCS diff", async () => {
|
||||
const viewer = await renderDiffViewer([], 20, {
|
||||
|
|
@ -234,24 +223,6 @@ test("branch diff source requests branch VCS diff", async () => {
|
|||
mode: "branch",
|
||||
context: "12",
|
||||
})
|
||||
expect(viewer.sessionDiffInput()).toBeUndefined()
|
||||
} finally {
|
||||
viewer.app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
test("last-turn diff source requests session diff", async () => {
|
||||
const viewer = await renderDiffViewer([], 20, {
|
||||
name: "diff",
|
||||
params: { mode: "last-turn", sessionID: "session-1", messageID: "message-1", returnRoute: startRoute },
|
||||
})
|
||||
try {
|
||||
expect(viewer.current()).toEqual({
|
||||
name: "diff",
|
||||
params: { mode: "last-turn", sessionID: "session-1", messageID: "message-1", returnRoute: startRoute },
|
||||
})
|
||||
expect(viewer.sessionDiffInput()).toEqual({ sessionID: "session-1", messageID: "message-1" })
|
||||
expect(viewer.vcsDiffInput()).toBeUndefined()
|
||||
} finally {
|
||||
viewer.app.renderer.destroy()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import path from "node:path"
|
|||
import { onCleanup } from "solid-js"
|
||||
import { ClipboardProvider } from "../../../src/context/clipboard"
|
||||
import type { FormWithLocation } from "../../../src/context/data"
|
||||
import { SDKProvider } from "../../../src/context/sdk"
|
||||
import { ClientProvider } from "../../../src/context/client"
|
||||
import { ThemeProvider } from "../../../src/context/theme"
|
||||
import { ConfigProvider } from "../../../src/config"
|
||||
import { OpencodeKeymapProvider, registerOpencodeKeymap } from "../../../src/keymap"
|
||||
|
|
@ -15,7 +15,7 @@ import { ToastProvider } from "../../../src/ui/toast"
|
|||
import { tmpdir } from "../../fixture/fixture"
|
||||
import { TestTuiContexts } from "../../fixture/tui-environment"
|
||||
import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
|
||||
import { createApi, createClient, createEventStream, createFetch } from "../../fixture/tui-sdk"
|
||||
import { createApi, createEventStream, createFetch } from "../../fixture/tui-client"
|
||||
|
||||
async function mountForm(root: string, width = 80) {
|
||||
const state = path.join(root, "state")
|
||||
|
|
@ -75,13 +75,13 @@ async function mountForm(root: string, width = 80) {
|
|||
>
|
||||
<OpencodeKeymapProvider keymap={keymap}>
|
||||
<ConfigProvider config={config}>
|
||||
<SDKProvider client={createClient(transport.fetch)} api={createApi(transport.fetch)}>
|
||||
<ClientProvider api={createApi(transport.fetch)}>
|
||||
<ThemeProvider mode="dark" source={{ discover: () => Promise.resolve({}) }}>
|
||||
<ToastProvider>
|
||||
<FormPrompt form={form} />
|
||||
</ToastProvider>
|
||||
</ThemeProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</ConfigProvider>
|
||||
</OpencodeKeymapProvider>
|
||||
</ClipboardProvider>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { describe, expect, test } from "bun:test"
|
|||
// Before the fix, two concurrent `submit()` calls (e.g. a double-pressed
|
||||
// Enter, or the input's native onSubmit racing another dispatch) each
|
||||
// passed the `if (!store.prompt.text) return false` guard, each
|
||||
// `await sdk.client.session.create(...)`, and each only captured
|
||||
// `await client.api.session.create(...)`, and each only captured
|
||||
// `inputText = store.prompt.text` AFTER that await. The first invocation
|
||||
// finished, sent the prompt, and cleared the store; the second invocation,
|
||||
// now past its await, read the cleared store and sent an empty prompt to a
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import type { OpenCodeClient, OpenCodeEvent } from "@opencode-ai/client"
|
||||
import { testRender } from "@opentui/solid"
|
||||
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
|
||||
import { onMount } from "solid-js"
|
||||
import { ProjectProvider, useProject } from "../../../src/context/project"
|
||||
import { SDKProvider, useSDK } from "../../../src/context/sdk"
|
||||
import { ClientProvider, useClient } from "../../../src/context/client"
|
||||
import { useEvent } from "../../../src/context/event"
|
||||
import { createApi, createClient, createEventStream, createFetch } from "../../fixture/tui-sdk"
|
||||
import { createApi, createEventStream, createFetch } from "../../fixture/tui-client"
|
||||
import { TestTuiContexts } from "../../fixture/tui-environment"
|
||||
import type { LogLevel, LogSink } from "../../../src/context/log"
|
||||
|
||||
|
|
@ -54,7 +53,7 @@ function update(version: string): OpenCodeEvent {
|
|||
}
|
||||
|
||||
async function mount(
|
||||
reconnect?: (attempt: number) => Promise<{ client: OpencodeClient; api: OpenCodeClient }>,
|
||||
reconnect?: (attempt: number) => Promise<{ api: OpenCodeClient }>,
|
||||
log?: LogSink,
|
||||
) {
|
||||
const events = createEventStream()
|
||||
|
|
@ -62,7 +61,7 @@ async function mount(
|
|||
const seen: OpenCodeEvent[] = []
|
||||
const workspaces: Array<string | undefined> = []
|
||||
let project!: ReturnType<typeof useProject>
|
||||
let sdk!: ReturnType<typeof useSDK>
|
||||
let client!: ReturnType<typeof useClient>
|
||||
let done!: () => void
|
||||
const ready = new Promise<void>((resolve) => {
|
||||
done = resolve
|
||||
|
|
@ -70,12 +69,12 @@ async function mount(
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts log={log}>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)} reconnect={reconnect}>
|
||||
<ClientProvider api={createApi(calls.fetch)} reconnect={reconnect}>
|
||||
<ProjectProvider>
|
||||
<Probe
|
||||
onReady={async (ctx) => {
|
||||
project = ctx.project
|
||||
sdk = ctx.sdk
|
||||
client = ctx.client
|
||||
await project.sync()
|
||||
done()
|
||||
}}
|
||||
|
|
@ -83,21 +82,21 @@ async function mount(
|
|||
workspaces={workspaces}
|
||||
/>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</ClientProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
await ready
|
||||
return { app, events, emit: events.emit, project, sdk, seen, workspaces }
|
||||
return { app, events, emit: events.emit, project, client, seen, workspaces }
|
||||
}
|
||||
|
||||
function Probe(props: {
|
||||
seen: OpenCodeEvent[]
|
||||
workspaces: Array<string | undefined>
|
||||
onReady: (ctx: { project: ReturnType<typeof useProject>; sdk: ReturnType<typeof useSDK> }) => void
|
||||
onReady: (ctx: { project: ReturnType<typeof useProject>; client: ReturnType<typeof useClient> }) => void
|
||||
}) {
|
||||
const project = useProject()
|
||||
const sdk = useSDK()
|
||||
const client = useClient()
|
||||
const event = useEvent()
|
||||
|
||||
onMount(() => {
|
||||
|
|
@ -105,7 +104,7 @@ function Probe(props: {
|
|||
props.seen.push(evt)
|
||||
props.workspaces.push(workspace)
|
||||
})
|
||||
props.onReady({ project, sdk })
|
||||
props.onReady({ project, client })
|
||||
})
|
||||
|
||||
return <box />
|
||||
|
|
@ -137,7 +136,7 @@ describe("useEvent", () => {
|
|||
{
|
||||
level: "debug",
|
||||
message: "event",
|
||||
tags: { component: "sdk", type: "session.renamed", aggregateID: "ses_test", seq: 1 },
|
||||
tags: { component: "client", type: "session.renamed", aggregateID: "ses_test", seq: 1 },
|
||||
},
|
||||
])
|
||||
} finally {
|
||||
|
|
@ -194,25 +193,24 @@ describe("useEvent", () => {
|
|||
const attempts: number[] = []
|
||||
const replacementEvents = createEventStream()
|
||||
const replacementCalls = createFetch(undefined, replacementEvents)
|
||||
const replacement = { client: createClient(replacementCalls.fetch), api: createApi(replacementCalls.fetch) }
|
||||
const { app, events, sdk, seen } = await mount(async (attempt) => {
|
||||
const replacement = { api: createApi(replacementCalls.fetch) }
|
||||
const { app, events, client, seen } = await mount(async (attempt) => {
|
||||
attempts.push(attempt)
|
||||
return replacement
|
||||
})
|
||||
|
||||
try {
|
||||
await wait(() => sdk.connection.status() === "connected")
|
||||
await wait(() => client.connection.status() === "connected")
|
||||
// Reconnection only runs when the stream is down, never while connected.
|
||||
expect(attempts).toEqual([])
|
||||
events.disconnect()
|
||||
await wait(() => sdk.connection.status() === "connected" && attempts.length > 0)
|
||||
await wait(() => client.connection.status() === "connected" && attempts.length > 0)
|
||||
replacementEvents.emit(event(vcs("rediscovered"), { directory: "/tmp/rediscovered" }))
|
||||
await wait(() => seen.some((item) => item.type === "vcs.branch.updated" && item.data.branch === "rediscovered"))
|
||||
|
||||
expect(sdk.client).toBe(replacement.client)
|
||||
expect(sdk.api).toBe(replacement.api)
|
||||
expect(client.api).toBe(replacement.api)
|
||||
expect(attempts).toEqual([1])
|
||||
const history = sdk.connection.internal.history()
|
||||
const history = client.connection.internal.history()
|
||||
expect(history.map((event) => [event.data.status, event.data.attempt])).toEqual([
|
||||
["connecting", 0],
|
||||
["connected", 0],
|
||||
|
|
@ -228,22 +226,22 @@ describe("useEvent", () => {
|
|||
|
||||
test("keeps the current client when reconnection fails", async () => {
|
||||
let calls = 0
|
||||
const { app, events, sdk, seen } = await mount(async () => {
|
||||
const { app, events, client, seen } = await mount(async () => {
|
||||
calls += 1
|
||||
throw new Error("no server")
|
||||
})
|
||||
|
||||
try {
|
||||
await wait(() => sdk.connection.status() === "connected")
|
||||
const original = sdk.client
|
||||
await wait(() => client.connection.status() === "connected")
|
||||
const original = client.api
|
||||
events.disconnect()
|
||||
// Reconnection rejects; the loop retries against the last known transport,
|
||||
// which succeeds once the fixture accepts the reconnect.
|
||||
await wait(() => calls > 0 && sdk.connection.status() === "connected")
|
||||
await wait(() => calls > 0 && client.connection.status() === "connected")
|
||||
events.emit(event(vcs("recovered"), { directory: "/tmp/recovered" }))
|
||||
await wait(() => seen.some((item) => item.type === "vcs.branch.updated" && item.data.branch === "recovered"))
|
||||
|
||||
expect(sdk.client).toBe(original)
|
||||
expect(client.api).toBe(original)
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue