- 首页
- Documentation
- 扩展
- 扩展加载(TypeScript/JavaScript 模块)
扩展加载(TypeScript/JavaScript 模块)
本文档介绍编码代理如何在启动时发现和加载扩展模块(.ts/.js)。
本文档不涵盖 gemini-extension.json 清单扩展(另有单独文档说明)。
此子系统的功能
Section titled “此子系统的功能”扩展加载会构建一个模块入口文件列表,使用 Bun 导入每个模块,执行其工厂函数,并返回:
- 已加载的扩展定义
- 按路径分类的加载错误(不会中止整体加载过程)
- 一个共享的扩展运行时对象,供后续
ExtensionRunner使用
主要实现文件
Section titled “主要实现文件”src/extensibility/extensions/loader.ts— 路径发现 + 导入/执行src/extensibility/extensions/index.ts— 公共导出src/extensibility/extensions/runner.ts— 加载后的运行时/事件执行src/discovery/builtin.ts— 用于扩展模块的原生自动发现提供程序src/config/settings.ts— 加载合并后的extensions/disabledExtensions设置
扩展加载的输入
Section titled “扩展加载的输入”1)自动发现的原生扩展模块
Section titled “1)自动发现的原生扩展模块”discoverAndLoadExtensions() 首先向发现提供程序请求具有 extension-module 能力的项目,然后仅保留提供程序为 native 的项目。
有效的原生位置:
- 项目级别:
<cwd>/.xcsh/extensions - 用户级别:
~/.xcsh/agent/extensions
路径根目录来自原生提供程序(SOURCE_PATHS.native)。
注意事项:
- 原生自动发现当前基于
.xcsh。 - 旧版
.pi仍可在package.json清单键(pi.extensions)中使用,但不作为此处的原生根目录。
2)显式配置的路径
Section titled “2)显式配置的路径”自动发现完成后,配置的路径会被追加并解析。
在主会话启动路径(sdk.ts)中的配置路径来源:
- CLI 提供的路径(
--extension/-e,--hook也被视为扩展路径) - 设置中的
extensions数组(合并全局 + 项目设置)
全局设置文件:
~/.xcsh/agent/config.yml(或通过PI_CODING_AGENT_DIR自定义代理目录)
项目设置文件:
<cwd>/.xcsh/settings.json
示例:
extensions: - ~/my-exts/safety.ts - ./local/ext-pack{ "extensions": ["./.xcsh/extensions/my-extra"]}启用/禁用控制
Section titled “启用/禁用控制”- CLI:
--no-extensions - SDK 选项:
disableExtensionDiscovery
行为差异:
- SDK:当
disableExtensionDiscovery=true时,仍会通过loadExtensions()加载additionalExtensionPaths。 - CLI 路径构建(
main.ts)在设置--no-extensions时会清除 CLI 扩展路径,因此在该模式下不会转发显式的-e/--hook。
禁用特定扩展模块
Section titled “禁用特定扩展模块”disabledExtensions 设置按扩展 ID 格式进行过滤:
extension-module:<derivedName>
derivedName 基于入口路径(getExtensionNameFromPath),例如:
/x/foo.ts->foo/x/bar/index.ts->bar
示例:
disabledExtensions: - extension-module:foo路径和入口解析
Section titled “路径和入口解析”对于配置的路径:
- 规范化 Unicode 空格
- 展开
~ - 如果是相对路径,则相对于当前
cwd解析
如果配置的路径是文件
Section titled “如果配置的路径是文件”直接作为模块入口候选使用。
如果配置的路径是目录
Section titled “如果配置的路径是目录”解析顺序:
- 该目录中的
package.json包含xcsh.extensions(或旧版pi.extensions)-> 使用声明的入口 index.tsindex.js- 否则扫描一级目录查找扩展入口:
- 直接的
*.ts/*.js - 子目录中的
index.ts/index.js - 子目录中包含
xcsh.extensions/pi.extensions的package.json
- 直接的
规则和约束:
- 不会递归发现超过一个子目录层级
- 声明的
extensions清单入口相对于该包目录解析 - 声明的入口仅在文件存在/可访问时才会被包含
- 在
*/index.{ts,js}对中,TypeScript 优先于 JavaScript - 符号链接被视为合格的文件/目录
忽略行为因来源而异
Section titled “忽略行为因来源而异”- 原生自动发现(发现辅助程序中的
discoverExtensionModulePaths)使用原生 glob,设置gitignore: true和hidden: false。 loader.ts中的显式配置目录扫描使用readdir规则,不应用 gitignore 过滤。
加载顺序和优先级
Section titled “加载顺序和优先级”discoverAndLoadExtensions() 构建一个有序列表,然后调用 loadExtensions()。
顺序:
- 原生自动发现的模块
- 显式配置的路径(按提供顺序)
在 sdk.ts 中,配置的顺序为:
- CLI 附加路径
- 设置中的
extensions
去重:
- 基于绝对路径
- 先出现的路径优先
- 后续重复项被忽略
含义:如果同一模块路径既被自动发现又被显式配置,它只会在第一个位置(自动发现阶段)加载一次。
模块导入和工厂函数契约
Section titled “模块导入和工厂函数契约”每个候选路径通过动态导入加载:
await import(resolvedPath)- 工厂函数为
module.default ?? module - 工厂函数必须是函数(
ExtensionFactory)
如果导出不是函数,该路径会以结构化错误的形式失败,加载过程继续进行。
故障处理和隔离
Section titled “故障处理和隔离”每个扩展路径的故障以 { path, error } 的形式捕获,不会阻止其他路径的加载。
常见情况:
- 导入失败 / 文件缺失
- 无效的工厂函数导出(非函数)
- 执行工厂函数时抛出异常
运行时隔离模型
Section titled “运行时隔离模型”- 扩展未经沙箱化(同一进程/运行时)。
- 它们共享一个
EventBus和一个ExtensionRuntime实例。 - 在加载期间,运行时操作方法会故意抛出
ExtensionRuntimeNotInitializedError;操作连接在后续的ExtensionRunner.initialize()中进行。
当事件通过 ExtensionRunner 运行时,处理程序异常会被捕获并作为扩展错误发出,而不是导致运行器循环崩溃。
最简用户/项目布局示例
Section titled “最简用户/项目布局示例”~/.xcsh/agent/ config.yml extensions/ guardrails.ts audit/ index.ts<repo>/ .xcsh/ settings.json extensions/ checks/ package.json lint-gates.tschecks/package.json:
{ "xcsh": { "extensions": ["./src/check-a.ts", "./src/check-b.js"] }}旧版清单键仍可接受:
{ "pi": { "extensions": ["./index.ts"] }}