IronClaw 的扩展支持层:为什么这个 crate 里一个扩展都装不下? IronClaw 的扩展支持层为什么这个 crate 里一个扩展都装不下【免费下载链接】ironclawIronClaw is an Agent OS focused on privacy, security and extensibility项目地址: https://gitcode.com/gh_mirrors/iro/ironclaw翻开crates/extensions/ironclaw_extension_support/这个目录你会撞上一个挺反直觉的事实它名字里带着 extensionREADME 的第一句话却急着撇清——共享支持 crate本身不是一个包。换句话说一个专门做扩展支持的 crate里面恰恰一个可安装扩展都没有。这不是命名失误而是 IronClaw 扩展支持层ironclaw_extension_support架构里最刻意的一处设计。读到这里先别急着找包在哪我们按它的真实职责重新排一遍。先说清楚它是谁一个旁边的共享支持层先给结论ironclaw_extension_support 在依赖层级上属于extensions家族的runtimes层它的角色不是放扩展的地方而是站在扩展旁边、给一堆扩展提供公共服务的支撑层。Cargo 里那句First-party userland extension implementations for IronClaw描述的是它实现了什么而不是它装着了什么。它扛着三件互相咬合的活包清单inventory登记哪些包目录要跟着 IronClaw 一起发布并通过include_str!/include_bytes!把每个包的 manifest、prompt、schema 和已编译的 WASM 内嵌进最终二进制原生执行器native executors为gsuite、web_access、coding、skills这一组能力提供确定性的工具行为同一套代码服务多个包供应商包名的唯一合法栖息地正因为它是被扩展特定性检查豁免的 crate像github、gmail、notion这类具体包名只允许出现在这里通用宿主代码永远不该手写包名。最后这条是理解整个架构的钥匙——它保证了通用宿主对具体供应商保持盲视所有知道自己是哪个供应商的知识都被压进了这一个 crate。PACKAGES 表为什么清单要派生自一张表而不是扫目录可安装的扩展包本身不住在这个 crate 里。每个包都是自包含的独立目录位于crates/extensions/packages/extension-id/同一个目录下打包了 manifest、prompts、schemas、已提交的wasm/产物以及产出该 WASM 的wasm-src/客端源码。本 crate 只是这些包旁边的共享层包清单放在src/packages/每个包模块用相对路径../../../packages/id/…把数据内嵌进来。真正的核心在 src/packages/mod.rs// 清单的唯一事实来源每个包一条 (id, bundle builder) 记录。 // 刻意不扫目录——目录里多一个包不等于要发布它发布与否只由这张表决定。 const PACKAGES: [PackageEntry] [ (github::ID, github::bundle), (gmail::ID, gmail::bundle), (gsuite::CALENDAR_ID, gsuite::google_calendar_bundle), (gsuite::DOCS_ID, gsuite::google_docs_bundle), (gsuite::DRIVE_ID, gsuite::google_drive_bundle), (gsuite::SHEETS_ID, gsuite::google_sheets_bundle), (gsuite::SLIDES_ID, gsuite::google_slides_bundle), (notion::ID, notion::bundle), (slack::ID, slack::bundle), (telegram::ID, telegram::bundle), (web_access::ID, web_access::bundle), ];每个条目是一个(id, bundle builder)二元组一共 11 个nearai故意不在其中下一节细说。这里的设计取舍很实在清单从表派生而不是靠扫描packages/目录。为什么这样更稳因为目录里可能躺着正在开发中、还没准备好发布的包也可能有临时试验的目录——一旦清单靠扫目录生成仓库里有就会悄悄变成要随产品发布。用一张显式表发布边界变成一次明确的、可 review 的改动。同一张表还喂出两个开销不同的视图bundled_packages()会执行 builder、把内嵌数据真正物化出来而bundled_package_ids()只读 id、完全不触碰内嵌内容。宿主代码如果只是想做个成员判断——比如这个 id 是不是宿主保留 id文件系统扩展不能 shadow 它——就该走后者几乎零成本。两者都派生自同一张表于是一个包不可能只出现在其中一种视图里从机制上杜绝了清单和 id 列表对不上的鬼影 bug。每个 bundle 用PackageBundle描述pub struct PackageBundle { pub id: static str, pub display_name: static str, pub manifest_toml: Cowstatic, str, // Cow允许宿主把内嵌文本替换成所有权副本 pub assets: VecPackageAsset, pub onboarding: OptionPackageOnboarding, // 面向用户的引导文案无 setup 需求的包为 None pub trust_effects: OptionVecEffectKind, // 显式的信任授予不是从 manifest 推导 }两个字段值得单独点名。manifest_toml是Cow这个细节是故意留的口子——它允许宿主在需要时把内嵌的 manifest 文本替换成一份带所有权的、被改写过的新文本nearai正是这么用的见下。trust_effects更关键它是显式数据而非从 manifest 推导出来的。信任授予是宿主的一项独立断言属于纵深防御——就算 manifest 里写了什么真正给这个包开多大权限是宿主自己白纸黑字列出来的那份效果列表说了算。信任的决策仍归 compositionHostTrustAssignment::first_partybundle 只负责供给那份效果清单。nearai 为什么被踢出了 PACKAGES这是整个 crate 最容易让人误读为 bug 的地方值得专门讲。src/packages/下确实有一个nearai模块它也内嵌着自己的 schema 和 prompt 资产但它没有在PACKAGES表里登记。原因写得很直白其它所有条目都是fn() - PackageBundle——bundle 是内嵌字节的一份纯函数二进制可以不知道里面装了什么就把整张清单交给 composition。唯独 NEAR AI 的 manifest单独拿出来是不完整的它的[mcp].server是个占位符必须等宿主用操作员的 LLM-admin 引导配置改写之后、在 manifest 被解析之前才填上真值。一个不吃配置的纯 builder 根本产不出这个值要是把没打补丁的 manifest 塞进清单就等于让二进制把一份指向占位端点的 manifest 递给了 composition。所以它被拆成两半内嵌资产仍然住在这个 crate 里跟其它包的内嵌放一起也仍然在特定性检查的豁免范围内而打补丁那一步留在拥有端点权威的ironclaw_extension_host里由它调用packages::nearai::nearai_bundle()拿到同一种PackageBundle形状、再把manifest_toml和那份manifest.toml资产换成打过补丁的文本。这里正好接上了上一个细节——manifest_toml之所以是Cow就是为了能装下被宿主改写过的、带所有权的 manifest。读src/packages/nearai.rs的模块头再下结论这不是疏漏而是文档化了的既定形态。依赖面、消费面和几个藏在 Cargo.toml 里的第三方依赖往上看它的依赖全部落在contracts/substrates层ironclaw_host_api契约、ironclaw_filesystem、ironclaw_auth、ironclaw_safety、ironclaw_skills、ironclaw_documents、ironclaw_observability、ironclaw_extractors。正因为依赖都压在契约层kernel 层的消费者才可能合法存在。消费方则包括ironclaw_host_runtime设计内的 kernel 消费方、ironclaw_extension_host、ironclaw_extension_manager、ironclaw_composition、ironclaw_cli以及根集成测试包。这个依赖向下压到契约、消费方在 kernel 及以上的分布是它被归到runtimes层而不是更高层的原因。再看几个第三方依赖它们各自对应一类很具体的能力similar 3给coding工具做 diff 预览与 patch 生成coding/diff_preview.rs直接用了TextDiff/ChangeTagunicode-normalization 1字符串规范化coding/text.rs里的模糊匹配就依赖它zip 8解压来自 HTTPS/GitHub 来源的 ZIP 技能包——这块能力在 WS3 时从ironclaw_host_runtime搬进了本 crateblake3 1用于资产摘要计算。收尾Executor/Adapter 接缝如何把第一方工具和宿主授权隔开最后落到贯穿全仓的那条边界规则上这也是本 crate 存在的全部理由。规则一句话这里的每个工具都只能是 executor绝不许是 capability handler。落到代码上意味着三点。执行器只吃本 crate 自己定义、且只携带契约层值的请求类型GsuiteDispatchRequest、WebAccessDispatchRequest、SkillUrlFetchContext之类——宿主每次调用时把ResourceScope、CapabilityId、Arcdyn RuntimeHttpEgress、Arcdyn RootFilesystem这样的柄递进来执行器返回本 crate 自己的错误类型…DispatchError、SkillManagementCapabilityError里面带着一个RuntimeDispatchErrorKind、以及在失败前已经烧掉的ResourceUsage由调用方去映射。而FirstPartyCapabilityHandler的实现、声明该工具的CapabilityManifest、以及注册表插入全都在外面——常驻内建工具住在ironclaw_host_runtime::first_party_tools二进制注册的工具则住在那个二进制的FirstPartyHandlerRegistrar里。这里有个容易被低估的点这条接缝是强制执行的不是一句君子协定。ironclaw_host_runtime和ironclaw_extension_registry都写进了本 crate 的禁用清单reborn_dependency_boundaries.rs。换句话说如果一个 executor 非得依赖它们俩之一才写得出来那说明接缝放错了位置——正确动作是把代码挪少一点而不是去改规则。正是这套机制把第一方工具与宿主的授权、审批、资源核算彻底解耦工具只负责把活儿干对能不能干、花了多少额度全部交给宿主侧。第一方身份本身不带来任何额外权威每次工具调用跨过的授权/审批阶段跟任何一次 capability 调用都一模一样。【免费下载链接】ironclawIronClaw is an Agent OS focused on privacy, security and extensibility项目地址: https://gitcode.com/gh_mirrors/iro/ironclaw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考