@aiyiran/myclaw 1.1.20 → 1.1.22

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.
@@ -5,7 +5,23 @@
5
5
  "Bash(python3 -c \"import json,sys; d=json.load\\(sys.stdin\\); agents=d.get\\('agents',{}\\).get\\('list',[]\\); [print\\(a.get\\('id'\\), '|', a.get\\('workspace',''\\)\\) for a in agents if a]\")",
6
6
  "Bash(node *)",
7
7
  "Bash(curl -s \"https://cdn.bootcdn.net/ajax/libs/qrcodejs/1.0.0/qrcode.min.js\" -o /Users/yiran/.openclaw/workspace-yiranclaw/myclaw/assets/qrcode.min.js)",
8
- "Bash(python3 *)"
8
+ "Bash(python3 *)",
9
+ "Bash(crontab -l)",
10
+ "Bash(launchctl list *)",
11
+ "Bash(claude config *)",
12
+ "Bash(defaults read *)",
13
+ "Bash(chmod +x *)",
14
+ "Bash(/usr/local/bin/claude config *)",
15
+ "Bash(claude --version)",
16
+ "Bash(mc --version)",
17
+ "Bash(mc server *)",
18
+ "Bash(kill %1)",
19
+ "Bash(wait)",
20
+ "Bash(CLAW_NAME=yiran timeout 6 python3 server/sync_workspace.py)",
21
+ "Bash(curl -s http://127.0.0.1:18800/api/artifacts?workspace=workspace)",
22
+ "Bash(curl *)",
23
+ "Bash(find /Users/yiran/.openclaw/workspace/ -not -path '*/.git/*' -not -path '*/.myclaw/*' -not -path '*/node_modules/*' -not -path '*/__pycache__/*' -type f -exec du -ch {} +)",
24
+ "Bash(find *)"
9
25
  ]
10
26
  }
11
27
  }
@@ -95,15 +95,19 @@
95
95
  }
96
96
 
97
97
  // ═══ 构建预览 URL ═══
98
- // 统一走 CDN,clawName 实时派生,wsPrefix 实时从当前 URL 计算
98
+ // 统一走 CDN,clawName 只从两个可信来源取:
99
+ // 远程 → hostname 派生(detectEnvironment)
100
+ // 本地 → resolveClawName() 缓存的 lastKnownClawName
99
101
  function buildPreviewUrl(data, assetPath) {
100
102
  var env = detectEnvironment();
101
- var clawName = env.remote ? env.clawName : lastKnownClawName;
102
103
  var wsPrefix = getWorkspaceId();
103
- if (!clawName || !wsPrefix) {
104
+ var clawName = env.remote ? env.clawName : lastKnownClawName;
105
+
106
+ if (!clawName) {
104
107
  console.error('[myclaw-artifacts] ❌ clawName 未就绪,无法构建预览链接');
105
108
  return null;
106
109
  }
110
+
107
111
  return 'https://cdn.yiranlaoshi.com/' + clawName + '/' + wsPrefix + '/' + assetPath;
108
112
  }
109
113
 
@@ -193,6 +197,42 @@
193
197
  var headerRight = document.createElement('span');
194
198
  headerRight.style.cssText = 'display:flex;align-items:center;gap:10px;';
195
199
 
200
+ // 刷新按钮(全量同步)
201
+ var resyncBtn = document.createElement('span');
202
+ resyncBtn.textContent = '\uD83D\uDD04';
203
+ resyncBtn.title = '\u5237\u65B0\u540C\u6B65';
204
+ resyncBtn.style.cssText = 'cursor:pointer;padding:2px 6px;border-radius:3px;font-size:13px;transition:all 0.15s;';
205
+ resyncBtn.onmouseenter = function () { resyncBtn.style.background = 'rgba(255,255,255,0.1)'; };
206
+ resyncBtn.onmouseleave = function () { if (!resyncBtn._syncing) resyncBtn.style.background = 'none'; };
207
+ resyncBtn.onclick = function () {
208
+ if (resyncBtn._syncing) return;
209
+ resyncBtn._syncing = true;
210
+ resyncBtn.style.animation = 'myclaw-spin 1s linear infinite';
211
+ resyncBtn.style.background = 'rgba(255,255,255,0.1)';
212
+
213
+ var wsPrefix = getWorkspaceId();
214
+ fetch(MYCLAW_API_BASE + '/api/resync?workspace=' + encodeURIComponent(wsPrefix), { method: 'POST' })
215
+ .then(function (res) { return res.json(); })
216
+ .then(function (data) {
217
+ if (data.ok) {
218
+ console.log('[myclaw-artifacts] \u2705 \u540C\u6B65\u5B8C\u6210');
219
+ var contentEl = document.querySelector('#myclaw-artifacts-content');
220
+ if (contentEl) fetchArtifacts(contentEl);
221
+ } else {
222
+ console.error('[myclaw-artifacts] \u274C \u540C\u6B65\u5931\u8D25:', data.error);
223
+ }
224
+ })
225
+ .catch(function (err) {
226
+ console.error('[myclaw-artifacts] \u274C \u540C\u6B65\u8BF7\u6C42\u5931\u8D25:', err.message);
227
+ })
228
+ .then(function () {
229
+ resyncBtn._syncing = false;
230
+ resyncBtn.style.animation = '';
231
+ resyncBtn.style.background = 'none';
232
+ });
233
+ };
234
+ headerRight.appendChild(resyncBtn);
235
+
196
236
  // 展示间跳转(发布按钮左边)
197
237
  var showcaseLink = document.createElement('a');
198
238
  var _scEnv = detectEnvironment();
@@ -289,49 +329,40 @@
289
329
  }
290
330
 
291
331
  function fetchArtifactsFromLocalAPI(wsPrefix) {
292
- return fetch(MYCLAW_API_BASE + '/api/artifacts?workspace=' + encodeURIComponent(wsPrefix))
332
+ var url = MYCLAW_API_BASE + '/api/artifacts?workspace=' + encodeURIComponent(wsPrefix);
333
+ console.log('[myclaw-artifacts] [1/3] 尝试 LocalAPI:', url);
334
+ return fetch(url)
293
335
  .then(function (res) {
294
336
  if (!res.ok) throw new Error('HTTP ' + res.status);
295
337
  return res.json();
338
+ })
339
+ .then(function (data) {
340
+ console.log('[myclaw-artifacts] ✅ LocalAPI 成功,assets:', (data.assets || []).length);
341
+ return data;
296
342
  });
297
343
  }
298
344
 
299
- function fetchArtifactsFromCDN(wsPrefix, clawName) {
300
- if (!clawName) return Promise.reject(new Error('no claw name'));
301
- var url = 'https://cdn.yiranlaoshi.com/' + clawName + '/' + wsPrefix + '/.myclaw/__MY_ARTIFACTS__.json?t=' + Date.now();
302
- return fetch(url).then(function (res) {
303
- if (!res.ok) throw new Error('HTTP ' + res.status);
304
- return res.text();
305
- }).then(function (text) {
306
- if (text.trim().indexOf('<') === 0) throw new Error('Not JSON');
307
- return JSON.parse(text);
308
- });
309
- }
310
-
311
345
  function fetchArtifactsFromServerAPI(wsPrefix) {
312
346
  var url = window.location.origin + '/cmd/api/preview?path=' + encodeURIComponent(wsPrefix + '/.myclaw/__MY_ARTIFACTS__.json');
347
+ console.log('[myclaw-artifacts] [2/2] 尝试 ServerAPI:', url);
313
348
  return fetch(url).then(function (res) {
314
349
  if (!res.ok) throw new Error('HTTP ' + res.status);
315
350
  return res.json();
351
+ }).then(function (data) {
352
+ console.log('[myclaw-artifacts] ✅ ServerAPI 成功');
353
+ return data;
316
354
  });
317
355
  }
318
356
 
319
357
  function fetchArtifacts(contentEl) {
320
- var env = detectEnvironment();
321
358
  var wsPrefix = getWorkspaceId();
322
- var fetcher;
323
359
 
324
- if (env.remote) {
325
- // 远程服务器 → 走 /cmd/api
326
- fetcher = fetchArtifactsFromServerAPI(wsPrefix);
327
- } else {
328
- // 本地环境 → 实时拿 clawName → 走 CDN
329
- fetcher = resolveClawName().then(function (clawName) {
330
- return fetchArtifactsFromCDN(wsPrefix, clawName);
331
- });
332
- }
333
-
334
- fetcher
360
+ // LocalAPI → ServerAPI(两层降级,不走 CDN)
361
+ fetchArtifactsFromLocalAPI(wsPrefix)
362
+ .catch(function (err) {
363
+ console.warn('[myclaw-artifacts] LocalAPI 失败:', err.message, '→ 降级 ServerAPI');
364
+ return fetchArtifactsFromServerAPI(wsPrefix);
365
+ })
335
366
  .then(function (data) {
336
367
  cachedData = data;
337
368
  if (!contentEl) return;
@@ -341,7 +372,8 @@
341
372
  }
342
373
  renderArtifactsList(contentEl, data);
343
374
  })
344
- .catch(function () {
375
+ .catch(function (err) {
376
+ console.error('[myclaw-artifacts] ❌ 全部失败:', err.message);
345
377
  if (contentEl) {
346
378
  contentEl.innerHTML = '<div style="text-align:center;padding:32px;color:#888;">暂无作品</div>';
347
379
  }
@@ -511,6 +543,22 @@
511
543
  if (document.querySelector('#myclaw-artifacts-preview')) return;
512
544
 
513
545
  var previewUrl = buildPreviewUrl(data, asset.path);
546
+ if (!previewUrl) {
547
+ console.log('[myclaw-artifacts] ↻ clawName 未就绪,尝试重新获取一次...');
548
+ resolveClawName()
549
+ .then(function () {
550
+ var retriedPreviewUrl = buildPreviewUrl(data, asset.path);
551
+ if (!retriedPreviewUrl) {
552
+ console.error('[myclaw-artifacts] ❌ clawName 重试获取后仍未就绪,无法打开预览');
553
+ return;
554
+ }
555
+ openPreviewModal(data, asset);
556
+ })
557
+ .catch(function (err) {
558
+ console.error('[myclaw-artifacts] ❌ clawName 重试获取失败:', err && err.message ? err.message : err);
559
+ });
560
+ return;
561
+ }
514
562
 
515
563
  var overlay = document.createElement('div');
516
564
  overlay.id = 'myclaw-artifacts-preview';
@@ -1178,6 +1226,35 @@
1178
1226
  if (modal) modal.remove();
1179
1227
  }
1180
1228
 
1229
+ // ═══ Fork 辅助函数 ═══
1230
+ function onForkError(err, btn, input) {
1231
+ console.error('[myclaw-fork]', err);
1232
+ btn.disabled = false;
1233
+ input.disabled = false;
1234
+ btn.textContent = '\u274C 失败,点击重试';
1235
+ btn.style.background = '#7a2a2a';
1236
+ btn.style.cursor = 'pointer';
1237
+ setTimeout(function () {
1238
+ btn.textContent = '\u786E\u8BA4 Fork';
1239
+ btn.style.background = '#4a4a7a';
1240
+ }, 2500);
1241
+ }
1242
+
1243
+ function showForkDoneToast(workspace, files) {
1244
+ var toast = document.createElement('div');
1245
+ toast.style.cssText = [
1246
+ 'position:fixed', 'bottom:32px', 'right:24px', 'z-index:999999',
1247
+ 'background:#10b981', 'color:#fff', 'padding:14px 20px',
1248
+ 'border-radius:8px', 'font-family:monospace', 'font-size:13px',
1249
+ 'box-shadow:0 4px 16px rgba(0,0,0,0.3)',
1250
+ 'animation:myclaw-slide-in-right 0.25s ease',
1251
+ ].join(';');
1252
+ toast.innerHTML = '\u2705 Fork 完成!<br>'
1253
+ + '<span style="opacity:0.85;font-size:11px;">' + workspace + '(' + files + ' 个文件)</span>';
1254
+ document.body.appendChild(toast);
1255
+ setTimeout(function () { toast.remove(); }, 4000);
1256
+ }
1257
+
1181
1258
  // ═══ Fork 他人作品弹框 ═══
1182
1259
  function openForkModal() {
1183
1260
  if (document.querySelector('#myclaw-fork-modal')) return;
@@ -1305,12 +1382,12 @@
1305
1382
  errVisible = false;
1306
1383
 
1307
1384
  forkSubmitBtn.disabled = true;
1308
- forkSubmitBtn.textContent = '\u23F3 \u7B49\u5F85\u4E2D...';
1385
+ forkSubmitBtn.textContent = '\u23F3 Fork 中...';
1309
1386
  forkSubmitBtn.style.background = '#3a3a5a';
1310
1387
  forkSubmitBtn.style.cursor = 'default';
1311
1388
  urlInput.disabled = true;
1312
1389
 
1313
- fetch('https://claw.kekouen.cn/sync', {
1390
+ fetch(MYCLAW_API_BASE + '/api/fork', {
1314
1391
  method: 'POST',
1315
1392
  headers: { 'Content-Type': 'application/json' },
1316
1393
  body: JSON.stringify({ url: url }),
@@ -1319,22 +1396,35 @@
1319
1396
  if (!res.ok) throw new Error('HTTP ' + res.status);
1320
1397
  return res.json();
1321
1398
  })
1322
- .then(function () {
1323
- forkSubmitBtn.textContent = '\u2705 Fork \u6210\u529F\uFF01';
1324
- forkSubmitBtn.style.background = '#10b981';
1325
- setTimeout(function () { closeForkModal(); }, 2000);
1399
+ .then(function (data) {
1400
+ if (!data.job_id) throw new Error(data.error || 'no job_id');
1401
+ // 轮询 job 状态
1402
+ var jobId = data.job_id;
1403
+ var timer = setInterval(function () {
1404
+ fetch(MYCLAW_API_BASE + '/api/fork/status?job_id=' + encodeURIComponent(jobId))
1405
+ .then(function (r) { return r.json(); })
1406
+ .then(function (job) {
1407
+ if (job.status === 'running') return;
1408
+ clearInterval(timer);
1409
+ if (job.status === 'done') {
1410
+ forkSubmitBtn.textContent = '\u2705 Fork 成功!';
1411
+ forkSubmitBtn.style.background = '#10b981';
1412
+ setTimeout(function () {
1413
+ closeForkModal();
1414
+ showForkDoneToast(job.workspace, job.files);
1415
+ }, 800);
1416
+ } else {
1417
+ throw new Error(job.error || 'failed');
1418
+ }
1419
+ })
1420
+ .catch(function (err) {
1421
+ clearInterval(timer);
1422
+ onForkError(err, forkSubmitBtn, urlInput);
1423
+ });
1424
+ }, 1500);
1326
1425
  })
1327
1426
  .catch(function (err) {
1328
- console.error('[myclaw-fork]', err);
1329
- forkSubmitBtn.disabled = false;
1330
- urlInput.disabled = false;
1331
- forkSubmitBtn.textContent = '\u274C \u8BF7\u6C42\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5';
1332
- forkSubmitBtn.style.background = '#7a2a2a';
1333
- forkSubmitBtn.style.cursor = 'pointer';
1334
- setTimeout(function () {
1335
- forkSubmitBtn.textContent = '\u786E\u8BA4 Fork';
1336
- forkSubmitBtn.style.background = '#4a4a7a';
1337
- }, 2500);
1427
+ onForkError(err, forkSubmitBtn, urlInput);
1338
1428
  });
1339
1429
  };
1340
1430
 
@@ -1369,6 +1459,10 @@
1369
1459
  ' from { transform: scale(0.7); opacity: 0; }',
1370
1460
  ' to { transform: scale(1); opacity: 1; }',
1371
1461
  '}',
1462
+ '@keyframes myclaw-spin {',
1463
+ ' from { transform: rotate(0deg); }',
1464
+ ' to { transform: rotate(360deg); }',
1465
+ '}',
1372
1466
  ].join('\n');
1373
1467
  document.head.appendChild(style);
1374
1468
  }
@@ -1377,6 +1471,8 @@
1377
1471
  function init() {
1378
1472
  injectStyles();
1379
1473
  createArtifactsButton();
1474
+ // 本地环境:启动时获取一次 clawName 并缓存,轮询期间复用
1475
+ resolveClawName();
1380
1476
  startPolling();
1381
1477
  var env = detectEnvironment();
1382
1478
  console.log('[myclaw-artifacts] ✅ 初始化完成 (' + (env.remote ? '远程: ' + env.clawName : '本地') + ')');
package/index.js CHANGED
@@ -301,9 +301,12 @@ function runNew() {
301
301
  const NC = colors.nc;
302
302
 
303
303
  // 支持两种调用:
304
- // mc new → 交互输入名字
305
- // mc new <name> → 直接使用传入的名字
304
+ // mc new → 交互输入名字
305
+ // mc new <name> → 直接使用传入的名字
306
+ // mc new <name> --first-message "..." → 自定义首条消息
306
307
  const rawName = args[1];
308
+ const firstMessageIdx = args.indexOf('--first-message');
309
+ const customFirstMessage = firstMessageIdx !== -1 ? args[firstMessageIdx + 1] : null;
307
310
 
308
311
  if (rawName) {
309
312
  doCreate(rawName);
@@ -371,7 +374,8 @@ function runNew() {
371
374
 
372
375
  var result;
373
376
  try {
374
- result = require('./create_agent').createAgent(input);
377
+ const createOpts = customFirstMessage ? { firstMessage: customFirstMessage } : {};
378
+ result = require('./create_agent').createAgent(input, createOpts);
375
379
  } catch (err) {
376
380
  console.log(R + '创建失败: ' + err.message + NC);
377
381
  process.exit(1);
@@ -1763,6 +1767,7 @@ function runSync(workspaceName) {
1763
1767
  const targetPyPath = path.join(targetDir, 'sync_workspace.py');
1764
1768
  const targetConfigPath = path.join(targetDir, 'config.json');
1765
1769
  const sourcePyPath = path.join(__dirname, 'server', 'sync_workspace.py');
1770
+ const sourceSchemaPath = path.join(__dirname, 'server', 'artifacts_schema.py');
1766
1771
 
1767
1772
  // 确保目标目录存在
1768
1773
  if (!fs.existsSync(targetDir)) {
@@ -1771,6 +1776,7 @@ function runSync(workspaceName) {
1771
1776
 
1772
1777
  // 同步 py 文件
1773
1778
  fs.copyFileSync(sourcePyPath, targetPyPath);
1779
+ fs.copyFileSync(sourceSchemaPath, path.join(targetDir, 'artifacts_schema.py'));
1774
1780
 
1775
1781
  // 读取配置(如果不存在则创建)
1776
1782
  let config;
@@ -1812,8 +1818,11 @@ async function runServer(name) {
1812
1818
  // 用户目录下的服务目录
1813
1819
  const targetDir = path.join(os.homedir(), '.openclaw', 'myclaw', 'server');
1814
1820
  const targetPyPath = path.join(targetDir, 'sync_workspace.py');
1821
+ const targetGuardPath = path.join(targetDir, 'myclaw-guard.sh');
1815
1822
  const targetConfigPath = path.join(targetDir, 'config.json');
1816
1823
  const sourcePyPath = path.join(__dirname, 'server', 'sync_workspace.py');
1824
+ const sourceSchemaPath = path.join(__dirname, 'server', 'artifacts_schema.py');
1825
+ const sourceGuardPath = path.join(__dirname, 'server', 'myclaw-guard.sh');
1817
1826
  const pidFile = path.join(os.homedir(), '.openclaw', '.myclaw-sync.pid');
1818
1827
 
1819
1828
  // 0. 杀死旧的 sync_workspace 进程(通过 PID 文件)
@@ -1828,15 +1837,36 @@ async function runServer(name) {
1828
1837
  try { fs.unlinkSync(pidFile); } catch (e) { }
1829
1838
  }
1830
1839
 
1840
+ // 0.5 清理竞争者:/root/sync_workspace.py
1841
+ const rivalPath = '/root/sync_workspace.py';
1842
+ try {
1843
+ // 先杀掉所有运行该脚本的进程
1844
+ try {
1845
+ execSyncLocal('pkill -f "' + rivalPath + '" 2>/dev/null || true', { shell: true, stdio: 'pipe' });
1846
+ console.log('[Server] 已终止竞争进程: ' + rivalPath);
1847
+ } catch (e) { /* 无进程时忽略 */ }
1848
+ // 再删除文件
1849
+ if (fs.existsSync(rivalPath)) {
1850
+ fs.unlinkSync(rivalPath);
1851
+ console.log('[Server] 已删除竞争脚本: ' + rivalPath);
1852
+ }
1853
+ } catch (e) {
1854
+ console.log('[Server] 清理竞争者失败(可忽略): ' + e.message);
1855
+ }
1856
+
1831
1857
  // 1. 创建目标目录
1832
1858
  if (!fs.existsSync(targetDir)) {
1833
1859
  fs.mkdirSync(targetDir, { recursive: true });
1834
1860
  console.log('[Server] 创建目录: ' + targetDir);
1835
1861
  }
1836
1862
 
1837
- // 2. 覆盖 py 文件(确保最新)
1863
+ // 2. 覆盖 py 文件 & guard 脚本(确保最新)
1838
1864
  fs.copyFileSync(sourcePyPath, targetPyPath);
1865
+ fs.copyFileSync(sourceSchemaPath, path.join(targetDir, 'artifacts_schema.py'));
1839
1866
  console.log('[Server] 同步脚本: ' + targetPyPath);
1867
+ fs.copyFileSync(sourceGuardPath, targetGuardPath);
1868
+ fs.chmodSync(targetGuardPath, 0o755);
1869
+ console.log('[Server] 守卫脚本: ' + targetGuardPath);
1840
1870
 
1841
1871
  // 3. 确保配置文件存在(如果已有就不动)
1842
1872
  let config;
@@ -1950,6 +1980,7 @@ function showHelp() {
1950
1980
  console.log(' inject-workspaceAndSoul 替换默认 workspace 的 SOUL.md 提示词');
1951
1981
  console.log(' inject-tooldeny deny image_generate + music_generate 内置工具');
1952
1982
  console.log(' inject-controlui 注入 controlUi 配置 (allowInsecureAuth + allowedOrigins)');
1983
+ console.log(' all 完整升级: up + patch + server + restart');
1953
1984
  console.log(' restart 重启 OpenClaw Gateway');
1954
1985
  console.log(' nuke ⚠️ 核弹重置 (需加 --dangerous): 清除数据 + onboard + up + patch + restart');
1955
1986
  console.log(' help 显示帮助信息');
@@ -2007,6 +2038,24 @@ if (!command) {
2007
2038
  if (detectPlatform() === 'windows') {
2008
2039
  runBat();
2009
2040
  }
2041
+ } else if (command === 'all') {
2042
+ // 完整升级:升级包 → patch → server → restart
2043
+ console.log('[mc all] 开始完整升级流程...');
2044
+ console.log('');
2045
+ console.log('[1/4] mc up — 升级包 + patch');
2046
+ runUpdate();
2047
+ runPatch();
2048
+ if (detectPlatform() === 'windows') {
2049
+ runBat();
2050
+ }
2051
+ console.log('');
2052
+ console.log('[2/4] mc restart — 重启 Gateway');
2053
+ runRestart();
2054
+ console.log('');
2055
+ console.log('[3/4] mc server — 更新 Python 守护进程');
2056
+ runServer(args[1]);
2057
+ console.log('');
2058
+ console.log('[4/4] 完成 ✅');
2010
2059
  } else if (command === 'start') {
2011
2060
  runStart();
2012
2061
  } else if (command === 'open') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.1.20",
3
+ "version": "1.1.22",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,32 @@
1
+ """
2
+ __MY_ARTIFACTS__.json 的统一初始结构定义
3
+
4
+ 所有需要创建或读取 artifacts 配置的模块都应使用此处的函数,
5
+ 确保字段一致。
6
+ """
7
+
8
+ from datetime import datetime, timezone, timedelta
9
+
10
+ ARTIFACTS_FILENAME = "__MY_ARTIFACTS__.json"
11
+
12
+
13
+ def now_iso():
14
+ """生成 +08:00 时间格式"""
15
+ tz = timezone(timedelta(hours=8))
16
+ return datetime.now(tz).isoformat()
17
+
18
+
19
+ def create_empty_artifacts(workspace_id, base_url=""):
20
+ """创建空的 artifacts 初始结构(以 registry.py 为准)"""
21
+ now = now_iso()
22
+ return {
23
+ "workspace_id": workspace_id,
24
+ "title": "我的资源库",
25
+ "release_version": 1,
26
+ "created_at": now,
27
+ "updated_at": now,
28
+ "version": 1,
29
+ "base_url": base_url,
30
+ "preview_path": "index.html",
31
+ "assets": []
32
+ }