@aiyiran/myclaw 1.1.171 → 1.1.173

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.
@@ -1736,7 +1736,7 @@ btn.addEventListener("click", function () {
1736
1736
  }
1737
1737
 
1738
1738
  // 弹出 input 小弹框
1739
- function promptAndRun(title, placeholder, hint, cmdTemplate, btnColor, defaultValue) {
1739
+ function promptAndRun(title, placeholder, hint, cmdTemplate, btnColor, defaultValue, allowAnyName) {
1740
1740
  var mask = document.createElement("div");
1741
1741
  mask.style.cssText = [
1742
1742
  "position:fixed",
@@ -1801,14 +1801,18 @@ btn.addEventListener("click", function () {
1801
1801
  input.focus();
1802
1802
  return;
1803
1803
  }
1804
- if (!/^[a-zA-Z0-9\-]+$/.test(val)) {
1804
+ // allowAnyName=true\uFF08\u5982\u300C\u65B0\u4F19\u4F34\u300D\uFF09\uFF1A\u5141\u8BB8\u4E2D\u6587\uFF0C\u540E\u7AEF mc new \u4F1A\u81EA\u52A8\u751F\u6210 ascii id \u5E76\u628A\u4E2D\u6587\u5B58\u4E3A\u663E\u793A\u540D\u3002
1805
+ // \u5426\u5219\uFF08\u5982\u300C\u6DFB\u52A0\u5BF9\u8BDD\u300D\uFF09\uFF1A\u4ECD\u8981\u6C42 ascii\uFF0C\u56E0\u4E3A\u5B83\u9700\u8981\u7684\u662F\u771F\u5B9E agent id\u3002
1806
+ if (!allowAnyName && !/^[a-zA-Z0-9\-]+$/.test(val)) {
1805
1807
  input.style.borderColor = "#ff4444";
1806
1808
  input.focus();
1807
1809
  return;
1808
1810
  }
1809
1811
  submitBtn.disabled = true;
1810
1812
  submitBtn.textContent = "\u6267\u884C\u4E2D...";
1811
- var cmd = cmdTemplate.replace("{name}", val);
1813
+ // \u5141\u8BB8\u4EFB\u610F\u540D\u65F6\uFF0Cname \u53EF\u80FD\u542B\u4E2D\u6587/\u7A7A\u683C\uFF0C\u52A0\u53CC\u5F15\u53F7\u9632\u6B62 shell \u62C6\u8BCD
1814
+ var safeName = allowAnyName ? '"' + val.replace(/"/g, '') + '"' : val;
1815
+ var cmd = cmdTemplate.replace("{name}", safeName);
1812
1816
  runCommand(cmd);
1813
1817
  setTimeout(function () { mask.remove(); }, 1000);
1814
1818
  }
@@ -1827,7 +1831,7 @@ btn.addEventListener("click", function () {
1827
1831
 
1828
1832
  // ── 按钮列表 ──
1829
1833
  var btns = [
1830
- { label: "\uD83E\uDD1D \u65B0\u4F19\u4F34", desc: "\u521B\u5EFA\u4E00\u4E2A\u65B0\u7684 AI \u4F19\u4F34", hasInput: true, inputTitle: "\u65B0\u5EFA\u4F19\u4F34", placeholder: "\u8F93\u5165\u65B0\u4F19\u4F34\u540D\u79F0\uFF0C\u5982 my-cat", hint: "\u7ED9\u4F60\u7684\u65B0 AI \u4F19\u4F34\u8D77\u4E2A\u540D\u5B57\uFF08\u82F1\u6587\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF09\uFF0C\u70B9\u51FB\u540E\u4F1A\u81EA\u52A8\u521B\u5EFA", cmd: "mc new {name}", color: "#3b82f6" },
1834
+ { label: "\uD83E\uDD1D \u65B0\u4F19\u4F34", desc: "\u521B\u5EFA\u4E00\u4E2A\u65B0\u7684 AI \u4F19\u4F34", hasInput: true, inputTitle: "\u65B0\u5EFA\u4F19\u4F34", placeholder: "\u8F93\u5165\u65B0\u4F19\u4F34\u540D\u79F0\uFF0C\u5982 my-cat", hint: "\u7ED9\u4F60\u7684\u65B0 AI \u4F19\u4F34\u8D77\u4E2A\u540D\u5B57\uFF08\u4E2D\u6587 / \u82F1\u6587\u5747\u53EF\uFF09\uFF0C\u70B9\u51FB\u540E\u4F1A\u81EA\u52A8\u521B\u5EFA", cmd: "mc new {name}", color: "#3b82f6", allowAnyName: true },
1831
1835
  { label: "\uD83D\uDCAC \u6DFB\u52A0\u5BF9\u8BDD", desc: "\u6253\u5F00\u5DF2\u6709\u4F19\u4F34\u7684\u5BF9\u8BDD\u7A97\u53E3", hasInput: true, inputTitle: "\u6DFB\u52A0\u5BF9\u8BDD", placeholder: "\u8F93\u5165\u4F19\u4F34\u540D\u79F0\uFF0C\u5982 kakaxi", hint: "\u8F93\u5165\u4F60\u7684\u4F19\u4F34\u7684\u540D\u79F0\uFF08\u82F1\u6587\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF09\uFF0C\u70B9\u51FB\u540E\u4F1A\u6253\u5F00\u5BF9\u8BDD\u7A97\u53E3", cmd: "mc tui {name}", color: "#10b981", getDefault: getCurrentAgentName },
1832
1836
  { label: "\uD83D\uDD04 \u91CD\u542F\u670D\u52A1", desc: "\u91CD\u542F\u6587\u4EF6\u670D\u52A1", hasInput: false, cmd: "mc server", color: "#ef4444" },
1833
1837
  { label: "\uD83E\uDD9E \u91CD\u542F\u9F99\u867E", desc: "\u91CD\u542F OpenClaw \u6838\u5FC3\u670D\u52A1", hasInput: false, cmd: "mc all", color: "#f59e0b" },
@@ -1876,7 +1880,7 @@ btn.addEventListener("click", function () {
1876
1880
 
1877
1881
  row.onclick = function () {
1878
1882
  if (item.hasInput) {
1879
- promptAndRun(item.inputTitle, item.placeholder, item.hint, item.cmd, item.color, item.getDefault ? item.getDefault() : '');
1883
+ promptAndRun(item.inputTitle, item.placeholder, item.hint, item.cmd, item.color, item.getDefault ? item.getDefault() : '', item.allowAnyName);
1880
1884
  } else {
1881
1885
  runCommand(item.cmd);
1882
1886
  }
package/create_agent.js CHANGED
@@ -55,11 +55,17 @@ function fail(msg, code = 1) {
55
55
  * - 非法字符替换为连字符
56
56
  * - 去除多余连字符
57
57
  */
58
+ // 与 OpenClaw 本体的 normalizeAgentId 逐字对齐(dist 里的实现):
59
+ // 允许 a-z 0-9 _ - ;连续非法字符压成一个 - ;去首尾 - ;不再压缩内部重复 -
60
+ // 故意不像旧版那样压 `-{2,}`,以保证「标准化后 === 原输入」这个判据
61
+ // 与 OpenClaw 真实落库结果一致,从而准确判断输入是否「天生满足」。
58
62
  function normalizeAgentId(raw) {
59
- let value = raw.trim().toLowerCase();
60
- value = value.replace(/[^a-z0-9-]/g, '-');
61
- value = value.replace(/-{2,}/g, '-').replace(/^-|-$/g, '');
62
- return value;
63
+ return (raw == null ? '' : String(raw))
64
+ .trim()
65
+ .toLowerCase()
66
+ .replace(/[^a-z0-9_-]+/g, '-')
67
+ .replace(/^-+/g, '')
68
+ .replace(/-+$/g, '');
63
69
  }
64
70
 
65
71
  /**
@@ -72,11 +78,47 @@ function validateAgentId(agentId) {
72
78
  if (agentId.length > 64) {
73
79
  fail('Agent ID 太长,请保持在 64 个字符以内。');
74
80
  }
75
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(agentId)) {
76
- fail('Agent ID 只能包含小写字母、数字和连字符(不能以连字符开头或结尾)。');
81
+ // 放宽到与 OpenClaw 一致:允许下划线,首尾必须是字母/数字
82
+ if (!/^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$/.test(agentId)) {
83
+ fail('Agent ID 只能包含小写字母、数字、连字符和下划线(不能以连字符/下划线开头或结尾)。');
77
84
  }
78
85
  }
79
86
 
87
+ // ── 中文/非法输入时的兜底 key 生成 ──────────────────────────────
88
+ // 格式:MMDD_<三个随机英文单词>_MMSS (北京时间),如 0630_brave-lunar-otter_1207
89
+ // 全小写 + 连字符 + 下划线,天然满足 validateAgentId;带日期/分秒 + 随机词,几乎不可能撞名。
90
+ const FALLBACK_WORDS = [
91
+ 'brave', 'lunar', 'otter', 'maple', 'pixel', 'comet', 'river', 'ember',
92
+ 'tiger', 'cloud', 'quartz', 'mango', 'panda', 'fox', 'willow', 'cedar',
93
+ 'orbit', 'frost', 'sunny', 'cocoa', 'pearl', 'raven', 'lotus', 'noble',
94
+ 'jade', 'amber', 'breeze', 'crisp', 'dawn', 'echo', 'flint', 'glow',
95
+ ];
96
+
97
+ function pad2(n) { return String(n).padStart(2, '0'); }
98
+
99
+ function generateFallbackKey() {
100
+ // 北京时间(UTC+8),与 buildBirthMessage 保持一致
101
+ const bj = new Date(Date.now() + 8 * 60 * 60 * 1000);
102
+ const MMDD = pad2(bj.getUTCMonth() + 1) + pad2(bj.getUTCDate());
103
+ const MMSS = pad2(bj.getUTCMinutes()) + pad2(bj.getUTCSeconds());
104
+ const pick = () => FALLBACK_WORDS[Math.floor(Math.random() * FALLBACK_WORDS.length)];
105
+ const words = pick() + '-' + pick() + '-' + pick();
106
+ return MMDD + '_' + words + '_' + MMSS;
107
+ }
108
+
109
+ // ── 命名决策:判断输入是否「天生满足」key 格式 ───────────────────
110
+ // 天生满足(标准化后与原输入完全一致)→ 直接用作 id,不另设显示名。
111
+ // 不满足(含中文/空格/大写等)→ 生成兜底 key,原始输入作为 identity.name(中文显示)。
112
+ function resolveAgentNaming(rawName) {
113
+ const trimmed = (rawName == null ? '' : String(rawName)).trim();
114
+ const normalized = normalizeAgentId(trimmed);
115
+ const isNative = !!normalized && normalized === trimmed;
116
+ if (isNative) {
117
+ return { agentId: normalized, displayName: null, isNative: true };
118
+ }
119
+ return { agentId: generateFallbackKey(), displayName: trimmed, isNative: false };
120
+ }
121
+
80
122
  /**
81
123
  * 生成北京时间出生消息
82
124
  */
@@ -138,8 +180,13 @@ function defaultWorkspaceFiles(agentId) {
138
180
  */
139
181
  function createAgent(rawName, opts) {
140
182
  opts = opts || {};
141
- // 标准化 & 验证名称
142
- const agentId = normalizeAgentId(rawName);
183
+ // 命名决策:优先用调用方(index.js)已解析好的结果,避免重复生成导致 id 漂移;
184
+ // 否则在此内部解析。
185
+ const naming = opts.agentId
186
+ ? { agentId: opts.agentId, displayName: opts.displayName != null ? opts.displayName : null }
187
+ : resolveAgentNaming(rawName);
188
+ const agentId = naming.agentId;
189
+ const displayName = naming.displayName; // 非 null 时写入 identity.name(中文显示名)
143
190
  validateAgentId(agentId);
144
191
 
145
192
  // 确定 OpenClaw 配置目录
@@ -208,12 +255,18 @@ function createAgent(rawName, opts) {
208
255
  fs.copyFileSync(configPath, backupPath);
209
256
 
210
257
  // 添加 Agent 条目
211
- configData.agents.list.push({
258
+ // 顶层 name 保持 ascii id(OpenClaw 不拿它当 UI 显示名);
259
+ // 中文/非法输入时,把原始输入写进 identity.name —— 这才是 UI 真正显示的名字。
260
+ const entry = {
212
261
  id: agentId,
213
262
  name: agentId,
214
263
  workspace: workspaceDir,
215
264
  agentDir: agentDir,
216
- });
265
+ };
266
+ if (displayName) {
267
+ entry.identity = { name: displayName };
268
+ }
269
+ configData.agents.list.push(entry);
217
270
 
218
271
  // 写入配置
219
272
  try {
@@ -242,6 +295,7 @@ function createAgent(rawName, opts) {
242
295
  return {
243
296
  ok: true,
244
297
  agentId: agentId,
298
+ displayName: displayName,
245
299
  sessionKey: 'agent:' + agentId + ':main',
246
300
  workspace: normalizePath(workspaceDir),
247
301
  agentDir: normalizePath(agentDir),
@@ -251,4 +305,4 @@ function createAgent(rawName, opts) {
251
305
  };
252
306
  }
253
307
 
254
- module.exports = { createAgent, normalizeAgentId, validateAgentId };
308
+ module.exports = { createAgent, normalizeAgentId, validateAgentId, resolveAgentNaming, generateFallbackKey };
package/index.js CHANGED
@@ -335,7 +335,7 @@ function runNew() {
335
335
 
336
336
  function askName() {
337
337
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
338
- rl.question(B + '请输入 Agent 名字(英文、数字、连字符): ' + NC, function (answer) {
338
+ rl.question(B + '请输入 Agent 名字(中文 / 英文均可): ' + NC, function (answer) {
339
339
  rl.close();
340
340
  const name = (answer || '').trim();
341
341
  if (!name) {
@@ -343,30 +343,27 @@ function runNew() {
343
343
  askName();
344
344
  return;
345
345
  }
346
- // 预检:必须包含英文字母或数字
347
- if (!/[a-zA-Z0-9]/.test(name)) {
348
- console.log(R + '名字必须包含英文或数字,请重新输入(如 my-agent)。' + NC);
349
- askName();
350
- return;
351
- }
352
346
  doCreate(name);
353
347
  });
354
348
  }
355
349
 
356
350
  function doCreate(input) {
357
- const { normalizeAgentId } = require('./create_agent');
351
+ const { resolveAgentNaming } = require('./create_agent');
352
+
353
+ // 命名决策:天生满足 key 格式 → 直接用作 id;否则生成 ascii key + 中文显示名
354
+ var naming = resolveAgentNaming(input);
355
+ var agentId = naming.agentId;
356
+ var displayName = naming.displayName;
358
357
 
359
- // 校验 1:中文名 / 非法字符
360
- var agentId = normalizeAgentId(input);
361
- if (!agentId || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(agentId)) {
362
- console.log(R + '名字必须是英文、数字或连字符(如 my-agent),请重新输入。' + NC);
358
+ if (!agentId) {
359
+ console.log(R + '名字无效,请重新输入。' + NC);
363
360
  console.log('');
364
361
  if (rawName) { process.exit(1); return; }
365
362
  askName();
366
363
  return;
367
364
  }
368
365
 
369
- // 校验 2:重名检查(不依赖 createAgent,避免它内部 process.exit)
366
+ // 重名检查(不依赖 createAgent,避免它内部 process.exit)
370
367
  const fs = require('fs');
371
368
  const path = require('path');
372
369
  const os = require('os');
@@ -375,23 +372,36 @@ function runNew() {
375
372
  var configData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
376
373
  var list = (configData.agents && configData.agents.list) || [];
377
374
  if (list.some(function (a) { return a && a.id === agentId; })) {
378
- console.log(R + 'Agent "' + agentId + '" 已存在,请换一个名字。' + NC);
379
- console.log('');
380
- if (rawName) { process.exit(1); return; }
381
- askName();
382
- return;
375
+ if (naming.isNative) {
376
+ // 用户直接输入的 ascii 名撞了 → 友好提示并重试
377
+ console.log(R + 'Agent "' + agentId + '" 已存在,请换一个名字。' + NC);
378
+ console.log('');
379
+ if (rawName) { process.exit(1); return; }
380
+ askName();
381
+ return;
382
+ }
383
+ // 生成的兜底 key 撞名(极罕见)→ 重新生成一次
384
+ naming = resolveAgentNaming(input);
385
+ agentId = naming.agentId;
386
+ displayName = naming.displayName;
383
387
  }
384
388
  } catch (e) {
385
389
  // 配置文件读不到就算了,交给 createAgent 处理
386
390
  }
387
391
 
388
392
  // 校验通过,开始创建
389
- console.log(Y + '正在创建 Agent "' + agentId + '"...' + NC);
393
+ if (displayName) {
394
+ console.log(Y + '正在创建 "' + displayName + '"(ID: ' + agentId + ')...' + NC);
395
+ } else {
396
+ console.log(Y + '正在创建 Agent "' + agentId + '"...' + NC);
397
+ }
390
398
  console.log('');
391
399
 
392
400
  var result;
393
401
  try {
394
402
  const createOpts = customFirstMessage ? { firstMessage: customFirstMessage } : {};
403
+ createOpts.agentId = agentId;
404
+ createOpts.displayName = displayName;
395
405
  result = require('./create_agent').createAgent(input, createOpts);
396
406
  } catch (err) {
397
407
  console.log(R + '创建失败: ' + err.message + NC);
@@ -399,9 +409,15 @@ function runNew() {
399
409
  }
400
410
 
401
411
  // 打印结果
402
- console.log(G + '✅' + NC + ' Agent "' + agentId + '" 创建完成');
412
+ var shownName = displayName ? (displayName + '' + agentId + '') : agentId;
413
+ console.log(G + '✅' + NC + ' Agent "' + shownName + '" 创建完成');
403
414
  console.log('');
404
- console.log(' ' + G + '✅' + NC + ' 名称标准化 ' + agentId);
415
+ if (displayName) {
416
+ console.log(' ' + G + '✅' + NC + ' 显示名 ' + displayName);
417
+ console.log(' ' + G + '✅' + NC + ' 生成 ID ' + agentId);
418
+ } else {
419
+ console.log(' ' + G + '✅' + NC + ' 名称标准化 ' + agentId);
420
+ }
405
421
  console.log(' ' + G + '✅' + NC + ' 环境检查 无冲突');
406
422
  console.log(' ' + G + '✅' + NC + ' 创建工作空间 已就绪');
407
423
  console.log(' ' + G + '✅' + NC + ' 注册配置 已写入');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.1.171",
3
+ "version": "1.1.173",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {