ygc-dsh-sync 1.2.2 → 1.3.0
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.
- package/README.md +107 -283
- package/client.js +425 -545
- package/index.js +311 -223
- package/lib/api.js +161 -747
- package/lib/core.js +73 -43
- package/lib/skill-learn.js +1115 -0
- package/lib/skill-optimize.js +888 -0
- package/lib/skill-scope.js +288 -0
- package/lib/skill-share-run.js +388 -0
- package/lib/skillshare.js +781 -0
- package/package.json +14 -3
package/client.js
CHANGED
|
@@ -113,17 +113,22 @@ function makePanel(React) {
|
|
|
113
113
|
const [note, setNote] = useState(null);
|
|
114
114
|
const [midDraft, setMidDraft] = useState('');
|
|
115
115
|
const [scan, setScan] = useState(null);
|
|
116
|
+
// 归属判定摘要(哪些技能归本插件管、哪些是市场第三方)
|
|
117
|
+
const [scopeInfo, setScopeInfo] = useState(null);
|
|
116
118
|
// 特征文件夹采纳:面板上待人工决定的那些(路径数组)
|
|
117
119
|
const [featurePicks, setFeaturePicks] = useState([]);
|
|
118
120
|
const [patrolTime, setPatrolTime] = useState('');
|
|
119
121
|
const [skillScan, setSkillScan] = useState(null);
|
|
120
|
-
const [skillPicks, setSkillPicks] = useState([]);
|
|
121
|
-
const [skillMasterDraft, setSkillMasterDraft] = useState('');
|
|
122
122
|
const [skillIv, setSkillIv] = useState('');
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
//
|
|
123
|
+
// 1.3.0:技能共享与优化。母版那套输入框与勾选表都没了,换成这几样——
|
|
124
|
+
// inspectResult 体检结果(纯机械,不调模型)
|
|
125
|
+
// learningList 待批草稿(学习 + 优化共用一份台账)
|
|
126
|
+
// draftPreview 草稿正文预览
|
|
127
|
+
// optRunning 后台优化是否在跑(面板靠它禁用按钮并显示进度)
|
|
128
|
+
const [inspectResult, setInspectResult] = useState(null);
|
|
129
|
+
const [learningList, setLearningList] = useState(null);
|
|
130
|
+
const [draftPreview, setDraftPreview] = useState(null);
|
|
131
|
+
const [optRunning, setOptRunning] = useState(false);
|
|
127
132
|
// 插件清单共享:明细单独取一次(要扫盘),根目录草稿跟着输入框走
|
|
128
133
|
const [pluginDetail, setPluginDetail] = useState(null);
|
|
129
134
|
const [pluginRootDraft, setPluginRootDraft] = useState('');
|
|
@@ -403,31 +408,33 @@ function makePanel(React) {
|
|
|
403
408
|
}
|
|
404
409
|
};
|
|
405
410
|
|
|
411
|
+
/* ---------------- 技能共享与优化(1.3.0,母版已取消) ----------------
|
|
412
|
+
*
|
|
413
|
+
* 面板上就这几个动作,全部对应 HTTP 路由,没有本地状态机:
|
|
414
|
+
* 查看现状 /skills/scan 只读,扫本机 + 别人的格子
|
|
415
|
+
* 立即共享 /skills/share 备份本机 → 拉缺失 → 差异进学习评估
|
|
416
|
+
* 体检 /skills/inspect 纯机械,不调模型、不写盘
|
|
417
|
+
* 优化 /skills/optimize/run 后台跑,出草稿
|
|
418
|
+
* 待批草稿 /skills/learning/list 批准 / 驳回 / 看正文
|
|
419
|
+
*/
|
|
420
|
+
|
|
406
421
|
const doSkillScan = async () => {
|
|
407
422
|
setBusy('skillscan');
|
|
408
423
|
setNote(null);
|
|
409
424
|
try {
|
|
410
425
|
const r = await call('/skills/scan', {});
|
|
411
426
|
setSkillScan(r);
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const bits = [
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (mn.skipped && mn.skipped.length) bits.push(`草稿跳过 ${mn.skipped.length}`);
|
|
424
|
-
if (r.push.length) bits.push(`待你裁决 ${r.push.length}`);
|
|
425
|
-
if (r.same.length) bits.push(`一致 ${r.same.length}`);
|
|
426
|
-
if (r.localOnly.length) bits.push(`本地独有 ${r.localOnly.length}`);
|
|
427
|
-
setNote({
|
|
428
|
-
tone: r.push.length ? 'warn' : 'ok',
|
|
429
|
-
text: bits.length ? `技能比对:${bits.join('、')}` : '技能比对:母版为空',
|
|
430
|
-
});
|
|
427
|
+
if (r && r.scope) setScopeInfo(r.scope);
|
|
428
|
+
const c = (r && r.counts) || {};
|
|
429
|
+
const bits = [
|
|
430
|
+
`本机自研 ${c.local ?? 0}`,
|
|
431
|
+
`他机特征本 ${c.peers ?? 0}`,
|
|
432
|
+
`缺失 ${c.missing ?? 0}`,
|
|
433
|
+
`差异 ${c.differ ?? 0}`,
|
|
434
|
+
`一致 ${c.same ?? 0}`,
|
|
435
|
+
];
|
|
436
|
+
if ((r.warnings || []).length) bits.push(`${r.warnings.length} 条提示`);
|
|
437
|
+
setNote({ tone: 'ok', text: `技能现状:${bits.join('、')}` });
|
|
431
438
|
} catch (e) {
|
|
432
439
|
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
433
440
|
} finally {
|
|
@@ -435,18 +442,48 @@ function makePanel(React) {
|
|
|
435
442
|
}
|
|
436
443
|
};
|
|
437
444
|
|
|
438
|
-
const
|
|
439
|
-
setBusy('
|
|
445
|
+
const doSkillShare = async () => {
|
|
446
|
+
setBusy('skillshare');
|
|
447
|
+
setNote({ tone: 'warn', text: '正在共享:备份本机技能 → 扫别人的格子 → 拉缺失 → 差异进学习评估(可能要几十秒)' });
|
|
448
|
+
try {
|
|
449
|
+
const r = await call('/skills/share', {});
|
|
450
|
+
const rep = (r && r.report) || {};
|
|
451
|
+
if (rep.skipped) {
|
|
452
|
+
setNote({ tone: 'warn', text: `共享未执行:${rep.skipped}` });
|
|
453
|
+
} else {
|
|
454
|
+
const bits = [
|
|
455
|
+
`备份 ${rep.backup ? rep.backup.counts.total : 0}`,
|
|
456
|
+
`他机 ${(rep.peers || []).length} 台`,
|
|
457
|
+
`缺失 ${(rep.missing && rep.missing.found) || 0}(已拉 ${(rep.missing && rep.missing.pulled) || 0})`,
|
|
458
|
+
`差异 ${(rep.differ && rep.differ.found) || 0}(评估 ${(rep.differ && rep.differ.evaluated) || 0})`,
|
|
459
|
+
`待批 ${rep.pending || 0}`,
|
|
460
|
+
];
|
|
461
|
+
const errs = (rep.errors || []).slice(0, 2);
|
|
462
|
+
setNote({
|
|
463
|
+
tone: errs.length ? 'warn' : 'ok',
|
|
464
|
+
text: `共享完成:${bits.join('、')}` + (errs.length ? `(${errs.join(';')})` : ''),
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
await refresh();
|
|
468
|
+
await doSkillLearning();
|
|
469
|
+
} catch (e) {
|
|
470
|
+
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
471
|
+
} finally {
|
|
472
|
+
setBusy('');
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const doSkillInspect = async () => {
|
|
477
|
+
setBusy('skillinspect');
|
|
440
478
|
setNote(null);
|
|
441
479
|
try {
|
|
442
|
-
const r = await call('/skills/
|
|
480
|
+
const r = await call('/skills/inspect', {});
|
|
481
|
+
setInspectResult(r);
|
|
482
|
+
if (r && r.scope) setScopeInfo((cur) => cur || { counts: r.scope, thirdParty: [] });
|
|
443
483
|
setNote({
|
|
444
|
-
tone: r.
|
|
445
|
-
text:
|
|
446
|
-
r.pendingPush.length ? `,${r.pendingPush.length} 个待推送` : ''
|
|
447
|
-
}${r.errors.length ? `,${r.errors.length} 个失败` : ''}`,
|
|
484
|
+
tone: (r.items || []).length ? 'warn' : 'ok',
|
|
485
|
+
text: `体检完成:${r.scanned} 个(健康 ${r.healthy},第三方跳过 ${r.thirdParty}),有问题的 ${(r.items || []).length} 个`,
|
|
448
486
|
});
|
|
449
|
-
await doSkillScan();
|
|
450
487
|
} catch (e) {
|
|
451
488
|
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
452
489
|
} finally {
|
|
@@ -454,19 +491,66 @@ function makePanel(React) {
|
|
|
454
491
|
}
|
|
455
492
|
};
|
|
456
493
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
494
|
+
/**
|
|
495
|
+
* 跑一轮优化。
|
|
496
|
+
*
|
|
497
|
+
* 后台跑,所以这里只管两件事:起跑、然后定时问进度。最多盯 5 分钟——
|
|
498
|
+
* 一轮通常 3 条 × 每条 20-60 秒;超过还没完就交回用户,让他稍后点「待批草稿」。
|
|
499
|
+
*/
|
|
500
|
+
const doSkillOptimize = async () => {
|
|
501
|
+
setBusy('skillopt');
|
|
502
|
+
setOptRunning(true);
|
|
503
|
+
setNote({ tone: 'warn', text: '优化已转后台:体检 → 出草稿(每条一次模型调用,约 20-60 秒)' });
|
|
504
|
+
try {
|
|
505
|
+
const r = await call('/skills/optimize/run', {});
|
|
506
|
+
if (r && r.started === false) {
|
|
507
|
+
setNote({ tone: 'warn', text: '上一轮还在跑,本次没有重复启动' });
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
for (let i = 0; i < 60; i += 1) {
|
|
511
|
+
await new Promise((ok) => setTimeout(ok, 5000));
|
|
512
|
+
const st = await call('/skills/optimize/state', {});
|
|
513
|
+
if (st.running) {
|
|
514
|
+
setNote({ tone: 'warn', text: `优化进行中…(已 ${(i + 1) * 5} 秒)` });
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const lr = st.lastReport;
|
|
518
|
+
setOptRunning(false);
|
|
519
|
+
if (!lr) {
|
|
520
|
+
setNote({ tone: 'warn', text: st.lastError ? `优化失败:${st.lastError}` : '优化结束,但没有拿到本轮报告(看宿主日志)' });
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const bits = [
|
|
524
|
+
`体检 ${lr.scanned || 0}`,
|
|
525
|
+
`健康 ${lr.healthy || 0}`,
|
|
526
|
+
`第三方跳过 ${lr.skippedThirdParty || 0}`,
|
|
527
|
+
`出草稿 ${lr.drafted || 0}`,
|
|
528
|
+
`审核未过 ${lr.rejected || 0}`,
|
|
529
|
+
];
|
|
530
|
+
if ((lr.merged || 0) > 0) bits.push(`合并 ${lr.merged}`);
|
|
531
|
+
setNote({
|
|
532
|
+
tone: (lr.errors || []).length ? 'warn' : 'ok',
|
|
533
|
+
text: `优化完成:${bits.join('、')}` + ((lr.errors || []).length ? `(${String(lr.errors[0]).slice(0, 80)})` : ''),
|
|
534
|
+
});
|
|
535
|
+
await refresh();
|
|
536
|
+
await doSkillLearning();
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
setOptRunning(false);
|
|
540
|
+
setNote({ tone: 'warn', text: '优化仍在后台跑,稍后点「待批草稿」看结果' });
|
|
541
|
+
} catch (e) {
|
|
542
|
+
setOptRunning(false);
|
|
543
|
+
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
544
|
+
} finally {
|
|
545
|
+
setBusy('');
|
|
461
546
|
}
|
|
462
|
-
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
const doSkillLearning = async () => {
|
|
550
|
+
setBusy('skilllearn');
|
|
463
551
|
try {
|
|
464
|
-
const r = await call('/skills/
|
|
465
|
-
|
|
466
|
-
tone: r.errors.length ? 'warn' : 'ok',
|
|
467
|
-
text: `已推送 ${r.pushed.length} 个技能到母版(旧版备份在 _backup/${r.stamp})`,
|
|
468
|
-
});
|
|
469
|
-
await doSkillScan();
|
|
552
|
+
const r = await call('/skills/learning/list', {});
|
|
553
|
+
setLearningList(r.items || []);
|
|
470
554
|
} catch (e) {
|
|
471
555
|
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
472
556
|
} finally {
|
|
@@ -474,11 +558,15 @@ function makePanel(React) {
|
|
|
474
558
|
}
|
|
475
559
|
};
|
|
476
560
|
|
|
477
|
-
const
|
|
478
|
-
setBusy(
|
|
561
|
+
const viewDraft = async (name) => {
|
|
562
|
+
setBusy(`draft:${name}`);
|
|
479
563
|
try {
|
|
480
|
-
await call('/skills/
|
|
481
|
-
|
|
564
|
+
const r = await call('/skills/learning/draft', { name });
|
|
565
|
+
if (!r.ok) {
|
|
566
|
+
setNote({ tone: 'warn', text: `读不到草稿:${r.message || name}` });
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
setDraftPreview({ name, text: r.text });
|
|
482
570
|
} catch (e) {
|
|
483
571
|
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
484
572
|
} finally {
|
|
@@ -486,11 +574,18 @@ function makePanel(React) {
|
|
|
486
574
|
}
|
|
487
575
|
};
|
|
488
576
|
|
|
489
|
-
const
|
|
490
|
-
setBusy(
|
|
577
|
+
const approveDraft = async (name) => {
|
|
578
|
+
setBusy(`approve:${name}`);
|
|
491
579
|
try {
|
|
492
|
-
const r = await call('/skills/
|
|
493
|
-
|
|
580
|
+
const r = await call('/skills/learning/approve', { name });
|
|
581
|
+
if (r.ok) {
|
|
582
|
+
setNote({ tone: 'ok', text: `${name} 已落地${r.backup ? `(旧版备份在 ${r.backup})` : ''}` });
|
|
583
|
+
setDraftPreview(null);
|
|
584
|
+
} else {
|
|
585
|
+
setNote({ tone: 'warn', text: r.message || '落地失败' });
|
|
586
|
+
}
|
|
587
|
+
await doSkillLearning();
|
|
588
|
+
await refresh();
|
|
494
589
|
} catch (e) {
|
|
495
590
|
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
496
591
|
} finally {
|
|
@@ -498,13 +593,49 @@ function makePanel(React) {
|
|
|
498
593
|
}
|
|
499
594
|
};
|
|
500
595
|
|
|
501
|
-
const
|
|
502
|
-
setBusy(
|
|
503
|
-
|
|
596
|
+
const rejectDraft = async (name) => {
|
|
597
|
+
setBusy(`reject:${name}`);
|
|
598
|
+
try {
|
|
599
|
+
const r = await call('/skills/learning/reject', { name, reason: '面板驳回' });
|
|
600
|
+
setNote({
|
|
601
|
+
tone: r.ok ? 'ok' : 'warn',
|
|
602
|
+
text: r.ok ? `${name} 已驳回(草稿留着,可复核)` : r.message || '驳回失败',
|
|
603
|
+
});
|
|
604
|
+
await doSkillLearning();
|
|
605
|
+
} catch (e) {
|
|
606
|
+
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
607
|
+
} finally {
|
|
608
|
+
setBusy('');
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
/** 人工改归属:市场第三方 ↔ 自己编写。改完立刻重算,不用等下一轮。 */
|
|
613
|
+
const setScope = async (name, scope) => {
|
|
614
|
+
setBusy(`scope:${name}`);
|
|
615
|
+
try {
|
|
616
|
+
const r = await call('/skills/scope/set', { name, scope });
|
|
617
|
+
if (r.ok) {
|
|
618
|
+
if (r.scopeSnapshot) setScopeInfo(r.scopeSnapshot);
|
|
619
|
+
setNote({
|
|
620
|
+
tone: 'ok',
|
|
621
|
+
text: `${name} 已改为「${scope === 'own' ? '自己编写' : '第三方'}」,下一轮同步与优化按新归属处理`,
|
|
622
|
+
});
|
|
623
|
+
} else {
|
|
624
|
+
setNote({ tone: 'warn', text: r.message || '改归属失败' });
|
|
625
|
+
}
|
|
626
|
+
} catch (e) {
|
|
627
|
+
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
628
|
+
} finally {
|
|
629
|
+
setBusy('');
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
const saveSkills = async (patch) => {
|
|
634
|
+
setBusy('skillcfg');
|
|
504
635
|
try {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
636
|
+
await call('/skills/config', patch);
|
|
637
|
+
await refresh();
|
|
638
|
+
setNote({ tone: 'ok', text: '技能共享设置已保存' });
|
|
508
639
|
} catch (e) {
|
|
509
640
|
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
510
641
|
} finally {
|
|
@@ -576,26 +707,6 @@ function makePanel(React) {
|
|
|
576
707
|
}
|
|
577
708
|
};
|
|
578
709
|
|
|
579
|
-
const changeMode = async (mode) => {
|
|
580
|
-
if (!listPicks.length) {
|
|
581
|
-
setNote({ tone: 'err', text: '请先勾选技能' });
|
|
582
|
-
return;
|
|
583
|
-
}
|
|
584
|
-
setBusy('mode');
|
|
585
|
-
try {
|
|
586
|
-
const r = await call('/skills/mode', { names: listPicks, mode });
|
|
587
|
-
const names = { sync: '同步', local: '本机独属', master: '母版有·本机不拉取' };
|
|
588
|
-
const bits = [`${r.applied} 个改为「${names[mode]}」`];
|
|
589
|
-
if (r.failed) bits.push(`${r.failed} 个失败`);
|
|
590
|
-
if (r.audit && r.audit.written) bits.push(`已记录 ${r.audit.written} 条母版改动`);
|
|
591
|
-
setNote({ tone: r.failed ? 'warn' : 'ok', text: bits.join(',') });
|
|
592
|
-
await loadSkillList();
|
|
593
|
-
} catch (e) {
|
|
594
|
-
setNote({ tone: 'err', text: String((e && e.message) || e) });
|
|
595
|
-
} finally {
|
|
596
|
-
setBusy('');
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
710
|
|
|
600
711
|
if (!state) {
|
|
601
712
|
return h(
|
|
@@ -612,6 +723,12 @@ function makePanel(React) {
|
|
|
612
723
|
const allSelected = ws.length > 0 && (selected || []).length === ws.length;
|
|
613
724
|
const folders = state.folders || [];
|
|
614
725
|
|
|
726
|
+
// 技能共享派生量:面板顶部那行摘要与按钮上的计数
|
|
727
|
+
const shareState = (state.skills && state.skills.share) || {};
|
|
728
|
+
const shareRoot = (shareState && shareState.root) || (state.skills && state.skills.shareRoot) || '';
|
|
729
|
+
const optLast = shareState.lastOptimize || null;
|
|
730
|
+
const pendingCount = (learningList || []).length;
|
|
731
|
+
|
|
615
732
|
const bannerClass =
|
|
616
733
|
note && note.tone === 'ok'
|
|
617
734
|
? 'dsb-banner dsb-banner-ok'
|
|
@@ -1310,75 +1427,63 @@ function makePanel(React) {
|
|
|
1310
1427
|
})(),
|
|
1311
1428
|
),
|
|
1312
1429
|
|
|
1313
|
-
//
|
|
1430
|
+
// 技能共享 + 本机优化(1.3.0)
|
|
1431
|
+
//
|
|
1432
|
+
// 母版取消了:技能的身体在特征文件夹里,一人一格。所以这一栏只有三件事——
|
|
1433
|
+
// ① 共享:把本机自研技能备份出去、把别人有的拉回来
|
|
1434
|
+
// ② 优化:点一下后台体检 + 精炼 + 合并 + 补全,出草稿等人批
|
|
1435
|
+
// ③ 归属:谁归本插件管;市场装的第三方技能不在这里面
|
|
1314
1436
|
h(
|
|
1315
1437
|
'div',
|
|
1316
1438
|
{ className: 'dsb-card' },
|
|
1317
1439
|
h(
|
|
1318
1440
|
'div',
|
|
1319
1441
|
{ className: 'dsb-card-h' },
|
|
1320
|
-
h('h3', { className: 'dsb-card-t' }, '
|
|
1321
|
-
state.skills && state.skills.lastRunAt
|
|
1322
|
-
? h('span', { className: 'dsb-tag' },
|
|
1442
|
+
h('h3', { className: 'dsb-card-t' }, '技能共享与优化'),
|
|
1443
|
+
state.skills && state.skills.share && state.skills.share.lastRunAt
|
|
1444
|
+
? h('span', { className: 'dsb-tag' }, `上次共享 ${fmtAgo(state.skills.share.lastRunAt)}`)
|
|
1323
1445
|
: null,
|
|
1324
1446
|
),
|
|
1325
1447
|
h(
|
|
1326
1448
|
'p',
|
|
1327
1449
|
{ className: 'dsb-sub', style: { marginBottom: 12 } },
|
|
1328
|
-
'
|
|
1450
|
+
'技能不再走母版:本机自研技能备份进自己的特征文件夹(含说明、来源、更新时间),别的机器的备份只读拉取。' +
|
|
1451
|
+
'同名但内容不同的技能不做覆盖,交给学习评估出合并稿。市场装的第三方技能不管理、不同步、不优化。',
|
|
1329
1452
|
),
|
|
1330
1453
|
|
|
1331
|
-
//
|
|
1454
|
+
// 操作行
|
|
1332
1455
|
h(
|
|
1333
1456
|
'div',
|
|
1334
|
-
{ className: 'dsb-actions', style: { marginBottom: 10 } },
|
|
1335
|
-
h('input', {
|
|
1336
|
-
className: 'dsb-input',
|
|
1337
|
-
style: { flex: 1, minWidth: 240 },
|
|
1338
|
-
value: skillMasterDraft !== '' ? skillMasterDraft : (state.skills && state.skills.masterRoot) || '',
|
|
1339
|
-
placeholder: '母版文件夹,例如 W:\\DSH同步工作空间\\系统文件\\Skills',
|
|
1340
|
-
disabled: busy === 'skillcfg',
|
|
1341
|
-
onChange: (e) => setSkillMasterDraft(e.target.value),
|
|
1342
|
-
onKeyDown: (e) => {
|
|
1343
|
-
if (e.key === 'Enter') {
|
|
1344
|
-
saveSkills({ masterRoot: e.target.value });
|
|
1345
|
-
setSkillMasterDraft('');
|
|
1346
|
-
}
|
|
1347
|
-
},
|
|
1348
|
-
}),
|
|
1457
|
+
{ className: 'dsb-actions', style: { marginBottom: 10, flexWrap: 'wrap' } },
|
|
1349
1458
|
h(
|
|
1350
1459
|
'button',
|
|
1351
|
-
{
|
|
1352
|
-
|
|
1353
|
-
disabled: busy === 'skillcfg',
|
|
1354
|
-
onClick: () => {
|
|
1355
|
-
const v = skillMasterDraft !== '' ? skillMasterDraft : (state.skills && state.skills.masterRoot) || '';
|
|
1356
|
-
if (v.trim()) {
|
|
1357
|
-
saveSkills({ masterRoot: v });
|
|
1358
|
-
setSkillMasterDraft('');
|
|
1359
|
-
}
|
|
1360
|
-
},
|
|
1361
|
-
},
|
|
1362
|
-
'保存路径',
|
|
1460
|
+
{ className: 'dsb-btn dsb-btn-primary dsb-btn-sm', onClick: doSkillScan, disabled: !!busy },
|
|
1461
|
+
busy === 'skillscan' ? '读取中…' : '查看现状',
|
|
1363
1462
|
),
|
|
1364
|
-
),
|
|
1365
|
-
state.skills && state.skills.masterRoot
|
|
1366
|
-
? h('div', { className: 'dsb-row-d', style: { marginBottom: 10 } }, `本地部署位置:${state.skills.localRoot || '~/.agents/skills'}`)
|
|
1367
|
-
: null,
|
|
1368
|
-
|
|
1369
|
-
// 操作
|
|
1370
|
-
h(
|
|
1371
|
-
'div',
|
|
1372
|
-
{ className: 'dsb-actions' },
|
|
1373
1463
|
h(
|
|
1374
1464
|
'button',
|
|
1375
|
-
{ className: 'dsb-btn dsb-btn-primary dsb-btn-sm', onClick:
|
|
1376
|
-
busy === '
|
|
1465
|
+
{ className: 'dsb-btn dsb-btn-primary dsb-btn-sm', onClick: doSkillShare, disabled: !!busy },
|
|
1466
|
+
busy === 'skillshare' ? '共享中…' : '立即共享',
|
|
1467
|
+
),
|
|
1468
|
+
h(
|
|
1469
|
+
'button',
|
|
1470
|
+
{ className: 'dsb-btn dsb-btn-sm', onClick: doSkillInspect, disabled: !!busy },
|
|
1471
|
+
busy === 'skillinspect' ? '体检中…' : '体检(不调模型)',
|
|
1472
|
+
),
|
|
1473
|
+
h(
|
|
1474
|
+
'button',
|
|
1475
|
+
{
|
|
1476
|
+
className: 'dsb-btn dsb-btn-sm',
|
|
1477
|
+
onClick: doSkillOptimize,
|
|
1478
|
+
disabled: !!busy || optRunning,
|
|
1479
|
+
title: '后台跑一轮:精炼 / 精简 / 合并重复 / 补工况 / 补踩坑 / 修死引用;出草稿,批准才落本机',
|
|
1480
|
+
},
|
|
1481
|
+
optRunning ? '优化中…' : '优化本机技能',
|
|
1377
1482
|
),
|
|
1378
1483
|
h(
|
|
1379
1484
|
'button',
|
|
1380
|
-
{ className: 'dsb-btn dsb-btn-sm', onClick:
|
|
1381
|
-
busy === '
|
|
1485
|
+
{ className: 'dsb-btn dsb-btn-sm', onClick: doSkillLearning, disabled: !!busy },
|
|
1486
|
+
busy === 'skilllearn' ? '读取中…' : `待批草稿(${pendingCount})`,
|
|
1382
1487
|
),
|
|
1383
1488
|
h(
|
|
1384
1489
|
'label',
|
|
@@ -1386,39 +1491,35 @@ function makePanel(React) {
|
|
|
1386
1491
|
h('input', {
|
|
1387
1492
|
type: 'checkbox',
|
|
1388
1493
|
className: 'dsb-chk',
|
|
1389
|
-
checked: !!(state.skills && state.skills.enabled),
|
|
1390
|
-
disabled: busy === 'skillcfg'
|
|
1494
|
+
checked: !!(state.skills && state.skills.enabled !== false),
|
|
1495
|
+
disabled: busy === 'skillcfg',
|
|
1391
1496
|
onChange: (e) => saveSkills({ enabled: e.target.checked }),
|
|
1392
1497
|
}),
|
|
1393
1498
|
h('span', { className: 'dsb-row-t' }, '自动同步'),
|
|
1394
1499
|
),
|
|
1395
1500
|
h(
|
|
1396
1501
|
'label',
|
|
1397
|
-
{ className: 'dsb-actions', style: { gap: 6 }, title: '
|
|
1502
|
+
{ className: 'dsb-actions', style: { gap: 6 }, title: '同名不同内容时调模型评估怎么合并;关掉只记录差异' },
|
|
1398
1503
|
h('input', {
|
|
1399
1504
|
type: 'checkbox',
|
|
1400
1505
|
className: 'dsb-chk',
|
|
1401
|
-
checked: !!(state.skills && state.skills.
|
|
1402
|
-
disabled: busy === 'skillcfg'
|
|
1403
|
-
onChange: (e) => saveSkills({
|
|
1506
|
+
checked: !!(state.skills && state.skills.share && state.skills.share.learn !== false),
|
|
1507
|
+
disabled: busy === 'skillcfg',
|
|
1508
|
+
onChange: (e) => saveSkills({ learn: e.target.checked }),
|
|
1404
1509
|
}),
|
|
1405
|
-
h('span', { className: 'dsb-row-t' }, '
|
|
1510
|
+
h('span', { className: 'dsb-row-t' }, '差异学习'),
|
|
1406
1511
|
),
|
|
1407
1512
|
h(
|
|
1408
1513
|
'label',
|
|
1409
|
-
{
|
|
1410
|
-
className: 'dsb-actions',
|
|
1411
|
-
style: { gap: 6 },
|
|
1412
|
-
title: '同步前先发现记忆插件的新优化:判断有没有必要 → 有必要就生成草稿 → 审核草稿 → 合格才通过',
|
|
1413
|
-
},
|
|
1514
|
+
{ className: 'dsb-actions', style: { gap: 6 }, title: '评估结论直接落到本机技能根(默认关:先出草稿,人批了再落)' },
|
|
1414
1515
|
h('input', {
|
|
1415
1516
|
type: 'checkbox',
|
|
1416
1517
|
className: 'dsb-chk',
|
|
1417
|
-
checked: !!(state.skills && state.skills.
|
|
1418
|
-
disabled: busy === 'skillcfg'
|
|
1419
|
-
onChange: (e) => saveSkills({
|
|
1518
|
+
checked: !!(state.skills && state.skills.share && state.skills.share.autoApply === true),
|
|
1519
|
+
disabled: busy === 'skillcfg',
|
|
1520
|
+
onChange: (e) => saveSkills({ autoApply: e.target.checked }),
|
|
1420
1521
|
}),
|
|
1421
|
-
h('span', { className: 'dsb-row-t' }, '
|
|
1522
|
+
h('span', { className: 'dsb-row-t' }, '评估通过直接落地'),
|
|
1422
1523
|
),
|
|
1423
1524
|
h('span', { className: 'dsb-row-t' }, '每'),
|
|
1424
1525
|
h('input', {
|
|
@@ -1429,7 +1530,7 @@ function makePanel(React) {
|
|
|
1429
1530
|
max: 10080,
|
|
1430
1531
|
step: 1,
|
|
1431
1532
|
value: skillIv || String((state.skills && state.skills.intervalMinutes) ?? 120),
|
|
1432
|
-
disabled: !(state.skills && state.skills.enabled) || busy === 'skillcfg',
|
|
1533
|
+
disabled: !(state.skills && state.skills.enabled !== false) || busy === 'skillcfg',
|
|
1433
1534
|
onChange: (e) => setSkillIv(e.target.value),
|
|
1434
1535
|
onBlur: (e) => {
|
|
1435
1536
|
const n = Number(e.target.value);
|
|
@@ -1439,58 +1540,95 @@ function makePanel(React) {
|
|
|
1439
1540
|
}),
|
|
1440
1541
|
h('span', { className: 'dsb-row-t' }, '分钟(默认 120)'),
|
|
1441
1542
|
),
|
|
1543
|
+
h(
|
|
1544
|
+
'div',
|
|
1545
|
+
{ className: 'dsb-row-d', style: { marginBottom: 10 } },
|
|
1546
|
+
`本机技能部署位置:${(state.skills && state.skills.localRoot) || '~/.agents/skills'}` +
|
|
1547
|
+
(shareRoot ? ` · 特征备份目录:${shareRoot}` : ''),
|
|
1548
|
+
),
|
|
1442
1549
|
|
|
1443
|
-
//
|
|
1444
|
-
|
|
1550
|
+
// 归属概览
|
|
1551
|
+
scopeInfo
|
|
1445
1552
|
? h(
|
|
1446
1553
|
'div',
|
|
1447
|
-
{ style: { marginTop:
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1554
|
+
{ style: { marginTop: 4, marginBottom: 10 } },
|
|
1555
|
+
h(
|
|
1556
|
+
'div',
|
|
1557
|
+
{ className: 'dsb-row-t', style: { marginBottom: 4 } },
|
|
1558
|
+
`归属:本机自研 ${(scopeInfo.counts && scopeInfo.counts.own) || 0} 个 · 市场第三方 ${(scopeInfo.counts && scopeInfo.counts.thirdParty) || 0} 个(第三方不同步、不优化)`,
|
|
1559
|
+
),
|
|
1560
|
+
(scopeInfo.thirdParty || []).length
|
|
1451
1561
|
? h(
|
|
1452
1562
|
'div',
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
'div',
|
|
1456
|
-
{ className: 'dsb-row-t', style: { marginBottom: 6 } },
|
|
1457
|
-
`母版更新:${skillScan.pull.length} 个${skillScan.auto === false ? '(仅比对,未执行)' : '(已自动拉取)'}`,
|
|
1458
|
-
),
|
|
1459
|
-
skillScan.pull.map((p) =>
|
|
1563
|
+
{ className: 'dsb-hist', style: { maxHeight: 110, overflowY: 'auto' } },
|
|
1564
|
+
(scopeInfo.thirdParty || []).map((x, i) =>
|
|
1460
1565
|
h(
|
|
1461
1566
|
'div',
|
|
1462
|
-
{
|
|
1567
|
+
{ key: `tp${i}`, style: { display: 'flex', gap: 8, alignItems: 'center' } },
|
|
1568
|
+
h('span', { style: { flex: 1 } }, `${x.name}(${x.by})`),
|
|
1463
1569
|
h(
|
|
1464
|
-
'
|
|
1465
|
-
{
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
),
|
|
1473
|
-
h('div', { className: 'dsb-row-d' }, p.reason || ''),
|
|
1570
|
+
'button',
|
|
1571
|
+
{
|
|
1572
|
+
className: 'dsb-btn dsb-btn-sm',
|
|
1573
|
+
disabled: !!busy,
|
|
1574
|
+
title: '误判成第三方?改成「自己编写」,之后就会参与共享与优化',
|
|
1575
|
+
onClick: () => setScope(x.name, 'own'),
|
|
1576
|
+
},
|
|
1577
|
+
'改为自己编写',
|
|
1474
1578
|
),
|
|
1475
1579
|
),
|
|
1476
1580
|
),
|
|
1477
1581
|
)
|
|
1478
|
-
:
|
|
1582
|
+
: h('div', { className: 'dsb-row-d' }, '没有识别到市场第三方技能'),
|
|
1583
|
+
)
|
|
1584
|
+
: null,
|
|
1479
1585
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1586
|
+
// 上次共享结果
|
|
1587
|
+
state.skills && state.skills.share && state.skills.share.lastResult
|
|
1588
|
+
? h(
|
|
1589
|
+
'div',
|
|
1590
|
+
{ className: 'dsb-row-d', style: { marginBottom: 8 } },
|
|
1591
|
+
(() => {
|
|
1592
|
+
const lr = state.skills.share.lastResult;
|
|
1593
|
+
return (
|
|
1594
|
+
`上次共享(${lr.reason || '定时'}):备份 ${lr.backup || 0} 个、他机 ${lr.peers || 0} 台、` +
|
|
1595
|
+
`缺失 ${lr.missing || 0}(已拉 ${lr.pulled || 0})、差异 ${lr.differ || 0}(评估 ${lr.evaluated || 0})、待批 ${lr.pending || 0}`
|
|
1596
|
+
);
|
|
1597
|
+
})(),
|
|
1598
|
+
)
|
|
1599
|
+
: null,
|
|
1600
|
+
|
|
1601
|
+
// 上次优化结果
|
|
1602
|
+
optLast
|
|
1603
|
+
? h(
|
|
1604
|
+
'div',
|
|
1605
|
+
{ className: 'dsb-row-d', style: { marginBottom: 8 } },
|
|
1606
|
+
`上次优化:体检 ${optLast.scanned || 0} 个(健康 ${optLast.healthy || 0},第三方跳过 ${optLast.skippedThirdParty || 0})、` +
|
|
1607
|
+
`出草稿 ${optLast.drafted || 0}、审核未过 ${optLast.rejected || 0}` +
|
|
1608
|
+
((optLast.draftedNames || []).length ? ` · ${optLast.draftedNames.join('、')}` : ''),
|
|
1609
|
+
)
|
|
1610
|
+
: null,
|
|
1611
|
+
|
|
1612
|
+
// 现状
|
|
1613
|
+
skillScan
|
|
1614
|
+
? h(
|
|
1615
|
+
'div',
|
|
1616
|
+
{ style: { marginTop: 12 } },
|
|
1617
|
+
|
|
1618
|
+
// 缺失(已拉 / 待拉)
|
|
1619
|
+
(skillScan.missing || []).length
|
|
1482
1620
|
? h(
|
|
1483
1621
|
'div',
|
|
1484
|
-
|
|
1622
|
+
null,
|
|
1485
1623
|
h(
|
|
1486
1624
|
'div',
|
|
1487
1625
|
{ className: 'dsb-row-t', style: { marginBottom: 6 } },
|
|
1488
|
-
|
|
1626
|
+
`本机没有、别人有:${skillScan.missing.length} 个`,
|
|
1489
1627
|
),
|
|
1490
|
-
skillScan.
|
|
1628
|
+
skillScan.missing.map((p) =>
|
|
1491
1629
|
h(
|
|
1492
1630
|
'div',
|
|
1493
|
-
{ className: 'dsb-row', key: p.name },
|
|
1631
|
+
{ className: 'dsb-row', key: `miss-${p.name}` },
|
|
1494
1632
|
h(
|
|
1495
1633
|
'div',
|
|
1496
1634
|
{ className: 'dsb-row-main' },
|
|
@@ -1498,439 +1636,181 @@ function makePanel(React) {
|
|
|
1498
1636
|
'div',
|
|
1499
1637
|
{ className: 'dsb-row-t' },
|
|
1500
1638
|
p.name,
|
|
1501
|
-
h('span', { className: 'dsb-tag dsb-tag-ok' },
|
|
1502
|
-
|
|
1503
|
-
h(
|
|
1504
|
-
'div',
|
|
1505
|
-
{ className: 'dsb-row-d' },
|
|
1506
|
-
`${p.reason}${BASIS_LABEL[p.basis] ? ` · ${BASIS_LABEL[p.basis]}` : ''}`,
|
|
1639
|
+
h('span', { className: 'dsb-tag dsb-tag-ok' }, '待拉取'),
|
|
1640
|
+
p.version ? h('span', { className: 'dsb-tag' }, `v${p.version}`) : null,
|
|
1507
1641
|
),
|
|
1642
|
+
h('div', { className: 'dsb-row-d' }, `来自 ${p.from || '未知特征本'}`),
|
|
1508
1643
|
),
|
|
1509
1644
|
),
|
|
1510
1645
|
),
|
|
1511
1646
|
)
|
|
1512
1647
|
: null,
|
|
1513
1648
|
|
|
1514
|
-
//
|
|
1515
|
-
skillScan.
|
|
1649
|
+
// 差异(交给学习评估)
|
|
1650
|
+
(skillScan.differ || []).length
|
|
1516
1651
|
? h(
|
|
1517
1652
|
'div',
|
|
1518
1653
|
{ style: { marginTop: 14 } },
|
|
1519
1654
|
h(
|
|
1520
1655
|
'div',
|
|
1521
1656
|
{ className: 'dsb-row-t', style: { marginBottom: 6 } },
|
|
1522
|
-
|
|
1657
|
+
`同名但内容不同:${skillScan.differ.length} 个(不覆盖,进学习评估)`,
|
|
1523
1658
|
),
|
|
1524
|
-
skillScan.
|
|
1659
|
+
skillScan.differ.map((p) =>
|
|
1525
1660
|
h(
|
|
1526
1661
|
'div',
|
|
1527
|
-
{ className: 'dsb-
|
|
1528
|
-
h('input', {
|
|
1529
|
-
type: 'checkbox',
|
|
1530
|
-
className: 'dsb-chk',
|
|
1531
|
-
checked: skillPicks.includes(p.name),
|
|
1532
|
-
onChange: () =>
|
|
1533
|
-
setSkillPicks((cur) =>
|
|
1534
|
-
cur.includes(p.name) ? cur.filter((x) => x !== p.name) : [...cur, p.name],
|
|
1535
|
-
),
|
|
1536
|
-
}),
|
|
1662
|
+
{ className: 'dsb-row', key: `diff-${p.name}` },
|
|
1537
1663
|
h(
|
|
1538
1664
|
'div',
|
|
1539
|
-
{ className: 'dsb-
|
|
1665
|
+
{ className: 'dsb-row-main' },
|
|
1540
1666
|
h(
|
|
1541
1667
|
'div',
|
|
1542
1668
|
{ className: 'dsb-row-t' },
|
|
1543
1669
|
p.name,
|
|
1544
|
-
h(
|
|
1545
|
-
'span',
|
|
1546
|
-
{ className: `dsb-tag ${p.action === 'overwrite' ? 'dsb-tag-warn' : ''}` },
|
|
1547
|
-
p.action === 'overwrite' ? '本地更新' : '内容不同',
|
|
1548
|
-
),
|
|
1670
|
+
h('span', { className: 'dsb-tag dsb-tag-warn' }, '内容不同'),
|
|
1549
1671
|
),
|
|
1550
1672
|
h(
|
|
1551
1673
|
'div',
|
|
1552
1674
|
{ className: 'dsb-row-d' },
|
|
1553
|
-
|
|
1554
|
-
p.masterVersion ? ` / 母版 v${p.masterVersion})` : p.localVersion ? ')' : ''
|
|
1555
|
-
}${BASIS_LABEL[p.basis] ? ` · ${BASIS_LABEL[p.basis]}` : ''}`,
|
|
1675
|
+
`来源 ${(p.peers || []).join('、')} · 不同指纹 ${p.distinctHashes || 1} 种`,
|
|
1556
1676
|
),
|
|
1557
|
-
p.diff && p.diff.changed
|
|
1558
|
-
? h(
|
|
1559
|
-
'div',
|
|
1560
|
-
{ className: 'dsb-row-d' },
|
|
1561
|
-
`母版独有 ${p.diff.masterOnly} 行、本机独有 ${p.diff.localOnly} 行(${p.diff.masterBytes} vs ${p.diff.localBytes} 字节)`,
|
|
1562
|
-
)
|
|
1563
|
-
: null,
|
|
1564
|
-
p.diff && p.diff.samples && p.diff.samples.length
|
|
1565
|
-
? h(
|
|
1566
|
-
'div',
|
|
1567
|
-
{
|
|
1568
|
-
className: 'dsb-row-d',
|
|
1569
|
-
style: { fontFamily: 'monospace', fontSize: 11, whiteSpace: 'pre-wrap' },
|
|
1570
|
-
},
|
|
1571
|
-
p.diff.samples.join('\n'),
|
|
1572
|
-
)
|
|
1573
|
-
: null,
|
|
1574
1677
|
),
|
|
1575
1678
|
),
|
|
1576
1679
|
),
|
|
1577
|
-
h(
|
|
1578
|
-
'div',
|
|
1579
|
-
{ className: 'dsb-actions', style: { marginTop: 10 } },
|
|
1580
|
-
h(
|
|
1581
|
-
'button',
|
|
1582
|
-
{
|
|
1583
|
-
className: 'dsb-btn dsb-btn-primary dsb-btn-sm',
|
|
1584
|
-
disabled: !!busy || !skillPicks.length,
|
|
1585
|
-
onClick: doSkillPush,
|
|
1586
|
-
},
|
|
1587
|
-
busy === 'skillpush' ? '推送中…' : `推送到母版(${skillPicks.length})`,
|
|
1588
|
-
),
|
|
1589
|
-
h(
|
|
1590
|
-
'button',
|
|
1591
|
-
{
|
|
1592
|
-
className: 'dsb-btn dsb-btn-sm',
|
|
1593
|
-
onClick: () => setSkillPicks(skillScan.push.map((x) => x.name)),
|
|
1594
|
-
},
|
|
1595
|
-
'全选',
|
|
1596
|
-
),
|
|
1597
|
-
),
|
|
1598
1680
|
)
|
|
1599
1681
|
: null,
|
|
1600
1682
|
|
|
1601
|
-
//
|
|
1683
|
+
// 计数
|
|
1602
1684
|
h(
|
|
1603
1685
|
'div',
|
|
1604
|
-
{ className: 'dsb-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
{
|
|
1608
|
-
busy === 'skilllog' ? '读取中…' : '查看母版修改记录',
|
|
1609
|
-
),
|
|
1610
|
-
h(
|
|
1611
|
-
'button',
|
|
1612
|
-
{
|
|
1613
|
-
className: 'dsb-btn dsb-btn-sm',
|
|
1614
|
-
onClick: loadSkillList,
|
|
1615
|
-
disabled: !!busy || !(state.skills && state.skills.masterRoot),
|
|
1616
|
-
},
|
|
1617
|
-
busy === 'skilllist' ? '读取中…' : '技能列表 / 设置属性',
|
|
1618
|
-
),
|
|
1619
|
-
skillLog
|
|
1620
|
-
? h(
|
|
1621
|
-
'span',
|
|
1622
|
-
{ className: 'dsb-row-d' },
|
|
1623
|
-
skillLog.exists
|
|
1624
|
-
? `${skillLog.rows.length} 条 · ${state.skills.masterRoot}\\_技能修改记录.md`
|
|
1625
|
-
: '记录文件尚未创建',
|
|
1626
|
-
)
|
|
1627
|
-
: null,
|
|
1686
|
+
{ className: 'dsb-row-d', style: { marginTop: 12 } },
|
|
1687
|
+
`本机自研 ${(skillScan.counts && skillScan.counts.local) || 0} 个 · ` +
|
|
1688
|
+
`一致 ${(skillScan.counts && skillScan.counts.same) || 0} 个 · ` +
|
|
1689
|
+
`他机特征本 ${(skillScan.counts && skillScan.counts.peers) || 0} 个`,
|
|
1628
1690
|
),
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
'div',
|
|
1632
|
-
{ className: 'dsb-hist', style: { marginTop: 8, maxHeight: 180, overflowY: 'auto' } },
|
|
1633
|
-
skillLog.rows.map((r, i) =>
|
|
1634
|
-
h(
|
|
1635
|
-
'div',
|
|
1636
|
-
{ key: i },
|
|
1637
|
-
`${r.time} · ${r.operation} · ${r.skill} · ${r.actor} · ${r.trigger}${
|
|
1638
|
-
r.detail ? ` · ${r.detail}` : ''
|
|
1639
|
-
}`,
|
|
1640
|
-
),
|
|
1641
|
-
),
|
|
1642
|
-
)
|
|
1643
|
-
: null,
|
|
1691
|
+
)
|
|
1692
|
+
: null,
|
|
1644
1693
|
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1694
|
+
// 体检结果
|
|
1695
|
+
inspectResult
|
|
1696
|
+
? h(
|
|
1697
|
+
'div',
|
|
1698
|
+
{ style: { marginTop: 14 } },
|
|
1699
|
+
h(
|
|
1700
|
+
'div',
|
|
1701
|
+
{ className: 'dsb-row-t', style: { marginBottom: 6 } },
|
|
1702
|
+
`体检:${inspectResult.scanned} 个(健康 ${inspectResult.healthy},第三方跳过 ${inspectResult.thirdParty})`,
|
|
1703
|
+
),
|
|
1704
|
+
(inspectResult.items || []).slice(0, 12).map((p) =>
|
|
1705
|
+
h(
|
|
1706
|
+
'div',
|
|
1707
|
+
{ className: 'dsb-row', key: `ins-${p.name}` },
|
|
1708
|
+
h(
|
|
1648
1709
|
'div',
|
|
1649
|
-
{
|
|
1710
|
+
{ className: 'dsb-row-main' },
|
|
1650
1711
|
h(
|
|
1651
1712
|
'div',
|
|
1652
|
-
{ className: 'dsb-row-t'
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1713
|
+
{ className: 'dsb-row-t' },
|
|
1714
|
+
p.name,
|
|
1715
|
+
h('span', { className: 'dsb-tag dsb-tag-warn' }, `分 ${p.score}`),
|
|
1716
|
+
h('span', { className: 'dsb-tag' }, p.actionLabel || p.action),
|
|
1656
1717
|
),
|
|
1657
|
-
skillScan.mnemon.applied.length
|
|
1658
|
-
? h(
|
|
1659
|
-
'div',
|
|
1660
|
-
{ className: 'dsb-hist', style: { maxHeight: 120, overflowY: 'auto' } },
|
|
1661
|
-
skillScan.mnemon.applied.map((a, i) =>
|
|
1662
|
-
h('div', { key: `ma${i}` }, `+ ${a.name}(${a.action === 'create' ? '新建' : '更新'})`),
|
|
1663
|
-
),
|
|
1664
|
-
)
|
|
1665
|
-
: null,
|
|
1666
|
-
skillScan.mnemon.skipped.length
|
|
1667
|
-
? h(
|
|
1668
|
-
'div',
|
|
1669
|
-
{ className: 'dsb-hist', style: { maxHeight: 140, overflowY: 'auto' } },
|
|
1670
|
-
skillScan.mnemon.skipped.map((s, i) =>
|
|
1671
|
-
h('div', { key: `ms${i}` }, `- ${s.name}:${s.detail || s.reason}`),
|
|
1672
|
-
),
|
|
1673
|
-
)
|
|
1674
|
-
: null,
|
|
1675
|
-
)
|
|
1676
|
-
: null,
|
|
1677
|
-
|
|
1678
|
-
// 优化闭环:发现了什么、入队了什么、审核通过什么
|
|
1679
|
-
skillScan.optimize && skillScan.optimize.discovered
|
|
1680
|
-
? h(
|
|
1681
|
-
'div',
|
|
1682
|
-
{ style: { marginTop: 10 } },
|
|
1683
1718
|
h(
|
|
1684
1719
|
'div',
|
|
1685
|
-
{ className: 'dsb-row-
|
|
1686
|
-
|
|
1687
|
-
`${skillScan.optimize.discovered.drafts} 份草稿 · 要做 ${(skillScan.optimize.todo || []).length} · ` +
|
|
1688
|
-
`不做 ${(skillScan.optimize.dropped || []).length} · 起草 ${(skillScan.optimize.pipeline?.stage?.drafted || []).length} · ` +
|
|
1689
|
-
`落地 ${(skillScan.optimize.accepted || []).length} · 结案 ${(skillScan.optimize.closed || []).length}`,
|
|
1690
|
-
),
|
|
1691
|
-
(skillScan.optimize.audited || []).length
|
|
1692
|
-
? h(
|
|
1693
|
-
'div',
|
|
1694
|
-
{ className: 'dsb-hist', style: { maxHeight: 140, overflowY: 'auto' } },
|
|
1695
|
-
skillScan.optimize.audited.map((a, i) =>
|
|
1696
|
-
h(
|
|
1697
|
-
'div',
|
|
1698
|
-
{ key: `oa${i}` },
|
|
1699
|
-
`${a.ok ? '✓' : '✗'} ${a.skillName}${a.source === 'mnemon-draft' ? '(记忆插件草稿)' : ''}${
|
|
1700
|
-
a.problems && a.problems.length ? ':' + a.problems.slice(0, 2).join(';') : ''
|
|
1701
|
-
}`,
|
|
1702
|
-
),
|
|
1703
|
-
),
|
|
1704
|
-
)
|
|
1705
|
-
: null,
|
|
1706
|
-
(skillScan.optimize.dropped || []).length
|
|
1707
|
-
? h(
|
|
1708
|
-
'div',
|
|
1709
|
-
{ className: 'dsb-hist', style: { maxHeight: 120, overflowY: 'auto' } },
|
|
1710
|
-
skillScan.optimize.dropped.map((d, i) =>
|
|
1711
|
-
h('div', { key: `od${i}` }, `- ${d.skillName}:${d.reason}`),
|
|
1712
|
-
),
|
|
1713
|
-
)
|
|
1714
|
-
: null,
|
|
1715
|
-
(skillScan.optimize.pipeline?.stage?.failed || []).length
|
|
1716
|
-
? h(
|
|
1717
|
-
'div',
|
|
1718
|
-
{ className: 'dsb-hist', style: { maxHeight: 120, overflowY: 'auto' } },
|
|
1719
|
-
skillScan.optimize.pipeline.stage.failed.map((f, i) =>
|
|
1720
|
-
h('div', { key: `of${i}` }, `✗ ${f.skillName}(${f.phase}):${f.reason}`),
|
|
1721
|
-
),
|
|
1722
|
-
)
|
|
1723
|
-
: null,
|
|
1724
|
-
h(
|
|
1725
|
-
'div',
|
|
1726
|
-
{ className: 'dsb-actions', style: { marginTop: 8, gap: 8 } },
|
|
1727
|
-
h(
|
|
1728
|
-
'button',
|
|
1729
|
-
{
|
|
1730
|
-
className: 'dsb-btn',
|
|
1731
|
-
disabled: busy === 'optrun',
|
|
1732
|
-
title: '立刻跑一轮:发现 → 起草 → 审核 → 落地 → 结案',
|
|
1733
|
-
onClick: async () => {
|
|
1734
|
-
setBusy('optrun');
|
|
1735
|
-
setNote({ tone: 'ok', text: '优化闭环:已提交,起草在后台跑(每条一次模型调用,约 20-60 秒)' });
|
|
1736
|
-
try {
|
|
1737
|
-
const r = await call('/skills/optimize/run', {});
|
|
1738
|
-
if (r.started === false) {
|
|
1739
|
-
setNote({ tone: 'warn', text: '上一轮还在跑,本次没有重复启动' });
|
|
1740
|
-
return;
|
|
1741
|
-
}
|
|
1742
|
-
// 后台跑,轮询看结果:最多盯 3 分钟,每 5 秒一次
|
|
1743
|
-
const startedAt = r.startedAt;
|
|
1744
|
-
for (let i = 0; i < 36; i++) {
|
|
1745
|
-
await new Promise((ok) => setTimeout(ok, 5000));
|
|
1746
|
-
const st = await call('/skills/optimize/state', {});
|
|
1747
|
-
if (st.running) {
|
|
1748
|
-
setNote({ tone: 'ok', text: `优化闭环起草中…(已 ${(i + 1) * 5} 秒)` });
|
|
1749
|
-
continue;
|
|
1750
|
-
}
|
|
1751
|
-
const lr = st.lastReport;
|
|
1752
|
-
if (!lr || (startedAt && lr.at && lr.at < startedAt)) {
|
|
1753
|
-
setNote({ tone: 'warn', text: '优化闭环已结束,但没有拿到本轮报告(看宿主日志)' });
|
|
1754
|
-
return;
|
|
1755
|
-
}
|
|
1756
|
-
setNote({
|
|
1757
|
-
tone: (lr.failed || []).length || (lr.errors || []).length ? 'warn' : 'ok',
|
|
1758
|
-
text:
|
|
1759
|
-
`优化闭环:起草 ${(lr.drafted || []).length}、落地 ${(lr.landed || []).length}、` +
|
|
1760
|
-
`失败 ${(lr.failed || []).length}、剩余 ${lr.remaining ?? '?'}` +
|
|
1761
|
-
((lr.errors || []).length ? `(首个错误:${String(lr.errors[0]).slice(0, 80)})` : ''),
|
|
1762
|
-
});
|
|
1763
|
-
return;
|
|
1764
|
-
}
|
|
1765
|
-
setNote({ tone: 'warn', text: '优化闭环仍在后台跑,稍后点「只看状态」看结果' });
|
|
1766
|
-
} catch (e) {
|
|
1767
|
-
setNote({ tone: 'warn', text: '优化闭环提交失败:' + (e && e.message ? e.message : String(e)) });
|
|
1768
|
-
} finally {
|
|
1769
|
-
setBusy(null);
|
|
1770
|
-
}
|
|
1771
|
-
},
|
|
1772
|
-
},
|
|
1773
|
-
busy === 'optrun' ? '后台起草中…' : '跑一轮优化闭环',
|
|
1774
|
-
),
|
|
1775
|
-
h(
|
|
1776
|
-
'button',
|
|
1777
|
-
{
|
|
1778
|
-
className: 'dsb-btn',
|
|
1779
|
-
disabled: busy === 'optdry',
|
|
1780
|
-
title: '读账本与运行态,不改任何文件',
|
|
1781
|
-
onClick: async () => {
|
|
1782
|
-
setBusy('optdry');
|
|
1783
|
-
try {
|
|
1784
|
-
const st = await call('/skills/optimize/state', {});
|
|
1785
|
-
const lv = st.ledgerView || {};
|
|
1786
|
-
const lr = st.lastReport;
|
|
1787
|
-
setNote({
|
|
1788
|
-
tone: 'ok',
|
|
1789
|
-
text:
|
|
1790
|
-
`账本:待处理 ${lv.pending ?? '?'}(已有草稿 ${lv.withDraft ?? 0})、` +
|
|
1791
|
-
`已结案 ${lv.adopted ?? '?'}、generation ${lv.generation ?? '?'}` +
|
|
1792
|
-
(st.running ? ' · 后台正在起草' : '') +
|
|
1793
|
-
(lr
|
|
1794
|
-
? ` · 上次:起草 ${(lr.drafted || []).length}、落地 ${(lr.landed || []).length}、剩余 ${lr.remaining}`
|
|
1795
|
-
: ' · 还没有手动跑过'),
|
|
1796
|
-
});
|
|
1797
|
-
} catch (e) {
|
|
1798
|
-
setNote({ tone: 'warn', text: '读状态失败:' + (e && e.message ? e.message : String(e)) });
|
|
1799
|
-
} finally {
|
|
1800
|
-
setBusy(null);
|
|
1801
|
-
}
|
|
1802
|
-
},
|
|
1803
|
-
},
|
|
1804
|
-
'只看状态',
|
|
1805
|
-
),
|
|
1720
|
+
{ className: 'dsb-row-d' },
|
|
1721
|
+
(p.issues || []).map((x) => `[${x.code}] ${x.why}`).join(';').slice(0, 260),
|
|
1806
1722
|
),
|
|
1807
|
-
)
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
// 统计
|
|
1811
|
-
h(
|
|
1812
|
-
'div',
|
|
1813
|
-
{ className: 'dsb-row-d', style: { marginTop: 12 } },
|
|
1814
|
-
`一致 ${skillScan.same.length} 个 · 本地独有 ${skillScan.localOnly.length} 个(跳过,不推母版)`,
|
|
1723
|
+
),
|
|
1724
|
+
),
|
|
1815
1725
|
),
|
|
1726
|
+
(inspectResult.items || []).length > 12
|
|
1727
|
+
? h('div', { className: 'dsb-row-d' }, `…还有 ${inspectResult.items.length - 12} 个,出草稿时按分数从高往低处理`)
|
|
1728
|
+
: null,
|
|
1816
1729
|
)
|
|
1817
1730
|
: null,
|
|
1818
1731
|
|
|
1819
|
-
//
|
|
1820
|
-
|
|
1732
|
+
// 待批草稿
|
|
1733
|
+
learningList
|
|
1821
1734
|
? h(
|
|
1822
1735
|
'div',
|
|
1823
1736
|
{ style: { marginTop: 16 } },
|
|
1824
1737
|
h(
|
|
1825
1738
|
'div',
|
|
1826
|
-
{ className: 'dsb-
|
|
1827
|
-
|
|
1828
|
-
h(
|
|
1829
|
-
'button',
|
|
1830
|
-
{
|
|
1831
|
-
className: 'dsb-btn dsb-btn-sm',
|
|
1832
|
-
onClick: () =>
|
|
1833
|
-
setListPicks(
|
|
1834
|
-
skillList.items.length === listPicks.length ? [] : skillList.items.map((i) => i.name),
|
|
1835
|
-
),
|
|
1836
|
-
},
|
|
1837
|
-
skillList.items.length === listPicks.length ? '全不选' : '全选',
|
|
1838
|
-
),
|
|
1839
|
-
),
|
|
1840
|
-
h(
|
|
1841
|
-
'p',
|
|
1842
|
-
{ className: 'dsb-sub', style: { marginBottom: 10 } },
|
|
1843
|
-
'属性只存本机。改属性会执行实质动作:设「本机独属」删母版、设「本机不拉取」删本机、设「同步」推送到母版——对母版的改动都会写进修改记录。',
|
|
1844
|
-
),
|
|
1845
|
-
h(
|
|
1846
|
-
'div',
|
|
1847
|
-
{ className: 'dsb-actions', style: { marginBottom: 10 } },
|
|
1848
|
-
h(
|
|
1849
|
-
'button',
|
|
1850
|
-
{
|
|
1851
|
-
className: 'dsb-btn dsb-btn-sm',
|
|
1852
|
-
disabled: !!busy || !listPicks.length,
|
|
1853
|
-
onClick: () => changeMode('sync'),
|
|
1854
|
-
},
|
|
1855
|
-
busy === 'mode' ? '处理中…' : `设为同步(${listPicks.length})`,
|
|
1856
|
-
),
|
|
1857
|
-
h(
|
|
1858
|
-
'button',
|
|
1859
|
-
{
|
|
1860
|
-
className: 'dsb-btn dsb-btn-sm',
|
|
1861
|
-
disabled: !!busy || !listPicks.length,
|
|
1862
|
-
onClick: () => changeMode('local'),
|
|
1863
|
-
},
|
|
1864
|
-
'设为本机独属',
|
|
1865
|
-
),
|
|
1866
|
-
h(
|
|
1867
|
-
'button',
|
|
1868
|
-
{
|
|
1869
|
-
className: 'dsb-btn dsb-btn-sm',
|
|
1870
|
-
disabled: !!busy || !listPicks.length,
|
|
1871
|
-
onClick: () => changeMode('master'),
|
|
1872
|
-
},
|
|
1873
|
-
'设为本机不拉取',
|
|
1874
|
-
),
|
|
1739
|
+
{ className: 'dsb-row-t', style: { marginBottom: 6 } },
|
|
1740
|
+
`待批草稿:${learningList.length} 份(批准才改本机技能,批准前自动备份)`,
|
|
1875
1741
|
),
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
{ style: { maxHeight: 300, overflowY: 'auto' } },
|
|
1879
|
-
skillList.items.map((it) => {
|
|
1880
|
-
const modeLabel =
|
|
1881
|
-
it.mode === 'sync' ? '同步' : it.mode === 'local' ? '本机独属' : '本机不拉取';
|
|
1882
|
-
const tagCls =
|
|
1883
|
-
it.mode === 'sync' ? 'dsb-tag-ok' : it.mode === 'local' ? '' : 'dsb-tag-warn';
|
|
1884
|
-
const statusText =
|
|
1885
|
-
it.status === 'same'
|
|
1886
|
-
? '一致'
|
|
1887
|
-
: it.status === 'diff'
|
|
1888
|
-
? `内容不同${it.masterVersion && it.localVersion ? `(母版 v${it.masterVersion} / 本机 v${it.localVersion})` : ''}${
|
|
1889
|
-
it.verdict === 'local'
|
|
1890
|
-
? ' · 本机新'
|
|
1891
|
-
: it.verdict === 'master'
|
|
1892
|
-
? ' · 母版新'
|
|
1893
|
-
: it.verdict === 'unknown'
|
|
1894
|
-
? ' · 待确认'
|
|
1895
|
-
: ''
|
|
1896
|
-
}`
|
|
1897
|
-
: it.status === 'local-only'
|
|
1898
|
-
? '仅本机有'
|
|
1899
|
-
: it.status === 'missing-local'
|
|
1900
|
-
? '仅母版有'
|
|
1901
|
-
: '母版有·本机不拉取';
|
|
1902
|
-
const statusHint =
|
|
1903
|
-
it.status === 'diff' && it.hint
|
|
1904
|
-
? `${it.hint}${BASIS_LABEL[it.basis] ? ` · ${BASIS_LABEL[it.basis]}` : ''}`
|
|
1905
|
-
: '';
|
|
1906
|
-
return h(
|
|
1907
|
-
'div',
|
|
1908
|
-
{ className: 'dsb-ws', key: it.name },
|
|
1909
|
-
h('input', {
|
|
1910
|
-
type: 'checkbox',
|
|
1911
|
-
className: 'dsb-chk',
|
|
1912
|
-
checked: listPicks.includes(it.name),
|
|
1913
|
-
onChange: () =>
|
|
1914
|
-
setListPicks((cur) =>
|
|
1915
|
-
cur.includes(it.name) ? cur.filter((x) => x !== it.name) : [...cur, it.name],
|
|
1916
|
-
),
|
|
1917
|
-
}),
|
|
1742
|
+
learningList.length
|
|
1743
|
+
? learningList.slice(0, 20).map((it) =>
|
|
1918
1744
|
h(
|
|
1919
1745
|
'div',
|
|
1920
|
-
{ className: 'dsb-ws
|
|
1746
|
+
{ className: 'dsb-ws', key: `ln-${it.skill}` },
|
|
1921
1747
|
h(
|
|
1922
1748
|
'div',
|
|
1923
|
-
{ className: 'dsb-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1749
|
+
{ className: 'dsb-ws-body' },
|
|
1750
|
+
h(
|
|
1751
|
+
'div',
|
|
1752
|
+
{ className: 'dsb-row-t' },
|
|
1753
|
+
it.skill,
|
|
1754
|
+
h('span', { className: 'dsb-tag dsb-tag-ok' }, it.label || it.action),
|
|
1755
|
+
it.basis ? h('span', { className: 'dsb-tag' }, it.basis) : null,
|
|
1756
|
+
it.confidence ? h('span', { className: 'dsb-tag' }, `置信 ${it.confidence}`) : null,
|
|
1757
|
+
it.peerFeature ? h('span', { className: 'dsb-tag' }, `来自 ${it.peerFeature}`) : null,
|
|
1758
|
+
),
|
|
1759
|
+
h('div', { className: 'dsb-row-d' }, String(it.reason || '').slice(0, 300)),
|
|
1760
|
+
it.audit && it.audit.problems && it.audit.problems.length
|
|
1761
|
+
? h('div', { className: 'dsb-row-d' }, `审核问题:${it.audit.problems.join(';')}`)
|
|
1762
|
+
: null,
|
|
1763
|
+
h(
|
|
1764
|
+
'div',
|
|
1765
|
+
{ className: 'dsb-actions', style: { marginTop: 6, gap: 6 } },
|
|
1766
|
+
h(
|
|
1767
|
+
'button',
|
|
1768
|
+
{ className: 'dsb-btn dsb-btn-sm', onClick: () => viewDraft(it.skill), disabled: !!busy },
|
|
1769
|
+
'看草稿',
|
|
1770
|
+
),
|
|
1771
|
+
h(
|
|
1772
|
+
'button',
|
|
1773
|
+
{
|
|
1774
|
+
className: 'dsb-btn dsb-btn-primary dsb-btn-sm',
|
|
1775
|
+
onClick: () => approveDraft(it.skill),
|
|
1776
|
+
disabled: !!busy,
|
|
1777
|
+
},
|
|
1778
|
+
busy === `approve:${it.skill}` ? '落地中…' : '批准落地',
|
|
1779
|
+
),
|
|
1780
|
+
h(
|
|
1781
|
+
'button',
|
|
1782
|
+
{ className: 'dsb-btn dsb-btn-sm', onClick: () => rejectDraft(it.skill), disabled: !!busy },
|
|
1783
|
+
'驳回',
|
|
1784
|
+
),
|
|
1785
|
+
),
|
|
1927
1786
|
),
|
|
1928
|
-
h('div', { className: 'dsb-row-d' }, statusText),
|
|
1929
|
-
statusHint ? h('div', { className: 'dsb-row-d' }, statusHint) : null,
|
|
1930
1787
|
),
|
|
1931
|
-
)
|
|
1932
|
-
}),
|
|
1933
|
-
|
|
1788
|
+
)
|
|
1789
|
+
: h('div', { className: 'dsb-row-d' }, '没有待批草稿:点「优化本机技能」出一批'),
|
|
1790
|
+
draftPreview
|
|
1791
|
+
? h(
|
|
1792
|
+
'div',
|
|
1793
|
+
null,
|
|
1794
|
+
h(
|
|
1795
|
+
'div',
|
|
1796
|
+
{ className: 'dsb-actions', style: { marginTop: 8, marginBottom: 4 } },
|
|
1797
|
+
h('span', { className: 'dsb-row-t' }, `草稿预览:${draftPreview.name}`),
|
|
1798
|
+
h(
|
|
1799
|
+
'button',
|
|
1800
|
+
{ className: 'dsb-btn dsb-btn-sm', onClick: () => setDraftPreview(null) },
|
|
1801
|
+
'收起',
|
|
1802
|
+
),
|
|
1803
|
+
),
|
|
1804
|
+
h(
|
|
1805
|
+
'pre',
|
|
1806
|
+
{
|
|
1807
|
+
className: 'dsb-hist',
|
|
1808
|
+
style: { maxHeight: 320, overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 11 },
|
|
1809
|
+
},
|
|
1810
|
+
draftPreview.text || '(空)',
|
|
1811
|
+
),
|
|
1812
|
+
)
|
|
1813
|
+
: null,
|
|
1934
1814
|
)
|
|
1935
1815
|
: null,
|
|
1936
1816
|
),
|