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.
- package/package.json +1 -1
- package/src/cli/comments.js +37 -6
- package/src/lib/args.js +8 -0
- package/src/lib/constants.js +5 -2
- package/src/watch/data-store.js +32 -16
- package/src/watch/db-schema.js +82 -0
- package/src/watch/db-stats.js +4 -4
- package/src/watch/db-tags.js +19 -0
- package/src/watch/public/app.js +1 -35
- package/src/watch/public/index.html +0 -5
- package/src/watch/server.js +332 -2
- package/src/watch/tag-selflearn.js +986 -0
package/src/watch/server.js
CHANGED
|
@@ -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
|
|
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(
|
|
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")
|