@aiyiran/myclaw 1.1.34 → 1.1.35

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.
Files changed (21) hide show
  1. package/assets/myclaw-artifacts.js +150 -100
  2. package/package.json +1 -1
  3. package/patches/patch-manifest.json +2 -1
  4. package/patches/patch-skill.js +18 -8
  5. package/server/sync_workspace.py +92 -0
  6. package/skills/yiran-course-template-pipeline/SKILL.md +1 -2
  7. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2651-demo/347/224/237/346/210/220.md +0 -7
  8. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2652-student/347/224/237/346/210/220.md +35 -0
  9. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2653-teacher/347/224/237/346/210/220.md +6 -5
  10. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2654-/346/211/223/345/214/205/350/220/275/347/233/230.md +26 -21
  11. package/skills/yiran-course-template-pipeline/references/teacher-fields.md +5 -5
  12. package/skills/yiran-course-template-pipeline/references/teacher-scaffold.json +1 -1
  13. package/skills/yiran-course-template-pipeline/scripts/build_template_index.py +14 -2
  14. package/skills/yiran-course-template-pipeline/scripts/move_template_task.py +43 -7
  15. package/skills/yiran-course-template-pipeline/scripts/publish_template.py +44 -120
  16. package/skills/yiran-course-template-pipeline/template-index.json +269 -0
  17. package/skills/yiran-course-template-pipeline/template-index.md +52 -0
  18. package/skills/yiran-course-template-pipeline//345/272/237/345/274/203/rewrite_html_refs.py +102 -0
  19. package/skills/yiran-playground-template-use/SKILL.md +2 -12
  20. package/skills/yiran-course-template-pipeline/cdn-convention.md +0 -77
  21. package/skills/yiran-playground-template-use/scripts/build_template_index.py +0 -103
@@ -0,0 +1,269 @@
1
+ {
2
+ "index_updated_at": "2026-04-22T05:11:25Z",
3
+ "templates": {
4
+ "A": {
5
+ "100": {
6
+ "系列": "A",
7
+ "编号": "100",
8
+ "名称": "给依然老师设计一个AI画像",
9
+ "文件夹名": "a100_给依然老师设计一个AI画像",
10
+ "路径": "templates/a100_给依然老师设计一个AI画像",
11
+ "student_json": "templates/a100_给依然老师设计一个AI画像/__student__.json",
12
+ "teacher_json": "templates/a100_给依然老师设计一个AI画像/__teacher__.json",
13
+ "入口文件": "templates/a100_给依然老师设计一个AI画像/index.html",
14
+ "一句话说明": "先让 AI 生成一个老师形象,再用网页把它展示出来,重点练习描述和小修改。",
15
+ "主能力标签": "表达构建",
16
+ "任务类型标签": "视听表达",
17
+ "关键词": [
18
+ "a100_给依然老师设计一个AI画像",
19
+ "先让 AI 生成一个老师形象,再用网页把它展示出来,重点练习描述和小修改。",
20
+ "初次接触",
21
+ "需要明确目标",
22
+ "持续迭代",
23
+ "围绕明确对象生成形象",
24
+ "把人物图片放进网页中完成展示",
25
+ "通过小幅修改让作品逐步变好",
26
+ "给依然老师设计一个AI画像",
27
+ "表达构建",
28
+ "视听表达"
29
+ ],
30
+ "files": [
31
+ "__demo__.html",
32
+ "__student-view__.html",
33
+ "__student__.json",
34
+ "__teacher-view__.html",
35
+ "__teacher__.json",
36
+ "index.html"
37
+ ],
38
+ "updated_at": "2026-04-22T04:32:31Z"
39
+ },
40
+ "101": {
41
+ "系列": "A",
42
+ "编号": "101",
43
+ "名称": "做3张同主题图片",
44
+ "文件夹名": "a101_做3张同主题图片",
45
+ "路径": "templates/a101_做3张同主题图片",
46
+ "student_json": "templates/a101_做3张同主题图片/__student__.json",
47
+ "teacher_json": "templates/a101_做3张同主题图片/__teacher__.json",
48
+ "入口文件": "templates/a101_做3张同主题图片/__demo__.html",
49
+ "一句话说明": "先确定一个有趣的主题,再用 AI 生成 3 张风格统一、但情节不同的系列图片。",
50
+ "主能力标签": "表达构建",
51
+ "任务类型标签": "视听表达",
52
+ "关键词": [
53
+ "a101_做3张同主题图片",
54
+ "先确定一个有趣的主题,再用 AI 生成 3 张风格统一、但情节不同的系列图片。",
55
+ "初次接触",
56
+ "需要明确目标",
57
+ "练习通过提示词锁定画面风格",
58
+ "理解作品一致性的价值",
59
+ "初步接触提示词的模块化修改",
60
+ "做3张同主题图片",
61
+ "表达构建",
62
+ "视听表达"
63
+ ],
64
+ "files": [
65
+ "__demo__.html",
66
+ "__student-view__.html",
67
+ "__student__.json",
68
+ "__teacher-view__.html",
69
+ "__teacher__.json"
70
+ ],
71
+ "updated_at": "2026-04-22T04:32:32Z"
72
+ },
73
+ "102": {
74
+ "系列": "A",
75
+ "编号": "102",
76
+ "名称": "图片资源展示",
77
+ "文件夹名": "a102_图片资源展示",
78
+ "路径": "templates/a102_图片资源展示",
79
+ "student_json": "templates/a102_图片资源展示/__student__.json",
80
+ "teacher_json": "templates/a102_图片资源展示/__teacher__.json",
81
+ "入口文件": "templates/a102_图片资源展示/index.html",
82
+ "一句话说明": "把图片放到网页上,做成一个方便点击查看的小展示页。",
83
+ "主能力标签": "第二能力:表达构建",
84
+ "任务类型标签": "视听表达",
85
+ "关键词": [
86
+ "a102_图片资源展示",
87
+ "把图片放到网页上,做成一个方便点击查看的小展示页。",
88
+ "刚开始接触网页展示的学生",
89
+ "需要先从添加素材开始动手的学生",
90
+ "把本地图片放进网页里",
91
+ "理解图片展示卡片的基本结构",
92
+ "为后续继续扩充图片资源做准备",
93
+ "图片资源展示",
94
+ "第二能力:表达构建",
95
+ "视听表达"
96
+ ],
97
+ "files": [
98
+ "__demo__.html",
99
+ "__student-view__.html",
100
+ "__student__.json",
101
+ "__teacher-view__.html",
102
+ "__teacher__.json",
103
+ "index.html",
104
+ "tmpsz_k5dxa.png",
105
+ "努力学习的小朋友.png",
106
+ "可爱机甲.png",
107
+ "小组吉祥物第一版_1024x1024_20260421_173654.png",
108
+ "小组吉祥物第二版-中等_1024x1024_20260421_175052.png",
109
+ "小组图标第一版-中等-微调_1024x1024_20260421_184340.png",
110
+ "小组图标第一版-中等_1024x1024_20260421_180057.png",
111
+ "小组徽章第一版-简单_1024x1024_20260421_184717.png",
112
+ "小组标志第一版-中等_1024x1024_20260421_175402.png",
113
+ "搞笑小朋友.png",
114
+ "搞笑小朋友_即梦.png",
115
+ "班级小组徽章Logo优化版_1024x1024_20260421_185141.png"
116
+ ],
117
+ "updated_at": "2026-04-22T04:47:01Z"
118
+ },
119
+ "103": {
120
+ "系列": "A",
121
+ "编号": "103",
122
+ "名称": "做一个介绍页面",
123
+ "文件夹名": "a103_做一个介绍页面",
124
+ "路径": "templates/a103_做一个介绍页面",
125
+ "student_json": "templates/a103_做一个介绍页面/__student__.json",
126
+ "teacher_json": "templates/a103_做一个介绍页面/__teacher__.json",
127
+ "入口文件": "templates/a103_做一个介绍页面/__demo__.html",
128
+ "一句话说明": "用一张图片加一段文字,做一个属于自己的介绍页面,练一练图文组合的技巧。",
129
+ "主能力标签": "表达构建",
130
+ "任务类型标签": "视听表达",
131
+ "关键词": [
132
+ "a103_做一个介绍页面",
133
+ "用一张图片加一段文字,做一个属于自己的介绍页面,练一练图文组合的技巧。",
134
+ "初次接触网页制作",
135
+ "有一点基础想练排版",
136
+ "图片与文字的组合方式",
137
+ "网页结构的基本层次感",
138
+ "排版的视觉优先级",
139
+ "做一个介绍页面",
140
+ "表达构建",
141
+ "视听表达"
142
+ ],
143
+ "files": [
144
+ "__demo__.html",
145
+ "__student-view__.html",
146
+ "__student__.json",
147
+ "__teacher-view__.html",
148
+ "__teacher__.json"
149
+ ],
150
+ "updated_at": "2026-04-22T04:32:34Z"
151
+ },
152
+ "104": {
153
+ "系列": "A",
154
+ "编号": "104",
155
+ "名称": "做一个会出招的神奇按钮",
156
+ "文件夹名": "a104_做一个会出招的神奇按钮",
157
+ "路径": "templates/a104_做一个会出招的神奇按钮",
158
+ "student_json": "templates/a104_做一个会出招的神奇按钮/__student__.json",
159
+ "teacher_json": "templates/a104_做一个会出招的神奇按钮/__teacher__.json",
160
+ "入口文件": "templates/a104_做一个会出招的神奇按钮/index.html",
161
+ "一句话说明": "先试试按钮效果,再改一点,最后加一个你自己的新按钮。",
162
+ "主能力标签": "迭代推进",
163
+ "任务类型标签": "游戏机制",
164
+ "关键词": [
165
+ "a104_做一个会出招的神奇按钮",
166
+ "先试试按钮效果,再改一点,最后加一个你自己的新按钮。",
167
+ "想先动手再思考的学生",
168
+ "刚开始学网页互动的学生",
169
+ "容易卡在空白页不知从哪开始的学生",
170
+ "先运行现成示例,快速进入任务",
171
+ "从已有功能里挑一个做小改动",
172
+ "理解一个按钮对应一个功能",
173
+ "把新增功能接入现有页面结构",
174
+ "做一个会出招的神奇按钮",
175
+ "迭代推进",
176
+ "游戏机制"
177
+ ],
178
+ "files": [
179
+ "__demo__.html",
180
+ "__student-view__.html",
181
+ "__student__.json",
182
+ "__teacher-view__.html",
183
+ "__teacher__.json",
184
+ "bg_music.mp3",
185
+ "index.html",
186
+ "炫酷机甲_1024x1024_20260416_125127.png"
187
+ ],
188
+ "updated_at": "2026-04-22T04:32:35Z"
189
+ }
190
+ },
191
+ "B": {
192
+ "100": {
193
+ "系列": "B",
194
+ "编号": "100",
195
+ "名称": "做一个按钮页面",
196
+ "文件夹名": "b100_做一个按钮页面",
197
+ "路径": "templates/b100_做一个按钮页面",
198
+ "student_json": "templates/b100_做一个按钮页面/__student__.json",
199
+ "teacher_json": "templates/b100_做一个按钮页面/__teacher__.json",
200
+ "入口文件": "templates/b100_做一个按钮页面/__demo__.html",
201
+ "一句话说明": "亲手做一个会“变魔术”的按钮,学习网页是如何接收指令并产生变化的。",
202
+ "主能力标签": "表达构建",
203
+ "任务类型标签": "游戏机制",
204
+ "关键词": [
205
+ "b100_做一个按钮页面",
206
+ "亲手做一个会“变魔术”的按钮,学习网页是如何接收指令并产生变化的。",
207
+ "喜欢做项目",
208
+ "需要明确目标",
209
+ "掌握基础的 JavaScript 点击事件绑定",
210
+ "理解 HTML/CSS/JS 三者如何协作完成交互",
211
+ "学习利用 CSS Class 切换来实现视觉变化",
212
+ "做一个按钮页面",
213
+ "表达构建",
214
+ "游戏机制"
215
+ ],
216
+ "files": [
217
+ "__demo__.html",
218
+ "__student-view__.html",
219
+ "__student__.json",
220
+ "__teacher-view__.html",
221
+ "__teacher__.json"
222
+ ],
223
+ "updated_at": "2026-04-22T04:32:36Z"
224
+ }
225
+ },
226
+ "C": {
227
+ "100": {
228
+ "系列": "C",
229
+ "编号": "100",
230
+ "名称": "给小组作品做一次小修改",
231
+ "文件夹名": "c100_给小组作品做一次小修改",
232
+ "路径": "templates/c100_给小组作品做一次小修改",
233
+ "student_json": "templates/c100_给小组作品做一次小修改/__student__.json",
234
+ "teacher_json": "templates/c100_给小组作品做一次小修改/__teacher__.json",
235
+ "入口文件": "templates/c100_给小组作品做一次小修改/index.html",
236
+ "一句话说明": "先看这 3 个小组作品,找出一个你最想改的小地方,再把这个修改要求说给 AI。",
237
+ "主能力标签": "问题判断",
238
+ "任务类型标签": "改编优化",
239
+ "关键词": [
240
+ "c100_给小组作品做一次小修改",
241
+ "先看这 3 个小组作品,找出一个你最想改的小地方,再把这个修改要求说给 AI。",
242
+ "已经能看懂图片内容,但不太会说哪里有问题的学生",
243
+ "会说喜欢或不喜欢,但不太会提出具体修改点的学生",
244
+ "已经能做第一版作品,想练习小步修改的学生",
245
+ "先发现一个具体问题",
246
+ "把大问题收成一个最小修改点",
247
+ "用简单的话把修改要求说清楚",
248
+ "比较修改前后有没有更接近自己的想法",
249
+ "给小组作品做一次小修改",
250
+ "问题判断",
251
+ "改编优化"
252
+ ],
253
+ "files": [
254
+ "__demo__.html",
255
+ "__student-view__.html",
256
+ "__student__.json",
257
+ "__teacher-view__.html",
258
+ "__teacher__.json",
259
+ "demo.html",
260
+ "index.html",
261
+ "小组吉祥物_26.png",
262
+ "小组图形标志_83.png",
263
+ "班级小组徽章_47.png"
264
+ ],
265
+ "updated_at": "2026-04-22T04:32:36Z"
266
+ }
267
+ }
268
+ }
269
+ }
@@ -0,0 +1,52 @@
1
+ # 模板索引
2
+
3
+ 每次 templates 内模板有变化后,都应重新运行本脚本。
4
+
5
+ ## A100 给依然老师设计一个AI画像
6
+ - 路径:templates/a100_给依然老师设计一个AI画像
7
+ - 入口文件:templates/a100_给依然老师设计一个AI画像/index.html
8
+ - 主能力标签:表达构建
9
+ - 任务类型标签:视听表达
10
+ - 关键词:a100_给依然老师设计一个AI画像, 先让 AI 生成一个老师形象,再用网页把它展示出来,重点练习描述和小修改。, 初次接触, 需要明确目标, 持续迭代, 围绕明确对象生成形象, 把人物图片放进网页中完成展示, 通过小幅修改让作品逐步变好, 给依然老师设计一个AI画像, 表达构建, 视听表达
11
+
12
+ ## A101 做3张同主题图片
13
+ - 路径:templates/a101_做3张同主题图片
14
+ - 入口文件:templates/a101_做3张同主题图片/__demo__.html
15
+ - 主能力标签:表达构建
16
+ - 任务类型标签:视听表达
17
+ - 关键词:a101_做3张同主题图片, 先确定一个有趣的主题,再用 AI 生成 3 张风格统一、但情节不同的系列图片。, 初次接触, 需要明确目标, 练习通过提示词锁定画面风格, 理解作品一致性的价值, 初步接触提示词的模块化修改, 做3张同主题图片, 表达构建, 视听表达
18
+
19
+ ## A102 图片资源展示
20
+ - 路径:templates/a102_图片资源展示
21
+ - 入口文件:templates/a102_图片资源展示/index.html
22
+ - 主能力标签:第二能力:表达构建
23
+ - 任务类型标签:视听表达
24
+ - 关键词:a102_图片资源展示, 把图片放到网页上,做成一个方便点击查看的小展示页。, 刚开始接触网页展示的学生, 需要先从添加素材开始动手的学生, 把本地图片放进网页里, 理解图片展示卡片的基本结构, 为后续继续扩充图片资源做准备, 图片资源展示, 第二能力:表达构建, 视听表达
25
+
26
+ ## A103 做一个介绍页面
27
+ - 路径:templates/a103_做一个介绍页面
28
+ - 入口文件:templates/a103_做一个介绍页面/__demo__.html
29
+ - 主能力标签:表达构建
30
+ - 任务类型标签:视听表达
31
+ - 关键词:a103_做一个介绍页面, 用一张图片加一段文字,做一个属于自己的介绍页面,练一练图文组合的技巧。, 初次接触网页制作, 有一点基础想练排版, 图片与文字的组合方式, 网页结构的基本层次感, 排版的视觉优先级, 做一个介绍页面, 表达构建, 视听表达
32
+
33
+ ## A104 做一个会出招的神奇按钮
34
+ - 路径:templates/a104_做一个会出招的神奇按钮
35
+ - 入口文件:templates/a104_做一个会出招的神奇按钮/index.html
36
+ - 主能力标签:迭代推进
37
+ - 任务类型标签:游戏机制
38
+ - 关键词:a104_做一个会出招的神奇按钮, 先试试按钮效果,再改一点,最后加一个你自己的新按钮。, 想先动手再思考的学生, 刚开始学网页互动的学生, 容易卡在空白页不知从哪开始的学生, 先运行现成示例,快速进入任务, 从已有功能里挑一个做小改动, 理解一个按钮对应一个功能, 把新增功能接入现有页面结构, 做一个会出招的神奇按钮, 迭代推进, 游戏机制
39
+
40
+ ## B100 做一个按钮页面
41
+ - 路径:templates/b100_做一个按钮页面
42
+ - 入口文件:templates/b100_做一个按钮页面/__demo__.html
43
+ - 主能力标签:表达构建
44
+ - 任务类型标签:游戏机制
45
+ - 关键词:b100_做一个按钮页面, 亲手做一个会“变魔术”的按钮,学习网页是如何接收指令并产生变化的。, 喜欢做项目, 需要明确目标, 掌握基础的 JavaScript 点击事件绑定, 理解 HTML/CSS/JS 三者如何协作完成交互, 学习利用 CSS Class 切换来实现视觉变化, 做一个按钮页面, 表达构建, 游戏机制
46
+
47
+ ## C100 给小组作品做一次小修改
48
+ - 路径:templates/c100_给小组作品做一次小修改
49
+ - 入口文件:templates/c100_给小组作品做一次小修改/index.html
50
+ - 主能力标签:问题判断
51
+ - 任务类型标签:改编优化
52
+ - 关键词:c100_给小组作品做一次小修改, 先看这 3 个小组作品,找出一个你最想改的小地方,再把这个修改要求说给 AI。, 已经能看懂图片内容,但不太会说哪里有问题的学生, 会说喜欢或不喜欢,但不太会提出具体修改点的学生, 已经能做第一版作品,想练习小步修改的学生, 先发现一个具体问题, 把大问题收成一个最小修改点, 用简单的话把修改要求说清楚, 比较修改前后有没有更接近自己的想法, 给小组作品做一次小修改, 问题判断, 改编优化
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ [废弃] 扫描 HTML 引用并重写为 CDN 绝对地址
4
+
5
+ 原本为解决 /api/file 加载时相对路径失效的问题而设计。
6
+ 现状:模板预览已改为直接从 CDN 加载,相对路径天然可用,重写无必要。
7
+ """
8
+
9
+ import re
10
+ from pathlib import Path
11
+ from typing import Dict, Optional, Set
12
+ from urllib.parse import urlparse, unquote
13
+
14
+ from qiniu import Auth, put_file_v2, CdnManager
15
+
16
+ QINIU_KEY = "T3tgxM7EMx1j4VESw4m4PIfFXoOvBo-wQEOQewXX"
17
+ QINIU_SECRET = "PVZvlKVOjX2RqlV2ILMg-QwpNMssOlpVbaEzypz0"
18
+ QINIU_BUCKET = "yiran1"
19
+ CDN_DOMAIN = "https://cdn.yiranlaoshi.com"
20
+ CDN_PREFIX = "myclaw/live/yiran/skills/yiran-playground-template-use/templates"
21
+
22
+ q = Auth(QINIU_KEY, QINIU_SECRET)
23
+ cdn_manager = CdnManager(q)
24
+
25
+ ATTR_RE = re.compile(r'(src|href)=["\']([^"\']+)["\']', re.IGNORECASE)
26
+ URL_RE = re.compile(r'url\(["\']?([^)"\']+)["\']?\)', re.IGNORECASE)
27
+ SKIP_PREFIXES = ('data:', '#', 'mailto:', 'javascript:')
28
+
29
+
30
+ def is_external(url):
31
+ return url.startswith('http://') or url.startswith('https://') or url.startswith('//')
32
+
33
+
34
+ def find_local_file(ref, base_dir):
35
+ if any(ref.startswith(p) for p in SKIP_PREFIXES):
36
+ return None
37
+ if is_external(ref):
38
+ filename = Path(unquote(urlparse(ref).path)).name
39
+ candidate = base_dir / filename
40
+ return candidate if candidate.is_file() else None
41
+ else:
42
+ candidate = (base_dir / ref).resolve()
43
+ if candidate.is_file() and str(candidate).startswith(str(base_dir.resolve())):
44
+ return candidate
45
+ return None
46
+
47
+
48
+ def collect_refs(html):
49
+ refs = set()
50
+ for m in ATTR_RE.finditer(html):
51
+ refs.add(m.group(2))
52
+ for m in URL_RE.finditer(html):
53
+ refs.add(m.group(1))
54
+ return refs
55
+
56
+
57
+ def rewrite_html(html, ref_map):
58
+ def replace_attr(m):
59
+ attr, val = m.group(1), m.group(2)
60
+ return '{}="{}"'.format(attr, ref_map.get(val, val))
61
+ def replace_url(m):
62
+ val = m.group(1)
63
+ return 'url("{}")'.format(ref_map.get(val, val))
64
+ html = ATTR_RE.sub(replace_attr, html)
65
+ html = URL_RE.sub(replace_url, html)
66
+ return html
67
+
68
+
69
+ def upload_file(local_path, folder_name):
70
+ key = "{}/{}/{}".format(CDN_PREFIX, folder_name, local_path.name)
71
+ token = q.upload_token(QINIU_BUCKET, key, 3600, policy={'insertOnly': 0})
72
+ ret, info = put_file_v2(token, key, str(local_path))
73
+ if info.status_code != 200:
74
+ raise RuntimeError("上传失败 {}: {}".format(local_path.name, info))
75
+ cdn_url = "{}/{}".format(CDN_DOMAIN, key)
76
+ cdn_manager.refresh_urls([cdn_url])
77
+ return cdn_url
78
+
79
+
80
+ def rewrite_and_upload(folder_path):
81
+ folder_name = folder_path.name
82
+ html_files = list(folder_path.glob('*.html'))
83
+ uploaded = {}
84
+ all_refs = {}
85
+
86
+ for html_file in html_files:
87
+ html = html_file.read_text(encoding='utf-8')
88
+ for ref in collect_refs(html):
89
+ if ref in all_refs:
90
+ continue
91
+ local = find_local_file(ref, folder_path)
92
+ if local:
93
+ if local not in uploaded:
94
+ cdn_url = upload_file(local, folder_name)
95
+ uploaded[local] = cdn_url
96
+ all_refs[ref] = uploaded[local]
97
+
98
+ for html_file in html_files:
99
+ html = html_file.read_text(encoding='utf-8')
100
+ new_html = rewrite_html(html, all_refs)
101
+ html_file.write_text(new_html, encoding='utf-8')
102
+ upload_file(html_file, folder_name)
@@ -82,10 +82,8 @@ python3 scripts/deploy_to_workspace.py <template_path> <workspace_path> --with-t
82
82
 
83
83
  - `SKILL.md`:本说明
84
84
  - `search-agent-prompt.md`:AI 搜索指引
85
- - `template-index.json`:模板索引(自动生成,不手改)
86
- - `template-index.md`:模板索引可读版本(自动生成,不手改)
85
+ - `template-index.json`:模板索引(由 pipeline 生成并同步,不手改)
87
86
  - `templates/`:模板库,每个子文件夹是一个独立模板
88
- - `scripts/build_template_index.py`:重建索引
89
87
  - `scripts/query_template.py`:查询主脚本
90
88
  - `scripts/prepare_playgrounds.py`:内部准备脚本
91
89
  - `scripts/deploy_to_workspace.py`:对外 workspace 投放脚本
@@ -94,12 +92,4 @@ python3 scripts/deploy_to_workspace.py <template_path> <workspace_path> --with-t
94
92
 
95
93
  ## 索引维护
96
94
 
97
- `yiran-course-template-pipeline` 打包新模板后会自动触发索引重建。
98
-
99
- `template-index.json` 与 `template-index.md` 都视为自动产物,不手动编辑。
100
-
101
- 如需手动重建:
102
-
103
- ```bash
104
- python3 scripts/build_template_index.py
105
- ```
95
+ `template-index.json` 由 `yiran-course-template-pipeline` 的 publish 流程统一生成和上传 CDN,本 skill 不负责维护。
@@ -1,77 +0,0 @@
1
- # CDN 约定
2
-
3
- ## 七牛云配置
4
-
5
- | 项 | 值 |
6
- |---|---|
7
- | Bucket | `yiran1` |
8
- | CDN 域名 | `https://cdn.yiranlaoshi.com` |
9
-
10
- ---
11
-
12
- ## 模板文件 CDN 路径规则
13
-
14
- ```
15
- https://cdn.yiranlaoshi.com/myclaw/live/yiran/skills/yiran-playground-template-use/templates/{文件夹名}/{文件名}
16
- ```
17
-
18
- 示例:
19
- ```
20
- https://cdn.yiranlaoshi.com/myclaw/live/yiran/skills/yiran-playground-template-use/templates/a103_做一个介绍页面/__demo__.html
21
- https://cdn.yiranlaoshi.com/myclaw/live/yiran/skills/yiran-playground-template-use/templates/a103_做一个介绍页面/__student-view__.html
22
- https://cdn.yiranlaoshi.com/myclaw/live/yiran/skills/yiran-playground-template-use/templates/a103_做一个介绍页面/banner.png
23
- ```
24
-
25
- 七牛 key(去掉域名后的部分):
26
- ```
27
- myclaw/live/yiran/skills/yiran-playground-template-use/templates/{文件夹名}/{文件名}
28
- ```
29
-
30
- ---
31
-
32
- ## 两阶段分工
33
-
34
- ### 阶段一:创作阶段(yiran-course-template-pipeline)
35
-
36
- **只关心内容质量,不关心具体 CDN 地址。**
37
-
38
- - 所有外部资源(图片、字体、JS 库)**必须使用 CDN 链接**,禁止本地相对路径
39
- - 使用哪个 CDN 不限,可以是 unpkg、jsDelivr、任意公开图床,也可以是 `cdn.yiranlaoshi.com`
40
- - 模板文件之间的引用(如 `<iframe src="__demo__.html">`)**视为相对路径,不允许**,需在创作时避免或由 publish 脚本修复
41
-
42
- ### 阶段二:发布阶段(publish_template.py)
43
-
44
- **统一接管资源,重新打包到指定 CDN。**
45
-
46
- 发布脚本会:
47
- 1. 扫描模板文件夹内所有 HTML 文件的资源引用(`src`、`href`、`url()`)
48
- 2. 对每个引用,判断本地是否存在对应文件(相对路径 → 直接找;其他 CDN 链接 → 尝试匹配本地同名文件)
49
- 3. 将本地能找到的文件上传到七牛,路径规则见上方
50
- 4. 将 HTML 中的原引用替换为新 CDN URL
51
- 5. 最后上传修改后的 HTML 文件本身
52
-
53
- 发布完成后,模板文件夹内的所有资源在 CDN 上完全自包含,不依赖任何外部路径。
54
-
55
- ---
56
-
57
- ## 调用方式
58
-
59
- ```bash
60
- # 在 move 之后执行
61
- python3 scripts/publish_template.py <templates 下的目标文件夹>
62
- ```
63
-
64
- 例:
65
- ```bash
66
- python3 scripts/publish_template.py /path/to/.openclaw/skills/yiran-playground-template-use/templates/a103_做一个介绍页面
67
- ```
68
-
69
- ---
70
-
71
- ## 完整流程
72
-
73
- ```
74
- 1. build_template_views.py 创作文件夹 → 生成 __student-view__.html 等
75
- 2. move_template_task.py 创作文件夹 → 移动改名到 templates/,重建索引
76
- 3. publish_template.py templates/xxx → 上传资源到七牛,重写 HTML 引用
77
- ```
@@ -1,103 +0,0 @@
1
- #!/usr/bin/env python3
2
- import json
3
- import re
4
- from pathlib import Path
5
-
6
- ROOT = Path(__file__).resolve().parent.parent
7
- TEMPLATES_DIR = ROOT / 'templates'
8
- OUTPUT_JSON = ROOT / 'template-index.json'
9
- OUTPUT_MD = ROOT / 'template-index.md'
10
-
11
-
12
- def read_json(path: Path):
13
- try:
14
- return json.loads(path.read_text(encoding='utf-8'))
15
- except Exception:
16
- return {}
17
-
18
-
19
- def parse_folder_name(name: str):
20
- m = re.match(r'^([a-zA-Z]+)(\d+)_(.+)$', name)
21
- if m:
22
- return m.group(1).upper(), str(int(m.group(2))), m.group(3)
23
- return None, None, None
24
-
25
-
26
- def get_entry_file(folder: Path) -> Path | None:
27
- for name in ['index.html', '__demo__.html', '__student-view__.html']:
28
- if (folder / name).exists():
29
- return folder / name
30
- return None
31
-
32
-
33
- def main():
34
- index = {}
35
- items_for_md = []
36
-
37
- for folder in sorted(TEMPLATES_DIR.iterdir() if TEMPLATES_DIR.exists() else []):
38
- if not folder.is_dir():
39
- continue
40
-
41
- series, number, _ = parse_folder_name(folder.name)
42
- if not series:
43
- continue
44
-
45
- teacher = read_json(folder / '__teacher__.json')
46
- student = read_json(folder / '__student__.json')
47
-
48
- name = teacher.get('任务名称') or student.get('任务标题', '')
49
- 一句话 = student.get('一句话说明', '')
50
- 能力标签 = teacher.get('主能力标签', '')
51
- 类型标签 = teacher.get('任务类型标签', '')
52
-
53
- keywords = list(dict.fromkeys(filter(None, [
54
- folder.name,
55
- 一句话,
56
- *teacher.get('适合学生类型', []),
57
- *teacher.get('训练重点', []),
58
- name,
59
- 能力标签,
60
- 类型标签,
61
- ])))
62
-
63
- entry = get_entry_file(folder)
64
- record = {
65
- '系列': series,
66
- '编号': number,
67
- '名称': name,
68
- '文件夹名': folder.name,
69
- '路径': str(folder.relative_to(ROOT)),
70
- 'student_json': str((folder / '__student__.json').relative_to(ROOT)) if (folder / '__student__.json').exists() else '',
71
- 'teacher_json': str((folder / '__teacher__.json').relative_to(ROOT)) if (folder / '__teacher__.json').exists() else '',
72
- '入口文件': str(entry.relative_to(ROOT)) if entry else '',
73
- '一句话说明': 一句话,
74
- '主能力标签': 能力标签,
75
- '任务类型标签': 类型标签,
76
- '关键词': keywords,
77
- }
78
-
79
- if series not in index:
80
- index[series] = {}
81
- index[series][number] = record
82
- items_for_md.append(record)
83
-
84
- OUTPUT_JSON.write_text(json.dumps(index, ensure_ascii=False, indent=2), encoding='utf-8')
85
-
86
- lines = ['# 模板索引', '', '每次 templates 内模板有变化后,都应重新运行本脚本。', '']
87
- for item in items_for_md:
88
- lines.append(f"## {item['系列']}{item['编号']} {item['名称']}")
89
- lines.append(f"- 路径:{item['路径']}")
90
- lines.append(f"- 入口文件:{item['入口文件']}")
91
- lines.append(f"- 主能力标签:{item['主能力标签']}")
92
- lines.append(f"- 任务类型标签:{item['任务类型标签']}")
93
- if item['关键词']:
94
- lines.append(f"- 关键词:{', '.join(item['关键词'])}")
95
- lines.append('')
96
-
97
- OUTPUT_MD.write_text('\n'.join(lines), encoding='utf-8')
98
- print(f'已生成: {OUTPUT_JSON}')
99
- print(f'已生成: {OUTPUT_MD}')
100
-
101
-
102
- if __name__ == '__main__':
103
- main()