@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.
@@ -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 = _btnBase + 'background:#a78bfa;border:none;color:#fff;';
2257
- deployBtn.onmouseenter = function () { deployBtn.style.background = '#8b5cf6'; };
2258
- deployBtn.onmouseleave = function () { deployBtn.style.background = '#a78bfa'; };
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 = getWorkspaceId() + ' ⬇';
2262
- copyLocalBtn.style.cssText = _btnBase + 'background:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.15);color:#cdd6f4;';
2263
- copyLocalBtn.onmouseenter = function () { copyLocalBtn.style.background = 'rgba(255,255,255,0.14)'; };
2264
- copyLocalBtn.onmouseleave = function () { copyLocalBtn.style.background = 'rgba(255,255,255,0.07)'; };
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/copy', {
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['文件夹名'], workspace: getWorkspaceId() }),
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('✓ 已复制到 ' + res.folder_rel_path, '#10b981');
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 || '复制失败'), '#ff4444');
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
- deployBtn.onclick = function () {
2347
+ function doDeployWith(btn, agent) {
2338
2348
  if (!currentTpl) return;
2339
- setBtnLoading(deployBtn, '创建中...');
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({ folder: currentTpl['文件夹名'] }),
2356
+ body: JSON.stringify(body),
2345
2357
  })
2346
2358
  .then(function (r) { return r.json(); })
2347
2359
  .then(function (res) {
2348
- resetBtn(deployBtn);
2360
+ resetBtn(btn);
2349
2361
  if (res.ok) {
2350
- var deploy = res.deploy || {};
2351
- setFooterStatus('✓ 马上创建完毕...', '#10b981');
2352
- if (deploy.agent && deploy.session) {
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(deployBtn);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.1.64",
3
+ "version": "1.1.65",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -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
- run_playgrounds_flow.py <template_path> (prepare + deploy + --with-tui)"""
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
- script = os.path.join(self._skill_scripts_root(), 'run_playgrounds_flow.py')
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-22T15:32:50Z",
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-22T11:09:12Z"
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/__demo__.html",
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-22T11:09:12Z"
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-22T11:09:12Z"
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/__demo__.html",
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-22T11:09:12Z"
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-22T11:09:12Z"
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/__demo__.html",
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-22T11:09:12Z"
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-22T11:09:12Z"
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/__demo__.html
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/__demo__.html
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/__demo__.html
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
- # source_dir 可能是版本子目录(如 v1/),拆出业务名和版本号
148
- if re.match(r'^v\d+$', source_dir.name):
149
- folder_name = source_dir.parent.name
150
- version_str = source_dir.name
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():