@aiyiran/myclaw 1.1.64 → 1.1.65
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/assets/myclaw-artifacts.js +36 -22
- package/package.json +1 -1
- package/server/sync_workspace.py +27 -4
- package/skills/yiran-course-template-pipeline/template-index.json +39 -11
- package/skills/yiran-course-template-pipeline/template-index.md +12 -3
- package/skills/yiran-playground-template-use/scripts/deploy_to_workspace.py +4 -11
|
@@ -2251,17 +2251,21 @@
|
|
|
2251
2251
|
|
|
2252
2252
|
var _btnBase = 'padding:5px 12px;border-radius:4px;font-size:11px;font-family:monospace;cursor:pointer;transition:all 0.15s;white-space:nowrap;';
|
|
2253
2253
|
|
|
2254
|
+
var _btnMuted = _btnBase + 'background:rgba(167,139,250,0.12);border:1px solid rgba(167,139,250,0.3);color:#a78bfa;';
|
|
2255
|
+
var _btnMutedHover = 'background:rgba(167,139,250,0.22);';
|
|
2256
|
+
var _btnMutedLeave = 'background:rgba(167,139,250,0.12);';
|
|
2257
|
+
|
|
2254
2258
|
var deployBtn = document.createElement('button');
|
|
2255
2259
|
deployBtn.textContent = 'workspace-playgrounds ⬇';
|
|
2256
|
-
deployBtn.style.cssText =
|
|
2257
|
-
deployBtn.onmouseenter = function () { deployBtn.style.background = '
|
|
2258
|
-
deployBtn.onmouseleave = function () { deployBtn.style.background = '
|
|
2260
|
+
deployBtn.style.cssText = _btnMuted;
|
|
2261
|
+
deployBtn.onmouseenter = function () { deployBtn.style.background = 'rgba(167,139,250,0.22)'; };
|
|
2262
|
+
deployBtn.onmouseleave = function () { deployBtn.style.background = 'rgba(167,139,250,0.12)'; };
|
|
2259
2263
|
|
|
2260
2264
|
var copyLocalBtn = document.createElement('button');
|
|
2261
|
-
copyLocalBtn.textContent =
|
|
2262
|
-
copyLocalBtn.style.cssText =
|
|
2263
|
-
copyLocalBtn.onmouseenter = function () { copyLocalBtn.style.background = 'rgba(
|
|
2264
|
-
copyLocalBtn.onmouseleave = function () { copyLocalBtn.style.background = 'rgba(
|
|
2265
|
+
copyLocalBtn.textContent = '新建工作区 ⬇';
|
|
2266
|
+
copyLocalBtn.style.cssText = _btnMuted;
|
|
2267
|
+
copyLocalBtn.onmouseenter = function () { copyLocalBtn.style.background = 'rgba(167,139,250,0.22)'; };
|
|
2268
|
+
copyLocalBtn.onmouseleave = function () { copyLocalBtn.style.background = 'rgba(167,139,250,0.12)'; };
|
|
2265
2269
|
|
|
2266
2270
|
var promptBtn = document.createElement('button');
|
|
2267
2271
|
promptBtn.textContent = '复制提示词';
|
|
@@ -2310,22 +2314,28 @@
|
|
|
2310
2314
|
|
|
2311
2315
|
copyLocalBtn.onclick = function () {
|
|
2312
2316
|
if (!currentTpl) return;
|
|
2313
|
-
setBtnLoading(copyLocalBtn, '
|
|
2317
|
+
setBtnLoading(copyLocalBtn, '创建中...');
|
|
2314
2318
|
setFooterStatus('');
|
|
2315
|
-
fetch(MYCLAW_API_BASE + '/api/template/
|
|
2319
|
+
fetch(MYCLAW_API_BASE + '/api/template/new-workspace', {
|
|
2316
2320
|
method: 'POST',
|
|
2317
2321
|
headers: { 'Content-Type': 'application/json' },
|
|
2318
|
-
body: JSON.stringify({ folder: currentTpl['文件夹名']
|
|
2322
|
+
body: JSON.stringify({ folder: currentTpl['文件夹名'] }),
|
|
2319
2323
|
})
|
|
2320
2324
|
.then(function (r) { return r.json(); })
|
|
2321
2325
|
.then(function (res) {
|
|
2322
2326
|
resetBtn(copyLocalBtn);
|
|
2323
2327
|
if (res.ok) {
|
|
2324
|
-
setFooterStatus('✓
|
|
2328
|
+
setFooterStatus('✓ 新工作区已创建:' + res.folder_rel_path, '#10b981');
|
|
2329
|
+
if (res.agent && res.session) {
|
|
2330
|
+
var sessionParam = 'agent:' + res.agent + ':' + res.session;
|
|
2331
|
+
setTimeout(function () {
|
|
2332
|
+
window.location.href = '/chat?session=' + encodeURIComponent(sessionParam);
|
|
2333
|
+
}, 10000);
|
|
2334
|
+
}
|
|
2325
2335
|
} else if (res.code === 'not_ready') {
|
|
2326
2336
|
setFooterStatus('⟳ 模板文件下载中,完成后即可使用', '#f59e0b');
|
|
2327
2337
|
} else {
|
|
2328
|
-
setFooterStatus('✗ ' + (res.error || '
|
|
2338
|
+
setFooterStatus('✗ ' + (res.error || '创建失败'), '#ff4444');
|
|
2329
2339
|
}
|
|
2330
2340
|
})
|
|
2331
2341
|
.catch(function (err) {
|
|
@@ -2334,23 +2344,24 @@
|
|
|
2334
2344
|
});
|
|
2335
2345
|
};
|
|
2336
2346
|
|
|
2337
|
-
|
|
2347
|
+
function doDeployWith(btn, agent) {
|
|
2338
2348
|
if (!currentTpl) return;
|
|
2339
|
-
setBtnLoading(
|
|
2349
|
+
setBtnLoading(btn, '创建中...');
|
|
2340
2350
|
setFooterStatus('');
|
|
2351
|
+
var body = { folder: currentTpl['文件夹名'] };
|
|
2352
|
+
if (agent) body.agent = agent;
|
|
2341
2353
|
fetch(MYCLAW_API_BASE + '/api/template/deploy', {
|
|
2342
2354
|
method: 'POST',
|
|
2343
2355
|
headers: { 'Content-Type': 'application/json' },
|
|
2344
|
-
body: JSON.stringify(
|
|
2356
|
+
body: JSON.stringify(body),
|
|
2345
2357
|
})
|
|
2346
2358
|
.then(function (r) { return r.json(); })
|
|
2347
2359
|
.then(function (res) {
|
|
2348
|
-
resetBtn(
|
|
2360
|
+
resetBtn(btn);
|
|
2349
2361
|
if (res.ok) {
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
var sessionParam = 'agent:' + deploy.agent + ':' + deploy.session;
|
|
2362
|
+
setFooterStatus('✓ 已创建,即将跳转...', '#10b981');
|
|
2363
|
+
if (res.agent && res.session) {
|
|
2364
|
+
var sessionParam = 'agent:' + res.agent + ':' + res.session;
|
|
2354
2365
|
setTimeout(function () {
|
|
2355
2366
|
window.location.href = '/chat?session=' + encodeURIComponent(sessionParam);
|
|
2356
2367
|
}, 10000);
|
|
@@ -2362,10 +2373,13 @@
|
|
|
2362
2373
|
}
|
|
2363
2374
|
})
|
|
2364
2375
|
.catch(function (err) {
|
|
2365
|
-
resetBtn(
|
|
2376
|
+
resetBtn(btn);
|
|
2366
2377
|
setFooterStatus('✗ ' + err.message, '#ff4444');
|
|
2367
2378
|
});
|
|
2368
|
-
}
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
deployBtn.onclick = function () { doDeployWith(deployBtn, 'playgrounds'); };
|
|
2382
|
+
copyLocalBtn.onclick = function () { doDeployWith(copyLocalBtn, null); };
|
|
2369
2383
|
|
|
2370
2384
|
rightPane.appendChild(rightHeader);
|
|
2371
2385
|
rightPane.appendChild(previewIframe);
|
package/package.json
CHANGED
package/server/sync_workspace.py
CHANGED
|
@@ -1138,8 +1138,10 @@ class MyclawAPIHandler(BaseHTTPRequestHandler):
|
|
|
1138
1138
|
self._send_json(data)
|
|
1139
1139
|
|
|
1140
1140
|
def _handle_template_deploy(self):
|
|
1141
|
-
"""POST /api/template/deploy Body: {"folder": "a100_..."}
|
|
1142
|
-
|
|
1141
|
+
"""POST /api/template/deploy Body: {"folder": "a100_...", "agent": "playgrounds"}
|
|
1142
|
+
agent 默认 "playgrounds";不传则自动生成唯一名 tpl-MMDDHHMM。
|
|
1143
|
+
流程:mc new {agent}(已存在忽略)→ deploy_to_workspace.py --with-tui
|
|
1144
|
+
"""
|
|
1143
1145
|
try:
|
|
1144
1146
|
length = int(self.headers.get('Content-Length', 0))
|
|
1145
1147
|
body = json.loads(self.rfile.read(length).decode('utf-8')) if length else {}
|
|
@@ -1152,10 +1154,31 @@ class MyclawAPIHandler(BaseHTTPRequestHandler):
|
|
|
1152
1154
|
if template_path is None:
|
|
1153
1155
|
return
|
|
1154
1156
|
|
|
1155
|
-
|
|
1157
|
+
if body.get('agent'):
|
|
1158
|
+
agent_name = body['agent']
|
|
1159
|
+
else:
|
|
1160
|
+
# 作品编号-版本号-小时分钟,例如 a104-v1-1911
|
|
1161
|
+
series_num = re.match(r'^([a-z]+\d+)_', folder)
|
|
1162
|
+
series_num = series_num.group(1) if series_num else 'tpl'
|
|
1163
|
+
version_str = os.path.basename(template_path) # template_path 末段就是 v1/v2...
|
|
1164
|
+
agent_name = '{}-{}-{}'.format(series_num, version_str, datetime.now().strftime('%H%M'))
|
|
1165
|
+
|
|
1166
|
+
# mc new(已存在则忽略错误)
|
|
1167
|
+
try:
|
|
1168
|
+
ret = subprocess.run(['mc', 'new', agent_name], capture_output=True, text=True, timeout=30)
|
|
1169
|
+
output = (ret.stdout or '') + (ret.stderr or '')
|
|
1170
|
+
if ret.returncode != 0 and '已存在' not in output and 'exists' not in output.lower():
|
|
1171
|
+
self._send_json({'ok': False, 'error': 'mc new 失败: ' + output.strip()}, 500)
|
|
1172
|
+
return
|
|
1173
|
+
except Exception as e:
|
|
1174
|
+
self._send_json({'ok': False, 'error': str(e)}, 500)
|
|
1175
|
+
return
|
|
1176
|
+
|
|
1177
|
+
workspace_path = os.path.join(get_openclaw_path(), 'workspace-' + agent_name)
|
|
1178
|
+
script = os.path.join(self._skill_scripts_root(), 'deploy_to_workspace.py')
|
|
1156
1179
|
|
|
1157
1180
|
data, err = self._run_skill_script(
|
|
1158
|
-
[sys.executable, script, template_path],
|
|
1181
|
+
[sys.executable, script, template_path, workspace_path, '--with-tui'],
|
|
1159
1182
|
timeout=60
|
|
1160
1183
|
)
|
|
1161
1184
|
if err:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"index_updated_at": "2026-04-
|
|
2
|
+
"index_updated_at": "2026-04-22T16:42:59Z",
|
|
3
3
|
"templates": {
|
|
4
4
|
"696de592": {
|
|
5
5
|
"id": "696de592",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"表达构建",
|
|
27
27
|
"视听表达"
|
|
28
28
|
],
|
|
29
|
-
"updated_at": "2026-04-
|
|
29
|
+
"updated_at": "2026-04-22T15:55:39Z"
|
|
30
30
|
},
|
|
31
31
|
"d0ff11ee": {
|
|
32
32
|
"id": "d0ff11ee",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"文件夹名": "a101_做3张同主题图片",
|
|
37
37
|
"version": "v1",
|
|
38
38
|
"路径": "templates/a101_做3张同主题图片/v1",
|
|
39
|
-
"入口文件": "templates/a101_做3张同主题图片/v1/
|
|
39
|
+
"入口文件": "templates/a101_做3张同主题图片/v1/index.html",
|
|
40
40
|
"一句话说明": "先确定一个有趣的主题,再用 AI 生成 3 张风格统一、但情节不同的系列图片。",
|
|
41
41
|
"主能力标签": "表达构建",
|
|
42
42
|
"任务类型标签": "视听表达",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"表达构建",
|
|
53
53
|
"视听表达"
|
|
54
54
|
],
|
|
55
|
-
"updated_at": "2026-04-
|
|
55
|
+
"updated_at": "2026-04-22T15:55:40Z"
|
|
56
56
|
},
|
|
57
57
|
"d48d94b6": {
|
|
58
58
|
"id": "d48d94b6",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"第二能力:表达构建",
|
|
79
79
|
"视听表达"
|
|
80
80
|
],
|
|
81
|
-
"updated_at": "2026-04-
|
|
81
|
+
"updated_at": "2026-04-22T15:55:40Z"
|
|
82
82
|
},
|
|
83
83
|
"e1aab9bf": {
|
|
84
84
|
"id": "e1aab9bf",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"文件夹名": "a103_做一个介绍页面",
|
|
89
89
|
"version": "v1",
|
|
90
90
|
"路径": "templates/a103_做一个介绍页面/v1",
|
|
91
|
-
"入口文件": "templates/a103_做一个介绍页面/v1/
|
|
91
|
+
"入口文件": "templates/a103_做一个介绍页面/v1/index.html",
|
|
92
92
|
"一句话说明": "用一张图片加一段文字,做一个属于自己的介绍页面,练一练图文组合的技巧。",
|
|
93
93
|
"主能力标签": "表达构建",
|
|
94
94
|
"任务类型标签": "视听表达",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"表达构建",
|
|
105
105
|
"视听表达"
|
|
106
106
|
],
|
|
107
|
-
"updated_at": "2026-04-
|
|
107
|
+
"updated_at": "2026-04-22T15:55:40Z"
|
|
108
108
|
},
|
|
109
109
|
"3c61e07d": {
|
|
110
110
|
"id": "3c61e07d",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"迭代推进",
|
|
133
133
|
"游戏机制"
|
|
134
134
|
],
|
|
135
|
-
"updated_at": "2026-04-
|
|
135
|
+
"updated_at": "2026-04-22T15:55:40Z"
|
|
136
136
|
},
|
|
137
137
|
"c360e192": {
|
|
138
138
|
"id": "c360e192",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"文件夹名": "b100_做一个按钮页面",
|
|
143
143
|
"version": "v1",
|
|
144
144
|
"路径": "templates/b100_做一个按钮页面/v1",
|
|
145
|
-
"入口文件": "templates/b100_做一个按钮页面/v1/
|
|
145
|
+
"入口文件": "templates/b100_做一个按钮页面/v1/index.html",
|
|
146
146
|
"一句话说明": "亲手做一个会“变魔术”的按钮,学习网页是如何接收指令并产生变化的。",
|
|
147
147
|
"主能力标签": "表达构建",
|
|
148
148
|
"任务类型标签": "游戏机制",
|
|
@@ -158,7 +158,35 @@
|
|
|
158
158
|
"表达构建",
|
|
159
159
|
"游戏机制"
|
|
160
160
|
],
|
|
161
|
-
"updated_at": "2026-04-
|
|
161
|
+
"updated_at": "2026-04-22T15:55:41Z"
|
|
162
|
+
},
|
|
163
|
+
"8a7e39dd": {
|
|
164
|
+
"id": "8a7e39dd",
|
|
165
|
+
"系列": "B",
|
|
166
|
+
"编号": "701",
|
|
167
|
+
"名称": "会跳舞的机器人",
|
|
168
|
+
"文件夹名": "b701_会跳舞的机器人",
|
|
169
|
+
"version": "v1",
|
|
170
|
+
"路径": "templates/b701_会跳舞的机器人/v1",
|
|
171
|
+
"入口文件": "templates/b701_会跳舞的机器人/v1/index.html",
|
|
172
|
+
"一句话说明": "先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。",
|
|
173
|
+
"主能力标签": "表达构建",
|
|
174
|
+
"任务类型标签": "视听表达",
|
|
175
|
+
"关键词": [
|
|
176
|
+
"b701_会跳舞的机器人",
|
|
177
|
+
"先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。",
|
|
178
|
+
"已经能开始动手,但还不太会把多种素材组织到一起的学生",
|
|
179
|
+
"会做简单网页,但还不太会处理视频、音乐和动画配合的学生",
|
|
180
|
+
"想从静态页面进入多媒体网页表达的学生",
|
|
181
|
+
"把机器人视频放进网页中",
|
|
182
|
+
"让视频和音乐形成基本配合",
|
|
183
|
+
"尝试处理循环播放和播放节奏",
|
|
184
|
+
"让网页从静态展示变成更有动态感的作品",
|
|
185
|
+
"会跳舞的机器人",
|
|
186
|
+
"表达构建",
|
|
187
|
+
"视听表达"
|
|
188
|
+
],
|
|
189
|
+
"updated_at": "2026-04-22T16:42:48Z"
|
|
162
190
|
},
|
|
163
191
|
"6490af0a": {
|
|
164
192
|
"id": "6490af0a",
|
|
@@ -186,7 +214,7 @@
|
|
|
186
214
|
"问题判断",
|
|
187
215
|
"改编优化"
|
|
188
216
|
],
|
|
189
|
-
"updated_at": "2026-04-
|
|
217
|
+
"updated_at": "2026-04-22T15:55:41Z"
|
|
190
218
|
}
|
|
191
219
|
}
|
|
192
220
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
- ID:d0ff11ee
|
|
16
16
|
- 版本:v1
|
|
17
17
|
- 路径:templates/a101_做3张同主题图片/v1
|
|
18
|
-
- 入口文件:templates/a101_做3张同主题图片/v1/
|
|
18
|
+
- 入口文件:templates/a101_做3张同主题图片/v1/index.html
|
|
19
19
|
- 主能力标签:表达构建
|
|
20
20
|
- 任务类型标签:视听表达
|
|
21
21
|
- 关键词:a101_做3张同主题图片, 先确定一个有趣的主题,再用 AI 生成 3 张风格统一、但情节不同的系列图片。, 初次接触, 需要明确目标, 练习通过提示词锁定画面风格, 理解作品一致性的价值, 初步接触提示词的模块化修改, 做3张同主题图片, 表达构建, 视听表达
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
- ID:e1aab9bf
|
|
34
34
|
- 版本:v1
|
|
35
35
|
- 路径:templates/a103_做一个介绍页面/v1
|
|
36
|
-
- 入口文件:templates/a103_做一个介绍页面/v1/
|
|
36
|
+
- 入口文件:templates/a103_做一个介绍页面/v1/index.html
|
|
37
37
|
- 主能力标签:表达构建
|
|
38
38
|
- 任务类型标签:视听表达
|
|
39
39
|
- 关键词:a103_做一个介绍页面, 用一张图片加一段文字,做一个属于自己的介绍页面,练一练图文组合的技巧。, 初次接触网页制作, 有一点基础想练排版, 图片与文字的组合方式, 网页结构的基本层次感, 排版的视觉优先级, 做一个介绍页面, 表达构建, 视听表达
|
|
@@ -51,11 +51,20 @@
|
|
|
51
51
|
- ID:c360e192
|
|
52
52
|
- 版本:v1
|
|
53
53
|
- 路径:templates/b100_做一个按钮页面/v1
|
|
54
|
-
- 入口文件:templates/b100_做一个按钮页面/v1/
|
|
54
|
+
- 入口文件:templates/b100_做一个按钮页面/v1/index.html
|
|
55
55
|
- 主能力标签:表达构建
|
|
56
56
|
- 任务类型标签:游戏机制
|
|
57
57
|
- 关键词:b100_做一个按钮页面, 亲手做一个会“变魔术”的按钮,学习网页是如何接收指令并产生变化的。, 喜欢做项目, 需要明确目标, 掌握基础的 JavaScript 点击事件绑定, 理解 HTML/CSS/JS 三者如何协作完成交互, 学习利用 CSS Class 切换来实现视觉变化, 做一个按钮页面, 表达构建, 游戏机制
|
|
58
58
|
|
|
59
|
+
## B701 会跳舞的机器人
|
|
60
|
+
- ID:8a7e39dd
|
|
61
|
+
- 版本:v1
|
|
62
|
+
- 路径:templates/b701_会跳舞的机器人/v1
|
|
63
|
+
- 入口文件:templates/b701_会跳舞的机器人/v1/index.html
|
|
64
|
+
- 主能力标签:表达构建
|
|
65
|
+
- 任务类型标签:视听表达
|
|
66
|
+
- 关键词:b701_会跳舞的机器人, 先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。, 已经能开始动手,但还不太会把多种素材组织到一起的学生, 会做简单网页,但还不太会处理视频、音乐和动画配合的学生, 想从静态页面进入多媒体网页表达的学生, 把机器人视频放进网页中, 让视频和音乐形成基本配合, 尝试处理循环播放和播放节奏, 让网页从静态展示变成更有动态感的作品, 会跳舞的机器人, 表达构建, 视听表达
|
|
67
|
+
|
|
59
68
|
## C100 给小组作品做一次小修改
|
|
60
69
|
- ID:6490af0a
|
|
61
70
|
- 版本:v1
|
|
@@ -144,17 +144,10 @@ def choose_entry_file(target_dir: Path):
|
|
|
144
144
|
|
|
145
145
|
def build_instance_name(source_dir: Path, remix_dir: Path):
|
|
146
146
|
import re
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
else:
|
|
152
|
-
folder_name = source_dir.name
|
|
153
|
-
version_str = ''
|
|
154
|
-
now = datetime.now()
|
|
155
|
-
base = f"{now.strftime('%m%d')}_{folder_name}_{now.strftime('%H%M')}"
|
|
156
|
-
if version_str:
|
|
157
|
-
base = f"{base}_{version_str}"
|
|
147
|
+
folder_name = source_dir.parent.name if re.match(r'^v\d+$', source_dir.name) else source_dir.name
|
|
148
|
+
# 去掉 a104_ 这样的系列编号前缀,只保留业务名
|
|
149
|
+
name = re.sub(r'^[a-z]+\d+_', '', folder_name) or folder_name
|
|
150
|
+
base = f"{name}_{datetime.now().strftime('%H%M')}"
|
|
158
151
|
candidate = base
|
|
159
152
|
suffix = 2
|
|
160
153
|
while (remix_dir / candidate).exists():
|