外观
配置
用 dataclass 定义设置,随插件发布 config.schema.json 和不含真实秘密的 config.example.json。Host 管理每次安装的实际 config.json;示例文件不会自动成为运行配置。
声明与读取
python
from dataclasses import dataclass, field
from neomega_runtime import Plugin
@dataclass
class Settings:
greeting: str = field(default="你好", metadata={
"title": "问候语", "description": "插件启动时写入日志的文本"
})
interval_seconds: int = field(default=60, metadata={
"title": "执行间隔(秒)", "minimum": 1, "maximum": 3600
})
class MyPlugin(Plugin):
config_type = Settings
async def on_validate(self, ctx):
if not ctx.config.greeting.strip():
raise ValueError("问候语不能为空")
async def on_start(self, ctx):
ctx.log.info("%s", ctx.config.greeting)
if __name__ == "__main__":
MyPlugin().run()ctx.config 在 on_validate 前完成加载和类型验证。文件不存在时使用 dataclass 默认值;没有默认值的必填字段仍会报错。验证阶段不会创建或重写配置文件。
支持嵌套 dataclass、字符串、整数、有限浮点数、布尔值、列表、字符串键字典及可选类型。未知字段、重复 JSON 键、非有限数及类型混用会被拒绝,true 不能充当整数。配置文件上限 256 KiB。
导出 schema
把下面内容另存为 export_config.py,在插件源码目录显式运行。确保 main.py 的 .run() 位于 if __name__ == "__main__" 保护下,避免导入时启动插件。
python
import json
from pathlib import Path
from neomega_runtime import schema_for_config
from main import Settings
Path("config.schema.json").write_text(
json.dumps(schema_for_config(Settings), ensure_ascii=False, indent=2),
encoding="utf-8",
)config.example.json:
json
{"greeting": "你好", "interval_seconds": 60}把 main.py、manifest.json、schema 和示例逐一加入 package.files.json。修改 Settings 后重新导出并运行 lint;lint 不会通过执行插件来判断 schema 是否与 dataclass 一致。
schema 支持类型、默认值、数组、对象、anyOf、数值上下限及 title、description、examples 等注释;这不是完整 JSON Schema 引擎。生成过程会计算 default_factory,因此不要在默认工厂里发请求或执行游戏动作。
保存与生效
在拥有目标实例管理权限时,通过工具包管理配置:
sh
python3 runtime-admin.py --url "$TARGET_URL" --token-file "$TARGET_TOKEN_FILE" configure hello向导先验证和预览,再应用。停止的插件只保存配置,不会被自动启动;运行中的插件通过重载应用。检查响应中的 saved、applied 和 reload_required,不能把“已保存”直接理解为“已生效”。
AI 自动化可按顺序使用:
sh
python3 runtime-admin.py --url "$TARGET_URL" --token-file "$TARGET_TOKEN_FILE" --json config-describe hello
python3 runtime-admin.py --url "$TARGET_URL" --token-file "$TARGET_TOKEN_FILE" --json config-get hello
python3 runtime-admin.py --url "$TARGET_URL" --token-file "$TARGET_TOKEN_FILE" --json config-plan hello --config-file candidate.json
python3 runtime-admin.py --url "$TARGET_URL" --token-file "$TARGET_TOKEN_FILE" --json config-apply hello --config-file candidate.json --expected-revision "$CONFIG_REVISION"前三步读取描述、当前值并预览候选变更。检查 valid 和 issues 后,使用刚读到的 revision 应用已获准的配置。发生冲突时重新读取并合并,不直接用新 revision 覆盖他人的改动。
revision 表示已保存内容版本,active_revision 表示 Host 观察到的生效版本。直接编辑磁盘文件不会即时更新 ctx.config。普通业务状态使用 ctx.storage,不要让插件在验证回调中改写管理员配置。
密钥引用
配置字段标记 secret: True,普通 JSON 仅保存 secret:cloud_api 这样的引用:
python
@dataclass
class CloudSettings:
api_key: str = field(metadata={"title": "云端密钥", "secret": True})
# 在已设置 config_type = CloudSettings 的回调内读取:
# key = ctx.secrets.get(ctx.config.api_key)该片段复用前面的 dataclass 和 field 导入。引用名需匹配 secret:[A-Za-z][A-Za-z0-9_-]{0,63},默认值和示例也必须使用引用。ctx.secrets.get(reference) -> str 按需读取,缺失或无效时抛出 SecretError;不要记录其返回值。
管理员使用 secret-set hello --secret-name cloud_api 隐藏输入,或指定 --secret-file 读取私有文件。secret-status hello 只报告是否已配置。读取时使用最新值;插件若缓存凭据,需要重载才能切换。插件是可信代码,私有文件权限不等于恶意插件沙箱。
配置错误与升级
ConfigError(code, path="config", *, expected=None, hint=None, line=None, column=None) 提供安全字段 code、path、expected、hint、line、column。as_dict() 返回非 None 字段。按定位修正候选文件;自定义业务验证也不要在异常文字中携带密钥。
新增带默认值的字段通常能读取旧配置;重命名、删除或收紧类型需要提供明确迁移步骤。保存前验证失败保留旧配置和旧进程;保存后重载失败可能出现 saved=true, applied=false,此时检查日志与运行状态,不承诺业务数据自动回滚。具体故障处理见错误页。
配置与密钥函数
普通插件设置 Plugin.config_type 即可自动加载;离线校验工具可使用以下纯读取入口:
| 函数 / 类 | 返回与限制 |
|---|---|
neomega_runtime.config.load_config(kind: type[T], path: Path) -> T | 读取文件、应用默认值并创建 dataclass;文件缺失等同 {},不写文件。 |
neomega_runtime.config_schema.schema_for_config(kind) | 返回声明对应的 schema 字典;也从 neomega_runtime 导出。 |
neomega_runtime.config_schema.check_config_schema(schema) | 检查支持的 schema 子集,成功返回 None,非法声明抛 ConfigError。 |
neomega_runtime.config_schema.validate_config(schema, value) | 返回加入默认值后的独立 JSON 值;只验证秘密引用,不读取秘密。 |
neomega_runtime.secrets.Secrets(data_dir: Path) | get(reference: str) -> str 读取该数据目录下的秘密;普通插件用 ctx.secrets。 |
schema 限制为至多 32 层、anyOf 每处 1–16 分支;递归 dataclass、不支持的关键字或不符合声明的默认值/示例会被拒绝。metadata={"secret": True} 仅用于字符串并导出为 x-secret: true,不是普通文本密码输入。
Secrets.get 要求秘密目录及文件不向组和其他用户开放,文件为普通文件,内容为 1–65536 字节 UTF-8。返回值原样保留换行;需要无换行的令牌应由管理员在保存时保证。读取无效引用、缺失、权限不符或编码错误均抛 SecretError,异常不含秘密值或文件路径。