dsh-github-flow 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +162 -58
- package/README.md +167 -60
- package/docs/UPGRADE-NOTES.md +93 -0
- package/docs/architecture.md +47 -17
- package/docs/images/1.png +0 -0
- package/docs/images/2.png +0 -0
- package/docs/images/3.png +0 -0
- package/docs/images/4.png +0 -0
- package/docs/npm-publish-guide.md +26 -3
- package/lib/api/route-helpers.d.ts +47 -0
- package/lib/api/route-helpers.js +134 -0
- package/lib/api/routes-actions.d.ts +23 -0
- package/lib/api/routes-actions.js +239 -0
- package/lib/api/routes-lifecycle.d.ts +9 -0
- package/lib/api/routes-lifecycle.js +126 -0
- package/lib/api/routes-overview.d.ts +11 -0
- package/lib/api/routes-overview.js +227 -0
- package/lib/api/routes-proxy.d.ts +18 -0
- package/lib/api/routes-proxy.js +63 -0
- package/lib/api/routes.d.ts +16 -6
- package/lib/api/routes.js +14 -504
- package/lib/client.js +785 -90
- package/lib/commands/gh.js +11 -2
- package/lib/executor.d.ts +17 -0
- package/lib/executor.js +146 -8
- package/lib/main.d.ts +1 -0
- package/lib/main.js +49 -9
- package/lib/proxy.d.ts +87 -0
- package/lib/proxy.js +275 -0
- package/lib/renderers/markdown.d.ts +29 -0
- package/lib/renderers/markdown.js +520 -0
- package/lib/tools/api.d.ts +4 -0
- package/lib/tools/api.js +14 -4
- package/lib/tools/help.d.ts +31 -0
- package/lib/tools/help.js +224 -0
- package/lib/tools/image-utils.d.ts +79 -0
- package/lib/tools/image-utils.js +247 -0
- package/lib/tools/issue.d.ts +40 -4
- package/lib/tools/issue.js +167 -19
- package/lib/tools/pr.d.ts +29 -4
- package/lib/tools/pr.js +144 -18
- package/lib/tools/release.d.ts +89 -0
- package/lib/tools/release.js +219 -0
- package/lib/tools/repo.d.ts +24 -4
- package/lib/tools/repo.js +114 -14
- package/lib/tools/run.d.ts +22 -1
- package/lib/tools/run.js +74 -14
- package/lib/tools/secret.d.ts +60 -0
- package/lib/tools/secret.js +175 -0
- package/lib/tools/skill.d.ts +63 -0
- package/lib/tools/skill.js +146 -0
- package/lib/types.d.ts +24 -1
- package/lib/update-settings.d.ts +18 -0
- package/lib/update-settings.js +60 -0
- package/lib/updater.d.ts +40 -1
- package/lib/updater.js +126 -9
- package/lib/upgrade-manager.d.ts +74 -1
- package/lib/upgrade-manager.js +356 -46
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +3 -2
- package/lib/client/icons.d.ts +0 -13
- package/lib/client/icons.js +0 -41
- package/lib/client/index.d.ts +0 -3
- package/lib/client/index.js +0 -36
- package/lib/client/panel.d.ts +0 -1
- package/lib/client/panel.js +0 -102
- package/lib/client/style.d.ts +0 -1
- package/lib/client/style.js +0 -555
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
const KNOWLEDGE_BASE = {
|
|
2
|
+
overview: `### 🧭 DSH GitHub Flow 工具箱全景导航
|
|
3
|
+
|
|
4
|
+
本插件提供面向 DeepSeek Harness 的原生 GitHub 协作与智能体基础设施工具矩阵:
|
|
5
|
+
|
|
6
|
+
| 工具名称 | 核心职责 | 典型调用场景 |
|
|
7
|
+
| :--- | :--- | :--- |
|
|
8
|
+
| \`github_pr\` | Pull Request 管理、审查与本地检出 | 查看 PR 列表/详情、比对 diff、本地检出代码验证、检查 CI 状态、执行带确认合并 |
|
|
9
|
+
| \`github_issue\` | Issue 任务追踪与分支开发关联 | 筛选待办议题、追加评论、编辑标题/标签、从 Issue 一键切出关联开发分支 |
|
|
10
|
+
| \`github_run\` | GitHub Actions 构建排查与工作流管理 | 查看构建列表、提取失败日志 (log_failed)、重新触发、手动触发 workflow_dispatch |
|
|
11
|
+
| \`github_repo\` | 仓库元数据、Fork 同步与全局检索 | 查看仓库与 topics、一键拉平 Fork 仓库 (sync)、检索全局代码与仓库 |
|
|
12
|
+
| \`github_api\` | 原生 GitHub REST/GraphQL 逃生门 | 调用任意端点、获取原始文件字节 (raw)、支持自动翻页 (paginate) |
|
|
13
|
+
| \`github_release\` | 版本发布与构建资产交付 | 查看发版历史、自动生成 Release Notes、上传 .tgz 等构建产物 |
|
|
14
|
+
| \`github_secret\` | CI/CD 部署密钥安全管理 | 查看 NPM_TOKEN 等密钥清单、经 stdin 安全写入密文、删除密钥 |
|
|
15
|
+
| \`github_skill\` | 跨 Agent 开源 Skills 生态发现 | 检索/预览/安装 GitHub 上的 Agent Skills (gh preview 特性) |
|
|
16
|
+
| \`github_help\` | 路由导航、官方语法速查与故障自愈 | 遇到不确定的操作、搜索语法或接口报错时查询用法与诊断配方 |
|
|
17
|
+
|
|
18
|
+
💡 **使用准则**:优先调用高层语义工具(pr / issue / run / repo / release / secret);当高层工具无法满足特殊接口需求时,再降级使用 \`github_api\`。`,
|
|
19
|
+
pr: `### 🔀 github_pr 详细操作指南
|
|
20
|
+
|
|
21
|
+
- **查看列表**: \`github_pr(action: 'list', state: 'open' | 'closed' | 'merged' | 'all', limit: 20, author: '@me')\`
|
|
22
|
+
- **下载并查看 PR 图片**: \`github_pr(action: 'view_images', pr_number: 123)\`
|
|
23
|
+
- 提取范围覆盖 PR 正文 + 评论,经 gh api 下载(UI 变更对比图、复现截图等)到本地并以图片内容块返回,Agent 审查时可直接看图;魔数校验 + 字节护栏与 Issue 侧一致。
|
|
24
|
+
- **按 URL 精准获取**: 附加 \`image_urls: [...]\` 只下载指定图片;触达目录上限时返回 remaining 清单可分批续传(与 Issue 侧用法完全一致)。
|
|
25
|
+
- **本地检出代码**: \`github_pr(action: 'checkout', pr_number: 123)\`
|
|
26
|
+
- 自动切换到本地分支并拉取最新提交,非常适用于 Agent 在本地沙箱中跑单元测试验证 PR 改动。
|
|
27
|
+
- **差异比对**: \`github_pr(action: 'diff', pr_number: 123, stat_only: true)\`
|
|
28
|
+
- 对于大 PR,强烈建议先开启 \`stat_only: true\` 查看改动文件概览,防止超大 diff 触发截断或撑爆上下文。
|
|
29
|
+
- **检查冲突状态**: \`github_pr(action: 'status')\`
|
|
30
|
+
- 查看当前分支关联 PR 的 merge conflict 状态。
|
|
31
|
+
- **安全合并**: \`github_pr(action: 'merge', pr_number: 123, confirm: true, merge_method: 'squash', delete_branch: false)\`
|
|
32
|
+
- 高危操作必须显式附带 \`confirm: true\`;若未传将返回拦截警告与待合并影响报告。`,
|
|
33
|
+
issue: `### 🎯 github_issue 详细操作指南
|
|
34
|
+
|
|
35
|
+
- **筛选 Issue**: \`github_issue(action: 'list', state: 'open' | 'closed' | 'all', labels: ['bug'], assignees: ['octocat'], limit: 20)\`
|
|
36
|
+
- **查看 Issue 附带截图**: \`github_issue(action: 'view', issue_number: 42)\`
|
|
37
|
+
- 返回正文中的图片 URL 清单(用户拖拽上传的报障截图等);需要"看图"时执行下方 view_images。
|
|
38
|
+
- **下载并查看 Issue 图片**: \`github_issue(action: 'view_images', issue_number: 42)\`
|
|
39
|
+
- 经 gh api 下载正文+评论图片到本地(~/.dsh/data/dsh-github-flow/images/,内容寻址去重),以图片内容块返回,Agent 可直接查看用户报障截图;魔数校验 + 字节护栏(单图 5MB、目录 15MB)。
|
|
40
|
+
- **按 URL 精准获取**: 附加 \`image_urls: ['<view 返回清单中的 URL>']\` 只下载指定图片(可单张可批量);传入 URL 必须在清单内(防任意下载)。
|
|
41
|
+
- 触达目录上限时返回 \`remaining\` 结构化清单,直接作为下一批 image_urls 续传即可。
|
|
42
|
+
- **关联开发分支**: \`github_issue(action: 'develop', issue_number: 42, branch_name: 'fix-login-bug')\`
|
|
43
|
+
- 自动在远端和本地创建带有官方关联关系的开发分支并切换过去,实现“读需求 -> 开分支写代码”的无缝衔接。
|
|
44
|
+
- **编辑 Issue**: \`github_issue(action: 'edit', issue_number: 42, title: '新标题', add_labels: ['p1'], remove_labels: ['triage'])\`
|
|
45
|
+
- **追加讨论**: \`github_issue(action: 'comment', issue_number: 42, body: '定位到问题原因...')\`
|
|
46
|
+
- **安全关闭**: \`github_issue(action: 'close', issue_number: 42, confirm: true)\`
|
|
47
|
+
- 关闭操作必须附带 \`confirm: true\`。`,
|
|
48
|
+
run: `### 🚀 github_run 详细操作指南
|
|
49
|
+
|
|
50
|
+
- **过滤构建记录**: \`github_run(action: 'list', branch: 'main', status: 'failure', limit: 10)\`
|
|
51
|
+
- 快速定位失败的 CI 构建,排查报错首选 \`status: 'failure'\`。
|
|
52
|
+
- **提取失败步骤日志**: \`github_run(action: 'log_failed', run_id: '12345678')\`
|
|
53
|
+
- 专为 Agent 优化的失败信息提取能力,直接定位报错位置;若无失败步骤则优雅提示。
|
|
54
|
+
- **主动触发工作流**: \`github_run(action: 'trigger', workflow: 'pr-check.yml', ref: 'main', inputs: { env: 'staging' })\`
|
|
55
|
+
- 提交代码修复后,主动触发远端 CI/CD 验证。
|
|
56
|
+
- **重新执行失败任务**: \`github_run(action: 'rerun', run_id: '12345678')\``,
|
|
57
|
+
repo: `### 📦 github_repo 详细操作指南
|
|
58
|
+
|
|
59
|
+
- **查看元数据与标签**: \`github_repo(action: 'view')\`
|
|
60
|
+
- 返回仓库基本信息、默认分支、Star 计数、许可证以及 \`repositoryTopics\`(用于 DSH 插件合规审查)。
|
|
61
|
+
- **读取 README 及截图**: \`github_repo(action: 'readme', repo: 'owner/repo')\`
|
|
62
|
+
- 返回 README 原文与图片 URL 清单(仓库内相对路径自动解析为 raw 链接;默认过滤徽章小图标,\`include_badges: true\` 保留)。
|
|
63
|
+
- **下载图片查看**: 附加 \`image_urls: [...]\` 精准下载指定图(留空下载全部截图);护栏与 Issue/PR 图片一致,触顶返回 remaining 可续传。
|
|
64
|
+
- **一键拉平 Fork 主干 (防止 Stale)**: \`github_repo(action: 'sync', branch: 'main')\`
|
|
65
|
+
- 解决开源项目贡献中因本地 Fork 落后导致上游 CI 门禁失败的顽疾。
|
|
66
|
+
- **代码检索**: \`github_repo(action: 'search_code', query: 'filename:package.json dsh.bundle')\``,
|
|
67
|
+
api: `### ⚡ github_api 底层逃生门指南
|
|
68
|
+
|
|
69
|
+
- **标准 GET 请求带参数**:
|
|
70
|
+
\`github_api(endpoint: 'repos/owner/repo/pulls', fields: { state: 'closed', per_page: 5 })\`
|
|
71
|
+
- 插件会自动追加 \`-X GET\`,将 fields 作为 URL Query String 传递,杜绝被 gh 自动改写为 POST。
|
|
72
|
+
- **读取仓库原始文件字节**:
|
|
73
|
+
\`github_api(endpoint: 'repos/owner/repo/contents/path/to/file.yml', raw: true)\`
|
|
74
|
+
- 附加 \`Accept: application/vnd.github.raw\`,返回纯文本而非 base64 包装。
|
|
75
|
+
- **全量聚合翻页**:
|
|
76
|
+
\`github_api(endpoint: 'repos/owner/repo/commits', paginate: true)\`
|
|
77
|
+
- **GraphQL 请求**:
|
|
78
|
+
\`github_api(endpoint: 'graphql', fields: { query: 'query { viewer { login } }' })\``,
|
|
79
|
+
release: `### 🏷️ github_release 版本发布指南
|
|
80
|
+
|
|
81
|
+
- **查看发版历史**: \`github_release(action: 'list', limit: 10)\`
|
|
82
|
+
- **查看某个版本详情**: \`github_release(action: 'view', tag: 'v0.1.9')\`(留空 tag 查看最新)
|
|
83
|
+
- **创建 Release(自动生成说明)**:
|
|
84
|
+
\`github_release(action: 'create', tag: 'v0.1.9', generate_notes: true, prerelease: false)\`
|
|
85
|
+
- 若远端尚无该 Tag,gh 会自动从 target 分支(默认主分支)最新提交打 Tag。
|
|
86
|
+
- **上传构建资产**:
|
|
87
|
+
\`github_release(action: 'upload', tag: 'v0.1.9', assets: ['./dsh-github-flow-0.1.9.tgz'])\`
|
|
88
|
+
- 覆盖同名旧资产必须附加 \`clobber: true, confirm: true\`(旧资产删除后不可恢复)。
|
|
89
|
+
- **下载资产**: \`github_release(action: 'download', tag: 'v0.1.9', pattern: '*.tgz', dir: './dist')\`
|
|
90
|
+
- **删除 Release**: \`github_release(action: 'delete', tag: 'v0.1.9', confirm: true)\`
|
|
91
|
+
- 高危不可逆操作,必须显式确认。`,
|
|
92
|
+
secret: `### 🔐 github_secret 部署密钥管理指南
|
|
93
|
+
|
|
94
|
+
- **查看密钥清单**: \`github_secret(action: 'list')\`
|
|
95
|
+
- 仅返回名称、可见性与更新时间;GitHub API 设计上就不允许读取密文。
|
|
96
|
+
- **设置/更新密钥(安全通道)**:
|
|
97
|
+
\`github_secret(action: 'set', name: 'NPM_TOKEN', value: 'npm_xxxx')\`
|
|
98
|
+
- 密文经子进程标准输入传递,绝不出现在命令行参数(防 ps 窃取),输出永不回显。
|
|
99
|
+
- gh 在本地用仓库公钥加密后才上传,明文不落盘。
|
|
100
|
+
- **组织/环境级密钥**: 附加 \`org: 'my-org'\` 或 \`env: 'production'\` 参数切换作用域。
|
|
101
|
+
- **删除密钥**: \`github_secret(action: 'delete', name: 'NPM_TOKEN', confirm: true)\`
|
|
102
|
+
- 删除后引用该密钥的 CI 工作流会立即失败,务必先确认无工作流依赖。`,
|
|
103
|
+
skill: `### 🧠 github_skill Agent Skills 生态指南
|
|
104
|
+
|
|
105
|
+
基于 GitHub CLI 的 \`gh skill\` preview 特性(需 gh >= 2.9x),跨仓库发现与安装开源 Agent Skills:
|
|
106
|
+
|
|
107
|
+
- **检索 Skills**: \`github_skill(action: 'search', query: 'code-review', limit: 15)\`
|
|
108
|
+
- 可附加 \`owner: 'github'\` 限定作者组织。
|
|
109
|
+
- **预览 Skill 内容**: \`github_skill(action: 'preview', repository: 'github/awesome-copilot', skill_name: 'code-review')\`
|
|
110
|
+
- 只读不落盘,适合安装前审阅 SKILL.md 行为约束。
|
|
111
|
+
- **安装 Skill**:
|
|
112
|
+
\`github_skill(action: 'install', repository: 'github/awesome-copilot', skill_name: 'code-review', agent: 'claude-code', scope: 'project', confirm: true)\`
|
|
113
|
+
- install 会写本地磁盘,必须 \`confirm: true\`;scope 默认 project(当前 git 仓库)。
|
|
114
|
+
- 支持的 agent 值:claude-code / cursor / github-copilot / codex / gemini-cli 等。
|
|
115
|
+
- **查看已安装**: \`github_skill(action: 'list')\`
|
|
116
|
+
- ⚠️ 若报「unknown command」,说明宿主机 gh 版本过低,需升级到 2.9x 以上。`,
|
|
117
|
+
search_syntax: `### 🔍 GitHub 官方高级检索语法速查手册
|
|
118
|
+
|
|
119
|
+
在 \`github_repo\`、\`github_api\` 或搜索场景中,请严格遵守 GitHub 官方搜索语法:
|
|
120
|
+
|
|
121
|
+
1. **日期区间语法 (严格使用两点 .. 表示)**:
|
|
122
|
+
- \`created:2026-10-01..2026-10-12\`(2026年10月1日到10月12日之间)
|
|
123
|
+
- \`created:>2026-10-01\` 或 \`created:<2026-10-12\`
|
|
124
|
+
- ⚠️ **禁忌**:GitHub 搜索**不支持**数字范围 \`6129..6695\`,这会导致静默返回 0 条结果。
|
|
125
|
+
2. **状态与类型修饰符**:
|
|
126
|
+
- \`is:pr\`、\`is:issue\`、\`is:open\`、\`is:closed\`、\`is:merged\`、\`is:draft\`
|
|
127
|
+
3. **关联者修饰符**:
|
|
128
|
+
- \`author:@me\`、\`author:username\`、\`assignee:@me\`、\`mentions:username\`
|
|
129
|
+
4. **CI 状态修饰符**:
|
|
130
|
+
- \`status:success\`、\`status:failure\`、\`status:pending\`
|
|
131
|
+
5. **代码搜索范围限制**:
|
|
132
|
+
- \`repo:owner/repo\`、\`path:src/\`、\`filename:package.json\`、\`extension:ts\``,
|
|
133
|
+
troubleshooting: `### 🛠️ 常见故障诊断与自愈配方
|
|
134
|
+
|
|
135
|
+
1. **报 \`HTTP 422: "base", "head" weren't supplied\`**:
|
|
136
|
+
- *原因*:使用 gh api 传递参数时缺少 \`-X GET\`,导致 gh 隐式将其视作 POST 新建 PR 请求。
|
|
137
|
+
- *解法*:本插件已在底层自动补全 \`-X GET\`;若使用原始端点,确保直接将参数拼接在 URL query 或传递 method: 'GET'。
|
|
138
|
+
2. **报 \`HTTP 403: API rate limit exceeded\`**:
|
|
139
|
+
- *原因*:GitHub 账号 API 配额耗尽或短时间内突发高并发被二级风控。
|
|
140
|
+
- *解法*:等待当前小时重置,或减少无意义的循环轮询;使用高层工具的针对性过滤条件减少请求次数。
|
|
141
|
+
3. **报 \`Submission gate: stale fork\` 或上游门禁失败**:
|
|
142
|
+
- *原因*:Fork 的仓库落后于主干,合并时带入了过期的配置或删除了近期条目。
|
|
143
|
+
- *解法*:调用 \`github_repo(action: 'sync')\` 自动对齐 upstream 主干代码。
|
|
144
|
+
4. **报 \`System cannot find GitHub CLI (gh)\`**:
|
|
145
|
+
- *解法*:宿主机尚未安装 gh。Windows 运行 \`winget install GitHub.cli\`,macOS 运行 \`brew install gh\`,然后执行 \`gh auth login\` 登录。
|
|
146
|
+
5. **报 \`unknown command "skill" for "gh"\`**:
|
|
147
|
+
- *原因*:\`gh skill\` 是 GitHub CLI 2.9x 引入的 preview 特性,宿主机版本过低。
|
|
148
|
+
- *解法*:升级 gh(Windows: \`winget upgrade GitHub.cli\`;macOS: \`brew upgrade gh\`)后重试;升级前可改用 \`github_api\` 检索 \`SKILL.md\` 文件路径作为替代。`
|
|
149
|
+
};
|
|
150
|
+
export function createHelpTool(executor) {
|
|
151
|
+
return {
|
|
152
|
+
name: 'github_help',
|
|
153
|
+
description: 'GitHub 工具箱使用指南与官方命令手册。当你不确定该用哪个工具、需要特定操作的高级语法与示例、或排查 GitHub 报错时调用。',
|
|
154
|
+
parameters: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: {
|
|
157
|
+
topic: {
|
|
158
|
+
type: 'string',
|
|
159
|
+
enum: [
|
|
160
|
+
'overview',
|
|
161
|
+
'pr',
|
|
162
|
+
'issue',
|
|
163
|
+
'run',
|
|
164
|
+
'repo',
|
|
165
|
+
'api',
|
|
166
|
+
'release',
|
|
167
|
+
'secret',
|
|
168
|
+
'skill',
|
|
169
|
+
'search_syntax',
|
|
170
|
+
'troubleshooting',
|
|
171
|
+
],
|
|
172
|
+
description: '帮助主题分类。留空则返回全局工具箱导航。',
|
|
173
|
+
},
|
|
174
|
+
gh_command: {
|
|
175
|
+
type: 'string',
|
|
176
|
+
description: '可选:直接查询 GitHub CLI 官方原始命令帮助手册(例如 "pr create"、"search code"、"ruleset")。',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
output: {
|
|
181
|
+
schema: { type: 'object' },
|
|
182
|
+
render(args, value) {
|
|
183
|
+
if (!value)
|
|
184
|
+
return [{ type: 'text', text: '无返回内容' }];
|
|
185
|
+
if (typeof value.result === 'string') {
|
|
186
|
+
return [{ type: 'text', text: value.result }];
|
|
187
|
+
}
|
|
188
|
+
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
async execute(args, execContext) {
|
|
192
|
+
const cwd = execContext?.cwd || process.cwd();
|
|
193
|
+
const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
|
|
194
|
+
// 1. 若显式指定了 gh_command,直接透传查询官方离线手册
|
|
195
|
+
if (args.gh_command && typeof args.gh_command === 'string') {
|
|
196
|
+
let parts = args.gh_command.trim().split(/\s+/);
|
|
197
|
+
// 容错调用方输入 "gh pr create" 带有前导 "gh" 的情形
|
|
198
|
+
if (parts[0] === 'gh') {
|
|
199
|
+
parts.shift();
|
|
200
|
+
}
|
|
201
|
+
const res = await executor.run(['help', ...parts], { cwd, sessionId, rawText: true });
|
|
202
|
+
if (!res.ok) {
|
|
203
|
+
return {
|
|
204
|
+
result: `未找到命令「gh ${args.gh_command}」的官方帮助手册:${res.error}\n建议通过 topic 参数查询内置指南。`,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return { result: res.rawOutput };
|
|
208
|
+
}
|
|
209
|
+
// 2. 查询内置知识库速查表
|
|
210
|
+
// 类型防护:DSH 网关对 enum 参数的拦截强度依赖实现,Agent 仍可能传非字符串
|
|
211
|
+
// (数字/对象/数组)。`-1 || 'overview'` 真值短路挡不住负数,会炸 toLowerCase。
|
|
212
|
+
// 与上方 gh_command 分支的 typeof 防护保持同一强度。
|
|
213
|
+
const rawTopic = typeof args.topic === 'string' && args.topic.trim() ? args.topic : 'overview';
|
|
214
|
+
const topic = rawTopic.toLowerCase().trim();
|
|
215
|
+
const content = KNOWLEDGE_BASE[topic];
|
|
216
|
+
if (content) {
|
|
217
|
+
return { result: content };
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
result: `未找到主题「${rawTopic}」的指南。可用主题:${Object.keys(KNOWLEDGE_BASE).join(', ')}。`,
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 把 README 中的仓库内相对图片路径解析为 raw.githubusercontent 绝对 URL。
|
|
3
|
+
* 强制落在 raw 域名模板内——不存在拼出任意域名的可能(SSRF 白名单天然覆盖)。
|
|
4
|
+
* 路径以 ./ ../ /docs/ 等形态出现;URL 编码空格等字符;无效形态返回 null。
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveRelativeImageUrl(rel: string, owner: string, repo: string, branch: string): string | null;
|
|
7
|
+
/**
|
|
8
|
+
* 判断 URL 是否为徽章类小图标(shields.io / badge 类 svg)。
|
|
9
|
+
* README 图片场景默认过滤:用户要看的是截图/架构图,版本徽章是排版噪音;
|
|
10
|
+
* include_badges=true 时保留(有人确实要查徽章状态)。
|
|
11
|
+
*/
|
|
12
|
+
export declare function isBadgeImageUrl(url: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 从正文(Issue/PR body 或评论)提取图片 URL。
|
|
15
|
+
* 域名白名单 + https 扩展名双条件:
|
|
16
|
+
* - GitHub 四资产域名直接放行(用户上传/旧版上传/仓库引用/camo 代理);
|
|
17
|
+
* - 外链强制 https 且带图片扩展名——关闭恶意正文诱导访问内网元数据端点
|
|
18
|
+
* (169.254.169.254)等 SSRF 面,file:/// 与 http:// 一律拒绝。
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractImageUrls(body: unknown): string[];
|
|
21
|
+
/**
|
|
22
|
+
* README 专用提取:绝对 URL 走通用提取器;仓库内相对路径解析为 raw URL。
|
|
23
|
+
* 默认过滤徽章小图标(isBadgeImageUrl),include_badges 开关保留。
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractReadmeImageUrls(body: unknown, ctx: {
|
|
26
|
+
owner: string;
|
|
27
|
+
repo: string;
|
|
28
|
+
branch: string;
|
|
29
|
+
includeBadges?: boolean;
|
|
30
|
+
}): {
|
|
31
|
+
urls: string[];
|
|
32
|
+
relativeResolved: number;
|
|
33
|
+
badgeFiltered: number;
|
|
34
|
+
};
|
|
35
|
+
/** 从魔数推断图片 MIME(下载内容才是事实源——URL 扩展名可伪造) */
|
|
36
|
+
export declare function sniffImageMime(buf: Buffer): string | null;
|
|
37
|
+
/** 图片下载护栏:按真实资源消耗计算——单图字节与目录总量,无张数截断 */
|
|
38
|
+
export declare const IMG_MAX_BYTES: number;
|
|
39
|
+
export declare const IMG_DIR_MAX_BYTES: number;
|
|
40
|
+
export interface DownloadedImage {
|
|
41
|
+
file: string;
|
|
42
|
+
mimeType: string;
|
|
43
|
+
url: string;
|
|
44
|
+
bytes: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ImageBatchResult {
|
|
47
|
+
images: DownloadedImage[];
|
|
48
|
+
failures: string[];
|
|
49
|
+
/**
|
|
50
|
+
* 因目录总量护栏触顶而未处理的 URL(结构化续传清单)。
|
|
51
|
+
* Agent 可直接 image_urls: result.remaining 发起下一批——remaining 中的
|
|
52
|
+
* URL 本就来自正文合法清单,交集校验天然通过,分批获取无需清理目录。
|
|
53
|
+
*/
|
|
54
|
+
remaining?: string[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 交集过滤:把调用方请求的 URL 硬约束在正文实际提取的合法清单内。
|
|
58
|
+
* 为什么必须交集而非直接信任传入:image_urls 来自模型生成参数,存在幻觉或
|
|
59
|
+
* 被诱导传入任意 URL 的可能——交集把下载目标限定在本条 Issue/PR 引用过的
|
|
60
|
+
* 图片范围,SSRF 防线(提取阶段的 https+域名白名单)自动覆盖全部下载目标。
|
|
61
|
+
* 返回 { matched, rejected }:matched 保持请求顺序(Agent 按自己的关注顺序拿结果)。
|
|
62
|
+
*/
|
|
63
|
+
export declare function filterRequestedUrls(requested: unknown, extracted: string[]): {
|
|
64
|
+
matched: string[];
|
|
65
|
+
rejected: string[];
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* 批量下载图片并落盘(Issue/PR 的 view_images 共用执行体)。
|
|
69
|
+
* 每张经 executor 的二进制通道(gh api),魔数校验 + 三重护栏 + 内容寻址;
|
|
70
|
+
* 权威目录不可写时降级系统临时目录(只损失跨会话去重)。
|
|
71
|
+
* 单张失败不中断批次——failures 记录原因由调用方透传给 Agent。
|
|
72
|
+
*/
|
|
73
|
+
export declare function downloadImages(urls: string[], run: (url: string) => Promise<{
|
|
74
|
+
ok: boolean;
|
|
75
|
+
data?: unknown;
|
|
76
|
+
error?: string;
|
|
77
|
+
}>): Promise<ImageBatchResult>;
|
|
78
|
+
/** render 的 image 内容块构造(text 块由调用方自行拼):仅双合法字段才产出块 */
|
|
79
|
+
export declare function buildImageBlocks(value: any): any[];
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue / PR 共享的图片附件工具链。
|
|
3
|
+
*
|
|
4
|
+
* 为什么从 issue.ts 抽出:PR 与 Issue 的图片机制完全同构(同样的
|
|
5
|
+
* user-attachments 域名、<img>/![]() 双语法、同一评论 API),view_images
|
|
6
|
+
* 在两侧落地时共享提取/嗅探/护栏/落盘全部逻辑,避免两份拷贝漂移。
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import crypto from 'node:crypto';
|
|
12
|
+
/**
|
|
13
|
+
* 把 README 中的仓库内相对图片路径解析为 raw.githubusercontent 绝对 URL。
|
|
14
|
+
* 强制落在 raw 域名模板内——不存在拼出任意域名的可能(SSRF 白名单天然覆盖)。
|
|
15
|
+
* 路径以 ./ ../ /docs/ 等形态出现;URL 编码空格等字符;无效形态返回 null。
|
|
16
|
+
*/
|
|
17
|
+
export function resolveRelativeImageUrl(rel, owner, repo, branch) {
|
|
18
|
+
if (typeof rel !== 'string' || !rel || rel.startsWith('#'))
|
|
19
|
+
return null;
|
|
20
|
+
// 任何带 scheme 的(http/javascript/data/file)与 Windows 盘符/UNC 路径都不是
|
|
21
|
+
// 仓库内相对路径——统一拒绝,只接受纯 POSIX 相对形态
|
|
22
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(rel))
|
|
23
|
+
return null;
|
|
24
|
+
if (/^[a-z]:[\\/]/i.test(rel) || rel.startsWith('\\\\') || rel.includes('\\'))
|
|
25
|
+
return null;
|
|
26
|
+
// 去掉 ./ 前缀;拒绝越出仓库的绝对路径与查询串
|
|
27
|
+
let p = rel.replace(/^\.\//, '');
|
|
28
|
+
if (p.startsWith('/') || p.includes('..') || p.includes('?') || p.includes('#'))
|
|
29
|
+
return null;
|
|
30
|
+
// 只放行仓库路径的合法字符集(字母数字、CJK/Unicode 文件名、._/- 空格),
|
|
31
|
+
// 从根上挡住把控制字符/协议残留拼进 raw URL 的畸形输入
|
|
32
|
+
if (!/^[\w\-. /\u0080-\uffff]+$/u.test(p))
|
|
33
|
+
return null;
|
|
34
|
+
try {
|
|
35
|
+
const encoded = p.split('/').map(encodeURIComponent).join('/');
|
|
36
|
+
return `https://raw.githubusercontent.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(branch)}/${encoded}`;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 判断 URL 是否为徽章类小图标(shields.io / badge 类 svg)。
|
|
44
|
+
* README 图片场景默认过滤:用户要看的是截图/架构图,版本徽章是排版噪音;
|
|
45
|
+
* include_badges=true 时保留(有人确实要查徽章状态)。
|
|
46
|
+
*/
|
|
47
|
+
export function isBadgeImageUrl(url) {
|
|
48
|
+
return /img\.shields\.io|badgen\.net|badge|\/svg\?style=/i.test(url);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 从正文(Issue/PR body 或评论)提取图片 URL。
|
|
52
|
+
* 域名白名单 + https 扩展名双条件:
|
|
53
|
+
* - GitHub 四资产域名直接放行(用户上传/旧版上传/仓库引用/camo 代理);
|
|
54
|
+
* - 外链强制 https 且带图片扩展名——关闭恶意正文诱导访问内网元数据端点
|
|
55
|
+
* (169.254.169.254)等 SSRF 面,file:/// 与 http:// 一律拒绝。
|
|
56
|
+
*/
|
|
57
|
+
export function extractImageUrls(body) {
|
|
58
|
+
if (typeof body !== 'string' || !body)
|
|
59
|
+
return [];
|
|
60
|
+
const urls = new Set();
|
|
61
|
+
// HTML <img src="..."> 与 Markdown  两种形态
|
|
62
|
+
const pattern = /(?:<img[^>]+src="([^"]+)"|!\[[^\]]*\]\(([^)\s]+)[^)]*\))/gi;
|
|
63
|
+
let m;
|
|
64
|
+
while ((m = pattern.exec(body)) !== null) {
|
|
65
|
+
const url = m[1] || m[2];
|
|
66
|
+
if (!url)
|
|
67
|
+
continue;
|
|
68
|
+
const isGithubAsset = url.startsWith('https://github.com/user-attachments/') ||
|
|
69
|
+
url.startsWith('https://user-images.githubusercontent.com/') ||
|
|
70
|
+
url.startsWith('https://raw.githubusercontent.com/') ||
|
|
71
|
+
url.startsWith('https://camo.githubusercontent.com/');
|
|
72
|
+
const looksLikeImage = url.startsWith('https://') &&
|
|
73
|
+
/\.(png|jpe?g|gif|webp|bmp|svg)([?#]|$)/i.test(url);
|
|
74
|
+
if (isGithubAsset || looksLikeImage)
|
|
75
|
+
urls.add(url);
|
|
76
|
+
}
|
|
77
|
+
return Array.from(urls);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* README 专用提取:绝对 URL 走通用提取器;仓库内相对路径解析为 raw URL。
|
|
81
|
+
* 默认过滤徽章小图标(isBadgeImageUrl),include_badges 开关保留。
|
|
82
|
+
*/
|
|
83
|
+
export function extractReadmeImageUrls(body, ctx) {
|
|
84
|
+
if (typeof body !== 'string' || !body)
|
|
85
|
+
return { urls: [], relativeResolved: 0, badgeFiltered: 0 };
|
|
86
|
+
const absolute = extractImageUrls(body);
|
|
87
|
+
const result = [];
|
|
88
|
+
let badgeFiltered = 0;
|
|
89
|
+
const keep = (u) => {
|
|
90
|
+
if (!ctx.includeBadges && isBadgeImageUrl(u)) {
|
|
91
|
+
badgeFiltered++;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
result.push(u);
|
|
95
|
+
};
|
|
96
|
+
for (const u of absolute)
|
|
97
|
+
keep(u);
|
|
98
|
+
// 相对路径:同一正则源头重新扫,取原始 src 再解析(绝对 URL 的正则会漏掉 ./docs/x.png)
|
|
99
|
+
let relativeResolved = 0;
|
|
100
|
+
const pattern = /(?:<img[^>]+src="([^"]+)"|!\[[^\]]*\]\(([^)\s]+)[^)]*\))/gi;
|
|
101
|
+
let m;
|
|
102
|
+
while ((m = pattern.exec(body)) !== null) {
|
|
103
|
+
const raw = m[1] || m[2];
|
|
104
|
+
if (!raw || /^[a-z]+:\/\//i.test(raw))
|
|
105
|
+
continue; // 只处理相对路径
|
|
106
|
+
const resolved = resolveRelativeImageUrl(raw, ctx.owner, ctx.repo, ctx.branch);
|
|
107
|
+
if (resolved) {
|
|
108
|
+
keep(resolved);
|
|
109
|
+
relativeResolved++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { urls: Array.from(new Set(result)), relativeResolved, badgeFiltered };
|
|
113
|
+
}
|
|
114
|
+
/** 从魔数推断图片 MIME(下载内容才是事实源——URL 扩展名可伪造) */
|
|
115
|
+
export function sniffImageMime(buf) {
|
|
116
|
+
// 长度下限只挡二进制格式(PNG/JPEG/GIF/WEBP 均有 ≥12 字节头);SVG 是文本,
|
|
117
|
+
// 无固定头长度——短到 4 字节的 '<svg' 也合法,必须在长度检查之前判定
|
|
118
|
+
if (buf.slice(0, 4).toString('ascii').toLowerCase() === '<svg')
|
|
119
|
+
return 'image/svg+xml';
|
|
120
|
+
if (buf.length < 12)
|
|
121
|
+
return null;
|
|
122
|
+
if (buf[0] === 0x89 && buf[1] === 0x50)
|
|
123
|
+
return 'image/png'; // PNG
|
|
124
|
+
if (buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff)
|
|
125
|
+
return 'image/jpeg'; // JPEG
|
|
126
|
+
if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46)
|
|
127
|
+
return 'image/gif'; // GIF
|
|
128
|
+
if (buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50)
|
|
129
|
+
return 'image/webp'; // RIFF....WEBP
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
/** 图片下载护栏:按真实资源消耗计算——单图字节与目录总量,无张数截断 */
|
|
133
|
+
export const IMG_MAX_BYTES = 5 * 1024 * 1024; // 单图 5MB
|
|
134
|
+
export const IMG_DIR_MAX_BYTES = 15 * 1024 * 1024; // 图片缓存目录总量 15MB
|
|
135
|
+
/** 图片落盘目录(按内容寻址命名,重复下载天然去重) */
|
|
136
|
+
function imageStoreDir() {
|
|
137
|
+
return path.join(os.homedir(), '.dsh', 'data', 'dsh-github-flow', 'images');
|
|
138
|
+
}
|
|
139
|
+
/** 粗略统计图片目录现有总量(遍历一次,文件数有护栏上限不会失控) */
|
|
140
|
+
function imageDirSize(dir) {
|
|
141
|
+
let total = 0;
|
|
142
|
+
try {
|
|
143
|
+
for (const f of fs.readdirSync(dir)) {
|
|
144
|
+
try {
|
|
145
|
+
total += fs.statSync(path.join(dir, f)).size;
|
|
146
|
+
}
|
|
147
|
+
catch { }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch { }
|
|
151
|
+
return total;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* 交集过滤:把调用方请求的 URL 硬约束在正文实际提取的合法清单内。
|
|
155
|
+
* 为什么必须交集而非直接信任传入:image_urls 来自模型生成参数,存在幻觉或
|
|
156
|
+
* 被诱导传入任意 URL 的可能——交集把下载目标限定在本条 Issue/PR 引用过的
|
|
157
|
+
* 图片范围,SSRF 防线(提取阶段的 https+域名白名单)自动覆盖全部下载目标。
|
|
158
|
+
* 返回 { matched, rejected }:matched 保持请求顺序(Agent 按自己的关注顺序拿结果)。
|
|
159
|
+
*/
|
|
160
|
+
export function filterRequestedUrls(requested, extracted) {
|
|
161
|
+
const matched = [];
|
|
162
|
+
const rejected = [];
|
|
163
|
+
const allow = new Set(extracted);
|
|
164
|
+
for (const u of Array.isArray(requested) ? requested : []) {
|
|
165
|
+
if (typeof u === 'string' && u && allow.has(u)) {
|
|
166
|
+
matched.push(u);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
rejected.push(typeof u === 'string' && u ? u : JSON.stringify(u));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { matched, rejected };
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 批量下载图片并落盘(Issue/PR 的 view_images 共用执行体)。
|
|
176
|
+
* 每张经 executor 的二进制通道(gh api),魔数校验 + 三重护栏 + 内容寻址;
|
|
177
|
+
* 权威目录不可写时降级系统临时目录(只损失跨会话去重)。
|
|
178
|
+
* 单张失败不中断批次——failures 记录原因由调用方透传给 Agent。
|
|
179
|
+
*/
|
|
180
|
+
export async function downloadImages(urls, run) {
|
|
181
|
+
let dir = imageStoreDir();
|
|
182
|
+
try {
|
|
183
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
dir = path.join(os.tmpdir(), 'dsh-github-flow-images');
|
|
187
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
188
|
+
}
|
|
189
|
+
let dirSize = imageDirSize(dir);
|
|
190
|
+
const images = [];
|
|
191
|
+
const failures = [];
|
|
192
|
+
const remaining = [];
|
|
193
|
+
let budgetExhausted = false;
|
|
194
|
+
// 无张数截断:字节护栏按真实资源消耗逐张判定。触顶后仍继续遍历——
|
|
195
|
+
// 剩余 URL 全部收进 remaining 供 Agent 分批续传,而非静默丢弃。
|
|
196
|
+
for (const url of urls) {
|
|
197
|
+
if (budgetExhausted) {
|
|
198
|
+
remaining.push(url);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const res = await run(url);
|
|
202
|
+
const buf = res.ok && Buffer.isBuffer(res.data) ? res.data : null;
|
|
203
|
+
if (!buf) {
|
|
204
|
+
failures.push(`${url.slice(-24)}: ${res.error || '非二进制响应'}`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (buf.length > IMG_MAX_BYTES) {
|
|
208
|
+
failures.push(`${url.slice(-24)}: 超过单图 5MB 上限(${(buf.length / 1024 / 1024).toFixed(1)}MB)`);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const mime = sniffImageMime(buf);
|
|
212
|
+
if (!mime) {
|
|
213
|
+
failures.push(`${url.slice(-24)}: 内容不是图片(魔数校验失败)`);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (dirSize + buf.length > IMG_DIR_MAX_BYTES) {
|
|
217
|
+
budgetExhausted = true;
|
|
218
|
+
remaining.push(url);
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
// 内容寻址(sha256 前 16 位)+ 扩展名:重复下载同一图去重,文件名可读
|
|
222
|
+
const ext = mime.includes('svg') ? 'svg' : mime.split('/')[1].replace('jpeg', 'jpg');
|
|
223
|
+
const file = path.join(dir, `${crypto.createHash('sha256').update(buf).digest('hex').slice(0, 16)}.${ext}`);
|
|
224
|
+
fs.writeFileSync(file, buf);
|
|
225
|
+
// 累计已写入量:下张图的总量护栏判定必须基于最新值(否则多图可突破上限)
|
|
226
|
+
dirSize += buf.length;
|
|
227
|
+
images.push({ file, mimeType: mime, url, bytes: buf.length });
|
|
228
|
+
}
|
|
229
|
+
// 触顶反馈(用户裁定的续传闭环):已获取 N/M + 结构化剩余清单,
|
|
230
|
+
// Agent 直接 image_urls: remaining 续传,无需从文本里解析 URL
|
|
231
|
+
if (remaining.length > 0) {
|
|
232
|
+
failures.push(`图片缓存目录达 15MB 上限:本次获取 ${images.length}/${urls.length} 张,剩余 ${remaining.length} 张未下载(可分批续传)`);
|
|
233
|
+
}
|
|
234
|
+
return { images, failures, remaining: remaining.length ? remaining : undefined };
|
|
235
|
+
}
|
|
236
|
+
/** render 的 image 内容块构造(text 块由调用方自行拼):仅双合法字段才产出块 */
|
|
237
|
+
export function buildImageBlocks(value) {
|
|
238
|
+
const blocks = [];
|
|
239
|
+
if (Array.isArray(value?.images)) {
|
|
240
|
+
for (const img of value.images) {
|
|
241
|
+
if (img && typeof img.file === 'string' && typeof img.mimeType === 'string') {
|
|
242
|
+
blocks.push({ type: 'image', path: img.file, mimeType: img.mimeType });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return blocks;
|
|
247
|
+
}
|
package/lib/tools/issue.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GhExecutor } from '../executor.js';
|
|
2
|
+
export { extractImageUrls as extractIssueImageUrls } from './image-utils.js';
|
|
2
3
|
export declare function createIssueTool(executor: GhExecutor): {
|
|
3
4
|
name: string;
|
|
4
5
|
description: string;
|
|
@@ -40,6 +41,44 @@ export declare function createIssueTool(executor: GhExecutor): {
|
|
|
40
41
|
};
|
|
41
42
|
description: string;
|
|
42
43
|
};
|
|
44
|
+
state: {
|
|
45
|
+
type: string;
|
|
46
|
+
enum: string[];
|
|
47
|
+
description: string;
|
|
48
|
+
};
|
|
49
|
+
limit: {
|
|
50
|
+
type: string;
|
|
51
|
+
description: string;
|
|
52
|
+
};
|
|
53
|
+
branch_name: {
|
|
54
|
+
type: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
57
|
+
add_labels: {
|
|
58
|
+
type: string;
|
|
59
|
+
items: {
|
|
60
|
+
type: string;
|
|
61
|
+
};
|
|
62
|
+
description: string;
|
|
63
|
+
};
|
|
64
|
+
remove_labels: {
|
|
65
|
+
type: string;
|
|
66
|
+
items: {
|
|
67
|
+
type: string;
|
|
68
|
+
};
|
|
69
|
+
description: string;
|
|
70
|
+
};
|
|
71
|
+
confirm: {
|
|
72
|
+
type: string;
|
|
73
|
+
description: string;
|
|
74
|
+
};
|
|
75
|
+
image_urls: {
|
|
76
|
+
type: string;
|
|
77
|
+
items: {
|
|
78
|
+
type: string;
|
|
79
|
+
};
|
|
80
|
+
description: string;
|
|
81
|
+
};
|
|
43
82
|
};
|
|
44
83
|
required: string[];
|
|
45
84
|
};
|
|
@@ -47,10 +86,7 @@ export declare function createIssueTool(executor: GhExecutor): {
|
|
|
47
86
|
schema: {
|
|
48
87
|
type: string;
|
|
49
88
|
};
|
|
50
|
-
render(args: any, value: any):
|
|
51
|
-
type: string;
|
|
52
|
-
text: any;
|
|
53
|
-
}[];
|
|
89
|
+
render(args: any, value: any): any[];
|
|
54
90
|
};
|
|
55
91
|
execute(args: any, execContext: any): Promise<Record<string, any>>;
|
|
56
92
|
};
|