tt-help-cli-ycl 1.4.11 → 1.4.13

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.
@@ -683,9 +683,18 @@ export function startWatchServer(
683
683
 
684
684
  if (req.method === "GET" && routePath === "/api/comment-tasks") {
685
685
  const limit = parseInt(params.limit) || 1;
686
- const tasks = store.getPendingCommentTasks(limit);
686
+ const locationsParam = params.locations || "";
687
+ const locations = locationsParam
688
+ ? locationsParam
689
+ .split(",")
690
+ .map((s) => s.trim().toUpperCase())
691
+ .filter(Boolean)
692
+ : null;
693
+ const tasks = store.getPendingCommentTasks(limit, locations);
687
694
  const ts = new Date().toISOString().slice(11, 19);
688
- console.error(`[JOB ${ts}] COMMENT-TASKS: ${tasks.length} tasks`);
695
+ console.error(
696
+ `[JOB ${ts}] COMMENT-TASKS: ${tasks.length} tasks${locations ? " (locations: " + locations.join(",") + ")" : ""}`,
697
+ );
689
698
  sendJSON(res, 200, { total: tasks.length, tasks });
690
699
  return;
691
700
  }
@@ -1491,6 +1500,327 @@ export function startWatchServer(
1491
1500
  return;
1492
1501
  }
1493
1502
 
1503
+ // ====== 自学习系统 API ======
1504
+
1505
+ // GET /api/tags/selflearn/status — 自学习系统状态总览
1506
+ if (req.method === "GET" && routePath === "/api/tags/selflearn/status") {
1507
+ try {
1508
+ const { getSelfLearnStatus } = await import("./tag-selflearn.js");
1509
+ const status = getSelfLearnStatus();
1510
+ sendJSON(res, 200, status);
1511
+ } catch (e) {
1512
+ sendJSON(res, 500, { error: e.message });
1513
+ }
1514
+ return;
1515
+ }
1516
+
1517
+ // POST /api/tags/selflearn/classify-dead — 分类所有 dead tag
1518
+ if (
1519
+ req.method === "POST" &&
1520
+ routePath === "/api/tags/selflearn/classify-dead"
1521
+ ) {
1522
+ try {
1523
+ const { classifyDeadTags } = await import("./tag-selflearn.js");
1524
+ const body = await readBody(req);
1525
+ const result = classifyDeadTags(body?.country || null);
1526
+ sendJSON(res, 200, result);
1527
+ } catch (e) {
1528
+ sendJSON(res, 500, { error: e.message });
1529
+ }
1530
+ return;
1531
+ }
1532
+
1533
+ // POST /api/tags/selflearn/cleanup-truly-dead — 删除真正不存在的 dead tag
1534
+ if (
1535
+ req.method === "POST" &&
1536
+ routePath === "/api/tags/selflearn/cleanup-truly-dead"
1537
+ ) {
1538
+ try {
1539
+ const { cleanupTrulyDeadTags } = await import("./tag-selflearn.js");
1540
+ const result = cleanupTrulyDeadTags();
1541
+ sendJSON(res, 200, result);
1542
+ } catch (e) {
1543
+ sendJSON(res, 500, { error: e.message });
1544
+ }
1545
+ return;
1546
+ }
1547
+
1548
+ // GET /api/tags/selflearn/dead-stats?country=DE — dead tag 分类统计
1549
+ if (
1550
+ req.method === "GET" &&
1551
+ routePath === "/api/tags/selflearn/dead-stats"
1552
+ ) {
1553
+ try {
1554
+ const { getDeadTagStats } = await import("./tag-selflearn.js");
1555
+ const country = params.country || null;
1556
+ const stats = getDeadTagStats(country);
1557
+ sendJSON(res, 200, stats || {});
1558
+ } catch (e) {
1559
+ sendJSON(res, 500, { error: e.message });
1560
+ }
1561
+ return;
1562
+ }
1563
+
1564
+ // POST /api/tags/selflearn/init-directions — 初始化某国的探索方向
1565
+ if (
1566
+ req.method === "POST" &&
1567
+ routePath === "/api/tags/selflearn/init-directions"
1568
+ ) {
1569
+ try {
1570
+ const { initDefaultDirections } = await import("./tag-selflearn.js");
1571
+ const body = await readBody(req);
1572
+ const country = body?.country;
1573
+ if (!country) {
1574
+ sendJSON(res, 400, { error: "country 参数必填" });
1575
+ return;
1576
+ }
1577
+ const result = initDefaultDirections(country);
1578
+ sendJSON(res, 200, {
1579
+ ok: true,
1580
+ count: result.length,
1581
+ directions: result,
1582
+ });
1583
+ } catch (e) {
1584
+ sendJSON(res, 500, { error: e.message });
1585
+ }
1586
+ return;
1587
+ }
1588
+
1589
+ // GET /api/tags/selflearn/directions?country=DE&level=1 — 获取方向列表
1590
+ if (
1591
+ req.method === "GET" &&
1592
+ routePath === "/api/tags/selflearn/directions"
1593
+ ) {
1594
+ try {
1595
+ const { getDirections } = await import("./tag-selflearn.js");
1596
+ const country = params.country;
1597
+ const level = parseInt(params.level) || 1;
1598
+ if (!country) {
1599
+ sendJSON(res, 400, { error: "country 参数必填" });
1600
+ return;
1601
+ }
1602
+ const directions = getDirections(country, level);
1603
+ sendJSON(res, 200, directions);
1604
+ } catch (e) {
1605
+ sendJSON(res, 500, { error: e.message });
1606
+ }
1607
+ return;
1608
+ }
1609
+
1610
+ // GET /api/tags/selflearn/cross-country?country=ES — 跨国家推荐
1611
+ if (
1612
+ req.method === "GET" &&
1613
+ routePath === "/api/tags/selflearn/cross-country"
1614
+ ) {
1615
+ try {
1616
+ const { getCrossCountryRecommendations } =
1617
+ await import("./tag-selflearn.js");
1618
+ const country = params.country;
1619
+ if (!country) {
1620
+ sendJSON(res, 400, { error: "country 参数必填" });
1621
+ return;
1622
+ }
1623
+ const limit = parseInt(params.limit) || 20;
1624
+ const recommendations = getCrossCountryRecommendations(
1625
+ country,
1626
+ limit,
1627
+ );
1628
+ sendJSON(res, 200, recommendations);
1629
+ } catch (e) {
1630
+ sendJSON(res, 500, { error: e.message });
1631
+ }
1632
+ return;
1633
+ }
1634
+
1635
+ // POST /api/tags/selflearn/record-cross-country — 记录跨国家学习结果
1636
+ if (
1637
+ req.method === "POST" &&
1638
+ routePath === "/api/tags/selflearn/record-cross-country"
1639
+ ) {
1640
+ try {
1641
+ const { recordCrossCountryLearning } =
1642
+ await import("./tag-selflearn.js");
1643
+ const body = await readBody(req);
1644
+ const {
1645
+ sourceCountry,
1646
+ sourceTag,
1647
+ targetCountry,
1648
+ targetTag,
1649
+ resultStatus,
1650
+ score,
1651
+ } = body || {};
1652
+ recordCrossCountryLearning(
1653
+ sourceCountry,
1654
+ sourceTag,
1655
+ targetCountry,
1656
+ targetTag,
1657
+ resultStatus,
1658
+ score,
1659
+ );
1660
+ sendJSON(res, 200, { ok: true });
1661
+ } catch (e) {
1662
+ sendJSON(res, 500, { error: e.message });
1663
+ }
1664
+ return;
1665
+ }
1666
+
1667
+ // POST /api/tags/selflearn/record-experiment — 记录实验
1668
+ if (
1669
+ req.method === "POST" &&
1670
+ routePath === "/api/tags/selflearn/record-experiment"
1671
+ ) {
1672
+ try {
1673
+ const { recordExperiment } = await import("./tag-selflearn.js");
1674
+ const body = await readBody(req);
1675
+ const result = recordExperiment(body);
1676
+ sendJSON(res, 200, result);
1677
+ } catch (e) {
1678
+ sendJSON(res, 500, { error: e.message });
1679
+ }
1680
+ return;
1681
+ }
1682
+
1683
+ // GET /api/tags/selflearn/experiments?country=DE&limit=10 — 获取实验历史
1684
+ if (
1685
+ req.method === "GET" &&
1686
+ routePath === "/api/tags/selflearn/experiments"
1687
+ ) {
1688
+ try {
1689
+ const { getExperimentHistory } = await import("./tag-selflearn.js");
1690
+ const country = params.country;
1691
+ if (!country) {
1692
+ sendJSON(res, 400, { error: "country 参数必填" });
1693
+ return;
1694
+ }
1695
+ const limit = parseInt(params.limit) || 10;
1696
+ const experiments = getExperimentHistory(country, limit);
1697
+ sendJSON(res, 200, experiments);
1698
+ } catch (e) {
1699
+ sendJSON(res, 500, { error: e.message });
1700
+ }
1701
+ return;
1702
+ }
1703
+
1704
+ // POST /api/tags/selflearn/analyst — 运行 LLM 分析师
1705
+ if (
1706
+ req.method === "POST" &&
1707
+ routePath === "/api/tags/selflearn/analyst"
1708
+ ) {
1709
+ try {
1710
+ const { runLLMAnalyst } = await import("./tag-selflearn.js");
1711
+ const body = await readBody(req);
1712
+ const countries = body?.countries || null;
1713
+ const report = await runLLMAnalyst(countries);
1714
+ sendJSON(res, 200, report);
1715
+ } catch (e) {
1716
+ sendJSON(res, 500, { error: e.message });
1717
+ }
1718
+ return;
1719
+ }
1720
+
1721
+ // GET /api/tags/selflearn/analyst/latest — 获取最新分析师报告
1722
+ if (
1723
+ req.method === "GET" &&
1724
+ routePath === "/api/tags/selflearn/analyst/latest"
1725
+ ) {
1726
+ try {
1727
+ const { getLatestAnalystReport } = await import("./tag-selflearn.js");
1728
+ const report = getLatestAnalystReport();
1729
+ sendJSON(res, 200, report || {});
1730
+ } catch (e) {
1731
+ sendJSON(res, 500, { error: e.message });
1732
+ }
1733
+ return;
1734
+ }
1735
+
1736
+ // POST /api/tags/selflearn/analyst/execute-cleanup — 执行分析师建议的清理
1737
+ if (
1738
+ req.method === "POST" &&
1739
+ routePath === "/api/tags/selflearn/analyst/execute-cleanup"
1740
+ ) {
1741
+ try {
1742
+ const { executeAnalystCleanup } = await import("./tag-selflearn.js");
1743
+ const result = executeAnalystCleanup();
1744
+ sendJSON(res, 200, result);
1745
+ } catch (e) {
1746
+ sendJSON(res, 500, { error: e.message });
1747
+ }
1748
+ return;
1749
+ }
1750
+
1751
+ // POST /api/tags/selflearn/discover-layered — 分层探索生成 tag
1752
+ if (
1753
+ req.method === "POST" &&
1754
+ routePath === "/api/tags/selflearn/discover-layered"
1755
+ ) {
1756
+ try {
1757
+ const {
1758
+ buildLayeredDiscoverPrompt,
1759
+ getCrossCountryRecommendations,
1760
+ getExperimentHistory,
1761
+ } = await import("./tag-selflearn.js");
1762
+ const { callLLM, parseTagsFromResponse, normalizeTag } =
1763
+ await import("./tag-service.js");
1764
+ const body = await readBody(req);
1765
+ const { country, level = 1, directionName, count = 10 } = body || {};
1766
+
1767
+ if (!country) {
1768
+ sendJSON(res, 400, { error: "country 参数必填" });
1769
+ return;
1770
+ }
1771
+
1772
+ // 收集分层探索所需数据
1773
+ const storeTags = store.getTagsByCountry(country, 50);
1774
+ const storeDead = store.getDeadTags(country);
1775
+ const crossCountryTips = getCrossCountryRecommendations(country, 5);
1776
+ const experimentHistory = getExperimentHistory(country, 5);
1777
+
1778
+ const prompt = buildLayeredDiscoverPrompt(country, {
1779
+ level,
1780
+ existingProductive: storeTags,
1781
+ existingDead: storeDead,
1782
+ directionName,
1783
+ experimentHistory,
1784
+ crossCountryTips,
1785
+ });
1786
+
1787
+ console.error(
1788
+ `[selflearn] ${country} L${level} discover (direction: ${directionName || "auto"})`,
1789
+ );
1790
+ const content = await callLLM(prompt, 0.7);
1791
+ const tags = parseTagsFromResponse(content);
1792
+
1793
+ // 全局去重
1794
+ const { getAllExistingTagNames } = await import("./db-tags.js");
1795
+ const allExisting = getAllExistingTagNames();
1796
+ const unique = [...new Set(tags)]
1797
+ .filter((t) => !allExisting.has(t))
1798
+ .slice(0, count);
1799
+
1800
+ // 入库
1801
+ const inserted = [];
1802
+ for (const tag of unique) {
1803
+ const result = store.insertTag(tag, [country], "llm");
1804
+ if (result.inserted) inserted.push(result.tag);
1805
+ }
1806
+
1807
+ sendJSON(res, 200, {
1808
+ country,
1809
+ level,
1810
+ directionName,
1811
+ generated: tags.length,
1812
+ afterDedup: unique.length,
1813
+ inserted: inserted.length,
1814
+ tags: inserted,
1815
+ });
1816
+ } catch (e) {
1817
+ sendJSON(res, 500, { error: e.message });
1818
+ }
1819
+ return;
1820
+ }
1821
+
1822
+ // ====== 原有静态资源 ======
1823
+
1494
1824
  if (
1495
1825
  req.method === "GET" &&
1496
1826
  (routePath === "/" || routePath === "/index.html")