@aiyiran/myclaw 1.1.60 → 1.1.62

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.
@@ -635,27 +635,38 @@
635
635
  bottomRow.appendChild(time);
636
636
  leftCol.appendChild(bottomRow);
637
637
 
638
- // 文件显示(上下两行:路径+文件名)
638
+ // 文件显示(上下两行:上层路径 + 直接父文件夹/文件名)
639
639
  var fname = document.createElement('div');
640
640
  fname.style.cssText = 'flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:2px;overflow:hidden;';
641
641
 
642
642
  var fullPath = asset.path || asset.name || '未命名';
643
- var lastSlash = fullPath.lastIndexOf('/');
644
- var dirPart = lastSlash > 0 ? fullPath.substring(0, lastSlash + 1) : '';
645
- var namePart = lastSlash > 0 ? fullPath.substring(lastSlash + 1) : fullPath;
643
+ var parts = fullPath.split('/').filter(function(p) { return p !== ''; });
644
+ // 上行:倒数第二层以上的路径(若只有一层目录则无上行)
645
+ // 下行:直接父文件夹/文件名
646
+ var topPart = '';
647
+ var bottomPart = fullPath;
648
+ if (parts.length >= 3) {
649
+ // 有多层:上行 = 除最后两段外的路径,下行 = 最后两段
650
+ topPart = parts.slice(0, parts.length - 2).join('/') + '/';
651
+ bottomPart = parts.slice(parts.length - 2).join('/');
652
+ } else if (parts.length === 2) {
653
+ // 只有一层目录:只显示下行(dir/file)
654
+ topPart = '';
655
+ bottomPart = parts.join('/');
656
+ }
646
657
 
647
- // 上行:目录路径(灰色极小字,单行,不换行)
648
- if (dirPart) {
658
+ // 上行:更上层路径(灰色极小字)
659
+ if (topPart) {
649
660
  var dirSpan = document.createElement('span');
650
- dirSpan.textContent = dirPart;
661
+ dirSpan.textContent = topPart;
651
662
  dirSpan.title = fullPath;
652
663
  dirSpan.style.cssText = 'color:#666;font-size:9px;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
653
664
  fname.appendChild(dirSpan);
654
665
  }
655
666
 
656
- // 下行:文件名(大字体,单行,不换行)
667
+ // 下行:直接父文件夹/文件名(大字体)
657
668
  var nameSpan = document.createElement('span');
658
- nameSpan.textContent = namePart;
669
+ nameSpan.textContent = bottomPart;
659
670
  nameSpan.title = fullPath;
660
671
  nameSpan.style.cssText = 'font-size:13px;font-weight:500;line-height:1.5;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
661
672
  fname.appendChild(nameSpan);
@@ -2340,7 +2351,7 @@
2340
2351
  if (deploy.agent && deploy.session) {
2341
2352
  var sessionParam = 'agent:' + deploy.agent + ':' + deploy.session;
2342
2353
  setTimeout(function () {
2343
- window.open('/chat?session=' + encodeURIComponent(sessionParam), '_blank');
2354
+ window.location.href = '/chat?session=' + encodeURIComponent(sessionParam);
2344
2355
  }, 10000);
2345
2356
  }
2346
2357
  } else if (res.code === 'not_ready') {
package/index.js CHANGED
@@ -1837,6 +1837,70 @@ async function ensureConfig(customName) {
1837
1837
  return config;
1838
1838
  }
1839
1839
 
1840
+ function runSyncTemplates() {
1841
+ const http = require('http');
1842
+ const apiPort = process.platform === 'linux' ? '8080' : '18800';
1843
+
1844
+ function apiGet(path, cb) {
1845
+ http.get('http://127.0.0.1:' + apiPort + path, (res) => {
1846
+ let data = '';
1847
+ res.on('data', chunk => data += chunk);
1848
+ res.on('end', () => {
1849
+ try { cb(null, JSON.parse(data)); } catch(e) { cb(e); }
1850
+ });
1851
+ }).on('error', cb);
1852
+ }
1853
+
1854
+ console.log(colors.blue + '[sync-templates]' + colors.nc + ' 正在检查模板更新...');
1855
+
1856
+ apiGet('/api/sync-templates', (err, res) => {
1857
+ if (err) {
1858
+ console.error(colors.red + '✗ 无法连接到本地服务(是否已运行 mc sync?): ' + err.message + colors.nc);
1859
+ process.exit(1);
1860
+ }
1861
+ if (res.error) {
1862
+ console.error(colors.red + '✗ ' + res.error + colors.nc);
1863
+ process.exit(1);
1864
+ }
1865
+ if (!res.changed) {
1866
+ console.log(colors.green + '✓ 模板已是最新,无需更新' + colors.nc);
1867
+ return;
1868
+ }
1869
+ if (!res.syncing) {
1870
+ console.log(colors.green + '✓ 更新完成' + colors.nc);
1871
+ return;
1872
+ }
1873
+
1874
+ // 后台正在同步,轮询进度
1875
+ console.log(colors.yellow + '⟳ 开始下载模板文件...' + colors.nc);
1876
+ const deadline = Date.now() + 10 * 60 * 1000;
1877
+
1878
+ function poll() {
1879
+ if (Date.now() > deadline) {
1880
+ console.error(colors.red + '✗ 同步超时(10分钟)' + colors.nc);
1881
+ process.exit(1);
1882
+ }
1883
+ setTimeout(() => {
1884
+ apiGet('/api/sync-status', (err, s) => {
1885
+ if (err) { poll(); return; }
1886
+ if (s.running) {
1887
+ process.stdout.write('\r ' + colors.yellow + '⟳ 进度: ' + s.completed.length + '/' + s.total + colors.nc + ' ');
1888
+ poll();
1889
+ } else {
1890
+ process.stdout.write('\n');
1891
+ const r = s.result || {};
1892
+ const parts = [];
1893
+ if (r.added && r.added.length) parts.push('新增 ' + r.added.length + ' 个');
1894
+ if (r.updated && r.updated.length) parts.push('更新 ' + r.updated.length + ' 个');
1895
+ console.log(colors.green + '✓ 同步完成' + (parts.length ? ':' + parts.join(',') : '') + colors.nc);
1896
+ }
1897
+ });
1898
+ }, 2000);
1899
+ }
1900
+ poll();
1901
+ });
1902
+ }
1903
+
1840
1904
  function runSync(workspaceName) {
1841
1905
  const { spawn } = require('child_process');
1842
1906
  const fs = require('fs');
@@ -2332,6 +2396,7 @@ function showHelp() {
2332
2396
  console.log(' open 打开浏览器控制台(自动带 token)');
2333
2397
  console.log(' tui 唤起新对话上下文 (用法: mc tui <agentname>)');
2334
2398
  console.log(' delete 批量删除 Agent(交互式勾选 + 二次确认)');
2399
+ console.log(' sync-templates 更新本地课程模板(从 CDN 增量下载)');
2335
2400
  console.log(' list-agents 列出所有 Agent(--json 输出机器可读)');
2336
2401
  console.log(' trash-paths <p1> <p2> 将路径移入回收站(跨平台)');
2337
2402
  console.log(' fix 兜底修复(自动补装 WSL + Chrome,仅限 Windows)');
@@ -2537,6 +2602,8 @@ if (!command) {
2537
2602
  runServer(args[1]); // args[1] 是可选的 name
2538
2603
  } else if (command === 'sync') {
2539
2604
  runSync(args[1]); // args[1] 是可选的 workspace 名称
2605
+ } else if (command === 'sync-templates') {
2606
+ runSyncTemplates();
2540
2607
  } else if (command === 'init') {
2541
2608
  console.log('init 还没实现,后续添加钩子');
2542
2609
  } else if (command === 'main') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.1.60",
3
+ "version": "1.1.62",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -143,7 +143,18 @@ def choose_entry_file(target_dir: Path):
143
143
 
144
144
 
145
145
  def build_instance_name(source_dir: Path, remix_dir: Path):
146
- base = f"{datetime.now().strftime('%m%d')}_{source_dir.name}_{datetime.now().strftime('%H%M')}"
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
158
  candidate = base
148
159
  suffix = 2
149
160
  while (remix_dir / candidate).exists():