144 lines
5.9 KiB
Markdown
144 lines
5.9 KiB
Markdown
## Context(背景)
|
||
|
||
本项目在 `skills/` 目录下开发用于大模型工具的技能(skills),每个子目录代表一个技能。目前,项目已经有一个 `python-runner` 技能,它使用 `uv` 提供了隔离的 Python 执行环境,支持依赖管理和临时文件隔离。
|
||
|
||
`js-runner/` 目录已存在但未完成,只包含一个测试文件(`test/axios.js`)。目前项目中还没有功能完整的 JavaScript/TypeScript 执行能力。
|
||
|
||
**当前状态:**
|
||
- `python-runner` 技能已完整实现(基于 uv 的隔离)
|
||
- `js-runner` 目录骨架存在但缺少实现
|
||
- 没有 JavaScript/TypeScript 的依赖管理机制
|
||
- 没有 JS/TS 脚本的临时文件生命周期管理
|
||
|
||
**约束条件:**
|
||
- 必须保持环境隔离(不污染系统包)
|
||
- 应遵循 `python-runner` 建立的模式以保持一致性
|
||
- 技能结构必须符合项目规范(`skills/<skill-name>/`)
|
||
- 必须同时支持 JavaScript 和 TypeScript 执行
|
||
|
||
## Goals / Non-Goals(目标 / 非目标)
|
||
|
||
**目标:**
|
||
- 提供辅助脚本供大模型调用(使用 JavaScript 编写,与 skill 主题一致)
|
||
- 在 SKILL.md 中清晰描述完整的调用流程
|
||
- 利用 Bun 的自动依赖管理能力
|
||
- 匹配 `python-runner` 的工作流程和用户体验,保持一致性
|
||
|
||
**非目标:**
|
||
- 提供完整的 IDE 或开发环境
|
||
- 脚本的长期存储或持久化
|
||
- 超越 JavaScript/TypeScript 的多语言支持
|
||
- 替换或修改现有的 `python-runner` 技能
|
||
- 构建自定义的 Bun 运行时包装器(直接使用 Bun)
|
||
|
||
## Decisions(技术决策)
|
||
|
||
### 1. 使用 Bun 作为 JavaScript 运行时
|
||
|
||
**理由:** Bun 相比替代方案具有显著优势:
|
||
- **Bun:** 比 Node.js 快约 100 倍的冷启动,内置包管理器(bun install),原生 TypeScript 支持,兼容 Node.js API
|
||
- **Node.js + npm/yarn:** 启动慢,需要外部包管理器,没有原生 TypeScript 支持
|
||
- **Deno:** 不同的 API 表面(不兼容 Node.js),生态系统较小,Node.js 用户学习曲线陡峭
|
||
|
||
**考虑的替代方案:**
|
||
- **Node.js with npm:** 因启动速度慢且无原生 TypeScript 支持而被拒绝
|
||
- **Deno:** 因 API 与 Node.js 生态不兼容且包管理不熟悉而被拒绝
|
||
|
||
### 2. 基于临时文件的执行模型
|
||
|
||
**理由:** 遵循 `python-runner` 的模式,在临时文件中执行脚本可以:
|
||
- 在执行之间保持干净的隔离
|
||
- 自动清理产物
|
||
- 不需要跨运行的状态管理
|
||
- 比内存执行实现更简单
|
||
|
||
**考虑的替代方案:**
|
||
- **通过 Bun 的 `Bun.transpileString()` 进行内存执行:** 因对模块导入和外部依赖支持不好而被拒绝
|
||
- **持久化目录结构:** 因避免执行之间的状态泄漏而被拒绝
|
||
|
||
### 3. 使用 Bun 的自动依赖管理
|
||
|
||
**理由:** Bun 可以直接运行脚本并自动处理依赖:
|
||
- **自动依赖解析:** 当脚本使用 `import` 或 `require` 引入外部包时,Bun 自动下载并缓存该包
|
||
- **无需手动安装:** 不需要手动运行 `bun install`,Bun 在运行时自动处理
|
||
- **智能缓存:** Bun 全局缓存已下载的包,后续运行无需重复下载
|
||
- **简化实现:** 不需要生成临时的 `package.json`,也不需要管理 `node_modules`
|
||
|
||
**工作流程:**
|
||
1. 用户直接运行脚本(内联或从文件)
|
||
2. 脚本中使用 `import` 或 `require` 引入依赖(如 `import axios from 'axios'`)
|
||
3. Bun 自动检测到未安装的依赖
|
||
4. Bun 下载并缓存依赖到 Bun 的全局缓存(`~/.bun/install/cache`)
|
||
5. 执行脚本
|
||
6. 清理临时脚本文件(Bun 的缓存保持不变)
|
||
|
||
**考虑的替代方案:**
|
||
- **手动 package.json + bun install:** 因 Bun 已经提供自动依赖处理而被拒绝(过度设计)
|
||
- **要求用户提供 package.json:** 因增加用户负担而被拒绝
|
||
|
||
### 4. 用于路径管理的辅助脚本
|
||
|
||
**理由:** `get_temp_path.js` 辅助脚本将:
|
||
- 为脚本生成唯一的临时文件路径
|
||
- 遵循特定于操作系统的临时目录约定
|
||
- 使用 JavaScript 编写,与 js-runner 主题一致
|
||
- 使用 Bun 运行(已确保环境就绪)
|
||
|
||
**实现方式:**
|
||
```javascript
|
||
import { tmpdir } from "os";
|
||
import { join } from "path";
|
||
|
||
export function getTempPath(extension) {
|
||
const timestamp = Date.now();
|
||
const random = Math.random().toString(36).substring(7);
|
||
return join(tmpdir(), `js-runner-${timestamp}-${random}.${extension}`);
|
||
}
|
||
```
|
||
|
||
## Risks / Trade-offs(风险 / 权衡)
|
||
|
||
### 风险:需要安装 Bun
|
||
|
||
**风险:** 用户必须单独安装 Bun,这增加了设置障碍。
|
||
|
||
**缓解措施:**
|
||
- 在 SKILL.md 中清晰记录安装过程(`curl -fsSL https://bun.sh/install | bash`)
|
||
- 如果未找到 Bun,提供有用的错误消息
|
||
- 考虑添加 "check" 命令来验证 Bun 安装
|
||
|
||
## Migration Plan(迁移计划)
|
||
|
||
由于这是一个没有现有生产使用的新技能:
|
||
|
||
1. **第一阶段:实现**
|
||
- 创建 `scripts/get_temp_path.js` 辅助脚本(使用 JavaScript 编写)
|
||
- 编写完整的 `SKILL.md` 文档,包含必要的 frontmatter
|
||
- 描述完整调用流程
|
||
|
||
2. **第二阶段:验证**
|
||
- 验证 `get_temp_path.js` 辅助脚本工作正常
|
||
- 验证 SKILL.md 格式符合 Agent Skills 规范
|
||
- 验证 SKILL.md 中的调用流程准确无误
|
||
|
||
3. **第三阶段:文档**
|
||
- 确保 SKILL.md 涵盖所有用例
|
||
- 为常见场景提供示例
|
||
- 记录错误处理和故障排除
|
||
|
||
**回滚策略:**
|
||
- 如果实现有问题,删除 `skills/js-runner/` 目录
|
||
- 对项目其他部分无影响,因为这是一个新的隔离技能
|
||
|
||
## Open Questions(待解决的问题)
|
||
|
||
1. **我们是否应该支持持久化项目目录?**
|
||
- 当前方法:只有临时文件
|
||
- 考虑:允许用户指定工作目录
|
||
- 决策:推迟到未来增强;首先专注于简单的脚本执行
|
||
|
||
2. **需要什么级别的错误报告?**
|
||
- 当前计划:按原样转发 Bun 的 stderr 输出
|
||
- 考虑:解析并美化常见错误
|
||
- 决策:从原始输出开始,根据用户需求增强
|