Explorar el Código

refactor(core): remove schema forwarding facades (#33577)

Kit Langton hace 1 mes
padre
commit
57895586c2

+ 2 - 2
packages/core/src/config.ts

@@ -3,10 +3,10 @@ export * as Config from "./config"
 import path from "path"
 import { type ParseError, parse } from "jsonc-parser"
 import { Context, Effect, Layer, Option, Schema } from "effect"
+import { Permission } from "@opencode-ai/schema/permission"
 import { FSUtil } from "./fs-util"
 import { Global } from "./global"
 import { Location } from "./location"
-import { PermissionSchema } from "./permission/schema"
 import { Policy } from "./policy"
 import { AbsolutePath } from "./schema"
 import { ConfigAgent } from "./config/agent"
@@ -56,7 +56,7 @@ export class Info extends Schema.Class<Info>("Config.Info")({
   username: Schema.String.pipe(Schema.optional).annotate({
     description: "Username displayed in conversations and used for telemetry identity",
   }),
-  permissions: PermissionSchema.Ruleset.pipe(Schema.optional).annotate({
+  permissions: Permission.Ruleset.pipe(Schema.optional).annotate({
     description: "Ordered tool permission rules applied to agent tool use",
   }),
   agents: Schema.Record(Schema.String, ConfigAgent.Info).pipe(Schema.optional).annotate({

+ 2 - 2
packages/core/src/config/agent.ts

@@ -1,7 +1,7 @@
 export * as ConfigAgent from "./agent"
 
 import { Schema } from "effect"
-import { PermissionSchema } from "../permission/schema"
+import { Permission } from "@opencode-ai/schema/permission"
 import { ConfigProvider } from "./provider"
 import { PositiveInt } from "../schema"
 
@@ -21,5 +21,5 @@ export class Info extends Schema.Class<Info>("ConfigV2.Agent")({
   color: Color.pipe(Schema.optional),
   steps: PositiveInt.pipe(Schema.optional),
   disabled: Schema.Boolean.pipe(Schema.optional),
-  permissions: PermissionSchema.Ruleset.pipe(Schema.optional),
+  permissions: Permission.Ruleset.pipe(Schema.optional),
 }) {}

+ 4 - 4
packages/core/src/credential.ts

@@ -3,8 +3,8 @@ export * as Credential from "./credential"
 import { asc, eq } from "drizzle-orm"
 import { Context, Effect, Layer, Schema } from "effect"
 import { Credential } from "@opencode-ai/schema/credential"
+import { Integration } from "@opencode-ai/schema/integration"
 import { Database } from "./database/database"
-import { IntegrationSchema } from "./integration/schema"
 import { CredentialTable } from "./credential/sql"
 
 export const ID = Credential.ID
@@ -21,7 +21,7 @@ export type Value = Credential.Value
 
 export class Info extends Schema.Class<Info>("Credential.Info")({
   id: ID,
-  integrationID: IntegrationSchema.ID,
+  integrationID: Integration.ID,
   label: Schema.String,
   value: Value,
 }) {}
@@ -30,12 +30,12 @@ export interface Interface {
   /** Returns every stored credential. */
   readonly all: () => Effect.Effect<Info[]>
   /** Returns stored credentials belonging to one integration. */
-  readonly list: (integrationID: IntegrationSchema.ID) => Effect.Effect<Info[]>
+  readonly list: (integrationID: Integration.ID) => Effect.Effect<Info[]>
   /** Returns one stored credential by ID. */
   readonly get: (id: ID) => Effect.Effect<Info | undefined>
   /** Replaces any credential for an integration and returns the new record. */
   readonly create: (input: {
-    readonly integrationID: IntegrationSchema.ID
+    readonly integrationID: Integration.ID
     readonly value: Value
     readonly label?: string
   }) => Effect.Effect<Info>

+ 1 - 2
packages/core/src/credential/sql.ts

@@ -1,11 +1,10 @@
 import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
 import { Timestamps } from "../database/schema.sql"
-import type { IntegrationSchema } from "../integration/schema"
 import type { Credential } from "../credential"
 
 export const CredentialTable = sqliteTable("credential", {
   id: text().$type<Credential.ID>().primaryKey(),
-  integration_id: text().$type<IntegrationSchema.ID>(),
+  integration_id: text().$type<Credential.Info["integrationID"]>(),
   label: text().notNull(),
   value: text({ mode: "json" }).$type<Credential.Value>().notNull(),
   connector_id: text(),

+ 2 - 2
packages/core/src/filesystem.ts

@@ -7,8 +7,8 @@ import { FSUtil } from "./fs-util"
 import { Location } from "./location"
 import { PositiveInt, RelativePath } from "./schema"
 import { FileSystemSearch } from "./filesystem/search"
-import { Entry, Match } from "./filesystem/schema"
-export { Entry, Match, Submatch } from "./filesystem/schema"
+import { Entry, Match } from "@opencode-ai/schema/filesystem"
+export { Entry, Match, Submatch } from "@opencode-ai/schema/filesystem"
 
 export const ReadInput = Schema.Struct({
   path: RelativePath,

+ 0 - 10
packages/core/src/filesystem/schema.ts

@@ -1,10 +0,0 @@
-import { FileSystem } from "@opencode-ai/schema/filesystem"
-
-export const Entry = FileSystem.Entry
-export type Entry = FileSystem.Entry
-
-export const Submatch = FileSystem.Submatch
-export type Submatch = FileSystem.Submatch
-
-export const Match = FileSystem.Match
-export type Match = FileSystem.Match

+ 4 - 9
packages/core/src/integration.ts

@@ -16,18 +16,17 @@ import {
 } from "effect"
 import { Integration } from "@opencode-ai/schema/integration"
 import { Credential } from "./credential"
-import { IntegrationSchema } from "./integration/schema"
 import { withStatics } from "./schema"
 import { State } from "./state"
 import { Identifier } from "./util/identifier"
 import { EventV2 } from "./event"
 import { IntegrationConnection } from "./integration/connection"
 
-export const ID = IntegrationSchema.ID
-export type ID = IntegrationSchema.ID
+export const ID = Integration.ID
+export type ID = Integration.ID
 
-export const MethodID = IntegrationSchema.MethodID
-export type MethodID = IntegrationSchema.MethodID
+export const MethodID = Integration.MethodID
+export type MethodID = Integration.MethodID
 
 export const AttemptID = Schema.String.pipe(
   Schema.brand("Integration.AttemptID"),
@@ -103,10 +102,6 @@ export interface EnvImplementation {
 
 export type Implementation = OAuthImplementation | KeyImplementation | EnvImplementation
 
-function isOAuthImplementation(implementation: Implementation): implementation is OAuthImplementation {
-  return implementation.method.type === "oauth"
-}
-
 export class Attempt extends Schema.Class<Attempt>("Integration.Attempt")({
   attemptID: AttemptID,
   url: Schema.String,

+ 0 - 9
packages/core/src/integration/schema.ts

@@ -1,9 +0,0 @@
-export * as IntegrationSchema from "./schema"
-
-import { Integration } from "@opencode-ai/schema/integration"
-
-export const ID = Integration.ID
-export type ID = Integration.ID
-
-export const MethodID = Integration.MethodID
-export type MethodID = Integration.MethodID

+ 11 - 14
packages/core/src/permission.ts

@@ -1,6 +1,7 @@
 export * as PermissionV2 from "./permission"
 
 import { Context, Deferred, Effect as EffectRuntime, Layer, Schema } from "effect"
+import { Permission } from "@opencode-ai/schema/permission"
 import { EventV2 } from "./event"
 import { Location } from "./location"
 import { AgentV2 } from "./agent"
@@ -9,14 +10,10 @@ import { SessionStore } from "./session/store"
 import { withStatics } from "./schema"
 import { Identifier } from "./util/identifier"
 import { Wildcard } from "./util/wildcard"
-import { PermissionSchema } from "./permission/schema"
 import { PermissionSaved } from "./permission/saved"
 
-export { Effect, Rule, Ruleset } from "./permission/schema"
-type Effect = PermissionSchema.Effect
-type Rule = PermissionSchema.Rule
-type Ruleset = PermissionSchema.Ruleset
-const missingAgentPermissions: Ruleset = [{ action: "*", resource: "*", effect: "deny" }]
+export { Effect, Rule, Ruleset } from "@opencode-ai/schema/permission"
+const missingAgentPermissions: Permission.Ruleset = [{ action: "*", resource: "*", effect: "deny" }]
 
 export const ID = Schema.String.check(Schema.isStartsWith("per")).pipe(
   Schema.brand("PermissionV2.ID"),
@@ -67,7 +64,7 @@ export type ReplyInput = typeof ReplyInput.Type
 
 export const AskResult = Schema.Struct({
   id: ID,
-  effect: PermissionSchema.Effect,
+  effect: Permission.Effect,
 }).annotate({ identifier: "PermissionV2.AskResult" })
 export type AskResult = typeof AskResult.Type
 
@@ -90,7 +87,7 @@ export class CorrectedError extends Schema.TaggedErrorClass<CorrectedError>()("P
 }) {}
 
 export class DeniedError extends Schema.TaggedErrorClass<DeniedError>()("PermissionV2.DeniedError", {
-  rules: PermissionSchema.Ruleset,
+  rules: Permission.Ruleset,
 }) {}
 
 export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("PermissionV2.NotFoundError", {
@@ -99,7 +96,7 @@ export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("Per
 
 export type Error = DeniedError | RejectedError | CorrectedError
 
-export function evaluate(action: string, resource: string, ...rulesets: Ruleset[]): Rule {
+export function evaluate(action: string, resource: string, ...rulesets: Permission.Ruleset[]): Permission.Rule {
   return (
     rulesets
       .flat()
@@ -111,7 +108,7 @@ export function evaluate(action: string, resource: string, ...rulesets: Ruleset[
   )
 }
 
-export function merge(...rulesets: Ruleset[]): Ruleset {
+export function merge(...rulesets: Permission.Ruleset[]): Permission.Ruleset {
   return rulesets.flat()
 }
 
@@ -156,7 +153,7 @@ export const layer = Layer.effect(
 
     const savedRules = EffectRuntime.fnUntraced(function* () {
       return (yield* saved.list({ projectID: location.project.id })).map(
-        (item): Rule => ({ action: item.action, resource: item.resource, effect: "allow" }),
+        (item): Permission.Rule => ({ action: item.action, resource: item.resource, effect: "allow" }),
       )
     })
 
@@ -170,11 +167,11 @@ export const layer = Layer.effect(
       return agent?.permissions ?? missingAgentPermissions
     })
 
-    function denied(input: AssertInput, rules: Ruleset) {
+    function denied(input: AssertInput, rules: Permission.Ruleset) {
       return input.resources.some((resource) => evaluate(input.action, resource, rules).effect === "deny")
     }
 
-    function relevant(input: AssertInput, rules: Ruleset) {
+    function relevant(input: AssertInput, rules: Permission.Ruleset) {
       return rules.filter((rule) => Wildcard.match(input.action, rule.action))
     }
 
@@ -183,7 +180,7 @@ export const layer = Layer.effect(
       if (denied(input, rules)) return { effect: "deny" as const, rules }
       const all = [...rules, ...(yield* savedRules())]
       const effects = input.resources.map((resource) => evaluate(input.action, resource, all).effect)
-      const effect: Effect = effects.includes("deny") ? "deny" : effects.includes("ask") ? "ask" : "allow"
+      const effect: Permission.Effect = effects.includes("deny") ? "deny" : effects.includes("ask") ? "ask" : "allow"
       return { effect, rules: all }
     })
 

+ 0 - 12
packages/core/src/permission/schema.ts

@@ -1,12 +0,0 @@
-export * as PermissionSchema from "./schema"
-
-import { Permission } from "@opencode-ai/schema/permission"
-
-export const Effect = Permission.Effect
-export type Effect = Permission.Effect
-
-export const Rule = Permission.Rule
-export type Rule = Permission.Rule
-
-export const Ruleset = Permission.Ruleset
-export type Ruleset = Permission.Ruleset

+ 1 - 2
packages/core/src/ripgrep.ts

@@ -2,9 +2,8 @@ export * as Ripgrep from "./ripgrep"
 
 import { Context, Effect, Fiber, Layer, Schema, Stream } from "effect"
 import { ChildProcess } from "effect/unstable/process"
-import path from "path"
+import { Entry, Match } from "@opencode-ai/schema/filesystem"
 import { LayerNode } from "./effect/layer-node"
-import { Entry, Match } from "./filesystem/schema"
 import { AppProcess, collectStream, waitForAbort } from "./process"
 import { NonNegativeInt, PositiveInt, RelativePath } from "./schema"
 import { RipgrepBinary } from "./ripgrep/binary"

+ 4 - 2
packages/core/test/shared-schema.test.ts

@@ -49,12 +49,12 @@ test("Core reuses the canonical shared schemas", async () => {
     import("@opencode-ai/core/command"),
     import("@opencode-ai/core/integration/connection"),
     import("@opencode-ai/core/credential"),
-    import("@opencode-ai/core/filesystem/schema"),
+    import("@opencode-ai/core/filesystem"),
     import("@opencode-ai/core/integration"),
     import("@opencode-ai/core/location"),
     import("@opencode-ai/llm"),
     import("@opencode-ai/core/model-request"),
-    import("@opencode-ai/core/permission/schema"),
+    import("@opencode-ai/core/permission"),
     import("@opencode-ai/core/project/schema"),
     import("@opencode-ai/core/reference"),
     import("@opencode-ai/core/session/input"),
@@ -80,6 +80,8 @@ test("Core reuses the canonical shared schemas", async () => {
     [coreFileSystem.Entry, FileSystem.Entry],
     [coreFileSystem.Submatch, FileSystem.Submatch],
     [coreFileSystem.Match, FileSystem.Match],
+    [coreIntegration.ID, Integration.ID],
+    [coreIntegration.MethodID, Integration.MethodID],
     [coreIntegration.When, Integration.When],
     [coreIntegration.TextPrompt, Integration.TextPrompt],
     [coreIntegration.SelectPrompt, Integration.SelectPrompt],