mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-28 13:33:35 +08:00
feat(core): identify the client on Console requests (#50896)
This commit is contained in:
@@ -3,6 +3,7 @@ import type { Scope } from "effect"
|
||||
import type { IntegrationOAuthMethodRegistration } from "@opencode/plugin/effect/integration"
|
||||
import { define } from "@opencode/plugin/effect/plugin"
|
||||
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||
import { App } from "../../app.js"
|
||||
import { Bus } from "../../bus.js"
|
||||
import { Credential } from "../../credential.js"
|
||||
import { Integration } from "../../integration.js"
|
||||
@@ -122,7 +123,10 @@ export const OpencodePlugin = define<HttpClient.HttpClient | Bus.Service | Manag
|
||||
id: "opencode.provider.opencode",
|
||||
effect: Effect.fn(function* (ctx) {
|
||||
const bus = yield* Bus.Service
|
||||
const http = yield* HttpClient.HttpClient
|
||||
const client = yield* HttpClient.HttpClient
|
||||
// Every request here goes to the Console, which reads the User-Agent to tell which OpenCode a member runs
|
||||
// and whether it evaluates the policies it is being sent.
|
||||
const http = HttpClient.mapRequest(client, HttpClientRequest.setHeader("User-Agent", App.useragent(ctx.app)))
|
||||
const managed = yield* ManagedPolicy.Service
|
||||
const loading = Semaphore.makeUnsafe(1)
|
||||
type ActiveConnection = Effect.Success<ReturnType<typeof ctx.integration.connection.active>>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Money } from "@opencode/schema/money"
|
||||
import { Effect, Layer, Stream } from "effect"
|
||||
import { TestClock } from "effect/testing"
|
||||
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||
import { App } from "@opencode/core/app"
|
||||
import { Config } from "@opencode/core/config"
|
||||
import { ConfigPolicyPlugin } from "@opencode/core/config/plugin/policy"
|
||||
import { Credential } from "@opencode/core/credential"
|
||||
@@ -38,11 +39,13 @@ const noRemoteConfig = HttpClient.make((request) =>
|
||||
function consoleServer(orgID: string | null | undefined, unavailable = false) {
|
||||
const config: { authorization: string | null; orgID: string | null }[] = []
|
||||
const requests: string[] = []
|
||||
const agents: (string | null)[] = []
|
||||
const server = Bun.serve({
|
||||
port: 0,
|
||||
fetch: async (request) => {
|
||||
const path = new URL(request.url).pathname
|
||||
requests.push(path)
|
||||
agents.push(request.headers.get("user-agent"))
|
||||
if (path === "/auth/device/code") {
|
||||
expect(await request.json()).toEqual({ client_id: "opencode-cli", supports_org_scope: true })
|
||||
return Response.json({
|
||||
@@ -74,7 +77,7 @@ function consoleServer(orgID: string | null | undefined, unavailable = false) {
|
||||
return new Response("Not found", { status: 404 })
|
||||
},
|
||||
})
|
||||
return { server, config, requests }
|
||||
return { server, config, requests, agents }
|
||||
}
|
||||
|
||||
function required<T>(value: T | undefined): T {
|
||||
@@ -284,7 +287,7 @@ describe("OpencodePlugin", () => {
|
||||
() =>
|
||||
Effect.acquireUseRelease(
|
||||
Effect.sync(() => consoleServer(scenario.orgID, scenario.unavailable)),
|
||||
({ server, config, requests }) =>
|
||||
({ server, config, requests, agents }) =>
|
||||
Effect.gen(function* () {
|
||||
const credentials = yield* Credential.Service
|
||||
const initial = yield* credentials.create({
|
||||
@@ -328,7 +331,9 @@ describe("OpencodePlugin", () => {
|
||||
}),
|
||||
).toEqual(stored.value)
|
||||
expect(requests).toEqual(["/auth/device/token", "/api/v2/config"])
|
||||
}),
|
||||
// The refresh and the config fetch both say which OpenCode is asking.
|
||||
expect(agents).toEqual(["opencode/beta/1.2.3/test", "opencode/beta/1.2.3/test"])
|
||||
}).pipe(Effect.provideService(App.Metadata, App.make({ name: "test", version: "1.2.3", channel: "beta" }))),
|
||||
({ server }) => Effect.promise(() => server.stop(true)),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -222,6 +222,7 @@ The OpenCode Console compiles a workspace's Providers and Tools policies into st
|
||||
|
||||
- `experimental` is omitted when the caller has no statements; omission and an empty array are equivalent.
|
||||
- The list is per caller and its order is significant. The client stores it exactly as received; it never reorders, dedupes, or normalizes statements.
|
||||
- Every request the Console plugin makes, this fetch and the token refresh included, carries the `User-Agent` `opencode/<channel>/<version>/<app>`. The Console reads it to tell which OpenCode a member runs and whether it evaluates the statements it is being sent; older builds that drop them are otherwise indistinguishable from ones that enforce.
|
||||
- `ManagedPolicy` (`packages/core/src/managed-policy.ts`) is the process-global home for the current statements and the organization name. The Console plugin (`opencode.provider.opencode`) writes it whenever its config snapshot is applied; the policy plugin reads it synchronously when evaluating.
|
||||
- Statements ride on the Console plugin's snapshot, so they follow the connection: a credential switch replaces them, and a disconnect or a 404 from the Console clears them. Statements from different connections never merge.
|
||||
- Freshness is the snapshot's freshness: the next poll (about one minute) or the next credential switch.
|
||||
|
||||
Reference in New Issue
Block a user