Buckets:
| 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 { 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" | |
| import { ConfigAttachments } from "./config/attachments" | |
| import { ConfigCompaction } from "./config/compaction" | |
| import { ConfigCommand } from "./config/command" | |
| import { ConfigExperimental } from "./config/experimental" | |
| import { ConfigFormatter } from "./config/formatter" | |
| import { ConfigLSP } from "./config/lsp" | |
| import { ConfigMCP } from "./config/mcp" | |
| import { ConfigPlugin } from "./config/plugin" | |
| import { ConfigProvider } from "./config/provider" | |
| import { ConfigReference } from "./config/reference" | |
| import { ConfigToolOutput } from "./config/tool-output" | |
| import { ConfigWatcher } from "./config/watcher" | |
| import { ConfigV1 } from "./v1/config/config" | |
| import { ConfigMigrateV1 } from "./v1/config/migrate" | |
| export class Info extends Schema.Class<Info>("Config.Info")({ | |
| $schema: Schema.optional(Schema.String).annotate({ | |
| description: "JSON schema reference for configuration validation", | |
| }), | |
| shell: Schema.String.pipe(Schema.optional).annotate({ | |
| description: "Default shell to use for terminal and shell tool execution", | |
| }), | |
| model: Schema.String.pipe(Schema.optional).annotate({ | |
| description: "Default model to use when no session or agent model is selected", | |
| }), | |
| default_agent: Schema.String.pipe(Schema.optional).annotate({ | |
| description: "Default primary agent to use when no session agent is selected", | |
| }), | |
| autoupdate: Schema.Union([Schema.Boolean, Schema.Literal("notify")]) | |
| .pipe(Schema.optional) | |
| .annotate({ | |
| description: "Automatically update or notify when a new version is available", | |
| }), | |
| share: Schema.Literals(["manual", "auto", "disabled"]).pipe(Schema.optional).annotate({ | |
| description: "Control whether sessions may be shared manually, automatically, or not at all", | |
| }), | |
| enterprise: Schema.Struct({ | |
| url: Schema.String.pipe(Schema.optional), | |
| }) | |
| .pipe(Schema.optional) | |
| .annotate({ | |
| description: "Enterprise sharing service configuration", | |
| }), | |
| username: Schema.String.pipe(Schema.optional).annotate({ | |
| description: "Username displayed in conversations and used for telemetry identity", | |
| }), | |
| permissions: PermissionSchema.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({ | |
| description: "Named built-in agent overrides and custom agent definitions", | |
| }), | |
| snapshots: Schema.Boolean.pipe(Schema.optional).annotate({ | |
| description: "Enable snapshots used for undo and revert behavior", | |
| }), | |
| watcher: ConfigWatcher.Info.pipe(Schema.optional).annotate({ | |
| description: "Filesystem watcher configuration", | |
| }), | |
| formatter: ConfigFormatter.Info.pipe(Schema.optional).annotate({ | |
| description: "Enable built-in formatters or configure formatter overrides", | |
| }), | |
| lsp: ConfigLSP.Info.pipe(Schema.optional).annotate({ | |
| description: "Enable built-in language servers or configure server overrides", | |
| }), | |
| attachments: ConfigAttachments.Info.pipe(Schema.optional).annotate({ | |
| description: "Attachment processing configuration", | |
| }), | |
| tool_output: ConfigToolOutput.Info.pipe(Schema.optional).annotate({ | |
| description: "Tool output truncation thresholds", | |
| }), | |
| mcp: ConfigMCP.Info.pipe(Schema.optional).annotate({ | |
| description: "MCP server configuration", | |
| }), | |
| compaction: ConfigCompaction.Info.pipe(Schema.optional).annotate({ | |
| description: "Conversation compaction behavior", | |
| }), | |
| skills: Schema.String.pipe(Schema.Array, Schema.optional).annotate({ | |
| description: "Additional paths or URLs to discover skills from", | |
| }), | |
| commands: Schema.Record(Schema.String, ConfigCommand.Info).pipe(Schema.optional).annotate({ | |
| description: "Named slash command definitions", | |
| }), | |
| instructions: Schema.String.pipe(Schema.Array, Schema.optional).annotate({ | |
| description: "Additional paths or URLs supplying ambient instructions", | |
| }), | |
| references: ConfigReference.Info.pipe(Schema.optional).annotate({ | |
| description: "Named local directories or Git repositories available as external context", | |
| }), | |
| plugins: ConfigPlugin.Plugins.pipe(Schema.optional).annotate({ | |
| description: "Ordered external plugin packages to load", | |
| }), | |
| experimental: ConfigExperimental.Experimental.pipe(Schema.optional), | |
| providers: Schema.Record(Schema.String, ConfigProvider.Info).pipe(Schema.optional), | |
| }) {} | |
| export class Document extends Schema.Class<Document>("Config.Document")({ | |
| type: Schema.Literal("document"), | |
| path: Schema.String.pipe(Schema.optional), | |
| info: Info, | |
| }) {} | |
| export class Directory extends Schema.Class<Directory>("Config.Directory")({ | |
| type: Schema.Literal("directory"), | |
| path: AbsolutePath, | |
| }) {} | |
| export type Entry = Document | Directory | |
| export function latest<K extends keyof Info>(entries: readonly Entry[], key: K): Info[K] | undefined { | |
| return entries | |
| .filter((entry): entry is Document => entry.type === "document") | |
| .findLast((entry) => entry.info[key] !== undefined)?.info[key] | |
| } | |
| export interface Interface { | |
| /** Returns location config documents and supplemental directories from lowest to highest priority. */ | |
| readonly entries: () => Effect.Effect<Entry[]> | |
| } | |
| export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Config") {} | |
| export const layer = Layer.effect( | |
| Service, | |
| Effect.gen(function* () { | |
| const fs = yield* FSUtil.Service | |
| const global = yield* Global.Service | |
| const location = yield* Location.Service | |
| const policy = yield* Policy.Service | |
| const names = ["config.json", "opencode.json", "opencode.jsonc"] | |
| const decodeOptions = { errors: "all", onExcessProperty: "ignore", propertyOrder: "original" } as const | |
| const decodeInfo = Schema.decodeUnknownOption(Info, decodeOptions) | |
| const decodeV1Info = Schema.decodeUnknownOption(ConfigV1.Info, decodeOptions) | |
| const loadFile = Effect.fnUntraced(function* (filepath: string) { | |
| const text = yield* fs.readFileStringSafe(filepath) | |
| if (!text) return | |
| const errors: ParseError[] = [] | |
| const input: unknown = parse(text, errors, { allowTrailingComma: true }) | |
| if (errors.length) return | |
| const info = Option.getOrUndefined( | |
| ConfigMigrateV1.isV1(input) | |
| ? decodeV1Info(input).pipe(Option.map(ConfigMigrateV1.migrate), Option.flatMap(decodeInfo)) | |
| : decodeInfo(input), | |
| ) | |
| if (!info) return | |
| return new Document({ type: "document", path: filepath, info }) | |
| }) | |
| const loadDirectory = Effect.fnUntraced(function* (directory: AbsolutePath) { | |
| return [ | |
| ...(yield* Effect.forEach(names, (file) => loadFile(path.join(directory, file))).pipe( | |
| Effect.map((configs) => configs.filter((config): config is Document => config !== undefined)), | |
| )), | |
| new Directory({ type: "directory", path: directory }), | |
| ] | |
| }) | |
| const globalDirectory = AbsolutePath.make(global.config) | |
| const locationIsGlobal = path.resolve(location.directory) === path.resolve(global.config) | |
| // Read configuration once when this location opens. Later calls reuse these | |
| // values until the location is reopened. | |
| const discovered = locationIsGlobal | |
| ? [] | |
| : yield* fs | |
| .up({ | |
| targets: [".opencode", ...names.toReversed()], | |
| start: location.directory, | |
| stop: location.project.directory, | |
| }) | |
| .pipe(Effect.orDie) | |
| const directories = [ | |
| globalDirectory, | |
| ...discovered | |
| .filter((item) => path.basename(item) === ".opencode") | |
| .toReversed() | |
| .map((directory) => AbsolutePath.make(directory)), | |
| ] | |
| // A config closer to the opened directory should win over one higher up. | |
| // Search starts nearby, so reverse the results before applying them. | |
| const directPaths = discovered.filter((item) => path.basename(item) !== ".opencode").toReversed() | |
| const direct = yield* Effect.forEach(directPaths, loadFile).pipe( | |
| Effect.orDie, | |
| Effect.map((configs) => configs.filter((config): config is Document => config !== undefined)), | |
| ) | |
| const supplementary = yield* Effect.forEach(directories, loadDirectory).pipe(Effect.orDie) | |
| // Apply general settings first and more specific settings last: | |
| // global config, project files, then `.opencode` files. | |
| const configs = [...(supplementary[0] ?? []), ...direct, ...supplementary.slice(1).flat()] | |
| // Rules use the opposite order so a user-global rule can override a | |
| // repository rule. Statement order inside each file stays unchanged. | |
| yield* policy.load( | |
| configs | |
| .filter((config): config is Document => config.type === "document") | |
| .toReversed() | |
| .flatMap((config) => config.info.experimental?.policies ?? []), | |
| ) | |
| return Service.of({ | |
| entries: Effect.fn("Config.entries")(function* () { | |
| return configs | |
| }), | |
| }) | |
| }), | |
| ) | |
| export const locationLayer = layer.pipe(Layer.provideMerge(Policy.locationLayer)) | |
Xet Storage Details
- Size:
- 9.46 kB
- Xet hash:
- 2f73088cca950d64d798b29b1d96aec00499a505977d1b8f4f7deac74ccb97d1
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.