diff --git a/V2_HTTP_API_AUDIT.md b/V2_HTTP_API_AUDIT.md index e5eebac3c59..2662689fb4e 100644 --- a/V2_HTTP_API_AUDIT.md +++ b/V2_HTTP_API_AUDIT.md @@ -1,7 +1,7 @@ # V2 HTTP API audit checklist **Source:** `packages/protocol/openapi.json` -**Current endpoint count:** 140 +**Current endpoint count:** 139 **Last regenerated:** 2026-09-13 ## How to use this checklist @@ -29,7 +29,7 @@ Review endpoints in document order. For each endpoint, select one disposition an ## Progress - [x] Group 1: Foundation and placement (4) -- [ ] Group 2: Configuration and capability catalogs (17) +- [ ] Group 2: Configuration and capability catalogs (16) - [ ] Group 3: Credentials, integrations, MCP, and web search (22) - [ ] Group 4: Session lifecycle (12) - [ ] Group 5: Session execution and inputs (11) @@ -41,6 +41,11 @@ Review endpoints in document order. For each endpoint, select one disposition an ## Resolved during audit +### [x] `POST /api/plugin/await-activation` + +- **Decision:** Remove +- **Notes:** Activation timing is an internal server concern. Catalog reads remain non-blocking. + ### [x] Location response wrappers - **Decision:** Reduce generic endpoint response locations to `{ directory }`. @@ -76,14 +81,13 @@ Review endpoints in document order. For each endpoint, select one disposition an ## Group 2: Configuration and capability catalogs -**Endpoints:** 17 +**Endpoints:** 16 | Done | Method | Path | Operation ID | Decision | Notes | |---|---|---|---|---|---| -| [ ] 008 | `GET` | `/api/agent` | `agent.list` | | | -| [ ] 009 | `GET` | `/api/agent/{agentID}` | `agent.get` | | | -| [ ] 010 | `GET` | `/api/plugin` | `plugin.list` | | | -| [ ] 011 | `POST` | `/api/plugin/await-activation` | `plugin.awaitActivation` | | | +| [x] 008 | `GET` | `/api/agent` | `agent.list` | Keep | Request and response accepted as-is. | +| [x] 009 | `GET` | `/api/agent/{agentID}` | `agent.get` | Keep | Request, response, and not-found error accepted as-is. | +| [x] 010 | `GET` | `/api/plugin` | `plugin.list` | Keep | Request and response accepted as-is. | | [ ] 012 | `POST` | `/api/plugin/check` | `plugin.check` | | | | [ ] 013 | `POST` | `/api/plugin/update` | `plugin.update` | | | | [ ] 014 | `GET` | `/api/model` | `model.list` | | | diff --git a/packages/cli/src/acp/service.ts b/packages/cli/src/acp/service.ts index c3182b56a9e..7d90922fd45 100644 --- a/packages/cli/src/acp/service.ts +++ b/packages/cli/src/acp/service.ts @@ -408,8 +408,7 @@ function turnStart(messageID: string, slash: PreparedPrompt["slash"], skill: Ski async function loadCatalog(client: OpenCodeClient, cwd: string): Promise { const location = { directory: cwd } - await client.plugin.awaitActivation({ location }) - // Some providers discover models in the background after activation has settled. + // Some providers discover models in the background after plugin startup begins. const deadline = Date.now() + 5_000 let missing = "No models are available" while (Date.now() < deadline) { diff --git a/packages/cli/test/acp/service-directory.test.ts b/packages/cli/test/acp/service-directory.test.ts index 757a812f688..3273bd51c69 100644 --- a/packages/cli/test/acp/service-directory.test.ts +++ b/packages/cli/test/acp/service-directory.test.ts @@ -4,47 +4,6 @@ import { makeACPFixture, makeSession, secondModel, testModel } from "./service-f import { flattenSelectOptions, requireSelectOption } from "./subprocess" describe("acp service directory behavior", () => { - test("does not cache an available model before plugin activation settles", async () => { - const requested = Promise.withResolvers() - const release = Promise.withResolvers() - let ready = false - await using fixture = makeACPFixture({ - fetch(request) { - requested.resolve() - if (request.path === "/api/plugin/await-activation") { - return release.promise.then(() => { - ready = true - return new Response(null, { status: 204 }) - }) - } - if (!ready && request.path === "/api/model") { - return Response.json({ data: [{ ...testModel, providerID: "ambient" }] }) - } - if (!ready && request.path === "/api/model/default") { - return Response.json({ data: { ...testModel, providerID: "ambient" } }) - } - if (request.path === "/api/session" && request.method === "POST") { - return Response.json({ data: { ...makeSession("ses_ready"), model: undefined } }) - } - return undefined - }, - }) - const pending = fixture.service.newSession({ cwd: "/workspace", mcpServers: [] }) - try { - await requested.promise - expect(fixture.requests.map((request) => request.path)).toEqual(["/api/plugin/await-activation"]) - expect(fixture.requests[0]?.query["location[directory]"]).toBe("/workspace") - release.resolve() - expect(currentValue(await pending, "model")).toBe("test/test-model") - expect( - fixture.requests.find((request) => request.path === "/api/session" && request.method === "POST")?.body, - ).toMatchObject({ model: { providerID: "test", id: "test-model" } }) - } finally { - release.resolve() - await pending.catch(() => {}) - } - }) - test("creates sessions from a catalog shared by concurrent callers in the same cwd", async () => { let created = 0 await using fixture = makeACPFixture({ @@ -69,7 +28,6 @@ describe("acp service directory behavior", () => { expect(currentValue(first[0], "mode")).toBe("build") expect( [ - "/api/plugin/await-activation", "/api/model", "/api/model/default", "/api/agent", @@ -86,7 +44,6 @@ describe("acp service directory behavior", () => { ["/workspace", "/other"], ["/workspace", "/other"], ["/workspace", "/other"], - ["/workspace", "/other"], ]) expect( fixture.requests diff --git a/packages/cli/test/acp/service-fixture.ts b/packages/cli/test/acp/service-fixture.ts index f690def4584..a3e97794d99 100644 --- a/packages/cli/test/acp/service-fixture.ts +++ b/packages/cli/test/acp/service-fixture.ts @@ -152,7 +152,6 @@ export function makeACPFixture(options: FixtureOptions = {}) { const directory = request.query["location[directory]"] ?? "/workspace" const location = { directory, project: { id: "global", directory } } - if (request.path === "/api/plugin/await-activation") return new Response(null, { status: 204 }) if (request.path === "/api/event") { let controller: ReadableStreamDefaultController | undefined return new Response( diff --git a/packages/cli/test/acp/service.test.ts b/packages/cli/test/acp/service.test.ts index 30ca6dab80d..aa6520c2103 100644 --- a/packages/cli/test/acp/service.test.ts +++ b/packages/cli/test/acp/service.test.ts @@ -18,7 +18,6 @@ describe("acp service", () => { body: request.method === "GET" ? undefined : await request.json().catch(() => undefined), }) const location = { directory: "/workspace", project: { id: "global", directory: "/workspace" } } - if (url.pathname === "/api/plugin/await-activation") return new Response(null, { status: 204 }) if (url.pathname === "/api/model") return Response.json({ location, data: [model] }) if (url.pathname === "/api/model/default") return Response.json({ location, data: model }) if (url.pathname === "/api/agent") return Response.json({ location, data: [agent] }) diff --git a/packages/client/src/effect/api/api.ts b/packages/client/src/effect/api/api.ts index b3003ad35b2..ae0dc2a692f 100644 --- a/packages/client/src/effect/api/api.ts +++ b/packages/client/src/effect/api/api.ts @@ -78,12 +78,6 @@ export type PluginListInput = { readonly location?: { readonly directory?: strin export type PluginListOutput = { readonly location: Location.PublicRef; readonly data: ReadonlyArray } export type PluginListOperation = (input?: PluginListInput) => Effect.Effect -export type PluginAwaitActivationInput = { readonly location?: { readonly directory?: string | undefined } | undefined } -export type PluginAwaitActivationOutput = void -export type PluginAwaitActivationOperation = ( - input?: PluginAwaitActivationInput, -) => Effect.Effect - export type PluginCheckInput = { readonly location?: { readonly directory?: string | undefined } | undefined readonly target?: string | undefined @@ -100,7 +94,6 @@ export type PluginUpdateOperation = (input: PluginUpdateInput) => Eff export interface PluginApi { readonly list: PluginListOperation - readonly awaitActivation: PluginAwaitActivationOperation readonly check: PluginCheckOperation readonly update: PluginUpdateOperation } diff --git a/packages/client/src/effect/generated/client.ts b/packages/client/src/effect/generated/client.ts index 655c45d7885..c09ed0a7a56 100644 --- a/packages/client/src/effect/generated/client.ts +++ b/packages/client/src/effect/generated/client.ts @@ -14,8 +14,6 @@ import type { AgentGetOutput, PluginListInput, PluginListOutput, - PluginAwaitActivationInput, - PluginAwaitActivationOutput, PluginCheckInput, PluginCheckOutput, PluginUpdateInput, @@ -322,11 +320,6 @@ const EndpointPluginList = (raw: RawClient["server.plugin"]) => (input?: PluginL raw["plugin.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError)), ) -const EndpointPluginAwaitActivation = (raw: RawClient["server.plugin"]) => (input?: PluginAwaitActivationInput) => - preserveEffect()( - raw["plugin.awaitActivation"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError)), - ) - const EndpointPluginCheck = (raw: RawClient["server.plugin"]) => (input?: PluginCheckInput) => preserveEffect()( raw["plugin.check"]({ query: { location: input?.["location"] }, payload: { target: input?.["target"] } }).pipe( @@ -343,7 +336,6 @@ const EndpointPluginUpdate = (raw: RawClient["server.plugin"]) => (input: Plugin const adaptGroupPlugin = (raw: RawClient["server.plugin"]) => ({ list: EndpointPluginList(raw), - awaitActivation: EndpointPluginAwaitActivation(raw), check: EndpointPluginCheck(raw), update: EndpointPluginUpdate(raw), }) diff --git a/packages/client/src/promise/generated/client.ts b/packages/client/src/promise/generated/client.ts index 0ed55398b14..63c07025ffa 100644 --- a/packages/client/src/promise/generated/client.ts +++ b/packages/client/src/promise/generated/client.ts @@ -8,8 +8,6 @@ import type { AgentGetOutput, PluginListInput, PluginListOutput, - PluginAwaitActivationInput, - PluginAwaitActivationOutput, PluginCheckInput, PluginCheckOutput, PluginUpdateInput, @@ -467,18 +465,6 @@ export function make(options: ClientOptions) { }, requestOptions, ), - awaitActivation: (input?: PluginAwaitActivationInput, requestOptions?: RequestOptions) => - request( - { - method: "POST", - path: `/api/plugin/await-activation`, - query: { location: input?.["location"] }, - successStatus: 204, - declaredStatuses: [400, 401], - empty: true, - }, - requestOptions, - ), check: (input?: PluginCheckInput, requestOptions?: RequestOptions) => request( { diff --git a/packages/client/src/promise/generated/types.ts b/packages/client/src/promise/generated/types.ts index 0c4c5e0a4f0..2a2e4f4fbbb 100644 --- a/packages/client/src/promise/generated/types.ts +++ b/packages/client/src/promise/generated/types.ts @@ -2625,12 +2625,6 @@ export type PluginListInput = { export type PluginListOutput = { location: LocationPublicRef; data: Array } -export type PluginAwaitActivationInput = { - readonly location?: { readonly location?: { readonly directory?: string | undefined } | undefined }["location"] -} - -export type PluginAwaitActivationOutput = void - export type PluginCheckInput = { readonly location?: { readonly location?: { readonly directory?: string | undefined } | undefined }["location"] readonly target?: { readonly target?: string | undefined }["target"] diff --git a/packages/protocol/openapi.json b/packages/protocol/openapi.json index cd01151fd38..d266fe8afe6 100644 --- a/packages/protocol/openapi.json +++ b/packages/protocol/openapi.json @@ -394,72 +394,6 @@ "summary": "List plugins" } }, - "/api/plugin/await-activation": { - "post": { - "tags": ["plugin"], - "operationId": "plugin.awaitActivation", - "parameters": [ - { - "name": "location", - "in": "query", - "schema": { - "anyOf": [ - { - "type": "object", - "properties": { - "directory": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "null" - } - ] - }, - "required": false, - "style": "deepObject", - "explode": true - } - ], - "security": [], - "responses": { - "204": { - "description": "" - }, - "400": { - "description": "InvalidRequestError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InvalidRequestErrorEncoded" - } - } - } - }, - "401": { - "description": "UnauthorizedError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnauthorizedErrorEncoded" - } - } - } - } - }, - "description": "Wait for configured plugin activation at a Location to settle, including missing-package installs. Completion does not imply every plugin succeeded or background resource discovery finished. Cancelling this wait does not cancel activation.", - "summary": "Wait for plugin activation" - } - }, "/api/plugin/check": { "post": { "tags": ["plugin"], diff --git a/packages/protocol/src/groups/plugin.ts b/packages/protocol/src/groups/plugin.ts index 4924ef0106a..24409d72233 100644 --- a/packages/protocol/src/groups/plugin.ts +++ b/packages/protocol/src/groups/plugin.ts @@ -20,21 +20,6 @@ export const PluginGroup = HttpApiGroup.make("server.plugin") }), ), ) - .add( - HttpApiEndpoint.post("plugin.awaitActivation", "/api/plugin/await-activation", { - query: LocationQuery, - success: HttpApiSchema.NoContent, - }) - .annotateMerge(locationQueryOpenApi) - .annotateMerge( - OpenApi.annotations({ - identifier: "plugin.awaitActivation", - summary: "Wait for plugin activation", - description: - "Wait for configured plugin activation at a Location to settle, including missing-package installs. Completion does not imply every plugin succeeded or background resource discovery finished. Cancelling this wait does not cancel activation.", - }), - ), - ) .add( HttpApiEndpoint.post("plugin.check", "/api/plugin/check", { query: LocationQuery, diff --git a/packages/server/src/handlers/plugin.ts b/packages/server/src/handlers/plugin.ts index 8e60a54c45b..31fe39c483e 100644 --- a/packages/server/src/handlers/plugin.ts +++ b/packages/server/src/handlers/plugin.ts @@ -13,11 +13,9 @@ export const PluginHandler = HttpApiBuilder.group(Api, "server.plugin", (handler return yield* response(Plugin.Service.use((plugin) => plugin.list())) }), ) - .handle("plugin.awaitActivation", () => Plugin.awaitActivation) .handle("plugin.check", (ctx) => Effect.gen(function* () { const plugins = yield* Plugin.Service - yield* plugins.awaitActivation const inventory = yield* plugins.list() const targets = [ ...new Set(inventory.flatMap((plugin) => (plugin.source.type === "package" ? [plugin.source.target] : []))), @@ -57,7 +55,6 @@ export const PluginHandler = HttpApiBuilder.group(Api, "server.plugin", (handler .handle("plugin.update", (ctx) => Effect.gen(function* () { const plugins = yield* Plugin.Service - yield* plugins.awaitActivation const inventory = new Set( (yield* plugins.list()).flatMap((plugin) => (plugin.source.type === "package" ? [plugin.source.target] : [])), ) diff --git a/packages/server/test/plugin-activation.test.ts b/packages/server/test/plugin-activation.test.ts deleted file mode 100644 index c278d2a1a48..00000000000 --- a/packages/server/test/plugin-activation.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { expect } from "bun:test" -import { mkdir } from "node:fs/promises" -import path from "node:path" -import { SdkPlugins } from "@opencode/core/plugin/sdk" -import { Plugin } from "@opencode/plugin/effect" -import { Context, Deferred, Effect, Fiber, Layer } from "effect" -import { HttpEffect, HttpRouter, HttpServer } from "effect/unstable/http" -import { tmpdirScoped } from "../../core/test/fixture/tmpdir" -import { it } from "../../core/test/lib/effect" -import { createRoutes } from "../src/routes" - -const fixture = Effect.fn(function* (plugin: Plugin.Plugin) { - const tmp = yield* tmpdirScoped("opencode-plugin-activation-") - const first = path.join(tmp.path, "first") - const second = path.join(tmp.path, "second") - const config = path.join(tmp.path, "config") - yield* Effect.promise(() => Promise.all([first, second, config].map((directory) => mkdir(directory)))) - const context = yield* Layer.build( - createRoutes({ - password: "secret", - database: { path: ":memory:" }, - models: { fetch: false }, - fs: { filewatcher: false }, - config: { - directory: config, - project: false, - content: JSON.stringify({ - providers: { - acme: { - models: { - reasoner: { name: "Configured Reasoner", limit: { context: 96_000, output: 8_000 } }, - }, - }, - }, - }), - }, - }).pipe(Layer.provide(HttpServer.layerServices)), - ) - const sdk = Context.get(context, SdkPlugins.Service) - yield* sdk.register(plugin) - const handler = Context.get(context, HttpRouter.HttpRouter).asHttpEffect().pipe(HttpEffect.toWebHandlerWith(context)) - return { - first, - second, - request: (method: "GET" | "POST", route: string, directory = first, signal?: AbortSignal) => - Effect.promise((interruption) => { - const url = new URL(route, "http://opencode.local") - url.searchParams.set("location[directory]", directory) - return handler( - new Request(url, { - method, - headers: { authorization: `Basic ${btoa("opencode:secret")}` }, - signal: signal ?? interruption, - }), - ) - }), - } -}) - -it.live( - "awaits activation only for the requested location without blocking model or plugin snapshots", - () => - Effect.gen(function* () { - const started = yield* Deferred.make() - const release = yield* Deferred.make() - const server = yield* fixture( - Plugin.define({ - id: "slow-plugin", - effect: (ctx) => - Effect.gen(function* () { - if (path.basename(ctx.location.directory) !== "first") return - yield* Deferred.succeed(started, undefined) - yield* Deferred.await(release) - }), - }), - ) - const pending = yield* server.request("POST", "/api/plugin/await-activation").pipe(Effect.forkScoped) - yield* Deferred.await(started) - expect(pending.pollUnsafe()).toBeUndefined() - - const models = yield* server.request("GET", "/api/model") - expect(models.status).toBe(200) - expect(yield* Effect.promise(() => models.json())).toMatchObject({ - location: { directory: server.first }, - data: expect.not.arrayContaining([expect.objectContaining({ providerID: "acme", id: "reasoner" })]), - }) - const plugins = yield* server.request("GET", "/api/plugin") - expect(plugins.status).toBe(200) - expect(yield* Effect.promise(() => plugins.json())).toMatchObject({ location: { directory: server.first } }) - - const second = yield* server.request("POST", "/api/plugin/await-activation", server.second) - expect(second.status).toBe(204) - expect(pending.pollUnsafe()).toBeUndefined() - - yield* Deferred.succeed(release, undefined) - const response = yield* Fiber.join(pending) - expect(response.status).toBe(204) - expect(yield* Effect.promise(() => response.text())).toBe("") - const configured = yield* server.request("GET", "/api/model") - expect(configured.status).toBe(200) - expect(yield* Effect.promise(() => configured.json())).toMatchObject({ - location: { directory: server.first }, - data: expect.arrayContaining([ - expect.objectContaining({ - providerID: "acme", - id: "reasoner", - name: "Configured Reasoner", - limit: { context: 96_000, output: 8_000 }, - }), - ]), - }) - const active = yield* server.request("GET", "/api/plugin") - expect(active.status).toBe(200) - expect(yield* Effect.promise(() => active.json())).toMatchObject({ - data: expect.arrayContaining([ - expect.objectContaining({ id: "slow-plugin", source: { type: "sdk" }, state: { status: "active" } }), - ]), - }) - }).pipe(Effect.timeout("10 seconds")), - 15_000, -) - -it.live( - "aborting an activation wait does not cancel plugin setup", - () => - Effect.gen(function* () { - const started = yield* Deferred.make() - const release = yield* Deferred.make() - const completed = yield* Deferred.make() - const interrupted = yield* Deferred.make() - const server = yield* fixture( - Plugin.define({ - id: "slow-plugin", - effect: () => - Effect.gen(function* () { - yield* Deferred.succeed(started, undefined) - yield* Deferred.await(release) - yield* Deferred.succeed(completed, undefined) - }).pipe(Effect.onInterrupt(() => Deferred.succeed(interrupted, undefined))), - }), - ) - const controller = new AbortController() - yield* Effect.addFinalizer(() => Effect.sync(() => controller.abort())) - const pending = yield* server - .request("POST", "/api/plugin/await-activation", server.first, controller.signal) - .pipe(Effect.forkScoped) - yield* Deferred.await(started) - controller.abort() - // HttpEffect resolves a cancelled Web request with 499 rather than rejecting its Promise. - expect((yield* Fiber.join(pending)).status).toBe(499) - expect(yield* Deferred.isDone(interrupted)).toBe(false) - expect(yield* Deferred.isDone(completed)).toBe(false) - - yield* Deferred.succeed(release, undefined) - expect((yield* server.request("POST", "/api/plugin/await-activation")).status).toBe(204) - expect(yield* Deferred.isDone(completed)).toBe(true) - expect(yield* Deferred.isDone(interrupted)).toBe(false) - const plugins = yield* server.request("GET", "/api/plugin") - expect(plugins.status).toBe(200) - expect(yield* Effect.promise(() => plugins.json())).toMatchObject({ - data: expect.arrayContaining([expect.objectContaining({ id: "slow-plugin", state: { status: "active" } })]), - }) - }).pipe(Effect.timeout("10 seconds")), - 15_000, -) - -it.live( - "settles activation when plugin setup fails and exposes the failure in the inventory", - () => - Effect.gen(function* () { - const server = yield* fixture( - Plugin.define({ - id: "failing-plugin", - effect: () => Effect.die(new Error("fixture setup failed")), - }), - ) - expect((yield* server.request("POST", "/api/plugin/await-activation")).status).toBe(204) - const plugins = yield* server.request("GET", "/api/plugin") - expect(plugins.status).toBe(200) - expect(yield* Effect.promise(() => plugins.json())).toMatchObject({ - location: { directory: server.first }, - data: expect.arrayContaining([ - expect.objectContaining({ - id: "failing-plugin", - source: { type: "sdk" }, - state: { status: "failed", error: expect.stringContaining("fixture setup failed") }, - }), - ]), - }) - }).pipe(Effect.timeout("10 seconds")), - 15_000, -) diff --git a/packages/server/test/provider.test.ts b/packages/server/test/provider.test.ts index b044fe57c8f..90aa825df36 100644 --- a/packages/server/test/provider.test.ts +++ b/packages/server/test/provider.test.ts @@ -1,7 +1,7 @@ import { expect } from "bun:test" import { SdkPlugins } from "@opencode/core/plugin/sdk" import { Plugin } from "@opencode/plugin/effect" -import { Context, Deferred, Effect, Fiber, Layer } from "effect" +import { Context, Deferred, Effect, Layer } from "effect" import { HttpEffect, HttpRouter, HttpServer } from "effect/unstable/http" import { tmpdirScoped } from "../../core/test/fixture/tmpdir" import { it } from "../../core/test/lib/effect" @@ -62,11 +62,9 @@ it.live( }), ) }) - const pending = yield* request("POST", "/api/plugin/await-activation").pipe(Effect.forkScoped) - yield* Deferred.await(started) - // Config providers activate after SDK plugins; reads must return the current snapshot without waiting. const list = yield* request("GET", "/api/provider").pipe(Effect.timeout("2 seconds")) + yield* Deferred.await(started) expect(list.status).toBe(200) expect(yield* Effect.promise(() => list.json())).toMatchObject({ location: { directory: tmp.path }, @@ -78,29 +76,7 @@ it.live( _tag: "ProviderNotFoundError", providerID: "custom", }) - expect(pending.pollUnsafe()).toBeUndefined() - yield* Deferred.succeed(release, undefined) - expect((yield* Fiber.join(pending)).status).toBe(204) - const provider = { - id: "custom", - name: "Configured Custom Provider", - activation: "enabled", - package: "@opencode/ai/providers/openai-compatible", - settings: { apiKey: "secret" }, - } - const configuredList = yield* request("GET", "/api/provider").pipe(Effect.timeout("2 seconds")) - expect(configuredList.status).toBe(200) - expect(yield* Effect.promise(() => configuredList.json())).toMatchObject({ - location: { directory: tmp.path }, - data: expect.arrayContaining([expect.objectContaining(provider)]), - }) - const configuredGet = yield* request("GET", "/api/provider/custom").pipe(Effect.timeout("2 seconds")) - expect(configuredGet.status).toBe(200) - expect(yield* Effect.promise(() => configuredGet.json())).toMatchObject({ - location: { directory: tmp.path }, - data: provider, - }) }), 15_000, ) diff --git a/packages/server/test/worktree.test.ts b/packages/server/test/worktree.test.ts index bc237cf5267..2c0234d2581 100644 --- a/packages/server/test/worktree.test.ts +++ b/packages/server/test/worktree.test.ts @@ -215,9 +215,6 @@ it.live( const api = OpenCode.make({ baseUrl: server.base, headers: server.headers }) yield* Effect.promise(async () => { await api.location.get({ location: { directory: source } }) - const url = new URL("/api/plugin/await-activation", server.base) - url.searchParams.set("location[directory]", source) - expect((await fetch(url, { method: "POST", headers: server.headers })).status).toBe(204) expect(await api.worktree.list({ location: { directory: target } })).toContainEqual({ directory: path.join(destination, "delegated"), strategy: "target-copy", diff --git a/packages/tui/src/feature-plugins/system/plugins.tsx b/packages/tui/src/feature-plugins/system/plugins.tsx index 93aee7cc66e..124c7b3025c 100644 --- a/packages/tui/src/feature-plugins/system/plugins.tsx +++ b/packages/tui/src/feature-plugins/system/plugins.tsx @@ -148,7 +148,6 @@ export function PluginsDialog(props: { location, targets: [entry.plugin.source.target], }) - .then(() => props.context.client.plugin.awaitActivation({ location })) .then(() => refetch()) .catch((cause) => { props.context.ui.toast.show({ diff --git a/services/www/openapi.json b/services/www/openapi.json index cd01151fd38..d266fe8afe6 100644 --- a/services/www/openapi.json +++ b/services/www/openapi.json @@ -394,72 +394,6 @@ "summary": "List plugins" } }, - "/api/plugin/await-activation": { - "post": { - "tags": ["plugin"], - "operationId": "plugin.awaitActivation", - "parameters": [ - { - "name": "location", - "in": "query", - "schema": { - "anyOf": [ - { - "type": "object", - "properties": { - "directory": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "null" - } - ] - }, - "required": false, - "style": "deepObject", - "explode": true - } - ], - "security": [], - "responses": { - "204": { - "description": "" - }, - "400": { - "description": "InvalidRequestError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InvalidRequestErrorEncoded" - } - } - } - }, - "401": { - "description": "UnauthorizedError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnauthorizedErrorEncoded" - } - } - } - } - }, - "description": "Wait for configured plugin activation at a Location to settle, including missing-package installs. Completion does not imply every plugin succeeded or background resource discovery finished. Cancelling this wait does not cancel activation.", - "summary": "Wait for plugin activation" - } - }, "/api/plugin/check": { "post": { "tags": ["plugin"], diff --git a/services/www/public/openapi.json b/services/www/public/openapi.json index cd01151fd38..d266fe8afe6 100644 --- a/services/www/public/openapi.json +++ b/services/www/public/openapi.json @@ -394,72 +394,6 @@ "summary": "List plugins" } }, - "/api/plugin/await-activation": { - "post": { - "tags": ["plugin"], - "operationId": "plugin.awaitActivation", - "parameters": [ - { - "name": "location", - "in": "query", - "schema": { - "anyOf": [ - { - "type": "object", - "properties": { - "directory": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "null" - } - ] - }, - "required": false, - "style": "deepObject", - "explode": true - } - ], - "security": [], - "responses": { - "204": { - "description": "" - }, - "400": { - "description": "InvalidRequestError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InvalidRequestErrorEncoded" - } - } - } - }, - "401": { - "description": "UnauthorizedError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnauthorizedErrorEncoded" - } - } - } - } - }, - "description": "Wait for configured plugin activation at a Location to settle, including missing-package installs. Completion does not imply every plugin succeeded or background resource discovery finished. Cancelling this wait does not cancel activation.", - "summary": "Wait for plugin activation" - } - }, "/api/plugin/check": { "post": { "tags": ["plugin"],