English | 中文
Accept configuration supplied through cordis.yml.
Export a Config type and a same-named Schemastery schema. Put defaults directly on the schema fields:
import type { Context } from 'cordis'
import Schema from 'schemastery'
export const name = 'my-plugin'
export interface Config {
greeting: string
maxRetries: number
verbose?: boolean
}
export const Config: Schema<Config> = Schema.object({
greeting: Schema.string().default('Hello'),
maxRetries: Schema.number().default(3),
verbose: Schema.boolean().default(false),
})
export function apply(ctx: Context, config: Config) {
console.log(config.greeting) // User value or schema default.
}
Configure it in cordis.yml:
- name: './src/my-plugin.ts'
config:
greeting: 'Hi there'
maxRetries: 5
When loading the plugin, Cordis uses the exported schema to validate configuration and fill defaults. Do not export a plain object as Config; it does not implement the Standard Schema interface required by Cordis.
Use Schemastery to express stricter validation:
import type { Context } from 'cordis'
import Schema from 'schemastery'
export const name = 'validated-plugin'
export interface Config {
apiKey: string
timeout: number
mode: 'fast' | 'accurate'
}
export const Config = Schema.object({
apiKey: Schema.string().required(),
timeout: Schema.number().default(30000),
mode: Schema.union(['fast', 'accurate']).default('fast'),
})
export function apply(ctx: Context, config: Config) {
// config is validated and type-safe.
}
The schema runs while the plugin loads. Invalid configuration fails the load with an actionable error.
Harness requires anything that two deployments may want to set differently to be a configuration field.
// Wrong: hardcoded timeout.
const TIMEOUT = 30000
// Correct: configurable.
export interface Config {
timeoutMs: number // Defaults to 30000.
}
The test is whether cordis.yml can change the value without a code edit.
If configuration refers to an unregistered LLM provider route or another nonexistent resource, fail early instead of silently skipping it:
import type { Context } from 'cordis'
import type {} from '@deepseek-ai/dsh-llm'
export interface ModelConfig {
provider: string
}
export function apply(ctx: Context, config: ModelConfig) {
if (!ctx.llm.listProviders().some(provider => provider.id === config.provider)) {
throw new Error(`LLM provider "${config.provider}" is not registered`)
}
}
A configuration edit hot-replaces the plugin: the framework unloads the old instance and loads a new one. Because registrations are effects and clean themselves up, replacement does not retain the old instance's registrations.