@aiyiran/myclaw 1.1.24 → 1.1.26

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.
Files changed (83) hide show
  1. package/.claude/settings.local.json +25 -1
  2. package/assets/myclaw-artifacts.js +1070 -126
  3. package/assets/myclaw-inject.js +913 -121
  4. package/delete_agents.js +268 -0
  5. package/index.js +361 -20
  6. package/package.json +1 -1
  7. package/patches/patch-manifest.json +10 -0
  8. package/server/sync_workspace.py +444 -14
  9. package/skills/yiran-course-template-pipeline/README.md +127 -0
  10. package/skills/yiran-course-template-pipeline/SKILL.md +65 -0
  11. package/skills/yiran-course-template-pipeline/assets/a100-teacher.example.html +66 -0
  12. package/skills/yiran-course-template-pipeline/assets/student-template.html +64 -0
  13. package/skills/yiran-course-template-pipeline/assets/teacher-portrait-demo.html +105 -0
  14. package/skills/yiran-course-template-pipeline/assets/teacher-task-view.html +110 -0
  15. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2651-demo/347/224/237/346/210/220.md +92 -0
  16. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2652-student/347/224/237/346/210/220.md +115 -0
  17. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2653-teacher/347/224/237/346/210/220.md +131 -0
  18. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2654-/346/211/223/345/214/205/350/220/275/347/233/230.md +77 -0
  19. package/skills/yiran-course-template-pipeline/references/student-example.json +38 -0
  20. package/skills/yiran-course-template-pipeline/references/student-fields.md +195 -0
  21. package/skills/yiran-course-template-pipeline/references/student-scaffold.json +34 -0
  22. package/skills/yiran-course-template-pipeline/references/teacher-fields.md +265 -0
  23. package/skills/yiran-course-template-pipeline/references/teacher-scaffold.json +25 -0
  24. package/skills/yiran-course-template-pipeline/scripts/build_template_views.py +125 -0
  25. package/skills/yiran-course-template-pipeline/scripts/move_template_task.py +59 -0
  26. package/skills/yiran-course-template-pipeline/scripts/render_student_page.py +52 -0
  27. package/skills/yiran-course-template-pipeline/scripts/render_teacher_view.py +108 -0
  28. package/skills/yiran-playground-template-use/SKILL.md +105 -0
  29. package/skills/yiran-playground-template-use/prompts/remix-handoff.txt +11 -0
  30. package/skills/yiran-playground-template-use/scripts/build_template_index.py +103 -0
  31. package/skills/yiran-playground-template-use/scripts/deploy_template.py +34 -0
  32. package/skills/yiran-playground-template-use/scripts/deploy_to_workspace.py +211 -0
  33. package/skills/yiran-playground-template-use/scripts/prepare_playgrounds.py +39 -0
  34. package/skills/yiran-playground-template-use/scripts/query_template.py +171 -0
  35. package/skills/yiran-playground-template-use/scripts/run_playgrounds_flow.py +44 -0
  36. package/skills/yiran-playground-template-use/scripts/start_tui_handoff.py +77 -0
  37. package/skills/yiran-playground-template-use/search-agent-prompt.md +39 -0
  38. package/skills/yiran-playground-template-use/template-index.json +136 -0
  39. package/skills/yiran-playground-template-use/template-index.md +38 -0
  40. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__demo__.html +140 -0
  41. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__student-view__.html +64 -0
  42. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__student__.json +38 -0
  43. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__teacher-view__.html +52 -0
  44. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__teacher__.json +36 -0
  45. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/index.html +61 -0
  46. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__demo__.html +131 -0
  47. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__student-view__.html +64 -0
  48. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__student__.json +34 -0
  49. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__teacher-view__.html +52 -0
  50. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__teacher__.json +34 -0
  51. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__demo__.html +77 -0
  52. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__student-view__.html +64 -0
  53. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__student__.json +38 -0
  54. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__teacher-view__.html +52 -0
  55. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__teacher__.json +34 -0
  56. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__demo__.html +162 -0
  57. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__student-view__.html +64 -0
  58. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__student__.json +34 -0
  59. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__teacher-view__.html +52 -0
  60. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__teacher__.json +34 -0
  61. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__demo__.html +180 -0
  62. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__student-view__.html +64 -0
  63. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__student__.json +38 -0
  64. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__teacher-view__.html +52 -0
  65. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__teacher__.json +41 -0
  66. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/demo.html +180 -0
  67. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/index.html +121 -0
  68. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271//345/260/217/347/273/204/345/220/211/347/245/245/347/211/251_26.png +0 -0
  69. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271//345/260/217/347/273/204/345/233/276/345/275/242/346/240/207/345/277/227_83.png +0 -0
  70. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271//347/217/255/347/272/247/345/260/217/347/273/204/345/276/275/347/253/240_47.png +0 -0
  71. package/skills/yiran-skill-media/SKILL.md +6 -15
  72. package/skills/yiran-skill-media/scripts/generate.py +47 -18
  73. package/skills/yiran-skill-media/scripts/generation_log.json +1 -56
  74. package/skills/yiran-skill-media/scripts/providers/__pycache__/__init__.cpython-311.pyc +0 -0
  75. package/skills/yiran-skill-media/scripts/providers/__pycache__/__init__.cpython-37.pyc +0 -0
  76. package/skills/yiran-skill-media/scripts/providers/__pycache__/jimeng_image.cpython-37.pyc +0 -0
  77. package/skills/yiran-skill-media/scripts/providers/__pycache__/jimeng_video.cpython-311.pyc +0 -0
  78. package/skills/yiran-skill-media/scripts/providers/__pycache__/jimeng_video.cpython-37.pyc +0 -0
  79. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_image.cpython-37.pyc +0 -0
  80. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_music.cpython-37.pyc +0 -0
  81. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_video.cpython-311.pyc +0 -0
  82. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_video.cpython-37.pyc +0 -0
  83. package/skills/yiran-skill-media/scripts/providers/__pycache__/vapi_image.cpython-37.pyc +0 -0
@@ -305,7 +305,510 @@
305
305
  } catch (e) {}
306
306
  }
307
307
 
308
- // ═══ 3. 语音按钮 ═══
308
+ // ═══ 3. Prompt 小助手按钮 ═══
309
+
310
+ var promptOpen = false;
311
+ var modalVoiceDestroy = null; // 弹框内语音组件的销毁函数
312
+
313
+ function createPromptButton() {
314
+ var btn = document.createElement("button");
315
+ btn.id = "myclaw-prompt-btn";
316
+ btn.className = "agent-chat__input-btn";
317
+ btn.title = "\u63d0\u95ee prompt \u5c0f\u52a9\u624b";
318
+ btn.setAttribute("aria-label", "\u63d0\u95ee prompt \u5c0f\u52a9\u624b");
319
+ btn.innerHTML = [
320
+ '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"',
321
+ ' viewBox="0 0 24 24" fill="none" stroke="currentColor"',
322
+ ' stroke-width="2" stroke-linecap="round" stroke-linejoin="round">',
323
+ ' <circle cx="12" cy="12" r="10"/>',
324
+ ' <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>',
325
+ ' <line x1="12" y1="17" x2="12.01" y2="17"/>',
326
+ '</svg>',
327
+ ].join("");
328
+
329
+ btn.addEventListener("click", function (e) {
330
+ e.stopPropagation();
331
+ if (promptOpen) {
332
+ closePromptModal();
333
+ } else {
334
+ openPromptModal();
335
+ }
336
+ });
337
+
338
+ return btn;
339
+ }
340
+
341
+ // ── 文案配置(集中管理,方便后续修改) ──
342
+
343
+ var ITERATION_CONFIG = {
344
+ problems: ["不好看", "不好玩", "太简单", "不清楚", "没反应", "不像我想的", "太乱了", "不知道接下来做什么"],
345
+ actions: ["加一个新东西", "改一下样子", "改一下内容", "改一下功能", "让它动起来", "让它更有趣", "让它更清楚", "先做一个最简单的新版本"],
346
+ };
347
+
348
+ function openPromptModal() {
349
+ if (document.querySelector("#myclaw-prompt-modal")) return;
350
+ promptOpen = true;
351
+
352
+ var iconBtn = document.querySelector("#myclaw-prompt-btn");
353
+ if (iconBtn) iconBtn.style.color = "#6366f1";
354
+
355
+ // ── 状态(关闭即清空) ──
356
+ var state = { problem: "", action: "", note: "" };
357
+
358
+ // ── 文本生成 ──
359
+ function buildText() {
360
+ if (!state.problem || !state.action) return "";
361
+ var t = "我现在这个作品的问题是【" + state.problem + "】。\n"
362
+ + "我下一步想先【" + state.action + "】。\n"
363
+ + "请根据我当前正在做的内容,帮我只改一个点,给我一个最简单、最容易成功的下一步方案。";
364
+ if (state.note.trim()) t += "\n补充说明:" + state.note.trim();
365
+ return t;
366
+ }
367
+
368
+ function buildPayload() {
369
+ return {
370
+ type: "iteration_helper",
371
+ problem: state.problem,
372
+ next_action: state.action,
373
+ extra_note: state.note,
374
+ generated_text: buildText(),
375
+ };
376
+ }
377
+
378
+ // ── 刷新预览 & 按钮可用性 ──
379
+ var previewEl, actionSendBtn, actionInsertBtn;
380
+ function refresh() {
381
+ var text = buildText();
382
+ var ready = !!(state.problem && state.action);
383
+ if (previewEl) {
384
+ previewEl.textContent = text || "先选上面两项,这里会自动生成要发给 AI 的内容 ✨";
385
+ previewEl.style.color = ready ? "#cdd6f4" : "#555";
386
+ }
387
+ if (actionSendBtn) {
388
+ actionSendBtn.disabled = !ready;
389
+ actionSendBtn.style.opacity = ready ? "1" : "0.4";
390
+ actionSendBtn.style.cursor = ready ? "pointer" : "not-allowed";
391
+ }
392
+ if (actionInsertBtn) {
393
+ actionInsertBtn.disabled = !ready;
394
+ actionInsertBtn.style.opacity = ready ? "1" : "0.4";
395
+ actionInsertBtn.style.cursor = ready ? "pointer" : "not-allowed";
396
+ }
397
+ }
398
+
399
+ // ── 芯片选择组(单选,再点可取消) ──
400
+ function makeChipGroup(items, activeColor, onChange) {
401
+ var wrap = document.createElement("div");
402
+ wrap.style.cssText = "display:flex;flex-wrap:wrap;gap:7px;";
403
+ var chips = [];
404
+ items.forEach(function (label) {
405
+ var chip = document.createElement("button");
406
+ chip.textContent = label;
407
+ chip.dataset.selected = "0";
408
+ chip.style.cssText = [
409
+ "padding:6px 13px",
410
+ "border-radius:20px",
411
+ "border:1.5px solid #3d3d5c",
412
+ "background:#252536",
413
+ "color:#888",
414
+ "font-size:13px",
415
+ "font-family:-apple-system,sans-serif",
416
+ "cursor:pointer",
417
+ "transition:all 0.15s",
418
+ "white-space:nowrap",
419
+ ].join(";");
420
+
421
+ function applyStyle(sel) {
422
+ if (sel) {
423
+ chip.style.background = activeColor;
424
+ chip.style.borderColor = activeColor;
425
+ chip.style.color = "#fff";
426
+ chip.style.fontWeight = "bold";
427
+ } else {
428
+ chip.style.background = "#252536";
429
+ chip.style.borderColor = "#3d3d5c";
430
+ chip.style.color = "#888";
431
+ chip.style.fontWeight = "normal";
432
+ }
433
+ }
434
+
435
+ chip.onmouseenter = function () {
436
+ if (chip.dataset.selected !== "1") chip.style.borderColor = activeColor;
437
+ };
438
+ chip.onmouseleave = function () {
439
+ if (chip.dataset.selected !== "1") chip.style.borderColor = "#3d3d5c";
440
+ };
441
+ chip.onclick = function () {
442
+ var nowOn = chip.dataset.selected !== "1";
443
+ // 取消同组其他
444
+ chips.forEach(function (c) { c.dataset.selected = "0"; applyStyle.call({chip:c}, false); });
445
+ // 重新用当前 chip 的 applyStyle(通过闭包拿到正确 chip)
446
+ chips.forEach(function (c) {
447
+ if (c !== chip) {
448
+ c.style.background = "#252536";
449
+ c.style.borderColor = "#3d3d5c";
450
+ c.style.color = "#888";
451
+ c.style.fontWeight = "normal";
452
+ c.dataset.selected = "0";
453
+ }
454
+ });
455
+ chip.dataset.selected = nowOn ? "1" : "0";
456
+ applyStyle(nowOn);
457
+ onChange(nowOn ? label : "");
458
+ };
459
+
460
+ chips.push(chip);
461
+ wrap.appendChild(chip);
462
+ });
463
+ return wrap;
464
+ }
465
+
466
+ // ── 区域标题 ──
467
+ function makeLabel(text) {
468
+ var el = document.createElement("div");
469
+ el.textContent = text;
470
+ el.style.cssText = "font-size:13px;font-weight:bold;color:#cdd6f4;margin-bottom:8px;font-family:-apple-system,sans-serif;";
471
+ return el;
472
+ }
473
+
474
+ // ══════════ DOM 组装 ══════════
475
+
476
+ // 遮罩
477
+ var overlay = document.createElement("div");
478
+ overlay.id = "myclaw-prompt-modal";
479
+ overlay.style.cssText = [
480
+ "position:fixed", "top:0", "left:0",
481
+ "width:100vw", "height:100vh",
482
+ "background:rgba(0,0,0,0.45)",
483
+ "z-index:99998",
484
+ "display:flex", "align-items:center", "justify-content:center",
485
+ "animation:myclaw-fade-in 0.15s ease",
486
+ ].join(";");
487
+
488
+ // 弹框
489
+ var box = document.createElement("div");
490
+ box.style.cssText = [
491
+ "width:560px", "max-width:96vw", "max-height:92vh",
492
+ "background:#1e1e2e",
493
+ "border-radius:12px",
494
+ "overflow:hidden",
495
+ "display:flex", "flex-direction:column",
496
+ "box-shadow:0 12px 48px rgba(0,0,0,0.6)",
497
+ ].join(";");
498
+
499
+ // 标题栏
500
+ var header = document.createElement("div");
501
+ header.style.cssText = [
502
+ "display:flex", "align-items:center", "justify-content:space-between",
503
+ "padding:13px 18px",
504
+ "background:linear-gradient(135deg,#6366f1,#8b5cf6)",
505
+ "color:#fff",
506
+ "font-size:15px", "font-weight:bold",
507
+ "font-family:-apple-system,sans-serif",
508
+ "user-select:none", "flex-shrink:0",
509
+ ].join(";");
510
+ header.innerHTML = "<span>🤔 我卡住了,帮我继续改</span>";
511
+ var closeX = document.createElement("span");
512
+ closeX.textContent = "✕";
513
+ closeX.style.cssText = "cursor:pointer;padding:2px 8px;border-radius:4px;font-size:15px;transition:background 0.15s;";
514
+ closeX.onmouseenter = function () { closeX.style.background = "rgba(255,255,255,0.2)"; };
515
+ closeX.onmouseleave = function () { closeX.style.background = "none"; };
516
+ closeX.onclick = function () { closePromptModal(); };
517
+ header.appendChild(closeX);
518
+
519
+ // 滚动内容区
520
+ var scroll = document.createElement("div");
521
+ scroll.style.cssText = "flex:1;overflow-y:auto;padding:20px 20px 8px;display:flex;flex-direction:column;gap:20px;";
522
+
523
+ // ── 第1区:问题 ──
524
+ var sec1 = document.createElement("div");
525
+ sec1.appendChild(makeLabel("现在最想改的问题是?"));
526
+ sec1.appendChild(makeChipGroup(ITERATION_CONFIG.problems, "#f59e0b", function (val) {
527
+ state.problem = val; refresh();
528
+ }));
529
+ scroll.appendChild(sec1);
530
+
531
+ // ── 第2区:行动 ──
532
+ var sec2 = document.createElement("div");
533
+ sec2.appendChild(makeLabel("你下一步想先改什么?"));
534
+ sec2.appendChild(makeChipGroup(ITERATION_CONFIG.actions, "#6366f1", function (val) {
535
+ state.action = val; refresh();
536
+ }));
537
+ scroll.appendChild(sec2);
538
+
539
+ // ── 第3区:补充(带语音输入) ──
540
+ var sec3 = document.createElement("div");
541
+ sec3.appendChild(makeLabel("可以补充一点点(可不填)"));
542
+ var placeholders = ["比如:我想做成飞行射击", "比如:我想更像小猫主题", "比如:按钮点了以后想有变化"];
543
+ var noteField = makeVoiceInputField(
544
+ placeholders[Math.floor(Math.random() * placeholders.length)],
545
+ 60,
546
+ function (val) { state.note = val; refresh(); }
547
+ );
548
+ modalVoiceDestroy = noteField.destroy;
549
+ sec3.appendChild(noteField.el);
550
+ scroll.appendChild(sec3);
551
+
552
+ // ── 预览区 ──
553
+ var previewWrap = document.createElement("div");
554
+ previewWrap.style.cssText = [
555
+ "padding:12px 14px",
556
+ "background:#252536",
557
+ "border-radius:8px",
558
+ "border-left:3px solid #6366f1",
559
+ ].join(";");
560
+ var previewLabel = document.createElement("div");
561
+ previewLabel.textContent = "将要发给 AI 的内容:";
562
+ previewLabel.style.cssText = "font-size:11px;color:#6366f1;font-weight:bold;margin-bottom:6px;font-family:monospace;letter-spacing:0.3px;";
563
+ previewEl = document.createElement("div");
564
+ previewEl.style.cssText = "font-size:13px;line-height:1.7;white-space:pre-wrap;font-family:-apple-system,sans-serif;word-break:break-all;";
565
+ previewWrap.appendChild(previewLabel);
566
+ previewWrap.appendChild(previewEl);
567
+ scroll.appendChild(previewWrap);
568
+
569
+ // ── 底部按钮区 ──
570
+ var footer = document.createElement("div");
571
+ footer.style.cssText = [
572
+ "display:flex", "gap:10px",
573
+ "padding:14px 20px",
574
+ "border-top:1px solid #2d2d3f",
575
+ "flex-shrink:0",
576
+ "background:#1e1e2e",
577
+ ].join(";");
578
+
579
+ actionSendBtn = document.createElement("button");
580
+ actionSendBtn.textContent = "➤ 直接发给 AI";
581
+ actionSendBtn.disabled = true;
582
+ actionSendBtn.style.cssText = [
583
+ "flex:1", "padding:11px 0",
584
+ "background:#10b981", "color:#fff",
585
+ "border:none", "border-radius:8px",
586
+ "font-size:14px", "font-weight:bold",
587
+ "font-family:-apple-system,sans-serif",
588
+ "cursor:not-allowed", "transition:all 0.15s", "opacity:0.4",
589
+ ].join(";");
590
+ actionSendBtn.onmouseenter = function () { if (!actionSendBtn.disabled) actionSendBtn.style.background = "#059669"; };
591
+ actionSendBtn.onmouseleave = function () { if (!actionSendBtn.disabled) actionSendBtn.style.background = "#10b981"; };
592
+ actionSendBtn.onclick = function () {
593
+ if (actionSendBtn.disabled) return;
594
+ var payload = buildPayload();
595
+ console.log("[myclaw-prompt] onSendDirect", payload);
596
+ insertPromptText(payload.generated_text);
597
+ closePromptModal();
598
+ setTimeout(function () { trySend(); }, 50);
599
+ };
600
+
601
+ actionInsertBtn = document.createElement("button");
602
+ actionInsertBtn.textContent = "⬇ 先放到聊天框";
603
+ actionInsertBtn.disabled = true;
604
+ actionInsertBtn.style.cssText = [
605
+ "flex:1", "padding:11px 0",
606
+ "background:#6366f1", "color:#fff",
607
+ "border:none", "border-radius:8px",
608
+ "font-size:14px", "font-weight:bold",
609
+ "font-family:-apple-system,sans-serif",
610
+ "cursor:not-allowed", "transition:all 0.15s", "opacity:0.4",
611
+ ].join(";");
612
+ actionInsertBtn.onmouseenter = function () { if (!actionInsertBtn.disabled) actionInsertBtn.style.background = "#4f46e5"; };
613
+ actionInsertBtn.onmouseleave = function () { if (!actionInsertBtn.disabled) actionInsertBtn.style.background = "#6366f1"; };
614
+ actionInsertBtn.onclick = function () {
615
+ if (actionInsertBtn.disabled) return;
616
+ var payload = buildPayload();
617
+ console.log("[myclaw-prompt] onInsertToInput", payload);
618
+ insertPromptText(payload.generated_text);
619
+ closePromptModal();
620
+ };
621
+
622
+ footer.appendChild(actionSendBtn);
623
+ footer.appendChild(actionInsertBtn);
624
+
625
+ // ── 拼装 ──
626
+ box.appendChild(header);
627
+ box.appendChild(scroll);
628
+ box.appendChild(footer);
629
+ overlay.appendChild(box);
630
+ overlay.onclick = function (e) { if (e.target === overlay) closePromptModal(); };
631
+ document.body.appendChild(overlay);
632
+
633
+ refresh();
634
+ }
635
+
636
+ function closePromptModal() {
637
+ if (modalVoiceDestroy) { modalVoiceDestroy(); modalVoiceDestroy = null; }
638
+ var modal = document.querySelector("#myclaw-prompt-modal");
639
+ if (modal) modal.remove();
640
+ promptOpen = false;
641
+
642
+ var btn = document.querySelector("#myclaw-prompt-btn");
643
+ if (btn) { btn.style.color = ""; }
644
+ }
645
+
646
+ /**
647
+ * 在 textarea 光标处插入 prompt 文字
648
+ */
649
+ function insertPromptText(text) {
650
+ var ta = document.querySelector(".agent-chat__input textarea");
651
+ if (!ta) return;
652
+
653
+ var start = ta.selectionStart || 0;
654
+ var end = ta.selectionEnd || 0;
655
+ var current = ta.value;
656
+ var newValue = current.substring(0, start) + text + current.substring(end);
657
+
658
+ var setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value").set;
659
+ setter.call(ta, newValue);
660
+ ta.dispatchEvent(new Event("input", { bubbles: true }));
661
+
662
+ var newCursor = start + text.length;
663
+ try { ta.setSelectionRange(newCursor, newCursor); } catch (e) {}
664
+ ta.focus();
665
+ }
666
+
667
+ // ═══ 3.6 带语音的输入框组件 ═══
668
+
669
+ /**
670
+ * makeVoiceInputField(placeholder, maxLength, onChange)
671
+ * 返回 { el, getValue, setValue, destroy }
672
+ *
673
+ * 完全独立:有自己的 VoiceInput 实例和录音状态,
674
+ * 不污染主 recording / voice 全局变量。
675
+ */
676
+ function makeVoiceInputField(placeholder, maxLength, onChange) {
677
+ var lVoice = null; // 本组件的 VoiceInput 实例
678
+ var lRecording = false; // 本组件的录音状态
679
+ var lStopping = false; // 本组件的 stopping 窗口
680
+
681
+ // ── 容器 ──
682
+ var wrap = document.createElement("div");
683
+ wrap.style.cssText = "display:flex;gap:8px;align-items:center;width:100%;box-sizing:border-box;";
684
+
685
+ // ── 输入框 ──
686
+ var input = document.createElement("input");
687
+ input.type = "text";
688
+ input.maxLength = maxLength || 60;
689
+ input.placeholder = placeholder || "";
690
+ input.style.cssText = [
691
+ "flex:1", "min-width:0", "padding:9px 12px",
692
+ "background:#252536",
693
+ "border:1.5px solid #3d3d5c",
694
+ "border-radius:8px",
695
+ "color:#cdd6f4",
696
+ "font-size:13px",
697
+ "font-family:-apple-system,sans-serif",
698
+ "outline:none",
699
+ "transition:border-color 0.15s, border-left-width 0.1s",
700
+ ].join(";");
701
+ input.onfocus = function () { if (!lRecording) input.style.borderColor = "#6366f1"; };
702
+ input.onblur = function () { if (!lRecording) input.style.borderColor = "#3d3d5c"; };
703
+ input.oninput = function () { if (onChange) onChange(input.value); };
704
+
705
+ // ── 麦克风按钮 ──
706
+ var micBtn = document.createElement("button");
707
+ micBtn.type = "button";
708
+ micBtn.title = "语音输入";
709
+ micBtn.style.cssText = [
710
+ "width:34px", "height:34px", "flex-shrink:0",
711
+ "border-radius:50%",
712
+ "border:1.5px solid #3d3d5c",
713
+ "background:#252536",
714
+ "color:#888",
715
+ "cursor:pointer",
716
+ "display:flex", "align-items:center", "justify-content:center",
717
+ "transition:all 0.15s",
718
+ "position:relative",
719
+ ].join(";");
720
+ micBtn.innerHTML = [
721
+ '<svg width="14" height="14" viewBox="0 0 24 24" fill="none"',
722
+ ' stroke="currentColor" stroke-width="2"',
723
+ ' stroke-linecap="round" stroke-linejoin="round">',
724
+ ' <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/>',
725
+ ' <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>',
726
+ ' <line x1="12" x2="12" y1="19" y2="22"/>',
727
+ '</svg>',
728
+ ].join("");
729
+
730
+ // ── UI 状态切换 ──
731
+ function setUI(recording) {
732
+ if (recording) {
733
+ micBtn.style.background = "#ff4444";
734
+ micBtn.style.borderColor = "#ff4444";
735
+ micBtn.style.color = "#fff";
736
+ micBtn.title = "停止录音";
737
+ input.style.borderColor = "#ff4444";
738
+ input.style.borderLeftWidth = "3px";
739
+ } else {
740
+ micBtn.style.background = "#252536";
741
+ micBtn.style.borderColor = "#3d3d5c";
742
+ micBtn.style.color = "#888";
743
+ micBtn.title = "语音输入";
744
+ input.style.borderColor = "#3d3d5c";
745
+ input.style.borderLeftWidth = "1.5px";
746
+ }
747
+ }
748
+
749
+ // ── 初始化本组件的 VoiceInput 实例 ──
750
+ function initLVoice() {
751
+ if (typeof window.VoiceInput === "undefined") return false;
752
+ lVoice = new window.VoiceInput({
753
+ onResult: function (text) {
754
+ if (!lRecording && !lStopping) return;
755
+ input.value = text;
756
+ if (onChange) onChange(text);
757
+ },
758
+ onStatusChange: function (oldS, newS) {
759
+ // 讯飞 60s 断开 → 自动重连
760
+ if (newS === "idle" && lRecording && !lStopping) {
761
+ setTimeout(function () {
762
+ if (lRecording && lVoice) lVoice.start();
763
+ }, 300);
764
+ }
765
+ },
766
+ onError: function (err) {
767
+ console.error("[voice-input-field] error:", err);
768
+ lRecording = false; lStopping = false;
769
+ setUI(false);
770
+ },
771
+ });
772
+ return true;
773
+ }
774
+
775
+ function startLocal() {
776
+ if (!lVoice && !initLVoice()) {
777
+ console.warn("[voice-input-field] VoiceInput SDK 未加载");
778
+ return;
779
+ }
780
+ lRecording = true;
781
+ setUI(true);
782
+ lVoice.start();
783
+ }
784
+
785
+ function stopLocal() {
786
+ if (!lRecording) return;
787
+ lStopping = true;
788
+ lRecording = false;
789
+ setUI(false);
790
+ setTimeout(function () {
791
+ lStopping = false;
792
+ if (lVoice) lVoice.stop();
793
+ }, 1500);
794
+ }
795
+
796
+ micBtn.onclick = function () {
797
+ if (lRecording) { stopLocal(); } else { startLocal(); }
798
+ };
799
+
800
+ wrap.appendChild(input);
801
+ wrap.appendChild(micBtn);
802
+
803
+ return {
804
+ el: wrap,
805
+ getValue: function () { return input.value; },
806
+ setValue: function (v) { input.value = v; if (onChange) onChange(v); },
807
+ destroy: function () { if (lRecording) stopLocal(); },
808
+ };
809
+ }
810
+
811
+ // ═══ 3.5 语音按钮 ═══
309
812
 
310
813
  function createVoiceButton() {
311
814
  var btn = document.createElement("button");
@@ -502,17 +1005,20 @@ btn.addEventListener("click", function () {
502
1005
  var toolbar = document.querySelector(".agent-chat__toolbar-left");
503
1006
  if (!toolbar || document.querySelector("#myclaw-voice-btn")) return;
504
1007
 
505
- var btn = createVoiceButton();
1008
+ var voiceBtn = createVoiceButton();
1009
+ var promptBtn = createPromptButton();
506
1010
 
507
- // 插入到工具栏最前面(排在 Attach file 等按钮之前)
1011
+ // 插入到工具栏最前面:prompt 按钮在最左,语音按钮紧跟其右
508
1012
  if (toolbar.firstChild) {
509
- toolbar.insertBefore(btn, toolbar.firstChild);
1013
+ toolbar.insertBefore(voiceBtn, toolbar.firstChild);
1014
+ toolbar.insertBefore(promptBtn, voiceBtn);
510
1015
  } else {
511
- toolbar.appendChild(btn);
1016
+ toolbar.appendChild(promptBtn);
1017
+ toolbar.appendChild(voiceBtn);
512
1018
  }
513
1019
 
514
1020
  injected = true;
515
- console.log("[myclaw-inject] \u2705 \u8bed\u97f3\u6309\u94ae\u5df2\u6ce8\u5165");
1021
+ console.log("[myclaw-inject] \u2705 \u8bed\u97f3\u6309\u94ae + prompt\u6309\u94ae\u5df2\u6ce8\u5165");
516
1022
  }
517
1023
 
518
1024
  // ═══ 注入录音态样式 ═══
@@ -586,6 +1092,23 @@ btn.addEventListener("click", function () {
586
1092
  }
587
1093
  // ═══ 6. 拦截发送按钮 ═══
588
1094
 
1095
+ /**
1096
+ * 尝试触发发送:仅当按钮不处于 Stop 状态时才发送
1097
+ * (AI 生成中时按钮带有 chat-send-btn--stop 类,此时禁止发送)
1098
+ */
1099
+ function trySend() {
1100
+ var btn = document.querySelector("button.chat-send-btn");
1101
+ if (!btn) {
1102
+ console.log("[myclaw-send] 找不到发送按钮");
1103
+ return;
1104
+ }
1105
+ if (btn.classList.contains("chat-send-btn--stop")) {
1106
+ console.log("[myclaw-send] AI 生成中,禁止发送");
1107
+ return;
1108
+ }
1109
+ btn.click();
1110
+ }
1111
+
589
1112
  /**
590
1113
  * 拦截 Enter 键:语音态下按回车 → 等待 2 秒后发送
591
1114
  */
@@ -601,8 +1124,7 @@ btn.addEventListener("click", function () {
601
1124
  console.log("[myclaw-voice] Enter按下, recording=", recording);
602
1125
  stopVoice(function () {
603
1126
  console.log("[myclaw-voice] Enter stopVoice callback firing...");
604
- var sendBtn = document.querySelector("button.chat-send-btn, button[title=\"Send\"]");
605
- if (sendBtn) sendBtn.click();
1127
+ trySend();
606
1128
  });
607
1129
 
608
1130
  }, true);
@@ -629,8 +1151,7 @@ btn.addEventListener("click", function () {
629
1151
  console.log("[myclaw-voice] 发送按钮点击(语音态), recording=", recording);
630
1152
  stopVoice(function () {
631
1153
  console.log("[myclaw-voice] 发送按钮 stopVoice callback firing...");
632
- var sendBtn = document.querySelector("button.chat-send-btn, button[title=\"Send\"]");
633
- if (sendBtn) sendBtn.click();
1154
+ trySend();
634
1155
  });
635
1156
  return;
636
1157
  }
@@ -1000,7 +1521,7 @@ btn.addEventListener("click", function () {
1000
1521
  form.appendChild(row);
1001
1522
  });
1002
1523
 
1003
- // ── 删除伙伴按钮 ──
1524
+ // ── 批量删除伙伴按钮 ──
1004
1525
  var delRow = document.createElement("div");
1005
1526
  delRow.style.cssText = [
1006
1527
  "padding:10px 14px",
@@ -1026,7 +1547,7 @@ btn.addEventListener("click", function () {
1026
1547
  delName.style.cssText = "font-size:13px;font-weight:bold;color:#ef4444;";
1027
1548
  delInfo.appendChild(delName);
1028
1549
  var delDesc = document.createElement("div");
1029
- delDesc.textContent = "\u5220\u9664\u4E00\u4E2A AI \u4F19\u4F34\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u6062\u590D";
1550
+ delDesc.textContent = "\u52FE\u9009\u5E76\u6279\u91CF\u5220\u9664 AI \u4F19\u4F34";
1030
1551
  delDesc.style.cssText = "font-size:11px;color:#888;";
1031
1552
  delInfo.appendChild(delDesc);
1032
1553
  delRow.appendChild(delInfo);
@@ -1037,7 +1558,7 @@ btn.addEventListener("click", function () {
1037
1558
  delRow.appendChild(delArrow);
1038
1559
 
1039
1560
  delRow.onclick = function () {
1040
- showDeleteConfirm();
1561
+ showBatchDeleteModal();
1041
1562
  };
1042
1563
 
1043
1564
  form.appendChild(delRow);
@@ -1085,131 +1606,402 @@ btn.addEventListener("click", function () {
1085
1606
 
1086
1607
  form.appendChild(lockRow);
1087
1608
 
1088
- // 删除伙伴 - 双重确认弹框
1089
- function showDeleteConfirm() {
1090
- var mask = document.createElement("div");
1609
+ // 批量删除伙伴弹框
1610
+ var MYCLAW_API_BASE_DEL = 'http://127.0.0.1:18800';
1611
+
1612
+ function fmtDate(ms) {
1613
+ if (!ms) return '未知';
1614
+ var d = new Date(ms);
1615
+ return d.getFullYear() + '/' + (d.getMonth() + 1) + '/' + d.getDate()
1616
+ + ' ' + String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
1617
+ }
1618
+
1619
+ function showBatchDeleteModal() {
1620
+ var mask = document.createElement('div');
1091
1621
  mask.style.cssText = [
1092
- "position:fixed",
1093
- "top:0;left:0;width:100vw;height:100vh",
1094
- "background:rgba(0,0,0,0.3)",
1095
- "z-index:999999",
1096
- "display:flex",
1097
- "align-items:center",
1098
- "justify-content:center",
1099
- "animation:myclaw-fade-in 0.15s ease",
1100
- ].join(";");
1622
+ 'position:fixed', 'top:0', 'left:0', 'width:100vw', 'height:100vh',
1623
+ 'background:rgba(0,0,0,0.5)', 'z-index:999999',
1624
+ 'display:flex', 'align-items:center', 'justify-content:center',
1625
+ 'animation:myclaw-fade-in 0.15s ease',
1626
+ ].join(';');
1101
1627
 
1102
- var box = document.createElement("div");
1628
+ var box = document.createElement('div');
1103
1629
  box.style.cssText = [
1104
- "width:360px",
1105
- "background:#1e1e2e",
1106
- "border-radius:8px",
1107
- "overflow:hidden",
1108
- "box-shadow:0 8px 32px rgba(0,0,0,0.5)",
1109
- ].join(";");
1630
+ 'width:560px', 'max-width:95vw', 'max-height:85vh',
1631
+ 'background:#1e1e2e', 'border-radius:10px', 'overflow:hidden',
1632
+ 'box-shadow:0 12px 48px rgba(0,0,0,0.7)',
1633
+ 'display:flex', 'flex-direction:column',
1634
+ ].join(';');
1635
+
1636
+ // 标题栏
1637
+ var hdr = document.createElement('div');
1638
+ hdr.style.cssText = 'padding:11px 16px;background:#ef4444;color:#fff;font-size:13px;font-family:monospace;display:flex;justify-content:space-between;align-items:center;flex-shrink:0;';
1639
+ var hdrTitle = document.createElement('span');
1640
+ hdrTitle.textContent = '🗑 批量删除伙伴';
1641
+ hdr.appendChild(hdrTitle);
1642
+ var hdrClose = document.createElement('span');
1643
+ hdrClose.textContent = '✕';
1644
+ hdrClose.style.cssText = 'cursor:pointer;padding:2px 6px;border-radius:3px;transition:background 0.15s;';
1645
+ hdrClose.onmouseenter = function () { hdrClose.style.background = 'rgba(255,255,255,0.2)'; };
1646
+ hdrClose.onmouseleave = function () { hdrClose.style.background = 'none'; };
1647
+ hdrClose.onclick = function () { mask.remove(); };
1648
+ hdr.appendChild(hdrClose);
1649
+ box.appendChild(hdr);
1650
+
1651
+ // 内容区(可滚动)
1652
+ var content = document.createElement('div');
1653
+ content.style.cssText = 'flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px;font-family:monospace;color:#cdd6f4;';
1654
+
1655
+ // 底部操作栏
1656
+ var footer = document.createElement('div');
1657
+ footer.style.cssText = 'padding:12px 16px;background:#252536;border-top:1px solid #3d3d5c;display:flex;gap:8px;align-items:center;flex-shrink:0;';
1658
+
1659
+ box.appendChild(content);
1660
+ box.appendChild(footer);
1661
+ mask.appendChild(box);
1662
+ mask.onclick = function (e) { if (e.target === mask) mask.remove(); };
1663
+ document.body.appendChild(mask);
1110
1664
 
1111
- // 标题
1112
- var h = document.createElement("div");
1113
- h.style.cssText = "padding:10px 14px;background:#ef4444;color:#fff;font-size:13px;display:flex;justify-content:space-between;align-items:center;";
1114
- h.innerHTML = '<span>\uD83D\uDDD1 \u5220\u9664\u4F19\u4F34</span>';
1115
- var x = document.createElement("span");
1116
- x.textContent = "\u2715";
1117
- x.style.cssText = "cursor:pointer;padding:2px 6px;border-radius:3px;";
1118
- x.onclick = function () { mask.remove(); };
1119
- h.appendChild(x);
1120
- box.appendChild(h);
1665
+ // ── Step 1: 加载列表 ──
1666
+ function showStep1() {
1667
+ content.innerHTML = '';
1668
+ footer.innerHTML = '';
1669
+ hdrTitle.textContent = '🗑 批量删除伙伴 — 选择';
1670
+
1671
+ var loading = document.createElement('div');
1672
+ loading.textContent = '⏳ 加载中...';
1673
+ loading.style.cssText = 'color:#888;font-size:12px;text-align:center;padding:20px 0;';
1674
+ content.appendChild(loading);
1675
+
1676
+ fetch(MYCLAW_API_BASE_DEL + '/api/agents')
1677
+ .then(function (r) { return r.json(); })
1678
+ .then(function (data) {
1679
+ content.innerHTML = '';
1680
+ var agents = (data.agents || []);
1681
+
1682
+ if (agents.length === 0) {
1683
+ var empty = document.createElement('div');
1684
+ empty.textContent = '没有找到任何 Agent。';
1685
+ empty.style.cssText = 'color:#888;font-size:12px;text-align:center;padding:20px 0;';
1686
+ content.appendChild(empty);
1687
+ return;
1688
+ }
1121
1689
 
1122
- // body
1123
- var body = document.createElement("div");
1124
- body.style.cssText = "padding:16px;display:flex;flex-direction:column;gap:12px;";
1125
-
1126
- var hint1 = document.createElement("div");
1127
- hint1.textContent = "\u8BF7\u8F93\u5165\u8981\u5220\u9664\u7684\u4F19\u4F34 ID\uFF1A";
1128
- hint1.style.cssText = "font-size:12px;color:#888;";
1129
- body.appendChild(hint1);
1130
-
1131
- var input1 = document.createElement("input");
1132
- input1.type = "text";
1133
- input1.placeholder = "\u4F19\u4F34 ID";
1134
- input1.style.cssText = "padding:8px 10px;background:#252536;border:1px solid #3d3d5c;border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;outline:none;";
1135
- body.appendChild(input1);
1136
-
1137
- var warn = document.createElement("div");
1138
- warn.textContent = "\u26A0 \u6B64\u64CD\u4F5C\u65E0\u6CD5\u6062\u590D\uFF0C\u786E\u8BA4\u540E\u5C06\u6C38\u4E45\u5220\u9664\uFF01";
1139
- warn.style.cssText = "font-size:11px;color:#ef4444;padding:8px;background:rgba(239,68,68,0.1);border-radius:4px;";
1140
- body.appendChild(warn);
1141
-
1142
- var confirmHint = document.createElement("div");
1143
- confirmHint.textContent = '\u8BF7\u8F93\u5165 "YES" \u786E\u8BA4\u5220\u9664\uFF1A';
1144
- confirmHint.style.cssText = "font-size:12px;color:#888;";
1145
- confirmHint.style.display = "none";
1146
- body.appendChild(confirmHint);
1147
-
1148
- var input2 = document.createElement("input");
1149
- input2.type = "text";
1150
- input2.placeholder = "YES";
1151
- input2.style.cssText = "padding:8px 10px;background:#252536;border:1px solid #3d3d5c;border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;outline:none;";
1152
- input2.style.display = "none";
1153
- body.appendChild(input2);
1690
+ // 说明行
1691
+ var legend = document.createElement('div');
1692
+ legend.style.cssText = 'display:flex;gap:16px;font-size:11px;color:#888;padding-bottom:4px;border-bottom:1px solid #3d3d5c;';
1693
+ legend.innerHTML = '<span><span style="color:#10b981">●</span> 已注册(JSON)</span>'
1694
+ + '<span><span style="color:#f59e0b">●</span> 孤立(仅有文件夹)</span>';
1695
+ content.appendChild(legend);
1696
+
1697
+ // 全选行
1698
+ var selAllRow = document.createElement('div');
1699
+ selAllRow.style.cssText = 'display:flex;align-items:center;gap:8px;font-size:12px;color:#888;cursor:pointer;padding:4px 0;';
1700
+ var selAllCb = document.createElement('input');
1701
+ selAllCb.type = 'checkbox';
1702
+ selAllCb.style.cssText = 'width:14px;height:14px;cursor:pointer;accent-color:#ef4444;';
1703
+ var selAllLabel = document.createElement('span');
1704
+ selAllLabel.textContent = '全选 / 取消全选';
1705
+ selAllRow.appendChild(selAllCb);
1706
+ selAllRow.appendChild(selAllLabel);
1707
+ content.appendChild(selAllRow);
1708
+
1709
+ // agent 列表
1710
+ var checkboxes = [];
1711
+ agents.forEach(function (ag) {
1712
+ var row = document.createElement('div');
1713
+ row.style.cssText = [
1714
+ 'display:flex', 'align-items:center', 'gap:10px',
1715
+ 'padding:8px 10px', 'border-radius:6px',
1716
+ 'background:#252536', 'cursor:pointer',
1717
+ 'transition:background 0.12s',
1718
+ ].join(';');
1719
+ row.onmouseenter = function () { row.style.background = '#2f2f4a'; };
1720
+ row.onmouseleave = function () { row.style.background = cb.checked ? '#2a1a1a' : '#252536'; };
1721
+
1722
+ var cb = document.createElement('input');
1723
+ cb.type = 'checkbox';
1724
+ cb.style.cssText = 'width:14px;height:14px;cursor:pointer;flex-shrink:0;accent-color:#ef4444;';
1725
+ cb.dataset.agentId = ag.id;
1726
+
1727
+ var dot = document.createElement('span');
1728
+ dot.textContent = '●';
1729
+ dot.style.cssText = 'font-size:10px;flex-shrink:0;color:' + (ag.inJson ? '#10b981' : '#f59e0b') + ';';
1730
+
1731
+ var info = document.createElement('div');
1732
+ info.style.cssText = 'flex:1;min-width:0;';
1733
+
1734
+ var nameEl = document.createElement('div');
1735
+ nameEl.textContent = ag.id;
1736
+ nameEl.style.cssText = 'font-size:13px;font-weight:bold;color:#cdd6f4;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
1737
+ info.appendChild(nameEl);
1738
+
1739
+ var metaEl = document.createElement('div');
1740
+ metaEl.style.cssText = 'font-size:10px;color:#666;display:flex;gap:12px;margin-top:2px;';
1741
+ metaEl.innerHTML = '<span>💬 ' + ag.sessionCount + ' 个会话</span>'
1742
+ + '<span>🕐 ' + fmtDate(ag.lastUpdated) + '</span>'
1743
+ + (!ag.inJson ? '<span style="color:#f59e0b">⚠ 未注册</span>' : '');
1744
+ info.appendChild(metaEl);
1745
+
1746
+ row.appendChild(cb);
1747
+ row.appendChild(dot);
1748
+ row.appendChild(info);
1749
+
1750
+ row.onclick = function (e) {
1751
+ if (e.target !== cb) cb.checked = !cb.checked;
1752
+ row.style.background = cb.checked ? '#2a1a1a' : '#252536';
1753
+ updateFooter();
1754
+ };
1755
+ cb.onchange = function () {
1756
+ row.style.background = cb.checked ? '#2a1a1a' : '#252536';
1757
+ updateFooter();
1758
+ };
1759
+
1760
+ checkboxes.push({ cb: cb, ag: ag });
1761
+ content.appendChild(row);
1762
+ });
1763
+
1764
+ // 全选逻辑
1765
+ selAllCb.onchange = function () {
1766
+ checkboxes.forEach(function (item) {
1767
+ item.cb.checked = selAllCb.checked;
1768
+ item.cb.closest ? item.cb.closest('div[style]') : null;
1769
+ });
1770
+ // 更新每行背景
1771
+ content.querySelectorAll('input[type=checkbox][data-agent-id]').forEach(function (c) {
1772
+ var r = c.parentElement;
1773
+ if (r) r.style.background = c.checked ? '#2a1a1a' : '#252536';
1774
+ });
1775
+ updateFooter();
1776
+ };
1777
+ selAllRow.onclick = function (e) {
1778
+ if (e.target !== selAllCb) { selAllCb.checked = !selAllCb.checked; selAllCb.onchange(); }
1779
+ };
1780
+
1781
+ function updateFooter() {
1782
+ var selected = checkboxes.filter(function (i) { return i.cb.checked; });
1783
+ footer.innerHTML = '';
1784
+ var countTip = document.createElement('span');
1785
+ countTip.style.cssText = 'flex:1;font-size:12px;color:#888;';
1786
+ countTip.textContent = '已选 ' + selected.length + ' / ' + agents.length + ' 个';
1787
+ footer.appendChild(countTip);
1788
+
1789
+ var cancelBtn = document.createElement('button');
1790
+ cancelBtn.textContent = '取消';
1791
+ cancelBtn.style.cssText = 'padding:7px 16px;background:#3d3d5c;border:none;border-radius:5px;color:#cdd6f4;font-size:12px;font-family:monospace;cursor:pointer;';
1792
+ cancelBtn.onclick = function () { mask.remove(); };
1793
+ footer.appendChild(cancelBtn);
1794
+
1795
+ var nextBtn = document.createElement('button');
1796
+ nextBtn.textContent = '下一步 →';
1797
+ nextBtn.disabled = selected.length === 0;
1798
+ nextBtn.style.cssText = 'padding:7px 18px;background:' + (selected.length > 0 ? '#ef4444' : '#5a3a3a') + ';border:none;border-radius:5px;color:#fff;font-size:12px;font-family:monospace;cursor:' + (selected.length > 0 ? 'pointer' : 'not-allowed') + ';transition:opacity 0.15s;';
1799
+ nextBtn.onclick = function () {
1800
+ if (selected.length > 0) showStep2(selected.map(function (i) { return i.ag; }));
1801
+ };
1802
+ footer.appendChild(nextBtn);
1803
+ }
1154
1804
 
1155
- var submitBtn = document.createElement("button");
1156
- submitBtn.textContent = "\u7EE7\u7EED";
1157
- submitBtn.style.cssText = "padding:8px 16px;background:#ef4444;border:none;border-radius:4px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;";
1158
- body.appendChild(submitBtn);
1805
+ updateFooter();
1806
+ })
1807
+ .catch(function (err) {
1808
+ content.innerHTML = '';
1809
+ var errEl = document.createElement('div');
1810
+ errEl.textContent = '❌ 加载失败,请确认 mc server 已启动(端口 18800)';
1811
+ errEl.style.cssText = 'color:#ef4444;font-size:12px;text-align:center;padding:20px 0;';
1812
+ content.appendChild(errEl);
1813
+ });
1814
+ }
1815
+
1816
+ // ── Step 2: 确认页 ──
1817
+ function showStep2(selected) {
1818
+ content.innerHTML = '';
1819
+ footer.innerHTML = '';
1820
+ hdrTitle.textContent = '🗑 确认删除 ' + selected.length + ' 个伙伴';
1821
+
1822
+ var warn = document.createElement('div');
1823
+ warn.style.cssText = 'font-size:11px;color:#ef4444;padding:8px 10px;background:rgba(239,68,68,0.12);border-radius:5px;border:1px solid rgba(239,68,68,0.3);';
1824
+ warn.innerHTML = '⚠ 以下伙伴将被移入回收站,此操作不可在本页面撤销,请仔细确认。';
1825
+ content.appendChild(warn);
1826
+
1827
+ selected.forEach(function (ag) {
1828
+ var row = document.createElement('div');
1829
+ row.style.cssText = 'padding:9px 12px;background:#252536;border-radius:6px;display:flex;flex-direction:column;gap:4px;';
1830
+
1831
+ var top = document.createElement('div');
1832
+ top.style.cssText = 'display:flex;align-items:center;gap:8px;';
1833
+
1834
+ var dot = document.createElement('span');
1835
+ dot.textContent = '●';
1836
+ dot.style.cssText = 'font-size:10px;color:' + (ag.inJson ? '#10b981' : '#f59e0b') + ';';
1837
+ top.appendChild(dot);
1838
+
1839
+ var nameEl = document.createElement('span');
1840
+ nameEl.textContent = ag.id;
1841
+ nameEl.style.cssText = 'font-size:13px;font-weight:bold;color:#ef4444;';
1842
+ top.appendChild(nameEl);
1843
+
1844
+ if (!ag.inJson) {
1845
+ var tag = document.createElement('span');
1846
+ tag.textContent = '孤立';
1847
+ tag.style.cssText = 'font-size:10px;color:#f59e0b;background:rgba(245,158,11,0.15);padding:1px 6px;border-radius:3px;';
1848
+ top.appendChild(tag);
1849
+ }
1850
+ row.appendChild(top);
1159
1851
 
1160
- var cancelBtn = document.createElement("button");
1161
- cancelBtn.textContent = "\u53D6\u6D88";
1162
- cancelBtn.style.cssText = "padding:8px 16px;background:#3d3d5c;border:none;border-radius:4px;color:#cdd6f4;font-size:12px;font-family:monospace;cursor:pointer;";
1163
- body.appendChild(cancelBtn);
1852
+ var meta = document.createElement('div');
1853
+ meta.style.cssText = 'font-size:10px;color:#666;display:flex;gap:12px;padding-left:18px;';
1854
+ meta.innerHTML = '<span>💬 ' + ag.sessionCount + ' 个会话</span>'
1855
+ + '<span>🕐 最后更新: ' + fmtDate(ag.lastUpdated) + '</span>';
1856
+ row.appendChild(meta);
1164
1857
 
1165
- box.appendChild(body);
1166
- mask.appendChild(box);
1167
- document.body.appendChild(mask);
1858
+ content.appendChild(row);
1859
+ });
1860
+
1861
+ // 底部
1862
+ var backBtn = document.createElement('button');
1863
+ backBtn.textContent = '← 返回';
1864
+ backBtn.style.cssText = 'padding:7px 16px;background:#3d3d5c;border:none;border-radius:5px;color:#cdd6f4;font-size:12px;font-family:monospace;cursor:pointer;';
1865
+ backBtn.onclick = function () { showStep1(); };
1866
+ footer.appendChild(backBtn);
1867
+
1868
+ var spacer = document.createElement('span');
1869
+ spacer.style.cssText = 'flex:1;';
1870
+ footer.appendChild(spacer);
1871
+
1872
+ var confirmBtn = document.createElement('button');
1873
+ confirmBtn.textContent = '确认删除 ' + selected.length + ' 个';
1874
+ confirmBtn.style.cssText = 'padding:7px 18px;background:#ef4444;border:none;border-radius:5px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;font-weight:bold;';
1875
+ confirmBtn.onclick = function () { showStep3(selected); };
1876
+ footer.appendChild(confirmBtn);
1877
+ }
1878
+
1879
+ // ── Step 3: 执行删除 ──
1880
+ function showStep3(selected) {
1881
+ content.innerHTML = '';
1882
+ footer.innerHTML = '';
1883
+ hdrTitle.textContent = '🗑 删除中...';
1884
+
1885
+ var inJson = selected.filter(function (ag) { return ag.inJson; });
1886
+ var orphans = selected.filter(function (ag) { return !ag.inJson; });
1887
+
1888
+ // 进度项
1889
+ var progressItems = {};
1890
+ selected.forEach(function (ag) {
1891
+ var row = document.createElement('div');
1892
+ row.style.cssText = 'padding:8px 12px;background:#252536;border-radius:6px;display:flex;align-items:center;gap:10px;';
1893
+
1894
+ var status = document.createElement('span');
1895
+ status.textContent = '⏳';
1896
+ status.style.cssText = 'font-size:14px;flex-shrink:0;';
1897
+
1898
+ var name = document.createElement('span');
1899
+ name.textContent = ag.id;
1900
+ name.style.cssText = 'flex:1;font-size:12px;color:#cdd6f4;';
1901
+
1902
+ var note = document.createElement('span');
1903
+ note.style.cssText = 'font-size:10px;color:#666;';
1904
+ note.textContent = ag.inJson ? '已注册' : '孤立';
1905
+
1906
+ row.appendChild(status);
1907
+ row.appendChild(name);
1908
+ row.appendChild(note);
1909
+ content.appendChild(row);
1910
+ progressItems[ag.id] = { row: row, status: status, note: note };
1911
+ });
1168
1912
 
1169
- input1.focus();
1913
+ function setStatus(id, icon, color, msg) {
1914
+ var item = progressItems[id];
1915
+ if (!item) return;
1916
+ item.status.textContent = icon;
1917
+ item.row.style.background = color;
1918
+ item.note.textContent = msg;
1919
+ }
1920
+
1921
+ // ── 阶段1:已注册 agent 通过 socket.io 调 openclaw agents delete --force
1922
+ // (官方负责:JSON 注销 + workspace trash + agents/<id>/agent/ trash)
1923
+ var cmdQueue = inJson.map(function (ag) { return ag.id; });
1924
+
1925
+ // ── 阶段1+2 文件系统清理路径(前端始终执行两个阶段)
1926
+ // 孤立 agent:workspace + agents/<id>/ 整个目录(含 agent/ 和 sessions/)
1927
+ // 已注册 agent:workspace + agents/<id>/ 整个目录
1928
+ // → openclaw 已清理 workspace 和 agent/,我们负责 sessions/ 残余(阶段2)
1929
+ // → workspace 路径不存在时 trashPath 会静默跳过,安全
1930
+ var orphanPaths = [];
1931
+ orphans.forEach(function (ag) {
1932
+ orphanPaths.push(ag.workspace);
1933
+ orphanPaths.push(ag.agentDir.replace(/\/agent$/, '')); // agents/<id>/ 整个目录
1934
+ });
1935
+ inJson.forEach(function (ag) {
1936
+ orphanPaths.push(ag.workspace);
1937
+ orphanPaths.push(ag.agentDir.replace(/\/agent$/, '')); // agents/<id>/ 整个目录(阶段2)
1938
+ });
1170
1939
 
1171
- var agentId = "";
1172
- submitBtn.onclick = function () {
1173
- if (!agentId) {
1174
- // 第一步:输入 agent ID
1175
- agentId = input1.value.trim();
1176
- if (!agentId) {
1177
- input1.style.borderColor = "#ef4444";
1178
- input1.focus();
1940
+ // 执行已注册 agent 删除
1941
+ var cmdDone = 0;
1942
+ function runNextCmd() {
1943
+ if (cmdDone >= cmdQueue.length) {
1944
+ runOrphanTrash();
1179
1945
  return;
1180
1946
  }
1181
- // 显示第二步确认
1182
- input1.style.display = "none";
1183
- hint1.style.display = "none";
1184
- confirmHint.style.display = "block";
1185
- input2.style.display = "block";
1186
- submitBtn.textContent = "\u786E\u8BA4\u5220\u9664";
1187
- input2.value = "";
1188
- input2.focus();
1189
- } else {
1190
- // 第二步:确认删除
1191
- var confirmVal = input2.value.trim();
1192
- if (confirmVal.toUpperCase() !== "YES") {
1193
- input2.style.borderColor = "#ef4444";
1194
- input2.focus();
1947
+ var agId = cmdQueue[cmdDone++];
1948
+ setStatus(agId, '🔄', '#252536', '删除中...');
1949
+ ensureSocketIO(function () {
1950
+ var runId = Date.now() + Math.random();
1951
+ cmdSocket.emit('run_command', { cmd: 'openclaw agents delete ' + agId + ' --force', runId: runId });
1952
+ // 假定 3 秒后完成(socket.io 无回调)
1953
+ setTimeout(function () {
1954
+ setStatus(agId, '✅', 'rgba(16,185,129,0.1)', '已删除');
1955
+ runNextCmd();
1956
+ }, 2500);
1957
+ });
1958
+ }
1959
+
1960
+ // 执行文件夹清理(孤立 agent + 已注册 agent 残留目录)
1961
+ function runOrphanTrash() {
1962
+ // 过滤掉空路径
1963
+ var validPaths = orphanPaths.filter(function (p) { return p && p.trim(); });
1964
+ if (validPaths.length === 0) {
1965
+ showDone();
1195
1966
  return;
1196
1967
  }
1197
- submitBtn.disabled = true;
1198
- submitBtn.textContent = "\u6267\u884C\u4E2D...";
1199
- runCommand("openclaw agents delete " + agentId + " --force");
1200
- setTimeout(function () {
1201
- mask.remove();
1202
- }, 1000);
1968
+ fetch(MYCLAW_API_BASE_DEL + '/api/agents/trash', {
1969
+ method: 'POST',
1970
+ headers: { 'Content-Type': 'application/json' },
1971
+ body: JSON.stringify({ paths: validPaths }),
1972
+ })
1973
+ .then(function (r) { return r.json(); })
1974
+ .then(function (res) {
1975
+ orphans.forEach(function (ag) {
1976
+ var allOk = (res.results || [])
1977
+ .filter(function (r) { return r.path && (r.path.includes(ag.id)); })
1978
+ .every(function (r) { return r.ok; });
1979
+ setStatus(ag.id, allOk ? '✅' : '⚠️', allOk ? 'rgba(16,185,129,0.1)' : 'rgba(245,158,11,0.1)', allOk ? '已移至回收站' : '部分失败');
1980
+ });
1981
+ showDone();
1982
+ })
1983
+ .catch(function () {
1984
+ orphans.forEach(function (ag) {
1985
+ setStatus(ag.id, '❌', 'rgba(239,68,68,0.1)', 'mc server 未启动');
1986
+ });
1987
+ showDone();
1988
+ });
1203
1989
  }
1204
- };
1205
1990
 
1206
- cancelBtn.onclick = function () {
1207
- mask.remove();
1208
- };
1991
+ function showDone() {
1992
+ hdrTitle.textContent = '🗑 删除完成';
1993
+ footer.innerHTML = '';
1994
+ var doneBtn = document.createElement('button');
1995
+ doneBtn.textContent = '关闭';
1996
+ doneBtn.style.cssText = 'padding:7px 24px;background:#10b981;border:none;border-radius:5px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;margin:0 auto;';
1997
+ doneBtn.onclick = function () { mask.remove(); };
1998
+ footer.appendChild(doneBtn);
1999
+ }
1209
2000
 
1210
- input2.onkeydown = function (e) {
1211
- if (e.key === "Enter") submitBtn.click();
1212
- };
2001
+ runNextCmd();
2002
+ }
2003
+
2004
+ showStep1();
1213
2005
  }
1214
2006
 
1215
2007
  box.appendChild(header);