dsh-github-flow 0.1.8 → 0.2.0
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 +139 -54
- package/README.md +142 -54
- package/docs/architecture.md +19 -4
- package/docs/images/1.png +0 -0
- package/docs/images/3.png +0 -0
- package/docs/images/4.png +0 -0
- package/docs/npm-publish-guide.md +12 -0
- package/lib/api/route-helpers.d.ts +47 -0
- package/lib/api/route-helpers.js +134 -0
- package/lib/api/routes-actions.d.ts +9 -0
- package/lib/api/routes-actions.js +115 -0
- package/lib/api/routes-lifecycle.d.ts +9 -0
- package/lib/api/routes-lifecycle.js +75 -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 +383 -55
- package/lib/executor.d.ts +17 -0
- package/lib/executor.js +28 -0
- package/lib/main.d.ts +1 -0
- package/lib/main.js +40 -9
- package/lib/proxy.d.ts +87 -0
- package/lib/proxy.js +275 -0
- package/lib/renderers/markdown.d.ts +24 -0
- package/lib/renderers/markdown.js +493 -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 +212 -0
- package/lib/tools/issue.d.ts +32 -1
- package/lib/tools/issue.js +94 -17
- package/lib/tools/pr.d.ts +22 -1
- package/lib/tools/pr.js +67 -15
- package/lib/tools/release.d.ts +89 -0
- package/lib/tools/release.js +219 -0
- package/lib/tools/repo.d.ts +13 -1
- package/lib/tools/repo.js +46 -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 +12 -0
- package/lib/updater.d.ts +8 -0
- package/lib/updater.js +9 -0
- package/lib/upgrade-manager.d.ts +4 -0
- package/lib/upgrade-manager.js +11 -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,175 @@
|
|
|
1
|
+
function normalizeOutput(data) {
|
|
2
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
3
|
+
return data;
|
|
4
|
+
}
|
|
5
|
+
if (Array.isArray(data)) {
|
|
6
|
+
return { items: data, count: data.length };
|
|
7
|
+
}
|
|
8
|
+
return { result: data !== undefined && data !== null ? data : '' };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 渲染 secret 工具输出。
|
|
12
|
+
* 独立于 markdown.ts 渲染器的原因:这里有一条硬性安全规则——任何分支都不允许
|
|
13
|
+
* 渲染 value 字段(哪怕是调试用途)。逻辑集中在本地一眼可审,避免在通用渲染器
|
|
14
|
+
* 里混入「按字段名黑名单过滤」的隐蔽约束。
|
|
15
|
+
*/
|
|
16
|
+
function renderSecretOutput(args, value) {
|
|
17
|
+
if (!value)
|
|
18
|
+
return '无返回内容';
|
|
19
|
+
if (value.status === 'AWAITING_CONFIRMATION') {
|
|
20
|
+
return [
|
|
21
|
+
`> ### ${value.warning}`,
|
|
22
|
+
`> - **影响评估**: ${value.impact}`,
|
|
23
|
+
`> - **确认指引**: ${value.requiredAction}`,
|
|
24
|
+
].join('\n');
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
27
|
+
const list = value.items.filter(Boolean);
|
|
28
|
+
if (list.length === 0)
|
|
29
|
+
return '📭 当前仓库尚未配置任何 Secret。';
|
|
30
|
+
const rows = list.map((s) => {
|
|
31
|
+
const name = `\`${String(s.name || '-')}\``;
|
|
32
|
+
const vis = s.visibility ? String(s.visibility) : '-';
|
|
33
|
+
const date = s.updatedAt ? s.updatedAt.slice(0, 10) : '-';
|
|
34
|
+
return `| ${name} | ${vis} | ${date} |`;
|
|
35
|
+
});
|
|
36
|
+
return [
|
|
37
|
+
`**当前已配置 ${list.length} 个 Secret(仅名称,永不展示明文):**\n`,
|
|
38
|
+
'| 密钥名称 | 可见性 | 最近更新 |',
|
|
39
|
+
'| :--- | :--- | :--- |',
|
|
40
|
+
...rows,
|
|
41
|
+
].join('\n');
|
|
42
|
+
}
|
|
43
|
+
if (typeof value.message === 'string') {
|
|
44
|
+
return `**${value.message}**`;
|
|
45
|
+
}
|
|
46
|
+
return JSON.stringify(value, null, 2);
|
|
47
|
+
}
|
|
48
|
+
export function createSecretTool(executor) {
|
|
49
|
+
return {
|
|
50
|
+
name: 'github_secret',
|
|
51
|
+
description: '管理 GitHub Actions 仓库密钥 (Secrets):查看已配置的密钥名称列表、设置/更新密钥值(如 NPM_TOKEN、部署密钥)。密文经标准输入安全传递,绝不出现在命令行参数中,也不会在输出中回显。删除需 confirm: true。',
|
|
52
|
+
parameters: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
action: {
|
|
56
|
+
type: 'string',
|
|
57
|
+
enum: ['list', 'set', 'delete'],
|
|
58
|
+
description: 'Secret 操作类型。',
|
|
59
|
+
},
|
|
60
|
+
repo: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
description: '目标仓库 [owner/repo],留空则自动识别当前本地 Git 仓库。',
|
|
63
|
+
},
|
|
64
|
+
name: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
description: '密钥名称(set/delete 动作使用,如 "NPM_TOKEN"、"DOCKER_PASSWORD")。',
|
|
67
|
+
},
|
|
68
|
+
value: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: '密钥明文内容(set 动作使用)。内部通过子进程标准输入传递,避免在进程命令行被 ps/任务管理器窃取。',
|
|
71
|
+
},
|
|
72
|
+
org: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
description: '组织级密钥的所属组织名(可选,映射 --org)。',
|
|
75
|
+
},
|
|
76
|
+
env: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: '环境级密钥的部署环境名(可选,映射 --env)。',
|
|
79
|
+
},
|
|
80
|
+
app: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
enum: ['actions', 'agents', 'codespaces', 'dependabot'],
|
|
83
|
+
description: '密钥归属的应用(可选,映射 --app,默认 actions)。',
|
|
84
|
+
},
|
|
85
|
+
visibility: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
enum: ['all', 'private', 'selected'],
|
|
88
|
+
description: '组织级密钥的可见范围(可选,仅 org 场景有意义)。',
|
|
89
|
+
},
|
|
90
|
+
confirm: {
|
|
91
|
+
type: 'boolean',
|
|
92
|
+
description: '删除密钥时的确认标志。删除不可逆,未传 true 时仅输出影响评估。',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
required: ['action'],
|
|
96
|
+
},
|
|
97
|
+
output: {
|
|
98
|
+
schema: { type: 'object' },
|
|
99
|
+
render(args, value) {
|
|
100
|
+
return [{ type: 'text', text: renderSecretOutput(args, value) }];
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
async execute(args, execContext) {
|
|
104
|
+
const cwd = execContext?.cwd || process.cwd();
|
|
105
|
+
const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
|
|
106
|
+
// org 作用域的密钥挂在组织上,与具体仓库无关:既不需要仓库识别,
|
|
107
|
+
// 也不能把 -R 与 --org 同时传给 gh(两者语义冲突,gh 会报错)。
|
|
108
|
+
const isOrgScope = Boolean(args.org);
|
|
109
|
+
let repoTarget = args.repo;
|
|
110
|
+
if (!repoTarget && !isOrgScope) {
|
|
111
|
+
const meta = await executor.getRepoMetadata(cwd, sessionId);
|
|
112
|
+
if (meta?.nameWithOwner) {
|
|
113
|
+
repoTarget = meta.nameWithOwner;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!repoTarget && !isOrgScope) {
|
|
117
|
+
throw new Error('未能自动识别当前工作区关联的 GitHub 仓库。请通过 `repo` 参数显式指定目标仓库 [owner/repo],或改用 `org` 参数操作组织级密钥。');
|
|
118
|
+
}
|
|
119
|
+
const runOpts = { cwd, sessionId };
|
|
120
|
+
// org/env/app 三类作用域标志对所有动作通用;仓库作用域时三项均不追加
|
|
121
|
+
const scopeFlags = [];
|
|
122
|
+
if (args.org) {
|
|
123
|
+
scopeFlags.push('--org', args.org);
|
|
124
|
+
if (args.visibility)
|
|
125
|
+
scopeFlags.push('--visibility', args.visibility);
|
|
126
|
+
}
|
|
127
|
+
else if (args.env) {
|
|
128
|
+
scopeFlags.push('--env', args.env);
|
|
129
|
+
}
|
|
130
|
+
if (args.app)
|
|
131
|
+
scopeFlags.push('--app', args.app);
|
|
132
|
+
const targetArgs = isOrgScope ? [] : ['-R', repoTarget];
|
|
133
|
+
switch (args.action) {
|
|
134
|
+
case 'list': {
|
|
135
|
+
const res = await executor.run(['secret', 'list', ...targetArgs, ...scopeFlags, '--json', 'name,updatedAt,visibility'], runOpts);
|
|
136
|
+
if (!res.ok)
|
|
137
|
+
throw new Error(res.error);
|
|
138
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
139
|
+
}
|
|
140
|
+
case 'set': {
|
|
141
|
+
if (!args.name)
|
|
142
|
+
throw new Error('set 操作必须提供 name(密钥名称)');
|
|
143
|
+
if (typeof args.value !== 'string' || args.value.length === 0) {
|
|
144
|
+
throw new Error('set 操作必须提供 value(密钥明文),且不允许为空字符串');
|
|
145
|
+
}
|
|
146
|
+
// 安全红线:密文只走 stdin。严禁退化为 -b <value> —— 那会把明文
|
|
147
|
+
// 留在进程列表与 shell 历史里,等于密钥泄漏。gh 本地会用仓库公钥
|
|
148
|
+
// 加密后才上传,明文不落盘、不出本机。
|
|
149
|
+
const res = await executor.run(['secret', 'set', args.name, ...targetArgs, ...scopeFlags], { ...runOpts, stdin: args.value });
|
|
150
|
+
if (!res.ok)
|
|
151
|
+
throw new Error(res.error);
|
|
152
|
+
return { message: `Secret「${args.name}」已更新(值不回显)` };
|
|
153
|
+
}
|
|
154
|
+
case 'delete': {
|
|
155
|
+
if (!args.name)
|
|
156
|
+
throw new Error('delete 操作必须提供 name');
|
|
157
|
+
if (args.confirm !== true) {
|
|
158
|
+
return {
|
|
159
|
+
status: 'AWAITING_CONFIRMATION',
|
|
160
|
+
warning: `⚠️ 高危操作拦截:你即将删除 Secret「${args.name}」${args.org ? `(组织 ${args.org})` : ''}。`,
|
|
161
|
+
impact: '删除后所有引用该密钥的 CI/CD 工作流将立即无法取到值,运行中的构建会失败。',
|
|
162
|
+
requiredAction: '如确认删除,请向用户核实并附加 `confirm: true` 重新调用。',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const res = await executor.run(['secret', 'delete', args.name, ...targetArgs, ...scopeFlags], runOpts);
|
|
166
|
+
if (!res.ok)
|
|
167
|
+
throw new Error(res.error);
|
|
168
|
+
return { message: `Secret「${args.name}」已删除` };
|
|
169
|
+
}
|
|
170
|
+
default:
|
|
171
|
+
throw new Error(`未知的 Secret 操作: ${args.action}`);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { GhExecutor } from '../executor.js';
|
|
2
|
+
export declare function createSkillTool(executor: GhExecutor): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: string;
|
|
7
|
+
properties: {
|
|
8
|
+
action: {
|
|
9
|
+
type: string;
|
|
10
|
+
enum: string[];
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
query: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
owner: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
repository: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
skill_name: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
agent: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
scope: {
|
|
34
|
+
type: string;
|
|
35
|
+
enum: string[];
|
|
36
|
+
description: string;
|
|
37
|
+
};
|
|
38
|
+
all: {
|
|
39
|
+
type: string;
|
|
40
|
+
description: string;
|
|
41
|
+
};
|
|
42
|
+
limit: {
|
|
43
|
+
type: string;
|
|
44
|
+
description: string;
|
|
45
|
+
};
|
|
46
|
+
confirm: {
|
|
47
|
+
type: string;
|
|
48
|
+
description: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
required: string[];
|
|
52
|
+
};
|
|
53
|
+
output: {
|
|
54
|
+
schema: {
|
|
55
|
+
type: string;
|
|
56
|
+
};
|
|
57
|
+
render(args: any, value: any): {
|
|
58
|
+
type: string;
|
|
59
|
+
text: string;
|
|
60
|
+
}[];
|
|
61
|
+
};
|
|
62
|
+
execute(args: any, execContext: any): Promise<Record<string, any>>;
|
|
63
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { renderSkillMarkdown } from '../renderers/markdown.js';
|
|
2
|
+
function normalizeOutput(data) {
|
|
3
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
4
|
+
return data;
|
|
5
|
+
}
|
|
6
|
+
if (Array.isArray(data)) {
|
|
7
|
+
return { items: data, count: data.length };
|
|
8
|
+
}
|
|
9
|
+
return { result: data !== undefined && data !== null ? data : '' };
|
|
10
|
+
}
|
|
11
|
+
export function createSkillTool(executor) {
|
|
12
|
+
return {
|
|
13
|
+
name: 'github_skill',
|
|
14
|
+
description: '通过 GitHub CLI 官方生态发现与管理 Agent Skills:跨仓库检索开源 Skills、预览 SKILL.md 内容、安装到本地 Agent 宿主(Claude Code / Cursor / Copilot 等)、查看已安装列表。install 会写入本地磁盘,需 confirm: true。基于 gh CLI preview 特性(需 gh >= 2.9x)。',
|
|
15
|
+
parameters: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
action: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
enum: ['search', 'preview', 'install', 'list'],
|
|
21
|
+
description: 'Skill 操作类型。',
|
|
22
|
+
},
|
|
23
|
+
query: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: '搜索关键词(search 动作使用,如 "python"、"devops"、"markdown")。',
|
|
26
|
+
},
|
|
27
|
+
owner: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: '限定作者或组织(search 动作可选,映射 --owner)。',
|
|
30
|
+
},
|
|
31
|
+
repository: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: '目标仓库全名 [owner/repo](preview/install 动作使用,如 "github/awesome-copilot")。',
|
|
34
|
+
},
|
|
35
|
+
skill_name: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'Skill 名称(preview/install 动作可选)。支持 "name"、"author/name" 命名空间形式或 "name@version" 锁定版本。',
|
|
38
|
+
},
|
|
39
|
+
agent: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: '安装到的目标 Agent 宿主(install 动作可选,如 "claude-code"、"cursor"、"github-copilot";省略时 gh 会按环境探测)。',
|
|
42
|
+
},
|
|
43
|
+
scope: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
enum: ['project', 'user'],
|
|
46
|
+
description: '安装范围(install 动作可选,默认 project:安装进当前 git 仓库;user:安装到用户主目录全局可用)。',
|
|
47
|
+
},
|
|
48
|
+
all: {
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
description: '安装仓库内全部 Skills 而非逐个选择(install 动作可选,映射 --all)。',
|
|
51
|
+
},
|
|
52
|
+
limit: {
|
|
53
|
+
type: 'number',
|
|
54
|
+
description: '搜索返回数量上限(search 动作使用,默认 15)。',
|
|
55
|
+
},
|
|
56
|
+
confirm: {
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
description: '安装操作确认标志。install 会向本地磁盘写入文件,未传 true 时仅输出将要执行的安装预览。',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
required: ['action'],
|
|
62
|
+
},
|
|
63
|
+
output: {
|
|
64
|
+
schema: { type: 'object' },
|
|
65
|
+
render(args, value) {
|
|
66
|
+
return [{ type: 'text', text: renderSkillMarkdown(args, value) }];
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
async execute(args, execContext) {
|
|
70
|
+
const cwd = execContext?.cwd || process.cwd();
|
|
71
|
+
const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
|
|
72
|
+
// skill 工具天然面向跨仓库生态(search/preview 均以显式 repository 为目标),
|
|
73
|
+
// 不做当前工作区仓库自动识别——识别失败反而会误导调用方。
|
|
74
|
+
const runOpts = { cwd, sessionId };
|
|
75
|
+
switch (args.action) {
|
|
76
|
+
case 'search': {
|
|
77
|
+
if (!args.query)
|
|
78
|
+
throw new Error('search 操作必须提供 query(搜索关键词)');
|
|
79
|
+
// 显式 limit 优先;缺省回退 executor.defaultListLimit(上限 50,gh skill 默认 15)
|
|
80
|
+
const limit = typeof args.limit === 'number' && args.limit > 0 ? Math.min(args.limit, 50) : Math.min(executor.defaultListLimit ?? 15, 50);
|
|
81
|
+
const searchCmd = [
|
|
82
|
+
'skill', 'search', args.query,
|
|
83
|
+
'-L', String(limit),
|
|
84
|
+
// 字段名以 gh 2.96.0 实测为准;preview 特性字段可能随版本演进,
|
|
85
|
+
// 渲染层已对缺失字段做回退,这里不做多余防御
|
|
86
|
+
'--json', 'skillName,repo,description,namespace,stars',
|
|
87
|
+
];
|
|
88
|
+
if (args.owner)
|
|
89
|
+
searchCmd.push('--owner', args.owner);
|
|
90
|
+
const res = await executor.run(searchCmd, runOpts);
|
|
91
|
+
if (!res.ok)
|
|
92
|
+
throw new Error(res.error);
|
|
93
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
94
|
+
}
|
|
95
|
+
case 'preview': {
|
|
96
|
+
if (!args.repository)
|
|
97
|
+
throw new Error('preview 操作必须提供 repository [owner/repo]');
|
|
98
|
+
const previewCmd = ['skill', 'preview', args.repository];
|
|
99
|
+
if (args.skill_name)
|
|
100
|
+
previewCmd.push(args.skill_name);
|
|
101
|
+
// SKILL.md 是长篇 Markdown 文档,套 JSON 反而破坏可读性
|
|
102
|
+
const res = await executor.run(previewCmd, { ...runOpts, rawText: true, timeoutMs: 60_000 });
|
|
103
|
+
if (!res.ok)
|
|
104
|
+
throw new Error(res.error);
|
|
105
|
+
return { result: res.rawOutput || '未找到该 Skill 的内容' };
|
|
106
|
+
}
|
|
107
|
+
case 'install': {
|
|
108
|
+
if (!args.repository)
|
|
109
|
+
throw new Error('install 操作必须提供 repository [owner/repo]');
|
|
110
|
+
if (args.confirm !== true) {
|
|
111
|
+
return {
|
|
112
|
+
status: 'AWAITING_CONFIRMATION',
|
|
113
|
+
warning: `⚠️ 安装确认拦截:你即将从仓库「${args.repository}」安装 Skill${args.skill_name ? `「${args.skill_name}」` : ''}到本地${args.scope === 'user' ? '用户主目录(全局)' : '当前项目'}。`,
|
|
114
|
+
impact: '安装会在本地磁盘写入 SKILL.md 及附属文件,并可能影响目标 Agent 宿主的默认行为。',
|
|
115
|
+
requiredAction: '如确认安装,请向用户核实并附加 `confirm: true` 重新调用。',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const installCmd = ['skill', 'install', args.repository];
|
|
119
|
+
if (args.skill_name)
|
|
120
|
+
installCmd.push(args.skill_name);
|
|
121
|
+
if (args.agent)
|
|
122
|
+
installCmd.push('--agent', args.agent);
|
|
123
|
+
if (args.scope)
|
|
124
|
+
installCmd.push('--scope', args.scope);
|
|
125
|
+
if (args.all === true)
|
|
126
|
+
installCmd.push('--all');
|
|
127
|
+
const res = await executor.run(installCmd, { ...runOpts, timeoutMs: 120_000 });
|
|
128
|
+
if (!res.ok)
|
|
129
|
+
throw new Error(res.error);
|
|
130
|
+
return { message: `Skill 安装成功(来源: ${args.repository})`, output: res.rawOutput };
|
|
131
|
+
}
|
|
132
|
+
case 'list': {
|
|
133
|
+
const listCmd = ['skill', 'list', '--json', 'skillName,sourceURL,scope,version,pinned,path'];
|
|
134
|
+
if (args.scope)
|
|
135
|
+
listCmd.push('--scope', args.scope);
|
|
136
|
+
const res = await executor.run(listCmd, runOpts);
|
|
137
|
+
if (!res.ok)
|
|
138
|
+
throw new Error(res.error);
|
|
139
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
140
|
+
}
|
|
141
|
+
default:
|
|
142
|
+
throw new Error(`未知的 Skill 操作: ${args.action}`);
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
package/lib/types.d.ts
CHANGED
|
@@ -20,6 +20,12 @@ export interface PluginConfig {
|
|
|
20
20
|
updateCheckIntervalMs?: number;
|
|
21
21
|
/** 自定义 npm Registry 镜像地址,留空则自动选用 npmmirror 与 npmjs */
|
|
22
22
|
registryUrl?: string;
|
|
23
|
+
/**
|
|
24
|
+
* 代理地址预置默认值(如 "http://127.0.0.1:7890")。
|
|
25
|
+
* 仅在用户未在驾驶舱保存过代理设置时生效;运行时 UI 保存的值优先级更高。
|
|
26
|
+
* 注意:放在 Cordis Config 意味着修改此项会触发插件 reload(静态默认值,可接受)。
|
|
27
|
+
*/
|
|
28
|
+
proxyUrl?: string;
|
|
23
29
|
}
|
|
24
30
|
export interface GhExecutionOptions {
|
|
25
31
|
/** 命令执行的工作目录,优先使用当前 Agent 会话所在的 Workspace 物理路径 */
|
|
@@ -32,6 +38,12 @@ export interface GhExecutionOptions {
|
|
|
32
38
|
env?: NodeJS.ProcessEnv;
|
|
33
39
|
/** 是否强制以纯文本形式返回,不进行 JSON 尝试解析 */
|
|
34
40
|
rawText?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* 写入子进程标准输入的内容。
|
|
43
|
+
* 为什么需要:gh secret set 不带 -b 时从 stdin 读取密文,这是唯一不把敏感值
|
|
44
|
+
* 暴露在进程命令行(ps / 任务管理器 / 审计日志可见)的传入通道。
|
|
45
|
+
*/
|
|
46
|
+
stdin?: string;
|
|
35
47
|
}
|
|
36
48
|
export interface GhResult<T = unknown> {
|
|
37
49
|
ok: boolean;
|
package/lib/updater.d.ts
CHANGED
|
@@ -25,6 +25,14 @@ export declare class UpdateChecker {
|
|
|
25
25
|
intervalMs?: number;
|
|
26
26
|
customRegistry?: string;
|
|
27
27
|
});
|
|
28
|
+
/**
|
|
29
|
+
* 应用代理到 npm 探测的 fetch 链路。
|
|
30
|
+
* 由 main.ts 在 ProxyManager 初始化/变更后调用:undici dispatcher 是进程级全局,
|
|
31
|
+
* 这里只做一次转发,真正的缓存/重建逻辑在 ProxyManager.applyFetchDispatcher 内。
|
|
32
|
+
*/
|
|
33
|
+
applyProxy(manager: {
|
|
34
|
+
applyFetchDispatcher(): boolean;
|
|
35
|
+
} | null): void;
|
|
28
36
|
/**
|
|
29
37
|
* 同步获取当前已知的版本状态(0ms 纯内存读取,绝不发外网请求)
|
|
30
38
|
*/
|
package/lib/updater.js
CHANGED
|
@@ -50,6 +50,15 @@ export class UpdateChecker {
|
|
|
50
50
|
: 86_400_000;
|
|
51
51
|
this.customRegistry = options.customRegistry;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* 应用代理到 npm 探测的 fetch 链路。
|
|
55
|
+
* 由 main.ts 在 ProxyManager 初始化/变更后调用:undici dispatcher 是进程级全局,
|
|
56
|
+
* 这里只做一次转发,真正的缓存/重建逻辑在 ProxyManager.applyFetchDispatcher 内。
|
|
57
|
+
*/
|
|
58
|
+
applyProxy(manager) {
|
|
59
|
+
if (manager)
|
|
60
|
+
manager.applyFetchDispatcher();
|
|
61
|
+
}
|
|
53
62
|
/**
|
|
54
63
|
* 同步获取当前已知的版本状态(0ms 纯内存读取,绝不发外网请求)
|
|
55
64
|
*/
|
package/lib/upgrade-manager.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { UpgradeExecutionResult, UpgradeStatusResult } from './types.js';
|
|
2
|
+
import type { ProxyManager } from './proxy.js';
|
|
2
3
|
/**
|
|
3
4
|
* 构建跨平台环境自愈增强的 ProcessEnv (重点解决 macOS GUI 应用丢失 PATH 的痛点)
|
|
4
5
|
*
|
|
@@ -24,6 +25,9 @@ export declare class UpgradeManager {
|
|
|
24
25
|
private isUpgrading;
|
|
25
26
|
private lastResult;
|
|
26
27
|
private logBuffer;
|
|
28
|
+
/** 代理管理器(可选):注入后一键升级的 npm/pnpm 子进程走用户代理 */
|
|
29
|
+
private proxyManager?;
|
|
30
|
+
attachProxyManager(manager: ProxyManager): void;
|
|
27
31
|
/**
|
|
28
32
|
* 探测当前插件是否是以本地 Git 源码软链接方式运行
|
|
29
33
|
*/
|
package/lib/upgrade-manager.js
CHANGED
|
@@ -68,6 +68,11 @@ export class UpgradeManager {
|
|
|
68
68
|
state: 'idle',
|
|
69
69
|
};
|
|
70
70
|
logBuffer = [];
|
|
71
|
+
/** 代理管理器(可选):注入后一键升级的 npm/pnpm 子进程走用户代理 */
|
|
72
|
+
proxyManager;
|
|
73
|
+
attachProxyManager(manager) {
|
|
74
|
+
this.proxyManager = manager;
|
|
75
|
+
}
|
|
71
76
|
/**
|
|
72
77
|
* 探测当前插件是否是以本地 Git 源码软链接方式运行
|
|
73
78
|
*/
|
|
@@ -200,7 +205,12 @@ export class UpgradeManager {
|
|
|
200
205
|
resolve(result);
|
|
201
206
|
};
|
|
202
207
|
// 注入跨平台增强环境(尤其治愈 macOS GUI 启动丢失 PATH 的痛点)
|
|
203
|
-
|
|
208
|
+
// 代理环境变量按 applyToPackageManager 开关合并:npm/pnpm 遵循 HTTPS_PROXY,
|
|
209
|
+
// 与 gh 链路同一管理器,保证「设置代理后升级也走代理」的体验一致性。
|
|
210
|
+
const childEnv = {
|
|
211
|
+
...buildCrossPlatformEnv(),
|
|
212
|
+
...(this.proxyManager?.getEnv('package-manager') || {}),
|
|
213
|
+
};
|
|
204
214
|
const child = spawn(shell, args, {
|
|
205
215
|
cwd: executionCwd,
|
|
206
216
|
windowsHide: true,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-github-flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"icon": "icon.svg",
|
|
5
|
-
"description": "GitHub
|
|
5
|
+
"description": "GitHub 工作流引擎:为 DeepSeek Harness 提供原生 GitHub CLI (gh) 集成——9 大 Agent 领域工具(PR 审查合并、Issue 任务流转、CI 失败诊断、发版交付、密钥管理、Agent Skills 生态)、/gh 人类快捷指令、Web UI 全景驾驶舱与一键跨平台自动升级;零凭证侵入复用宿主机 gh 登录态,内置 Token 防溢出截断、高危操作二次确认与密文安全输入通道。",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./lib/main.js",
|
|
8
8
|
"repository": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
],
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsc && node scripts/build.mjs",
|
|
33
|
+
"test": "node test/run-all.mjs",
|
|
33
34
|
"prepare": "pnpm run build"
|
|
34
35
|
},
|
|
35
36
|
"dsh": {
|
package/lib/client/icons.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub 官方高精度的 Invertocat 矢量路径
|
|
3
|
-
*/
|
|
4
|
-
export declare const GITHUB_OCTOCAT_PATH = "M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.72 6.56 11.52C4.78 11.32 2.92 10.63 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.32 9.47 11.52C9.76 11.77 10.01 12.25 10.01 13C10.01 14.08 10 14.95 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z";
|
|
5
|
-
/**
|
|
6
|
-
* 基础矢量 Logo
|
|
7
|
-
*/
|
|
8
|
-
export declare function createOctocatSvg(size?: number, color?: string): any;
|
|
9
|
-
/**
|
|
10
|
-
* 现代暗黑科技感徽标 Artwork(用于卡片与主要入口)
|
|
11
|
-
* 带有暗夜流光微渐变与精致白描
|
|
12
|
-
*/
|
|
13
|
-
export declare function createOctocatBadge(size?: number): any;
|
package/lib/client/icons.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub 官方高精度的 Invertocat 矢量路径
|
|
3
|
-
*/
|
|
4
|
-
export const GITHUB_OCTOCAT_PATH = "M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.72 6.56 11.52C4.78 11.32 2.92 10.63 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.32 9.47 11.52C9.76 11.77 10.01 12.25 10.01 13C10.01 14.08 10 14.95 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z";
|
|
5
|
-
/**
|
|
6
|
-
* 基础矢量 Logo
|
|
7
|
-
*/
|
|
8
|
-
export function createOctocatSvg(size = 18, color = "currentColor") {
|
|
9
|
-
return React.createElement("svg", {
|
|
10
|
-
viewBox: "0 0 16 16",
|
|
11
|
-
width: size,
|
|
12
|
-
height: size,
|
|
13
|
-
fill: color,
|
|
14
|
-
style: { flexShrink: 0, display: "inline-block", verticalAlign: "middle" }
|
|
15
|
-
}, React.createElement("path", {
|
|
16
|
-
fillRule: "evenodd",
|
|
17
|
-
clipRule: "evenodd",
|
|
18
|
-
d: GITHUB_OCTOCAT_PATH
|
|
19
|
-
}));
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* 现代暗黑科技感徽标 Artwork(用于卡片与主要入口)
|
|
23
|
-
* 带有暗夜流光微渐变与精致白描
|
|
24
|
-
*/
|
|
25
|
-
export function createOctocatBadge(size = 36) {
|
|
26
|
-
var iconSize = Math.round(size * 0.58);
|
|
27
|
-
return React.createElement("div", {
|
|
28
|
-
style: {
|
|
29
|
-
display: "flex",
|
|
30
|
-
alignItems: "center",
|
|
31
|
-
justifyContent: "center",
|
|
32
|
-
width: `${size}px`,
|
|
33
|
-
height: `${size}px`,
|
|
34
|
-
borderRadius: "10px",
|
|
35
|
-
background: "linear-gradient(135deg, #2ea043 0%, #1f6feb 100%)", // GitHub 科技绿蓝微渐变
|
|
36
|
-
boxShadow: "0 2px 8px rgba(31, 111, 235, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.2)",
|
|
37
|
-
flexShrink: 0,
|
|
38
|
-
position: "relative"
|
|
39
|
-
}
|
|
40
|
-
}, createOctocatSvg(iconSize, "#ffffff"));
|
|
41
|
-
}
|
package/lib/client/index.d.ts
DELETED
package/lib/client/index.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { GITHUB_PANEL_CSS } from './style.js';
|
|
2
|
-
import { createGithubPanelComponent } from './panel.js';
|
|
3
|
-
export const name = 'dsh-github-flow';
|
|
4
|
-
export const inject = ['slots'];
|
|
5
|
-
export function apply(ctx) {
|
|
6
|
-
// 1. 注入样式
|
|
7
|
-
if (typeof styles !== 'undefined' && styles.insert) {
|
|
8
|
-
styles.insert(GITHUB_PANEL_CSS);
|
|
9
|
-
}
|
|
10
|
-
else if (typeof document !== 'undefined') {
|
|
11
|
-
const styleEl = document.createElement('style');
|
|
12
|
-
styleEl.textContent = GITHUB_PANEL_CSS;
|
|
13
|
-
document.head.appendChild(styleEl);
|
|
14
|
-
}
|
|
15
|
-
// 2. 侧边栏图标组件
|
|
16
|
-
function GithubSidebarIcon() {
|
|
17
|
-
return React.createElement('div', { className: 'dsh-github-icon', title: 'GitHub 工作台' }, React.createElement('svg', { viewBox: '0 0 16 16', width: 18, height: 18, fill: 'currentColor' }, React.createElement('path', {
|
|
18
|
-
d: 'M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z',
|
|
19
|
-
})));
|
|
20
|
-
}
|
|
21
|
-
// 3. 注册到 sidebar.panellist(排在 schedules 自动任务后,workspaces 工作区前)
|
|
22
|
-
if (ctx.slots && typeof ctx.slots.register === 'function') {
|
|
23
|
-
ctx.slots.register({
|
|
24
|
-
slot: 'sidebar.panellist',
|
|
25
|
-
id: 'github',
|
|
26
|
-
order: 20,
|
|
27
|
-
label: 'GitHub',
|
|
28
|
-
}, GithubSidebarIcon);
|
|
29
|
-
// 4. 注册中央主视图(main slot, key: 'github')
|
|
30
|
-
const GithubWorkspacePanel = createGithubPanelComponent();
|
|
31
|
-
ctx.slots.register({
|
|
32
|
-
slot: 'main',
|
|
33
|
-
key: 'github',
|
|
34
|
-
}, GithubWorkspacePanel);
|
|
35
|
-
}
|
|
36
|
-
}
|
package/lib/client/panel.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function createGithubPanelComponent(): () => any;
|