配置和工具
插件代码如何读取运行时配置快照、持久化配置写入,并复用共享运行时工具。属于 插件运行时辅助工具 参考的一部分;api.runtime.config 命名空间 包含对应的命名空间条目。
配置加载与写入¶
优先使用已经传入当前调用路径的配置,例如注册期间的 api.config,或通道/提供商回调中的 cfg 参数。这样可以保持同一个进程快照贯穿整个处理流程,而不是在热路径上重新解析配置。
仅当长生命周期处理器需要当前进程快照,且该函数没有传入配置时,才使用 api.runtime.config.current()。返回值是只读的;编辑前请克隆它或使用变更辅助函数。
工具工厂会接收 ctx.runtimeConfig 以及 ctx.getRuntimeConfig()。当配置可能在工具定义创建之后发生变化时,在长生命周期工具的 execute 回调中使用该 getter。
使用 api.runtime.config.mutateConfigFile(...) 或 api.runtime.config.replaceConfigFile(...) 持久化更改。每次写入都必须选择一个明确的 afterWrite 策略:
afterWrite: { mode: "auto" }由 gateway 的 reload planner 决定。afterWrite: { mode: "restart", reason: "..." }在写入方知道热重载不安全时强制进行干净重启。afterWrite: { mode: "none", reason: "..." }仅在调用方负责后续处理时抑制自动重载/重启。
变更辅助函数会返回 afterWrite 以及带类型的 followUp 摘要,以便调用方记录或测试其是否请求了重启。gateway 仍然负责决定该重启实际发生的时间。
Owner 授权命令会将其捕获的 ctx.assertOwnerCurrent 作为
writeOptions.assertCurrent 传递。配置写入器会在异步准备之后、发布之前重新检查它,然后完成已接受写入的结算。不要将其替换为较早的 senderIsOwner 布尔值,也不要仅在变更返回之后才检查它。
对于运行时配置访问和写入,请使用 current()、传入的 cfg、mutateConfigFile(...) 或
replaceConfigFile(...)。
对于直接 SDK 导入,请优先使用聚焦的配置子路径,而不是宽泛的 openclaw/plugin-sdk/config-runtime 兼容 barrel:config-contracts 用于类型,runtime-config-snapshot 用于当前进程快照,config-mutation 用于写入。从 api.pluginConfig 读取条目作用域的值;仅将提供的工具上下文用于其运行时全局配置快照,并将插件特定的合并保留在该边界处。内置插件测试应直接 mock 这些聚焦子路径,而不是 mock 宽泛的兼容 barrel。
当使用直接 config-mutation 导入来替换源快照时,请将编辑后的配置作为 sourceConfig 传递给 replaceConfigFile,并保留其 snapshot、
baseHash、writeOptions 以及明确的 afterWrite 策略。运行时派生的
替换继续使用 nextConfig。源替换和聚焦变更
会保留其文件快照的引用,即使活动运行时使用不同的快照也是如此。
直接 SDK 的 updateConfig 辅助函数返回由其 mutator 生成的配置。
其磁盘写入会使用原始读取快照恢复环境引用。
OpenClaw 内部运行时代码遵循相同方向:在 CLI、gateway 或进程边界处加载一次配置,然后传递该值。成功的变更写入会刷新进程运行时快照并推进其内部修订;长生命周期缓存应以运行时拥有的缓存键为键,而不是在本地序列化配置。长生命周期运行时模块对环境中的 loadConfig() 调用具有零容忍扫描器;请使用传入的 cfg、请求的 context.getRuntimeConfig(),或在显式进程边界处使用 getRuntimeConfig()。
提供商和通道执行路径必须使用活动运行时配置快照,而不是为配置回读或编辑返回的文件快照。文件快照会保留源值,例如用于 UI 和写入的 SecretRef 标记;提供商回调需要已解析的运行时视图。当辅助函数可能以活动源快照或活动运行时快照调用时,在读取凭据之前通过 selectApplicableRuntimeConfig() 路由。选择器仅当提供的不同配置与运行时快照的配对源匹配(包括解析来源)时,才会替换该配置。没有该源的固定快照不能覆盖显式配置,包括其机密已经解析的命令作用域配置。如果没有提供配置,选择器返回运行时快照。
保留的通道监视器可以在启动时绑定一次来自
openclaw/plugin-sdk/runtime-config-snapshot 的 createRuntimeConfigReader(cfg)。当提供的配置属于活动运行时时,读取器会跟随
运行时更新;否则保留显式作用域的配置,包括尚未发布任何运行时的情况。每轮读取一次,并将该快照贯穿准入
和回复。进程级控制(例如诊断)应在发出点读取。
createChannelInboundDebouncer 将其返回的数字 debounceMs 和默认
队列计时作为启动快照保留。对于实时计时,请传递其现有的
resolveDebounceMs(entry) 回调,并使用绑定的配置读取器解析。
如果 pending-key 或 shutdown 簿记也依赖于延迟,请在 entry 上捕获一个
值,并将其同时用于簿记和回调。
通道的 reload.noopPrefixes 仅使该通道退出共享策略
刷新。仅当每个保留的消费者都实时读取该前缀或
不消费它时,才声明前缀。未声明的通道仍会刷新;一个通道的声明
不能抑制兄弟通道的重载。更窄的 reload.configPrefixes 条目可以
在更宽泛的 no-op 前缀下保留重启行为。
可复用运行时工具¶
对于接受 Node HTTP agent 的库,请使用来自
openclaw/plugin-sdk/fetch-runtime 的 createNodeProxyAgent。使用 mode: "env" 时,为
固定目标提供 targetUrl;当库自行选择目标时
(例如媒体上传主机),则省略它。可复用形式会快照代理
环境,并为每个请求(包括重定向)评估 NO_PROXY。
托管代理 CA 信任仅适用于匹配的代理连接。在拥有连接关闭时调用
agent?.destroy()。来自同一 SDK 入口的 Undici dispatcher 应放在 fetch 的 dispatcher 选项中,而不是 Node 的 agent。
从 openclaw/plugin-sdk/agent-harness-runtime 导入 execPolicy,用于宿主的 exec 模式代数。execPolicy.resolveExecModePolicy({ mode, security, ask }) 返回 mode、security、ask 和 auto-review 设置。显式 mode 会决定这些设置;如果没有,则辅助函数保留 security/ask 对并推导其显示 mode。execPolicy.minSecurity(a, b) 选择更严格的 security 值,execPolicy.maxAsk(a, b) 选择更强的审批要求。Provider 适配器保留其自身的严格输入验证和原生 sandbox/approval 投影。
这些带类型的对象成员取代了 infra-runtime 中已弃用的 minSecurity 和 maxAsk 导出。已弃用的 resolveExecModeFromPolicy、resolveExecPolicyForMode 和 resolveExecModePolicy 导出也可以迁移到 execPolicy.resolveExecModePolicy,并选择它们所需的返回字段。
原生命令探测应使用来自 openclaw/plugin-sdk/process-runtime 的 runCommandWithTimeout,并传入 timeoutMs、调用方的 signal 以及 killProcessTree: true。对于输出始终为 UTF-8 的命令(例如 JSON 状态探测),请使用同一子路径中的 runUtf8CommandWithTimeout。有界的命令结果可能在已取消的远程启动交付其 PID 之前返回。当命令拥有会话预留或临时输出时,在释放这些资源之前,请在执行周围 await 同一子路径中的 withCommandProcessScope。该 scope 会等待迟到的启动和进程清理;不确定的清理仍会作为错误处理。
对于需要 Node.js 的子进程,请使用同一子路径中的 resolveNodeRuntimeExecutable。它会复用当前 Node 可执行文件,并在宿主运行于 Bun 下时解析真实的 Node 二进制文件,跳过 Bun 的 node shim。不可用的 Node 运行时返回 undefined;调用方报告缺失的要求。
交互式进程适配器可以使用同一子路径中的 spawnTerminalPty。它负责平台特定的终端创建。在 macOS 和 Linux 上,Bun 仅在提供 Bun.Terminal.pause() 和 resume() 的构建中使用其原生 PTY 而不使用 Node,例如同时包含 macOS 子进程退出修复的 OpenClaw Bun fork 构建。其他 Bun 版本使用 Node 辅助函数,并要求已安装的 Node 运行时;OpenClaw 在选择时会跳过 Bun 的 node shim。Node 和 Windows 继续使用 node-pty。参见
Bun 兼容性。
通过其第二个参数传入调用方的构造 signal 和当前权限检查。调用方负责输出订阅、终止以及在释放其后端资源之前等待终端退出。
Sandbox 命令适配器保留来自 openclaw/plugin-sdk/sandbox 的 sandbox 所有者每流输出上限 SANDBOX_COMMAND_MAX_BUFFER_BYTES。
来自 openclaw/plugin-sdk/process-runtime 的 WorkerTaskPool 在终止失败时会保留 workers 和未消费的输入。请在同一 pool 上重试 close();只有在确认关闭后才处理依赖文件。可选的 onRetirementFailure(error) 观察者在终止失败时同步运行。它可以返回 void 或 Promise<void>;观察者抛出异常和拒绝不会替换终止错误,也不会释放托管权,并且关闭不会等待该观察者。
prepareWorker() 可以返回用于一次性临时文件的 temporaryDirectory,以及用于生产者拥有的资源的可选异步 releaseResources() 回调。两者都会保留到确认 Worker 退出;如果构造在 Worker 存在之前失败,清理也会运行。当两者都提供时,pool 会先尝试删除临时目录,然后即使该删除失败也会调用 releaseResources()。清理失败会变成警告。资源清理本身在 Worker 退出后不会占用执行容量;待处理的输入准备仍可能保留它,如下所述。close() 会在完成前等待清理回调。终止失败时不会运行任何清理步骤;请在同一 pool 上重试 close() 以确认退出并释放它们。
取消可能在异步输入工厂完成之前拒绝 run()。pool 会保留其输入和容量,直到准备和所需的 worker 退役都完成,然后调用 onInputConsumed。当取消的初始退役成功时,原生执行回执先于结果拒绝。失败的停止可能更早拒绝,同时保留原生托管权和待处理回执以便重试。
输入工厂必须独立于同一 pool 的 close() 完成:在待处理的工厂内等待关闭会形成循环,因为关闭会等待该工厂。在等待 close() 之前,取消工厂拥有的任何被等待的工作,然后在释放工厂仍捕获的资源之前等待关闭。run() 的 signal 会取消任务;它不会中断工厂等待的任意工作。
即使 run() 已经拒绝,也要处理来自 close() 的错误。对于已取消的待处理准备,输入和执行回执回调失败会由 close() 报告;准入保持保留,直到关闭观察到清理失败。
当启动由你的插件拥有的隔离 Gateway 子进程时,在应用调用方覆盖之后,从其环境中移除 SUPERVISOR_HINT_ENV_VARS。此列表从 openclaw/plugin-sdk/process-runtime 导出;否则,继承的父级服务标记会将重启所有权分配给该父级的 supervisor。
使用同一子路径中的 splitCommandArgs(raw) 来分组带引号的进程参数。反斜杠和 # 保持字面量;没有 shell 扩展。未完成的引号返回 null,除非调用方传入 { allowUnclosedQuotes: true } 以保留现有的宽松输入约定。空的带引号参数会被省略。
现有进程所有者可以使用 signalProcessTree。其 onComplete 回调在 Unix 信号发送或有界的 Windows taskkill 尝试之后运行,而不是证明每个进程都已退出。请保持探测在清理期间处于待处理状态,仅对你创建的进程组使用 detached: true,并在其根进程仍然存活时启动 Windows 树终止。
直接投递 agent 回复的通道插件可以在投递时,在修改 hooks 之后,从 openclaw/plugin-sdk/interactive-runtime 调用 renderPresentationForDelivery(handler, payload)。提供该通道的 presentationCapabilities 和 renderPresentation 回调;该回调接收一个 payload,其中包含经过规范化、适配后的 presentation,并将规范化后的原始 presentation 作为第二个参数传入。对于必须保留被原生限制截断的标签的整卡文本回退,请使用原始 presentation。此机制共享核心出站渲染的回退文本策略,并在渲染后移除可移植 presentation 字段。该回调可以是同步或异步的。
使用来自 openclaw/plugin-sdk/error-runtime 的 attachErrorDiagnostic(error, text),在不更改其身份、消息或失败分类的情况下,为抛出的错误附加补充操作员诊断。先掩码不透明凭据;该辅助函数还会脱敏已识别的秘密,并最多保留 2,048 个字符。formatErrorMessageForDisplay(error) 会通过嵌套原因和聚合项包含最近附加的诊断。仅将其用于终端显示边界,绝不用于重试或身份验证决策。agent 生命周期错误和终端 CLI 日志会自动渲染这些诊断;成功运行保持静默。原生 RPC 错误消息保留其原始文本;agent.wait 会在其终端结果边界渲染补充诊断。
通道插件必须通过其注入的 api.runtime.agent.runCommandFromIngress(options, runtime) 能力,接受经过身份验证的 agent 回合。宿主仅接受来自为 options.messageChannel 注册的、确切处于活动状态且受信任的插件的所有者权限;访客回合保留其非所有者身份。公开的 agentCommandFromIngress SDK 辅助函数从不接受调用方提供的所有者声明。
模型选择器集成使用两个聚焦的运行时子路径。从 openclaw/plugin-sdk/interactive-runtime 导入带类型的 ModelPickerAction 和 ModelPickerCapabilityProfile 契约。从 openclaw/plugin-sdk/model-session-runtime 导入 applySessionModelSelection(...) 及其结果类型;这是实时会话变更接缝,包括其权威冲突检查和提交后效果。较低层的 applyModelOverrideToSessionEntry(...) 辅助函数不是选择器持久化 API。
仅当通道回调无法进入完整的实时会话事务,并且已经拥有一个原子化的规范会话条目补丁时,才将 applyModelOverrideWithAuthProfileCompatibility(...) 用作直接持久化回退。传入活动配置、已解析的 agent 目录、条目、变更前的有效提供商以及经过验证的选择。该辅助函数仅变更该条目:当已记录的凭据提供商或已配置的别名兼容时,它保留固定的 auth profile;清除不兼容的固定项,并强制应用模型选择锁。调用方仍负责模型允许列表验证、原子持久化、markLiveSwitchPending 以及任何提交后效果。只要完整事务可用,就优先使用 applySessionModelSelection(...)。
模型选择器操作仅携带有界的快照和目录令牌。通道参与者身份、源消息绑定以及序列化回调数据保留在通道的私有认证信封中。通道编解码器通过 { modelPicker: true } 选择解析这些操作;不具备选择器能力的通道继续采用失败关闭策略,而不是将该操作视为不透明回调。
对于由 bot 创建的入站消息,请使用入站 botLoopProtection 事实。核心在会话记录和分发之前应用共享的内存滑动窗口守卫,而不将该策略绑定到单一通道。该守卫跟踪 (scopeId, conversationId, participant pair) 键,将一对参与者的两个方向合并计数,一旦窗口预算被超出就应用冷却,并酌情修剪不活动条目。可重试传输还应提供稳定的 eventId;在已接受的事件仍处于活动窗口期间重放它,不会消耗另一个预算槽位。被抑制的事件不会添加任何保留的事件身份状态。
向操作员暴露此行为的通道插件应优先使用共享的 channels.defaults.botLoopProtection 形状作为基线预算,然后在其上层叠加通道/提供商特定的覆盖项。共享配置使用秒,因为它是面向用户的:
type ChannelBotLoopProtectionConfig = {
enabled?: boolean;
maxEventsPerWindow?: number;
windowSeconds?: number;
cooldownSeconds?: number;
};
将规范化后的 bot 对事实与已解析的回合一起传入。核心负责解析默认值、单位转换以及 enabled 语义:
return {
channel: "example",
routeSessionKey,
storePath,
ctxPayload,
recordInboundSession,
runDispatch,
botLoopProtection: {
scopeId: "account-1",
conversationId: "channel-1",
senderId: "bot-a",
receiverId: "bot-b",
eventId: providerEvent.id,
config: channelConfig.botLoopProtection,
defaultsConfig: runtimeConfig.channels?.defaults?.botLoopProtection,
defaultEnabled: allowBotsMode !== "off",
},
};
仅对于不经过共享入站回复运行器的自定义两方事件循环,才直接使用 openclaw/plugin-sdk/pair-loop-guard-runtime。
阶段计时诊断¶
openclaw/plugin-sdk/time-runtime 导出 createStageTimingTracker(now?) 和 formatStageTimings(stages)。跟踪器记录取整后的非负 durationMs 和 elapsedMs 值。mark(name) 测量自上一个标记以来的时间;measure(name, run) 和 measureSync(name, run) 记录显式跨度,包括失败的工作,并保留回调的结果或错误。已测量的跨度不会推进 mark 使用的检查点。
snapshot() 返回 { totalMs, stages },其中包含一个复制的阶段数组。可选时钟默认为 Date.now。格式化会生成以逗号分隔的 name:durationMs@elapsedMs 条目(带 ms 单位)或 none。调用方保留对日志标签、警告阈值以及何时发出摘要的所有权。
对于进程级性能日志,
openclaw/plugin-sdk/diagnostic-runtime 导出
areDiagnosticsEnabledForProcess(): boolean 和 createSubsystemLogger。这个
聚焦的入口点在插件描述符注册期间不会加载实时会话诊断或网络调度器
配置。该谓词读取当前进程范围的
诊断设置;isDiagnosticsEnabled(config) 则读取提供的
配置快照。这两个函数都不会更改设置或启用
导出器。在收集仅诊断状态之前,
请结合进程谓词与选定的日志级别:
import {
areDiagnosticsEnabledForProcess,
createSubsystemLogger,
} from "openclaw/plugin-sdk/diagnostic-runtime";
const log = createSubsystemLogger("example/catalog");
function diagnosticsEnabled() {
return areDiagnosticsEnabledForProcess() && log.isEnabled("warn");
}
在发出延迟摘要时重新检查这些门控。保持字段有界且 不含内容,并在诊断接收器失败时保留操作结果。 此谓词不会启用或授权审计身份收集。
onInternalDiagnosticEvent(listener, interest?) 在为其监听器复制
事件负载之前过滤事件。include 和 exclude 适用于每个事件;
可选的 includeTrusted 列表仅进一步限制由调度器标记为可信
的事件。省略它会保留现有行为,而空列表
仅接受通过 include/exclude 的不可信事件。事件负载字段
不能覆盖调度器的信任元数据。被接受的事件
保留其各自的冻结副本;此过滤器不会更改诊断收集或
队列行为。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw