@sokeai/cli 1.0.41 → 1.0.53
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.md +1 -24
- package/package.json +2 -2
- package/scripts/build-binaries.sh +1 -30
- package/scripts/e2e-test.sh +63 -3
- package/scripts/install.js +52 -110
- package/scripts/local-test.js +10 -85
- package/scripts/local-test.sh +52 -7
- package/scripts/push.sh +12 -4
- package/scripts/release.sh +50 -40
- package/scripts/test-auto-detect.js +71 -48
- package/skills/README.md +0 -1
- package/skills/oss-upload/skill.md +50 -0
- package/skills/oss-upload/upload.py +355 -0
- package/skills/soke-assign/README.md +310 -0
- package/skills/soke-assign/SKILL.md +383 -0
- package/skills/soke-course/README.md +168 -92
- package/skills/soke-course/SKILL.md +372 -460
- package/skills/soke-exam/SKILL.md +565 -0
- package/skills/soke-exam/references/exam-get-exam-user.md +212 -0
- package/skills/soke-learning-map/SKILL.md +544 -0
- package/skills/soke-learning-profile/SKILL.md +0 -1
- package/skills/soke-lesson/README.md +53 -0
- package/skills/soke-lesson/SKILL.md +510 -0
- package/skills/soke-material/README.md +49 -0
- package/skills/soke-material/SKILL.md +390 -0
- package/scripts/ci/check-legacy-constants.sh +0 -27
- package/scripts/ci/check-yaml-env-whitelist.sh +0 -33
- package/scripts/mcp-stdout-scan.sh +0 -46
- package/scripts/regress-auth.sh +0 -56
- package/scripts/regress-baseline.md +0 -84
- package/scripts/test-local-reconcile.js +0 -55
- package/skills/soke-business-training-report/SKILL.md +0 -58
- package/skills/soke-course/SUMMARY.md +0 -236
- package/skills/soke-course/templates/batch-create-from-excel.md +0 -75
package/scripts/release.sh
CHANGED
|
@@ -48,10 +48,10 @@ check_tools() {
|
|
|
48
48
|
check_git_status() {
|
|
49
49
|
echo -e "${YELLOW}检查 Git 状态...${NC}"
|
|
50
50
|
|
|
51
|
-
# 检查当前分支是否为
|
|
51
|
+
# 检查当前分支是否为 soke-cli
|
|
52
52
|
CURRENT_BRANCH=$(git branch --show-current)
|
|
53
|
-
if [ "$CURRENT_BRANCH" != "
|
|
54
|
-
echo -e "${RED}错误: 当前分支为 $CURRENT_BRANCH,发布必须在
|
|
53
|
+
if [ "$CURRENT_BRANCH" != "soke-cli" ]; then
|
|
54
|
+
echo -e "${RED}错误: 当前分支为 $CURRENT_BRANCH,发布必须在 soke-cli 分支上进行${NC}"
|
|
55
55
|
exit 1
|
|
56
56
|
fi
|
|
57
57
|
|
|
@@ -62,6 +62,19 @@ check_git_status() {
|
|
|
62
62
|
exit 1
|
|
63
63
|
fi
|
|
64
64
|
|
|
65
|
+
# 配置 GitHub 远程仓库
|
|
66
|
+
GITHUB_REMOTE="git@github.com:sokeai/soke-cli.git"
|
|
67
|
+
if git remote get-url github &> /dev/null; then
|
|
68
|
+
CURRENT_URL=$(git remote get-url github)
|
|
69
|
+
if [ "$CURRENT_URL" != "$GITHUB_REMOTE" ]; then
|
|
70
|
+
echo -e "${YELLOW}更新 github 远程仓库地址...${NC}"
|
|
71
|
+
git remote set-url github "$GITHUB_REMOTE"
|
|
72
|
+
fi
|
|
73
|
+
else
|
|
74
|
+
echo -e "${YELLOW}添加 github 远程仓库...${NC}"
|
|
75
|
+
git remote add github "$GITHUB_REMOTE"
|
|
76
|
+
fi
|
|
77
|
+
|
|
65
78
|
echo -e "${GREEN}✓ 工作目录干净${NC}"
|
|
66
79
|
echo ""
|
|
67
80
|
}
|
|
@@ -138,8 +151,6 @@ upload_github_release() {
|
|
|
138
151
|
|
|
139
152
|
\`\`\`bash
|
|
140
153
|
npm install -g @sokeai/cli
|
|
141
|
-
# 或
|
|
142
|
-
npm install -g @zevolve/cli
|
|
143
154
|
\`\`\`
|
|
144
155
|
|
|
145
156
|
## 更新日志
|
|
@@ -163,39 +174,38 @@ npm install -g @zevolve/cli
|
|
|
163
174
|
publish_npm() {
|
|
164
175
|
echo -e "${YELLOW}发布到 NPM...${NC}"
|
|
165
176
|
|
|
166
|
-
#
|
|
177
|
+
# 如果有环境变量,优先配置(会持久化到 ~/.npmrc)
|
|
178
|
+
if [ -n "$NPM_TOKEN" ]; then
|
|
179
|
+
npm config set //registry.npmjs.org/:_authToken="${NPM_TOKEN}"
|
|
180
|
+
echo -e "${GREEN}✓ 已通过 NPM_TOKEN 配置认证${NC}"
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# 验证认证是否有效(支持 npm login 和 token 两种方式)
|
|
167
184
|
if ! npm whoami &> /dev/null; then
|
|
168
|
-
echo -e "${RED}错误:
|
|
169
|
-
echo "
|
|
185
|
+
echo -e "${RED}错误: NPM 认证失败${NC}"
|
|
186
|
+
echo ""
|
|
187
|
+
echo "可能原因:"
|
|
188
|
+
echo " 1. 未配置认证令牌"
|
|
189
|
+
echo " 2. 令牌已过期或被撤销"
|
|
190
|
+
echo ""
|
|
191
|
+
echo "解决方法(任选其一):"
|
|
192
|
+
echo " 方法1: 持久化配置令牌(推荐,一次配置永久生效)"
|
|
193
|
+
echo " npm config set //registry.npmjs.org/:_authToken=npm_xxx"
|
|
194
|
+
echo ""
|
|
195
|
+
echo " 方法2: 交互式登录(需要浏览器)"
|
|
196
|
+
echo " npm login"
|
|
197
|
+
echo ""
|
|
198
|
+
echo "令牌创建地址: https://www.npmjs.com/settings/<YOUR_USER>/tokens"
|
|
199
|
+
echo "注意: 需要创建 Granular Access Token,并勾选「Bypass 2FA」"
|
|
170
200
|
exit 1
|
|
171
201
|
fi
|
|
172
202
|
|
|
173
|
-
|
|
174
|
-
|
|
203
|
+
echo -e "${GREEN}✓ NPM 认证已就绪 ($(npm whoami 2>/dev/null))${NC}"
|
|
204
|
+
|
|
205
|
+
# 发布
|
|
175
206
|
npm publish --access public
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# 2. 发布 @zevolve/cli
|
|
179
|
-
echo -e "${BLUE}正在发布 @zevolve/cli...${NC}"
|
|
180
|
-
cp package.json package.json.bak
|
|
181
|
-
|
|
182
|
-
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
183
|
-
sed -i '' 's/"name": "@sokeai\/cli"/"name": "@zevolve\/cli"/' package.json
|
|
184
|
-
else
|
|
185
|
-
sed -i 's/"name": "@sokeai\/cli"/"name": "@zevolve\/cli"/' package.json
|
|
186
|
-
fi
|
|
187
|
-
|
|
188
|
-
# 尝试发布,如果失败则恢复原文件并退出
|
|
189
|
-
if ! npm publish --access public; then
|
|
190
|
-
echo -e "${RED}错误: @zevolve/cli 发布失败${NC}"
|
|
191
|
-
mv package.json.bak package.json
|
|
192
|
-
exit 1
|
|
193
|
-
fi
|
|
194
|
-
|
|
195
|
-
mv package.json.bak package.json
|
|
196
|
-
echo -e "${GREEN}✓ @zevolve/cli 发布成功${NC}"
|
|
197
|
-
|
|
198
|
-
echo -e "${GREEN}✓ 所有 NPM 包发布成功${NC}"
|
|
207
|
+
|
|
208
|
+
echo -e "${GREEN}✓ NPM 包发布成功${NC}"
|
|
199
209
|
echo ""
|
|
200
210
|
}
|
|
201
211
|
|
|
@@ -214,7 +224,7 @@ main() {
|
|
|
214
224
|
|
|
215
225
|
check_tools
|
|
216
226
|
check_git_status
|
|
217
|
-
check_tag
|
|
227
|
+
# check_tag
|
|
218
228
|
run_tests
|
|
219
229
|
build_binaries
|
|
220
230
|
create_tag
|
|
@@ -227,14 +237,11 @@ main() {
|
|
|
227
237
|
echo -e "${GREEN}========================================${NC}"
|
|
228
238
|
echo ""
|
|
229
239
|
echo -e "版本: ${GREEN}v${VERSION}${NC}"
|
|
230
|
-
echo -e "NPM
|
|
231
|
-
echo -e "
|
|
232
|
-
echo -e "GitHub: ${BLUE}https://github.com/sokeai/soke-cli/releases/tag/v${VERSION}${NC}"
|
|
240
|
+
echo -e "NPM: ${BLUE}https://www.npmjs.com/package/@sokeai/cli${NC}"
|
|
241
|
+
echo -e "GitHub: ${BLUE}https://github.com/sokeai/soke-cli/releases/tag/v${VERSION}${NC}"
|
|
233
242
|
echo ""
|
|
234
243
|
echo "安装或升级到最新版本:"
|
|
235
244
|
echo -e "${YELLOW}npm install -g @sokeai/cli@latest${NC}"
|
|
236
|
-
echo -e "${YELLOW}或${NC}"
|
|
237
|
-
echo -e "${YELLOW}npm install -g @zevolve/cli@latest${NC}"
|
|
238
245
|
}
|
|
239
246
|
|
|
240
247
|
# 确认发布
|
|
@@ -248,9 +255,12 @@ echo " 4. 创建 Git 标签并推送"
|
|
|
248
255
|
echo " 5. 上传到 GitHub Releases"
|
|
249
256
|
echo " 6. 发布到 NPM"
|
|
250
257
|
echo ""
|
|
251
|
-
read -p "确认继续?
|
|
258
|
+
read -p "确认继续? [Y/n] " -n 1 -r
|
|
252
259
|
echo ""
|
|
253
260
|
|
|
261
|
+
# 默认是 Y
|
|
262
|
+
REPLY=${REPLY:-Y}
|
|
263
|
+
|
|
254
264
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
255
265
|
echo "已取消发布"
|
|
256
266
|
exit 0
|
|
@@ -5,14 +5,66 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
|
-
const os = require('os');
|
|
9
8
|
const path = require('path');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
|
|
10
|
+
// 复制 detectSkillNames 函数
|
|
11
|
+
function detectSkillNames(packagedSkillsDir) {
|
|
12
|
+
if (!fs.existsSync(packagedSkillsDir)) return [];
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const entries = fs.readdirSync(packagedSkillsDir, { withFileTypes: true });
|
|
16
|
+
return entries
|
|
17
|
+
.filter(entry => entry.isDirectory() && entry.name.startsWith('soke-'))
|
|
18
|
+
.map(entry => entry.name)
|
|
19
|
+
.sort();
|
|
20
|
+
} catch (_) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 复制 parseSkillMetadata 函数
|
|
26
|
+
function parseSkillMetadata(skillDir) {
|
|
27
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
28
|
+
if (!fs.existsSync(skillMdPath)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const content = fs.readFileSync(skillMdPath, 'utf8');
|
|
34
|
+
|
|
35
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
36
|
+
if (!frontmatterMatch) return null;
|
|
37
|
+
|
|
38
|
+
const frontmatter = frontmatterMatch[1];
|
|
39
|
+
const metadata = {};
|
|
40
|
+
|
|
41
|
+
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
42
|
+
if (nameMatch) metadata.name = nameMatch[1].trim();
|
|
43
|
+
|
|
44
|
+
const summaryMatch = frontmatter.match(/^summary:\s*(.+)$/m);
|
|
45
|
+
if (summaryMatch) metadata.summary = summaryMatch[1].trim();
|
|
46
|
+
|
|
47
|
+
const descMatch = frontmatter.match(/^description:\s*["'](.+)["']$/m);
|
|
48
|
+
if (descMatch) {
|
|
49
|
+
metadata.description = descMatch[1].trim();
|
|
50
|
+
} else {
|
|
51
|
+
const descMatch2 = frontmatter.match(/^description:\s*(.+)$/m);
|
|
52
|
+
if (descMatch2) metadata.description = descMatch2[1].trim();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const versionMatch = frontmatter.match(/^version:\s*(.+)$/m);
|
|
56
|
+
if (versionMatch) metadata.version = versionMatch[1].trim();
|
|
57
|
+
|
|
58
|
+
const binsMatch = frontmatter.match(/bins:\s*\[(.+?)\]/);
|
|
59
|
+
if (binsMatch) {
|
|
60
|
+
metadata.bins = binsMatch[1].split(',').map(b => b.trim().replace(/['"]/g, ''));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return metadata;
|
|
64
|
+
} catch (_) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
16
68
|
|
|
17
69
|
// 测试
|
|
18
70
|
const packageRoot = path.join(__dirname, '..');
|
|
@@ -50,13 +102,19 @@ for (const skillName of skillNames) {
|
|
|
50
102
|
// 验证结果
|
|
51
103
|
console.log('📊 验证结果:\n');
|
|
52
104
|
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
105
|
+
const expectedSkills = ['soke-course', 'soke-exam', 'soke-shared'];
|
|
106
|
+
const missingSkills = expectedSkills.filter(s => !skillNames.includes(s));
|
|
107
|
+
const extraSkills = skillNames.filter(s => !expectedSkills.includes(s));
|
|
108
|
+
|
|
109
|
+
if (missingSkills.length > 0) {
|
|
110
|
+
console.log(`❌ 缺少的 skills: ${missingSkills.join(', ')}`);
|
|
111
|
+
} else {
|
|
112
|
+
console.log('✅ 所有预期的 skills 都已检测到');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (extraSkills.length > 0) {
|
|
116
|
+
console.log(`ℹ️ 额外的 skills: ${extraSkills.join(', ')}`);
|
|
58
117
|
}
|
|
59
|
-
console.log('✅ 基础 skills 检测通过');
|
|
60
118
|
|
|
61
119
|
console.log('');
|
|
62
120
|
console.log('🎉 测试完成!');
|
|
@@ -65,38 +123,3 @@ console.log('💡 提示:');
|
|
|
65
123
|
console.log(' - 新增 skill 时,只需在 skills/ 目录下创建 soke-* 目录');
|
|
66
124
|
console.log(' - 确保每个 skill 都有 SKILL.md 文件,包含完整的 frontmatter');
|
|
67
125
|
console.log(' - install.js 会自动检测并注册所有 skills');
|
|
68
|
-
|
|
69
|
-
// 验证清理逻辑
|
|
70
|
-
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'soke-skill-prune-'));
|
|
71
|
-
const targetDir = path.join(tempRoot, 'skills');
|
|
72
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
73
|
-
fs.mkdirSync(path.join(targetDir, 'soke-stale'));
|
|
74
|
-
fs.mkdirSync(path.join(targetDir, 'soke-course'));
|
|
75
|
-
fs.mkdirSync(path.join(targetDir, 'custom-skill'));
|
|
76
|
-
|
|
77
|
-
const removedDirs = pruneManagedSkillDirs(targetDir, ['soke-course']);
|
|
78
|
-
if (!removedDirs.includes('soke-stale')) {
|
|
79
|
-
console.error('❌ pruneManagedSkillDirs 未删除过期 managed skill');
|
|
80
|
-
process.exit(1);
|
|
81
|
-
}
|
|
82
|
-
if (!fs.existsSync(path.join(targetDir, 'custom-skill'))) {
|
|
83
|
-
console.error('❌ pruneManagedSkillDirs 错误删除了非受管 skill');
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const registry = {
|
|
88
|
-
skills: [
|
|
89
|
-
{ name: 'soke-stale' },
|
|
90
|
-
{ name: 'soke-course' },
|
|
91
|
-
{ name: 'custom-skill' }
|
|
92
|
-
]
|
|
93
|
-
};
|
|
94
|
-
const removedEntries = pruneRegistrySkills(registry, ['soke-course']);
|
|
95
|
-
if (removedEntries !== 1) {
|
|
96
|
-
console.error('❌ pruneRegistrySkills 未正确移除过期条目');
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
if (!registry.skills.find((entry) => entry.name === 'custom-skill')) {
|
|
100
|
-
console.error('❌ pruneRegistrySkills 错误移除了非受管条目');
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
package/skills/README.md
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oss-upload
|
|
3
|
+
description: 上传本地文件到阿里云OSS存储
|
|
4
|
+
skill_version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# OSS文件上传工具
|
|
8
|
+
|
|
9
|
+
这个skill用于将本地文件上传到阿里云OSS对象存储。
|
|
10
|
+
|
|
11
|
+
## 功能特性
|
|
12
|
+
|
|
13
|
+
- 通过授客AI接口动态获取上传签名
|
|
14
|
+
- 支持表单POST方式上传文件(无需安装阿里云SDK)
|
|
15
|
+
- 自动处理认证和签名
|
|
16
|
+
- 支持自定义文件路径
|
|
17
|
+
- 支持进度显示
|
|
18
|
+
|
|
19
|
+
## 使用方法
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 上传单个文件
|
|
23
|
+
soke-cli oss upload <本地文件路径> [--object-name <OSS对象名>]
|
|
24
|
+
|
|
25
|
+
# 示例
|
|
26
|
+
soke-cli oss upload ./document.pdf
|
|
27
|
+
soke-cli oss upload ./document.pdf --object-name uploads/2024/document.pdf
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 参数说明
|
|
31
|
+
|
|
32
|
+
- `file`: 本地文件路径(必填)
|
|
33
|
+
- `--object-name`: OSS中的对象名称(可选,默认使用文件名)
|
|
34
|
+
|
|
35
|
+
## 配置要求
|
|
36
|
+
|
|
37
|
+
需要先登录授客AI:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
|
|
41
|
+
# 用户登录
|
|
42
|
+
soke-cli auth login
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 实现原理
|
|
46
|
+
|
|
47
|
+
1. 从配置文件读取用户访问令牌(UserToken)
|
|
48
|
+
2. 调用授客AI接口获取OSS上传签名和配置
|
|
49
|
+
3. 使用获取的签名通过表单方式上传文件到OSS
|
|
50
|
+
4. 无需在本地配置OSS密钥,更安全便捷
|