feat(core): add opencode integration (#33555)

This commit is contained in:
Dax 2026-06-23 19:45:02 -04:00 committed by GitHub
commit cf80b5c470
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1061 additions and 439 deletions

View file

@ -13,6 +13,8 @@
"./tool": "./src/tool.ts",
"./tui": "./src/tui.ts",
"./v2/effect": "./src/v2/effect/index.ts",
"./v2/effect/integration": "./src/v2/effect/integration.ts",
"./v2/effect/plugin": "./src/v2/effect/plugin.ts",
"./v2/promise": "./src/v2/promise/index.ts"
},
"files": [

View file

@ -1,13 +1,41 @@
import type {
ConnectionInfo,
CredentialOAuth,
CredentialValue,
IntegrationEnvMethod,
IntegrationInfo,
IntegrationInputs,
IntegrationKeyMethod,
IntegrationMethod,
IntegrationOAuthMethod,
IntegrationRef,
} from "@opencode-ai/sdk/v2/types"
import type { Effect, Scope } from "effect"
import type { Hooks } from "./registration.js"
export type IntegrationMethod = IntegrationOAuthMethod | IntegrationKeyMethod | IntegrationEnvMethod
export type IntegrationOAuthAuthorization = {
readonly url: string
readonly instructions: string
} & (
| {
readonly mode: "auto"
readonly callback: Effect.Effect<CredentialOAuth, unknown>
}
| {
readonly mode: "code"
readonly callback: (code: string) => Effect.Effect<CredentialOAuth, unknown>
}
)
export type IntegrationOAuthMethodRegistration = {
readonly integrationID: string
readonly method: IntegrationOAuthMethod
readonly authorize: (
inputs: IntegrationInputs,
) => Effect.Effect<IntegrationOAuthAuthorization, unknown, Scope.Scope>
readonly refresh?: (credential: CredentialOAuth) => Effect.Effect<CredentialOAuth, unknown>
readonly label?: (credential: CredentialOAuth) => string | undefined
}
export type IntegrationMethodRegistration =
| IntegrationOAuthMethodRegistration
| {
readonly integrationID: string
readonly method: IntegrationKeyMethod
@ -18,9 +46,9 @@ export type IntegrationMethodRegistration =
}
export interface IntegrationDraft {
list(): readonly Pick<IntegrationInfo, "id" | "name">[]
get(id: string): Pick<IntegrationInfo, "id" | "name"> | undefined
update(id: string, update: (integration: Pick<IntegrationInfo, "id" | "name">) => void): void
list(): readonly IntegrationRef[]
get(id: string): IntegrationRef | undefined
update(id: string, update: (integration: IntegrationRef) => void): void
remove(id: string): void
readonly method: {
list(integrationID: string): readonly IntegrationMethod[]
@ -29,6 +57,9 @@ export interface IntegrationDraft {
}
}
export type IntegrationHooks = Hooks<{
transform: IntegrationDraft
}>
export interface IntegrationHooks extends Hooks<{ transform: IntegrationDraft }> {
readonly connection: {
readonly active: (integrationID: string) => Effect.Effect<ConnectionInfo | undefined>
readonly resolve: (connection: ConnectionInfo) => Effect.Effect<CredentialValue | undefined, unknown>
}
}

View file

@ -1,12 +1,12 @@
import type { Effect, Scope } from "effect"
import type { PluginContext } from "./context.js"
export interface Plugin {
export interface Plugin<R = Scope.Scope> {
readonly id: string
readonly effect: (context: PluginContext) => Effect.Effect<void, never, Scope.Scope>
readonly effect: (context: PluginContext) => Effect.Effect<void, never, R>
}
export function define(plugin: Plugin) {
export function define<R = Scope.Scope>(plugin: Plugin<R>) {
return plugin
}

View file

@ -1,14 +1,9 @@
import type { SkillV2Info } from "@opencode-ai/sdk/v2/types"
import type { SkillV2Source } from "@opencode-ai/sdk/v2/types"
import type { Hooks } from "./registration.js"
export type SkillSource =
| { readonly type: "directory"; readonly path: string }
| { readonly type: "url"; readonly url: string }
| { readonly type: "embedded"; readonly skill: SkillV2Info }
export interface SkillDraft {
source(source: SkillSource): void
list(): readonly SkillSource[]
source(source: SkillV2Source): void
list(): readonly SkillV2Source[]
}
export type SkillHooks = Hooks<{

View file

@ -10,8 +10,7 @@ export type { CommandDraft, CommandHooks } from "./command.js"
export type {
IntegrationDraft,
IntegrationHooks,
IntegrationMethod,
IntegrationMethodRegistration,
} from "./integration.js"
export type { ReferenceDraft, ReferenceHooks } from "./reference.js"
export type { SkillDraft, SkillHooks, SkillSource } from "./skill.js"
export type { SkillDraft, SkillHooks } from "./skill.js"

View file

@ -1,8 +1,19 @@
import type { IntegrationDraft, IntegrationMethod, IntegrationMethodRegistration } from "../effect/integration.js"
import type {
IntegrationDraft,
IntegrationMethodRegistration,
} from "../effect/integration.js"
import type { CredentialValue } from "@opencode-ai/sdk/v2/types"
import type { Hooks } from "./registration.js"
export type { IntegrationDraft, IntegrationMethod, IntegrationMethodRegistration }
export type { IntegrationDraft, IntegrationMethodRegistration }
export type IntegrationHooks = Hooks<{
transform: IntegrationDraft
}>
export interface IntegrationHooks extends Hooks<{ transform: IntegrationDraft }> {
readonly connection: {
readonly active: (
integrationID: string,
) => Promise<import("@opencode-ai/sdk/v2/types").ConnectionInfo | undefined>
readonly resolve: (
connection: import("@opencode-ai/sdk/v2/types").ConnectionInfo,
) => Promise<CredentialValue | undefined>
}
}

View file

@ -1,7 +1,7 @@
import type { SkillDraft, SkillSource } from "../effect/skill.js"
import type { SkillDraft } from "../effect/skill.js"
import type { Hooks } from "./registration.js"
export type { SkillDraft, SkillSource }
export type { SkillDraft }
export type SkillHooks = Hooks<{
transform: SkillDraft