opencode-agent[bot] 2 mesi fa
parent
commit
3ab19bfd7d

+ 1 - 5
packages/core/src/catalog.ts

@@ -93,11 +93,7 @@ export const layer = Layer.effect(
     const integrations = yield* Integration.Service
     const scope = yield* Scope.Scope
 
-    const available = (
-      provider: ProviderV2.Info,
-      integration: Integration.Info | undefined,
-      connected: boolean,
-    ) => {
+    const available = (provider: ProviderV2.Info, integration: Integration.Info | undefined, connected: boolean) => {
       if (provider.disabled) return false
       if (typeof provider.request.body.apiKey === "string") return true
       if (connected) return true

+ 13 - 10
packages/core/src/integration.ts

@@ -228,7 +228,10 @@ export interface Interface {
       readonly label?: string
     }) => Effect.Effect<Attempt, AuthorizationError>
     /** Updates a stored credential exposed as a connection. */
-    readonly update: (credentialID: Credential.ID, updates: Partial<Pick<Credential.Stored, "label">>) => Effect.Effect<void>
+    readonly update: (
+      credentialID: Credential.ID,
+      updates: Partial<Pick<Credential.Stored, "label">>,
+    ) => Effect.Effect<void>
     /** Removes a stored credential connection. */
     readonly remove: (credentialID: Credential.ID) => Effect.Effect<void>
   }
@@ -339,9 +342,11 @@ export const locationLayer = Layer.effect(
     })
 
     const connections = (entry: Entry, saved: readonly Credential.Stored[]): IntegrationConnection.Info[] => {
-      const connected = saved.map(
-        (credential) => ({ type: "credential" as const, id: credential.id, label: credential.label }),
-      )
+      const connected = saved.map((credential) => ({
+        type: "credential" as const,
+        id: credential.id,
+        label: credential.label,
+      }))
       const detected = entry.methods
         .filter((method) => method.type === "env")
         .flatMap((method) => method.names.filter((name) => process.env[name]))
@@ -446,12 +451,10 @@ export const locationLayer = Layer.effect(
         list: Effect.fn("Integration.connection.list")(function* () {
           const saved = Map.groupBy(yield* credentials.all(), (credential) => credential.integrationID)
           return new Map(
-            new Set([...state.get().integrations.keys(), ...saved.keys()])
-              .values()
-              .flatMap((id) => {
-                const connection = activeConnection(state.get().integrations.get(id), saved.get(id) ?? [])
-                return connection ? [[id, connection] as const] : []
-              }),
+            new Set([...state.get().integrations.keys(), ...saved.keys()]).values().flatMap((id) => {
+              const connection = activeConnection(state.get().integrations.get(id), saved.get(id) ?? [])
+              return connection ? [[id, connection] as const] : []
+            }),
           )
         }),
         forIntegration: Effect.fn("Integration.connection.forIntegration")(function* (id) {

+ 1 - 3
packages/core/src/plugin/provider/opencode.ts

@@ -14,9 +14,7 @@ export const OpencodePlugin = PluginV2.define({
         if (!item) return
         const integration = yield* integrations.get(Integration.ID.make(item.provider.id))
         hasKey = Boolean(
-          process.env.OPENCODE_API_KEY ||
-            integration?.connections.length ||
-            item.provider.request.body.apiKey,
+          process.env.OPENCODE_API_KEY || integration?.connections.length || item.provider.request.body.apiKey,
         )
         evt.provider.update(item.provider.id, (provider) => {
           if (!hasKey) provider.request.body.apiKey = "public"

+ 1 - 5
packages/core/src/session/runner/model.ts

@@ -48,11 +48,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/v2
 /** Test or embedding seam for supplying a model resolver directly. */
 export const layerWith = (resolve: Interface["resolve"]) => Layer.succeed(Service, Service.of({ resolve }))
 
-const apiKey = (
-  model: ModelV2.Info,
-  connection?: IntegrationConnection.Info,
-  credential?: Credential.Stored,
-) => {
+const apiKey = (model: ModelV2.Info, connection?: IntegrationConnection.Info, credential?: Credential.Stored) => {
   if (credential?.value.type === "key") return Auth.value(credential.value.key)
   if (credential?.value.type === "oauth") return Auth.value(credential.value.access)
   const value = model.request.body.apiKey ?? model.api.settings?.apiKey

+ 4 - 4
packages/core/test/catalog.test.ts

@@ -36,12 +36,12 @@ describe("CatalogV2", () => {
     Effect.gen(function* () {
       const catalog = yield* Catalog.Service
       const events = yield* EventV2.Service
-      const updated = yield* events.subscribe(Catalog.Event.Updated).pipe(Stream.take(1), Stream.runCollect, Effect.forkScoped)
+      const updated = yield* events
+        .subscribe(Catalog.Event.Updated)
+        .pipe(Stream.take(1), Stream.runCollect, Effect.forkScoped)
       yield* Effect.yieldNow
 
-      yield* (yield* catalog.transform())((editor) =>
-        editor.provider.update(ProviderV2.ID.make("test"), () => {}),
-      )
+      yield* (yield* catalog.transform())((editor) => editor.provider.update(ProviderV2.ID.make("test"), () => {}))
 
       expect((yield* Fiber.join(updated)).length).toBe(1)
     }),

+ 110 - 107
packages/core/test/config/provider.test.ts

@@ -136,123 +136,126 @@ describe("ConfigProviderPlugin.Plugin", () => {
   )
 
   it.effect("loads configured providers and applies later model overrides", () =>
-    withEnv({ CUSTOM_API_KEY: "secret" }, () => Effect.gen(function* () {
-      const catalog = yield* Catalog.Service
-      const integrations = yield* Integration.Service
-      const plugin = yield* PluginV2.Service
-      const providerID = ProviderV2.ID.make("custom")
-      const modelID = ModelV2.ID.make("chat")
-      const config = Config.Service.of({
-        entries: () =>
-          Effect.succeed([
-            new Config.Document({
-              type: "document",
-              info: decode({
-                model: "custom/first",
-                providers: {
-                  custom: {
-                    name: "Configured",
-                    env: ["CUSTOM_API_KEY"],
-                    api: { type: "native", settings: {} },
-                    request: request({ first: "first", shared: "first" }),
-                    models: {
-                      chat: {
-                        name: "First",
-                        capabilities: { tools: true, input: ["text"], output: ["text"] },
-                        disabled: true,
-                        limit: { context: 100, output: 50 },
-                        cost: { input: 1, output: 2 },
-                        request: request({ first: "first", shared: "first" }, "retained"),
-                        variants: [
-                          {
-                            id: "fast",
-                            headers: { first: "first", shared: "first" },
-                          },
-                        ],
+    withEnv({ CUSTOM_API_KEY: "secret" }, () =>
+      Effect.gen(function* () {
+        const catalog = yield* Catalog.Service
+        const integrations = yield* Integration.Service
+        const plugin = yield* PluginV2.Service
+        const providerID = ProviderV2.ID.make("custom")
+        const modelID = ModelV2.ID.make("chat")
+        const config = Config.Service.of({
+          entries: () =>
+            Effect.succeed([
+              new Config.Document({
+                type: "document",
+                info: decode({
+                  model: "custom/first",
+                  providers: {
+                    custom: {
+                      name: "Configured",
+                      env: ["CUSTOM_API_KEY"],
+                      api: { type: "native", settings: {} },
+                      request: request({ first: "first", shared: "first" }),
+                      models: {
+                        chat: {
+                          name: "First",
+                          capabilities: { tools: true, input: ["text"], output: ["text"] },
+                          disabled: true,
+                          limit: { context: 100, output: 50 },
+                          cost: { input: 1, output: 2 },
+                          request: request({ first: "first", shared: "first" }, "retained"),
+                          variants: [
+                            {
+                              id: "fast",
+                              headers: { first: "first", shared: "first" },
+                            },
+                          ],
+                        },
                       },
                     },
                   },
-                },
+                }),
               }),
-            }),
-            new Config.Document({
-              type: "document",
-              info: decode({
-                model: "custom/default",
-                providers: {
-                  custom: {
-                    api: { type: "aisdk", package: "custom-sdk", url: "https://example.test" },
-                    request: request({ last: "last", shared: "last" }),
-                    models: {
-                      default: {
-                        name: "Default",
-                      },
-                      chat: {
-                        api: { id: "api-chat" },
-                        name: "Last",
-                        limit: { output: 75 },
-                        request: request({ last: "last", shared: "last" }),
-                        variants: [
-                          {
-                            id: "fast",
-                            headers: { last: "last", shared: "last" },
-                          },
-                          {
-                            id: "slow",
-                            headers: { slow: "slow" },
-                          },
-                        ],
+              new Config.Document({
+                type: "document",
+                info: decode({
+                  model: "custom/default",
+                  providers: {
+                    custom: {
+                      api: { type: "aisdk", package: "custom-sdk", url: "https://example.test" },
+                      request: request({ last: "last", shared: "last" }),
+                      models: {
+                        default: {
+                          name: "Default",
+                        },
+                        chat: {
+                          api: { id: "api-chat" },
+                          name: "Last",
+                          limit: { output: 75 },
+                          request: request({ last: "last", shared: "last" }),
+                          variants: [
+                            {
+                              id: "fast",
+                              headers: { last: "last", shared: "last" },
+                            },
+                            {
+                              id: "slow",
+                              headers: { slow: "slow" },
+                            },
+                          ],
+                        },
                       },
                     },
                   },
-                },
+                }),
               }),
-            }),
-            new Config.Document({
-              type: "document",
-              info: decode({
-                providers: {
-                  custom: { name: "Renamed" },
-                },
+              new Config.Document({
+                type: "document",
+                info: decode({
+                  providers: {
+                    custom: { name: "Renamed" },
+                  },
+                }),
               }),
-            }),
-          ]),
-      })
+            ]),
+        })
 
-      yield* plugin.add({
-        ...ConfigProviderPlugin.Plugin,
-        effect: ConfigProviderPlugin.Plugin.effect.pipe(
-          Effect.provideService(Config.Service, config),
-          Effect.provideService(Catalog.Service, catalog),
-          Effect.provideService(Integration.Service, integrations),
-        ),
-      })
+        yield* plugin.add({
+          ...ConfigProviderPlugin.Plugin,
+          effect: ConfigProviderPlugin.Plugin.effect.pipe(
+            Effect.provideService(Config.Service, config),
+            Effect.provideService(Catalog.Service, catalog),
+            Effect.provideService(Integration.Service, integrations),
+          ),
+        })
 
-      const provider = yield* catalog.provider.get(providerID)
-      const model = yield* catalog.model.get(providerID, modelID)
-      expect(Option.getOrUndefined(yield* catalog.model.default())?.id).toBe(ModelV2.ID.make("default"))
-      expect(provider.name).toBe("Renamed")
-      expect((yield* integrations.get(Integration.ID.make("custom")))?.methods).toContainEqual(
-        { type: "env", names: ["CUSTOM_API_KEY"] },
-      )
-      expect((yield* integrations.get(Integration.ID.make("custom")))?.name).toBe("Renamed")
-      expect(provider.disabled).toBeUndefined()
-      expect(provider.api).toEqual({ type: "aisdk", package: "custom-sdk", url: "https://example.test" })
-      expect(provider.request.headers).toEqual({ first: "first", shared: "last", last: "last" })
-      expect(model.api.id).toBe(ModelV2.ID.make("api-chat"))
-      expect(model.name).toBe("Last")
-      expect(model.capabilities).toEqual({ tools: true, input: ["text"], output: ["text"] })
-      expect(model.enabled).toBe(false)
-      expect(model.limit).toEqual({ context: 100, output: 75 })
-      expect(model.cost).toEqual([{ input: 1, output: 2, cache: { read: 0, write: 0 }, tier: undefined }])
-      expect(model.request.headers).toEqual({ first: "first", shared: "last", last: "last" })
-      expect(model.request.variant).toBe("retained")
-      expect(model.variants.map((variant) => variant.id)).toEqual([
-        ModelV2.VariantID.make("fast"),
-        ModelV2.VariantID.make("slow"),
-      ])
-      expect(model.variants[0]?.headers).toEqual({ first: "first", shared: "last", last: "last" })
-      expect(model.variants[1]?.headers).toEqual({ slow: "slow" })
-    })),
+        const provider = yield* catalog.provider.get(providerID)
+        const model = yield* catalog.model.get(providerID, modelID)
+        expect(Option.getOrUndefined(yield* catalog.model.default())?.id).toBe(ModelV2.ID.make("default"))
+        expect(provider.name).toBe("Renamed")
+        expect((yield* integrations.get(Integration.ID.make("custom")))?.methods).toContainEqual({
+          type: "env",
+          names: ["CUSTOM_API_KEY"],
+        })
+        expect((yield* integrations.get(Integration.ID.make("custom")))?.name).toBe("Renamed")
+        expect(provider.disabled).toBeUndefined()
+        expect(provider.api).toEqual({ type: "aisdk", package: "custom-sdk", url: "https://example.test" })
+        expect(provider.request.headers).toEqual({ first: "first", shared: "last", last: "last" })
+        expect(model.api.id).toBe(ModelV2.ID.make("api-chat"))
+        expect(model.name).toBe("Last")
+        expect(model.capabilities).toEqual({ tools: true, input: ["text"], output: ["text"] })
+        expect(model.enabled).toBe(false)
+        expect(model.limit).toEqual({ context: 100, output: 75 })
+        expect(model.cost).toEqual([{ input: 1, output: 2, cache: { read: 0, write: 0 }, tier: undefined }])
+        expect(model.request.headers).toEqual({ first: "first", shared: "last", last: "last" })
+        expect(model.request.variant).toBe("retained")
+        expect(model.variants.map((variant) => variant.id)).toEqual([
+          ModelV2.VariantID.make("fast"),
+          ModelV2.VariantID.make("slow"),
+        ])
+        expect(model.variants[0]?.headers).toEqual({ first: "first", shared: "last", last: "last" })
+        expect(model.variants[1]?.headers).toEqual({ slow: "slow" })
+      }),
+    ),
   )
 })

+ 3 - 5
packages/core/test/integration.test.ts

@@ -147,11 +147,9 @@ describe("Integration", () => {
           method: { type: "key", label: "API key" },
         }),
       )
-      const updated = yield* events.subscribe(Integration.Event.Updated).pipe(
-        Stream.take(1),
-        Stream.runCollect,
-        Effect.forkScoped,
-      )
+      const updated = yield* events
+        .subscribe(Integration.Event.Updated)
+        .pipe(Stream.take(1), Stream.runCollect, Effect.forkScoped)
       yield* Effect.yieldNow
 
       yield* integrations.connection.key({

File diff suppressed because it is too large
+ 608 - 1294
packages/sdk/openapi.json


Some files were not shown because too many files changed in this diff