mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 658008fc54 | |||
| cd49956028 | |||
| fa9277de20 |
@@ -10,6 +10,7 @@ import { ConfigCompaction } from "@opencode/schema/config/compaction"
|
||||
import { ConfigFormatter } from "@opencode/schema/config/formatter"
|
||||
import { ConfigLSP } from "@opencode/schema/config/lsp"
|
||||
import { ConfigMedia } from "@opencode/schema/config/media"
|
||||
import { ConfigModel } from "@opencode/schema/config/model"
|
||||
import { ConfigMCP } from "@opencode/schema/config/mcp"
|
||||
import { ConfigPlugin } from "@opencode/schema/config/plugin"
|
||||
import { ConfigPolicy } from "@opencode/schema/config/policy"
|
||||
@@ -18,13 +19,11 @@ import { ConfigReference } from "@opencode/schema/config/reference"
|
||||
import { ConfigExperimental } from "@opencode/schema/config/experimental"
|
||||
import { Permission } from "@opencode/schema/permission"
|
||||
import { ConfigAgentV1 } from "../v1/config/agent.js"
|
||||
import { ConfigAttachmentV1 } from "../v1/config/attachment.js"
|
||||
import { ConfigCommandV1 } from "../v1/config/command.js"
|
||||
import { ConfigMCPV1 } from "../v1/config/mcp.js"
|
||||
import { ConfigPermissionV1 } from "../v1/config/permission.js"
|
||||
import { ConfigPluginV1 } from "../v1/config/plugin.js"
|
||||
import { ConfigProviderV1 } from "../v1/config/provider.js"
|
||||
import { ConfigV1 } from "../v1/config/config.js"
|
||||
import { ConfigMigrateV1 } from "../v1/config/migrate.js"
|
||||
import { PositiveInt } from "../schema.js"
|
||||
|
||||
@@ -53,6 +52,7 @@ const unsupportedExperimental = [
|
||||
] as const
|
||||
const unsupportedProvider = ["id", "whitelist", "blacklist"] as const
|
||||
const unsupportedModel = ["release_date", "attachment", "reasoning", "temperature", "experimental"] as const
|
||||
const LegacyAutoupdate = Schema.Literals([true, false, "notify"])
|
||||
|
||||
export function normalize(input: unknown): Result {
|
||||
if (!isRecord(input))
|
||||
@@ -71,7 +71,7 @@ export function normalize(input: unknown): Result {
|
||||
? decodeEncoded(Schema.Boolean, input.snapshot, ["snapshot"], diagnostics)
|
||||
: undefined
|
||||
const legacyUpdate = own(input, "autoupdate")
|
||||
? decodeValue(ConfigV1.Info.fields.autoupdate, input.autoupdate, ["autoupdate"], diagnostics)
|
||||
? decodeValue(LegacyAutoupdate, input.autoupdate, ["autoupdate"], diagnostics)
|
||||
: undefined
|
||||
const nativeUpdate = own(input, "update")
|
||||
? decodeEncoded(Info.fields.update, input.update, ["update"], diagnostics)
|
||||
@@ -82,15 +82,18 @@ export function normalize(input: unknown): Result {
|
||||
: undefined
|
||||
: undefined
|
||||
const legacyMedia = own(input, "attachment")
|
||||
? decodeValue(ConfigAttachmentV1.Info, input.attachment, ["attachment"], diagnostics)
|
||||
? decodeEncoded(ConfigMedia.Info, input.attachment, ["attachment"], diagnostics)
|
||||
: undefined
|
||||
if (legacyMedia !== undefined) {
|
||||
const migrated = ConfigMigrateV1.migrate({ attachment: legacyMedia }).media
|
||||
if (migrated !== undefined) encoded.media = canonical(ConfigMedia.Info, migrated)
|
||||
}
|
||||
if (legacyMedia !== undefined) encoded.media = legacyMedia
|
||||
if (legacySnapshots !== undefined) encoded.snapshots = legacySnapshots
|
||||
const migratedUpdate =
|
||||
legacyUpdate === undefined ? undefined : ConfigMigrateV1.migrate({ autoupdate: legacyUpdate }).update
|
||||
legacyUpdate === false
|
||||
? "disable"
|
||||
: legacyUpdate === "notify"
|
||||
? "notify"
|
||||
: legacyUpdate === true
|
||||
? "auto"
|
||||
: undefined
|
||||
const update = prefer(migratedUpdate, nativeUpdate, ["update"], diagnostics)
|
||||
if (update !== undefined) encoded.update = update
|
||||
if (legacyShare !== undefined) encoded.share = legacyShare
|
||||
@@ -135,9 +138,7 @@ export function normalize(input: unknown): Result {
|
||||
const legacySmallModel = own(input, "small_model")
|
||||
? decodeValue(Schema.String, input.small_model, ["small_model"], diagnostics)
|
||||
: undefined
|
||||
const migratedSmallModel = legacySmallModel
|
||||
? ConfigMigrateV1.migrate({ small_model: legacySmallModel }).agents?.title?.model
|
||||
: undefined
|
||||
const migratedSmallModel = ConfigMigrateV1.modelSelection(legacySmallModel)
|
||||
if (legacySmallModel && !migratedSmallModel)
|
||||
diagnostics.push({
|
||||
kind: "unsupported",
|
||||
@@ -197,10 +198,14 @@ export function normalize(input: unknown): Result {
|
||||
normalizeFormatter(input, encoded, diagnostics)
|
||||
normalizeLsp(input, encoded, diagnostics)
|
||||
|
||||
const model = own(input, "model")
|
||||
? decodeValue(ConfigModel.Selection, input.model, ["model"], diagnostics)
|
||||
: undefined
|
||||
if (model) encoded.model = { ...model, providerID: ConfigMigrateV1.providerID(model.providerID) }
|
||||
|
||||
const nativeAtomic = {
|
||||
$schema: Info.fields.$schema,
|
||||
shell: Info.fields.shell,
|
||||
model: Info.fields.model,
|
||||
default_agent: Info.fields.default_agent,
|
||||
share: Info.fields.share,
|
||||
enterprise: Info.fields.enterprise,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export * as ConfigMigrateV1 from "./migrate.js"
|
||||
|
||||
import { Info } from "@opencode/schema/config"
|
||||
import { ConfigAgent } from "@opencode/schema/config/agent"
|
||||
import { Schema } from "effect"
|
||||
import { ConfigV1 } from "./config.js"
|
||||
import { ConfigAgentV1 } from "./agent.js"
|
||||
import { ConfigCommandV1 } from "./command.js"
|
||||
import { ConfigMCPV1 } from "./mcp.js"
|
||||
@@ -14,102 +12,16 @@ import { Provider } from "../../provider.js"
|
||||
import { Model } from "../../model.js"
|
||||
|
||||
const decodeOptions = { errors: "all", onExcessProperty: "ignore", propertyOrder: "original" } as const
|
||||
const decodeInfo = Schema.decodeUnknownSync(Schema.fromJsonString(Info), decodeOptions)
|
||||
const encodeInfo = Schema.encodeSync(Info)
|
||||
const decodeAgent = Schema.decodeUnknownSync(Schema.fromJsonString(ConfigAgent.Info), decodeOptions)
|
||||
const encodeAgent = Schema.encodeSync(ConfigAgent.Info)
|
||||
export function migrate(info: typeof ConfigV1.Info.Type) {
|
||||
return encodeInfo(
|
||||
decodeInfo(
|
||||
JSON.stringify({
|
||||
$schema: info.$schema,
|
||||
shell: info.shell,
|
||||
model: modelSelection(info.model),
|
||||
default_agent: info.default_agent,
|
||||
update:
|
||||
info.autoupdate === false
|
||||
? "disable"
|
||||
: info.autoupdate === "notify"
|
||||
? "notify"
|
||||
: info.autoupdate === true
|
||||
? "auto"
|
||||
: undefined,
|
||||
share: info.share ?? (info.autoshare ? "auto" : undefined),
|
||||
enterprise: info.enterprise,
|
||||
username: info.username,
|
||||
permissions: permissions(info.permission, info.tools),
|
||||
agents: agents(info),
|
||||
snapshots: info.snapshot,
|
||||
watcher: info.watcher,
|
||||
formatter: info.formatter,
|
||||
lsp: info.lsp,
|
||||
media: info.attachment,
|
||||
tool_output: info.tool_output,
|
||||
mcp: mcp(info),
|
||||
compaction: info.compaction && {
|
||||
auto: info.compaction.auto,
|
||||
prune: info.compaction.prune,
|
||||
keep: {
|
||||
tokens: info.compaction.preserve_recent_tokens,
|
||||
},
|
||||
buffer: info.compaction.reserved,
|
||||
},
|
||||
skills: info.skills && [...(info.skills.paths ?? []), ...(info.skills.urls ?? [])],
|
||||
commands: commands(info.command),
|
||||
instructions: info.instructions,
|
||||
references: info.references ?? info.reference,
|
||||
experimental: experimental(info),
|
||||
plugins: info.plugin?.map((plugin) =>
|
||||
typeof plugin === "string" ? plugin : { package: plugin[0], options: plugin[1] },
|
||||
),
|
||||
providers: providers(info.provider),
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
function experimental(info: typeof ConfigV1.Info.Type) {
|
||||
const policies = [
|
||||
...(info.enabled_providers === undefined
|
||||
? []
|
||||
: [
|
||||
{ action: "provider.use" as const, resource: "*", effect: "deny" as const },
|
||||
...info.enabled_providers.map((resource) => ({
|
||||
action: "provider.use" as const,
|
||||
resource: providerID(resource),
|
||||
effect: "allow" as const,
|
||||
})),
|
||||
]),
|
||||
...(info.disabled_providers ?? []).map((resource) => ({
|
||||
action: "provider.use" as const,
|
||||
resource: providerID(resource),
|
||||
effect: "deny" as const,
|
||||
})),
|
||||
]
|
||||
if (info.experimental?.subagent_depth === undefined && !policies.length) return
|
||||
return {
|
||||
subagent_depth: info.experimental?.subagent_depth,
|
||||
policies: policies.length ? policies : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
function permissions(info?: ConfigPermissionV1.Info, tools?: Readonly<Record<string, boolean>>) {
|
||||
const rules: Array<{ action: string; resource: string; effect: ConfigPermissionV1.Action }> = Object.entries(
|
||||
tools ?? {},
|
||||
).map(([action, enabled]) => ({
|
||||
action: normalizeAction(action),
|
||||
resource: "*",
|
||||
effect: enabled ? ("allow" as const) : ("deny" as const),
|
||||
}))
|
||||
for (const [key, rule] of Object.entries(info ?? {})) {
|
||||
if (!rule) continue
|
||||
function permissions(info?: ConfigPermissionV1.Info) {
|
||||
const rules = Object.entries(info ?? {}).flatMap(([key, rule]) => {
|
||||
if (!rule) return []
|
||||
const action = normalizeAction(key)
|
||||
if (typeof rule === "string") {
|
||||
rules.push({ action, resource: "*", effect: rule })
|
||||
continue
|
||||
}
|
||||
rules.push(...Object.entries(rule).map(([resource, effect]) => ({ action, resource, effect })))
|
||||
}
|
||||
if (typeof rule === "string") return [{ action, resource: "*", effect: rule }]
|
||||
return Object.entries(rule).map(([resource, effect]) => ({ action, resource, effect }))
|
||||
})
|
||||
return rules.length ? rules : undefined
|
||||
}
|
||||
|
||||
@@ -121,23 +33,6 @@ export function normalizeAction(action: string) {
|
||||
return action
|
||||
}
|
||||
|
||||
function agents(info: typeof ConfigV1.Info.Type) {
|
||||
const entries = [
|
||||
...Object.entries(info.agent ?? {}),
|
||||
...Object.entries(info.mode ?? {}).map(([name, agent]) => [name, { ...agent, mode: "primary" as const }] as const),
|
||||
]
|
||||
const result = Object.fromEntries(entries.flatMap(([name, agent]) => (agent ? [[name, migrateAgent(agent)]] : [])))
|
||||
const small = modelSelection(info.small_model)
|
||||
if (!small) return entries.length ? result : undefined
|
||||
return {
|
||||
...result,
|
||||
title: {
|
||||
model: small,
|
||||
...result.title,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function migrateAgent(info: ConfigAgentV1.Info) {
|
||||
const body = {
|
||||
...info.options,
|
||||
@@ -178,7 +73,7 @@ export function commands(info?: Readonly<Record<string, ConfigCommandV1.Info>>)
|
||||
)
|
||||
}
|
||||
|
||||
function modelSelection(input?: string, variant?: string) {
|
||||
export function modelSelection(input?: string, variant?: string) {
|
||||
if (input === undefined || !/^[^/#]+\/[^#]+$/.test(input)) return undefined
|
||||
const separator = input.indexOf("/")
|
||||
return {
|
||||
@@ -188,17 +83,6 @@ function modelSelection(input?: string, variant?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function mcp(info: typeof ConfigV1.Info.Type) {
|
||||
const servers = Object.fromEntries(
|
||||
Object.entries(info.mcp ?? {}).flatMap(([name, server]) =>
|
||||
"type" in server ? [[name, migrateMcp(server)] as const] : [],
|
||||
),
|
||||
)
|
||||
const timeout = info.experimental?.mcp_timeout
|
||||
if (!timeout && !Object.keys(servers).length) return undefined
|
||||
return { timeout: timeout === undefined ? undefined : { catalog: timeout, execution: timeout }, servers }
|
||||
}
|
||||
|
||||
export function migrateMcp(info: ConfigMCPV1.Info) {
|
||||
const disabled = info.enabled === undefined ? undefined : !info.enabled
|
||||
if (info.type === "local")
|
||||
@@ -226,18 +110,6 @@ export function migrateMcp(info: ConfigMCPV1.Info) {
|
||||
}
|
||||
}
|
||||
|
||||
function providers(info?: Readonly<Record<string, ConfigProviderV1.Info>>) {
|
||||
if (!info) return undefined
|
||||
return Object.fromEntries(
|
||||
Object.entries(info).flatMap(([name, provider]) => {
|
||||
const id = providerID(name)
|
||||
// If both names are present, keep the settings under the current name and ignore the old one.
|
||||
if (id !== name && info[id]) return []
|
||||
return [[id, migrateProvider(name, provider)]]
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export function migrateProvider(sourceID: string, info: ConfigProviderV1.Info) {
|
||||
if (sourceID === "azure-cognitive-services") return migrateAzureCognitiveServicesProvider(info)
|
||||
if (sourceID === "google-vertex-anthropic") return migrateGoogleVertexAnthropicProvider(info)
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Global } from "@opencode/util/global"
|
||||
import { Permission } from "@opencode/core/permission"
|
||||
import { AgentPlugin } from "@opencode/core/plugin/agent"
|
||||
import { AbsolutePath } from "@opencode/core/schema"
|
||||
import { ConfigMigrateV1 } from "@opencode/core/v1/config/migrate"
|
||||
import { ConfigNormalize } from "@opencode/core/config/normalize"
|
||||
import { ConfigAgentV1 } from "@opencode/core/v1/config/agent"
|
||||
import { advance, drain } from "../lib/clock"
|
||||
import { tmpdir, tmpdirScoped } from "../fixture/tmpdir"
|
||||
@@ -24,6 +24,12 @@ import { agentHost, host } from "../plugin/host"
|
||||
|
||||
const it = testEffect(AppNodeBuilder.build(LayerNode.group([Agent.node, Bus.node, FSUtil.node, Global.node])))
|
||||
const decode = Schema.decodeUnknownSync(Info)
|
||||
|
||||
function migrateV1(input: unknown) {
|
||||
const result = ConfigNormalize.normalize(input)
|
||||
if (result.type !== "normalized") throw new Error("expected normalized config")
|
||||
return result.encoded
|
||||
}
|
||||
const defaultPermissions = (global: Global.Interface): Permission.Ruleset => [
|
||||
...Agent.Info.default(Agent.ID.make("test")).permissions,
|
||||
{ action: "external_directory", resource: path.join(global.data, "shell", "*", "*"), effect: "allow" },
|
||||
@@ -171,7 +177,7 @@ permissions:
|
||||
new Document({
|
||||
type: "document",
|
||||
info: decode(
|
||||
ConfigMigrateV1.migrate({
|
||||
migrateV1({
|
||||
permission: {
|
||||
bash: "ask",
|
||||
edit: "ask",
|
||||
@@ -752,7 +758,7 @@ function loadHomePermissions(home: string) {
|
||||
new Document({
|
||||
type: "document",
|
||||
info: decode(
|
||||
ConfigMigrateV1.migrate({
|
||||
migrateV1({
|
||||
permission: {
|
||||
external_directory: {
|
||||
"~/p/**": "allow",
|
||||
|
||||
@@ -11,8 +11,7 @@ import { AppNodeBuilder } from "@opencode/core/effect/app-node-builder"
|
||||
import { makeGlobalNode } from "@opencode/util/effect/app-node"
|
||||
import { LayerNode } from "@opencode/util/effect/layer-node"
|
||||
import { Credential } from "@opencode/core/credential"
|
||||
import { ConfigMigrateV1 } from "@opencode/core/v1/config/migrate"
|
||||
import { ConfigV1 } from "@opencode/core/v1/config/config"
|
||||
import { ConfigV1 } from "../fixture/v1-config/config"
|
||||
import { ConfigNormalize } from "@opencode/core/config/normalize"
|
||||
import { Watcher } from "@opencode/core/filesystem/watcher"
|
||||
import { Bus } from "@opencode/core/bus"
|
||||
@@ -30,6 +29,18 @@ import { testEffect } from "../lib/effect"
|
||||
|
||||
const it = testEffect(Layer.empty)
|
||||
const selection = Schema.decodeUnknownSync(ConfigModel.Selection)
|
||||
const decodeInfo = Schema.decodeUnknownSync(Info, {
|
||||
errors: "all",
|
||||
onExcessProperty: "ignore",
|
||||
propertyOrder: "original",
|
||||
})
|
||||
const encodeInfo = Schema.encodeSync(Info)
|
||||
|
||||
function migrateV1(input: unknown) {
|
||||
const result = ConfigNormalize.normalize(input)
|
||||
if (result.type !== "normalized") throw new Error("expected normalized config")
|
||||
return encodeInfo(decodeInfo(result.encoded))
|
||||
}
|
||||
|
||||
function inFixture(root: string, target: string) {
|
||||
const relative = path.relative(root, target)
|
||||
@@ -634,24 +645,19 @@ describe("Config", () => {
|
||||
test("migrates arbitrary v1 configuration into valid v2 configuration", () => {
|
||||
FastCheck.assert(
|
||||
FastCheck.property(Schema.toArbitrary(ConfigV1.Info)(FastCheck), (info) => {
|
||||
const parsed = Schema.decodeUnknownSync(ConfigV1.Info)(
|
||||
Schema.decodeUnknownSync(Schema.fromJsonString(Schema.Unknown))(
|
||||
Schema.encodeUnknownSync(Schema.fromJsonString(Schema.Unknown))(info),
|
||||
),
|
||||
)
|
||||
Schema.decodeUnknownSync(Info)(ConfigMigrateV1.migrate(parsed), { errors: "all" })
|
||||
migrateV1(JSON.parse(JSON.stringify(info)))
|
||||
}),
|
||||
{ numRuns: 100 },
|
||||
)
|
||||
}, 30_000)
|
||||
|
||||
test("migrates the v1 experimental subagent depth", () => {
|
||||
expect(ConfigMigrateV1.migrate({ experimental: { subagent_depth: 2 } }).experimental?.subagent_depth).toBe(2)
|
||||
expect(migrateV1({ experimental: { subagent_depth: 2 } }).experimental?.subagent_depth).toBe(2)
|
||||
})
|
||||
|
||||
test("migrates the v1 small model to the title agent", () => {
|
||||
expect(
|
||||
ConfigMigrateV1.migrate({
|
||||
migrateV1({
|
||||
small_model: "anthropic/claude-haiku-4-5",
|
||||
agent: { title: { prompt: "Custom title prompt" } },
|
||||
}).agents?.title,
|
||||
@@ -662,10 +668,10 @@ describe("Config", () => {
|
||||
})
|
||||
|
||||
test("migrates the v1 update policy", () => {
|
||||
expect(ConfigMigrateV1.migrate({ autoupdate: false }).update).toBe("disable")
|
||||
expect(ConfigMigrateV1.migrate({ autoupdate: "notify" }).update).toBe("notify")
|
||||
expect(ConfigMigrateV1.migrate({ autoupdate: true }).update).toBe("auto")
|
||||
expect(ConfigMigrateV1.migrate({}).update).toBeUndefined()
|
||||
expect(migrateV1({ autoupdate: false }).update).toBe("disable")
|
||||
expect(migrateV1({ autoupdate: "notify" }).update).toBe("notify")
|
||||
expect(migrateV1({ autoupdate: true }).update).toBe("auto")
|
||||
expect(migrateV1({}).update).toBeUndefined()
|
||||
})
|
||||
|
||||
test("normalizes the native auto update policy", () => {
|
||||
@@ -678,7 +684,7 @@ describe("Config", () => {
|
||||
|
||||
test("migrates v1 provider lists to policies", () => {
|
||||
expect(
|
||||
ConfigMigrateV1.migrate({
|
||||
migrateV1({
|
||||
enabled_providers: ["anthropic", "openai"],
|
||||
disabled_providers: ["openai"],
|
||||
}).experimental?.policies,
|
||||
@@ -688,13 +694,13 @@ describe("Config", () => {
|
||||
{ action: "provider.use", resource: "openai", effect: "allow" },
|
||||
{ action: "provider.use", resource: "openai", effect: "deny" },
|
||||
])
|
||||
expect(ConfigMigrateV1.migrate({ enabled_providers: [] }).experimental?.policies).toEqual([
|
||||
expect(migrateV1({ enabled_providers: [] }).experimental?.policies).toEqual([
|
||||
{ action: "provider.use", resource: "*", effect: "deny" },
|
||||
])
|
||||
})
|
||||
|
||||
test("migrates v1 provider setup options into AISDK settings", () => {
|
||||
const migrated = ConfigMigrateV1.migrate({
|
||||
const migrated = migrateV1({
|
||||
provider: {
|
||||
bedrock: {
|
||||
npm: "@ai-sdk/amazon-bedrock",
|
||||
@@ -719,7 +725,7 @@ describe("Config", () => {
|
||||
})
|
||||
|
||||
test("renames old provider IDs while migrating v1 configuration", () => {
|
||||
const migrated = ConfigMigrateV1.migrate({
|
||||
const migrated = migrateV1({
|
||||
model: "azure-cognitive-services/deployment",
|
||||
enabled_providers: ["google-vertex-anthropic"],
|
||||
disabled_providers: ["azure-cognitive-services"],
|
||||
@@ -768,7 +774,7 @@ describe("Config", () => {
|
||||
})
|
||||
|
||||
test("preserves the generated base URL for v1 Azure OpenAI-compatible providers", () => {
|
||||
const migrated = ConfigMigrateV1.migrate({
|
||||
const migrated = migrateV1({
|
||||
provider: {
|
||||
"azure-cognitive-services": {
|
||||
npm: "@ai-sdk/openai-compatible",
|
||||
@@ -787,7 +793,7 @@ describe("Config", () => {
|
||||
})
|
||||
|
||||
test("ignores old provider IDs when the current provider ID is configured", () => {
|
||||
const migrated = ConfigMigrateV1.migrate({
|
||||
const migrated = migrateV1({
|
||||
provider: {
|
||||
azure: { models: { current: {} } },
|
||||
"azure-cognitive-services": { models: { legacy: {} } },
|
||||
@@ -801,7 +807,7 @@ describe("Config", () => {
|
||||
})
|
||||
|
||||
test("preserves the built-in package for v1 Vertex Anthropic custom models", () => {
|
||||
const migrated = ConfigMigrateV1.migrate({
|
||||
const migrated = migrateV1({
|
||||
provider: {
|
||||
"google-vertex-anthropic": {
|
||||
models: { claude: {} },
|
||||
@@ -816,7 +822,7 @@ describe("Config", () => {
|
||||
})
|
||||
|
||||
test("migrates v1 interleaved fields to compatibility", () => {
|
||||
const migrated = ConfigMigrateV1.migrate({
|
||||
const migrated = migrateV1({
|
||||
provider: {
|
||||
custom: {
|
||||
models: {
|
||||
@@ -838,7 +844,7 @@ describe("Config", () => {
|
||||
for (const subtask of [true, false]) {
|
||||
test(`migrates v1 command configuration with subtask: ${subtask}`, () => {
|
||||
expect(
|
||||
ConfigMigrateV1.migrate({
|
||||
migrateV1({
|
||||
command: {
|
||||
review: {
|
||||
template: "Review changes",
|
||||
@@ -864,7 +870,7 @@ describe("Config", () => {
|
||||
|
||||
test("normalizes renamed permission actions when migrating v1 permissions", () => {
|
||||
expect(
|
||||
ConfigMigrateV1.migrate({
|
||||
migrateV1({
|
||||
permission: {
|
||||
task: "ask",
|
||||
bash: { "git status": "allow", "*": "deny" },
|
||||
|
||||
@@ -149,6 +149,21 @@ describe("ConfigNormalize", () => {
|
||||
expect(() => Schema.decodeUnknownSync(Info)(result.encoded)).not.toThrow()
|
||||
})
|
||||
|
||||
test("renames legacy provider IDs in the top-level model", () => {
|
||||
expect(normalized({ model: "google-vertex-anthropic/claude#high" }).encoded.model).toEqual({
|
||||
providerID: "google-vertex",
|
||||
model: "claude",
|
||||
variant: "high",
|
||||
})
|
||||
expect(
|
||||
normalized({ model: { providerID: "azure-cognitive-services", model: "deployment" } }).encoded.model,
|
||||
).toEqual({ providerID: "azure", model: "deployment" })
|
||||
expect(normalized({ model: "anthropic/claude" }).encoded.model).toEqual({
|
||||
providerID: "anthropic",
|
||||
model: "claude",
|
||||
})
|
||||
})
|
||||
|
||||
test("migrates the legacy small model to the title agent", () => {
|
||||
const result = normalized({ small_model: "anthropic/claude-haiku-4-5" })
|
||||
expect(result.encoded.agents).toEqual({
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
export * as ConfigAttachmentV1 from "./attachment.js"
|
||||
export * as ConfigAttachmentV1 from "./attachment"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { PositiveInt } from "../../schema.js"
|
||||
import { PositiveInt } from "@opencode/core/schema"
|
||||
|
||||
export const Image = Schema.Struct({
|
||||
auto_resize: Schema.optional(Schema.Boolean).annotate({
|
||||
+14
-23
@@ -1,27 +1,20 @@
|
||||
export * as ConfigV1 from "./config.js"
|
||||
export * as ConfigV1 from "./config"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { ConfigReference } from "@opencode/schema/config/reference"
|
||||
import { NonNegativeInt, PositiveInt, type DeepMutable } from "../../schema.js"
|
||||
import { ConfigAgentV1 } from "./agent.js"
|
||||
import { ConfigAttachmentV1 } from "./attachment.js"
|
||||
import { ConfigCommandV1 } from "./command.js"
|
||||
import { ConfigFormatterV1 } from "./formatter.js"
|
||||
import { ConfigLayoutV1 } from "./layout.js"
|
||||
import { ConfigLSPV1 } from "./lsp.js"
|
||||
import { ConfigMCPV1 } from "./mcp.js"
|
||||
import { ConfigPermissionV1 } from "./permission.js"
|
||||
import { ConfigPluginV1 } from "./plugin.js"
|
||||
import { ConfigProviderV1 } from "./provider.js"
|
||||
import { ConfigServerV1 } from "./server.js"
|
||||
import { ConfigSkillsV1 } from "./skills.js"
|
||||
|
||||
export type Layout = ConfigLayoutV1.Layout
|
||||
|
||||
export const WellKnown = Schema.Struct({
|
||||
config: Schema.optional(Schema.Json),
|
||||
remote_config: Schema.optional(Schema.Json),
|
||||
})
|
||||
import { NonNegativeInt, PositiveInt } from "@opencode/core/schema"
|
||||
import { ConfigAgentV1 } from "@opencode/core/v1/config/agent"
|
||||
import { ConfigAttachmentV1 } from "./attachment"
|
||||
import { ConfigCommandV1 } from "@opencode/core/v1/config/command"
|
||||
import { ConfigFormatterV1 } from "./formatter"
|
||||
import { ConfigLayoutV1 } from "./layout"
|
||||
import { ConfigLSPV1 } from "./lsp"
|
||||
import { ConfigMCPV1 } from "@opencode/core/v1/config/mcp"
|
||||
import { ConfigPermissionV1 } from "@opencode/core/v1/config/permission"
|
||||
import { ConfigPluginV1 } from "@opencode/core/v1/config/plugin"
|
||||
import { ConfigProviderV1 } from "@opencode/core/v1/config/provider"
|
||||
import { ConfigServerV1 } from "./server"
|
||||
import { ConfigSkillsV1 } from "./skills"
|
||||
|
||||
const LogLevelRef = Schema.Literals(["DEBUG", "INFO", "WARN", "ERROR"]).annotate({
|
||||
identifier: "LogLevel",
|
||||
@@ -187,5 +180,3 @@ export const Info = Schema.Struct({
|
||||
}),
|
||||
),
|
||||
}).annotate({ identifier: "Config" })
|
||||
|
||||
export type Info = DeepMutable<Schema.Schema.Type<typeof Info>>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export * as ConfigFormatterV1 from "./formatter.js"
|
||||
export * as ConfigFormatterV1 from "./formatter"
|
||||
|
||||
import { Schema } from "effect"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export * as ConfigLayoutV1 from "./layout.js"
|
||||
export * as ConfigLayoutV1 from "./layout"
|
||||
|
||||
import { Schema } from "effect"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * as ConfigLSPV1 from "./lsp.js"
|
||||
export * as ConfigLSPV1 from "./lsp"
|
||||
|
||||
import { Schema } from "effect"
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
export * as ConfigServerV1 from "./server.js"
|
||||
export * as ConfigServerV1 from "./server"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { PositiveInt } from "../../schema.js"
|
||||
import { PositiveInt } from "@opencode/core/schema"
|
||||
|
||||
export const Server = Schema.Struct({
|
||||
port: Schema.optional(PositiveInt).annotate({
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export * as ConfigSkillsV1 from "./skills.js"
|
||||
export * as ConfigSkillsV1 from "./skills"
|
||||
|
||||
import { Schema } from "effect"
|
||||
|
||||
Reference in New Issue
Block a user