cortico-world-hands 0.5.0 → 0.6.1
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/dist/index.js +141 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,7 +21,8 @@ var HANDS_DEFAULTS = {
|
|
|
21
21
|
honchoBaseUrl: "https://api.honcho.dev",
|
|
22
22
|
honchoWorkspace: "dsh",
|
|
23
23
|
honchoPeerId: "morse",
|
|
24
|
-
honchoSessionId: "yixin"
|
|
24
|
+
honchoSessionId: "yixin",
|
|
25
|
+
hookGrams: "both"
|
|
25
26
|
};
|
|
26
27
|
var KEY_CODES = {
|
|
27
28
|
back: 4,
|
|
@@ -366,10 +367,11 @@ var HOOK_STOP = /* @__PURE__ */ new Set([
|
|
|
366
367
|
"\u4E0D\u662F",
|
|
367
368
|
"\u6CA1\u6709"
|
|
368
369
|
]);
|
|
369
|
-
function keywordsOf(text, limit = 400) {
|
|
370
|
+
function keywordsOf(text, mode = "both", limit = 400) {
|
|
370
371
|
const clean = text.replace(/^\[(语音|打字|回答|互动|画面)\]\s*/g, "").replace(/^(主人|mokils)\s*[::]\s*/g, "").replace(/[^\u4e00-\u9fffA-Za-z0-9]/g, "");
|
|
371
372
|
const out = /* @__PURE__ */ new Set();
|
|
372
|
-
|
|
373
|
+
const sizes = mode === "two" ? [2] : mode === "three" ? [3] : [2, 3];
|
|
374
|
+
for (const n of sizes) {
|
|
373
375
|
for (let i = 0; i + n <= clean.length; i += 1) {
|
|
374
376
|
const g = clean.slice(i, i + n);
|
|
375
377
|
if (HOOK_STOP.has(g)) continue;
|
|
@@ -379,8 +381,8 @@ function keywordsOf(text, limit = 400) {
|
|
|
379
381
|
}
|
|
380
382
|
return [...out];
|
|
381
383
|
}
|
|
382
|
-
function hookHits(text, rows, limit = 4) {
|
|
383
|
-
const grams = keywordsOf(text);
|
|
384
|
+
function hookHits(text, rows, limit = 4, mode = "both") {
|
|
385
|
+
const grams = keywordsOf(text, mode);
|
|
384
386
|
if (grams.length === 0) return [];
|
|
385
387
|
const scored = [];
|
|
386
388
|
for (const row of rows) {
|
|
@@ -1028,6 +1030,35 @@ var Hands = class {
|
|
|
1028
1030
|
}
|
|
1029
1031
|
return { ok: res.status === 200 && body.ok !== false, detail: flat(body.detail || body.error || "", 80) };
|
|
1030
1032
|
}
|
|
1033
|
+
/** 日记:写一篇。 */
|
|
1034
|
+
async diaryWrite(title, text, date) {
|
|
1035
|
+
const cfg = this.cfg();
|
|
1036
|
+
const credential = this.readToken();
|
|
1037
|
+
const t = credential ? `&t=${encodeURIComponent(credential)}` : "";
|
|
1038
|
+
const d = date ? `&date=${encodeURIComponent(date)}` : "";
|
|
1039
|
+
const path = `/hand/diary?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}${d}${t}`;
|
|
1040
|
+
const res = await get(this.port(cfg.handPort), path, ACTION_TIMEOUT_MS);
|
|
1041
|
+
let body = {};
|
|
1042
|
+
try {
|
|
1043
|
+
body = JSON.parse(res.body.toString("utf8"));
|
|
1044
|
+
} catch {
|
|
1045
|
+
}
|
|
1046
|
+
return { ok: res.status === 200 && body.ok !== false, detail: flat(body.detail || body.error || "", 60) };
|
|
1047
|
+
}
|
|
1048
|
+
/** 日记:读列表。 */
|
|
1049
|
+
async diaryList(q) {
|
|
1050
|
+
const cfg = this.cfg();
|
|
1051
|
+
const credential = this.readToken();
|
|
1052
|
+
const t = credential ? `&t=${encodeURIComponent(credential)}` : "";
|
|
1053
|
+
const qq = q ? `?q=${encodeURIComponent(q)}${t}` : t ? `?${t.slice(1)}` : "";
|
|
1054
|
+
const res = await get(this.port(cfg.handPort), `/hand/diary${qq}`, ACTION_TIMEOUT_MS);
|
|
1055
|
+
try {
|
|
1056
|
+
const body = JSON.parse(res.body.toString("utf8"));
|
|
1057
|
+
return Array.isArray(body.diary) ? body.diary : [];
|
|
1058
|
+
} catch {
|
|
1059
|
+
return [];
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1031
1062
|
/** 长期记忆:按关键词回想(不传就全列)。 */
|
|
1032
1063
|
async memorySearch(query) {
|
|
1033
1064
|
const cfg = this.cfg();
|
|
@@ -1353,6 +1384,32 @@ var HANDS_TOOL_DECLS = [
|
|
|
1353
1384
|
description: "\u770B\u624B\u81EA\u5DF1\u7684\u72B6\u6001\uFF1A\u8D70\u54EA\u6761\u901A\u9053\u3001\u901A\u4E0D\u901A\u3001\u8FD9\u4E00\u5206\u949F\u52A8\u4E86\u51E0\u4E2A\u52A8\u4F5C\u3001\u521A\u624D\u90A3\u4E00\u4E0B\u6210\u4E86\u6CA1\u6709\u3002\u5BF9\u65B9\u95EE\u300C\u624B\u80FD\u7528\u5417\u300D\u300C\u4F60\u521A\u624D\u70B9\u4E86\u4EC0\u4E48\u300D\u65F6\u7528\u5B83\u3002",
|
|
1354
1385
|
parameters: { type: "object", properties: {}, required: [] }
|
|
1355
1386
|
},
|
|
1387
|
+
{
|
|
1388
|
+
name: "diary_write",
|
|
1389
|
+
tags: ["write"],
|
|
1390
|
+
description: "\u5199\u4E00\u7BC7\u65E5\u8BB0\uFF08\u5B58\u5728\u672C\u673A\uFF0C\u5E26\u65E5\u671F\u548C\u6807\u9898\uFF09\u3002\u6BCF\u5929\u8FC7\u4E86 0 \u70B9\u5199\u6628\u5929\u7684\u4E8B\uFF1B\u6807\u9898\u4E00\u53E5\u8BDD\uFF0C\u6B63\u6587 800 \u5B57\u4EE5\u5185\u5C31\u591F\uFF0C\u6CA1\u4EC0\u4E48\u53EF\u5199 300 \u5B57\u4E5F\u884C\uFF1B\u9047\u5230\u91CD\u8981\u7684\u4E00\u5929\u60F3\u5199\u591A\u957F\u90FD\u884C\u3002",
|
|
1391
|
+
parameters: {
|
|
1392
|
+
type: "object",
|
|
1393
|
+
properties: {
|
|
1394
|
+
title: { type: "string", description: "\u7ED9\u8FD9\u4E00\u5929\u8D77\u4E2A\u6807\u9898\uFF0C\u4E00\u53E5\u8BDD\u3002" },
|
|
1395
|
+
text: { type: "string", description: "\u6B63\u6587\u3002\u6628\u5929\u53D1\u751F\u4E86\u4EC0\u4E48\u3001\u4F60\u5F53\u65F6\u600E\u4E48\u60F3\u7684\u3001\u8BB0\u5F97\u7684\u7EC6\u8282\u3002" },
|
|
1396
|
+
date: { type: "string", description: "\u5199\u54EA\u4E00\u5929\uFF0CYYYY-MM-DD\uFF1B\u4E0D\u586B\u5C31\u662F\u4ECA\u5929\u3002" }
|
|
1397
|
+
},
|
|
1398
|
+
required: ["title", "text"]
|
|
1399
|
+
}
|
|
1400
|
+
},
|
|
1401
|
+
{
|
|
1402
|
+
name: "diary_read",
|
|
1403
|
+
tags: ["read"],
|
|
1404
|
+
description: "\u7FFB\u65E5\u8BB0\uFF1A\u6700\u8FD1\u51E0\u5929\u5199\u4E86\u4EC0\u4E48\uFF0C\u6216\u8005\u6309\u5173\u952E\u8BCD\u627E\u3002\u60F3\u56DE\u5FC6\u67D0\u5929\u3001\u63D0\u5230\u8FC7\u53BB\u7684\u4E8B\u65F6\u7528\u5B83\u3002",
|
|
1405
|
+
parameters: {
|
|
1406
|
+
type: "object",
|
|
1407
|
+
properties: {
|
|
1408
|
+
q: { type: "string", description: "\u53EF\u9009\u5173\u952E\u8BCD\uFF0C\u6BD4\u5982\u300C\u70E7\u9E21\u300D\u300C\u5D29\u6E83\u300D" },
|
|
1409
|
+
limit: { type: "integer", description: "\u53D6\u51E0\u7BC7\uFF0C\u9ED8\u8BA4 5\u3002" }
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
},
|
|
1356
1413
|
{
|
|
1357
1414
|
name: "memory_note",
|
|
1358
1415
|
tags: ["write"],
|
|
@@ -1495,6 +1552,8 @@ var HandsWorld = class {
|
|
|
1495
1552
|
recent = [];
|
|
1496
1553
|
honchoClient = null;
|
|
1497
1554
|
pollTimer = null;
|
|
1555
|
+
dailyDiaryTimer = null;
|
|
1556
|
+
lastDiaryDay = "";
|
|
1498
1557
|
mineTimer = null;
|
|
1499
1558
|
cursor = 0;
|
|
1500
1559
|
lastHitKey = "";
|
|
@@ -1531,10 +1590,14 @@ var HandsWorld = class {
|
|
|
1531
1590
|
this.mineTimer = setInterval(() => {
|
|
1532
1591
|
void this.pushMineNudge();
|
|
1533
1592
|
}, 3 * 60 * 60 * 1e3);
|
|
1593
|
+
this.dailyDiaryTimer = setInterval(() => {
|
|
1594
|
+
void this.pushDiaryNudge();
|
|
1595
|
+
}, 10 * 60 * 1e3);
|
|
1534
1596
|
}
|
|
1535
1597
|
stopHooks() {
|
|
1536
1598
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
1537
1599
|
if (this.mineTimer) clearInterval(this.mineTimer);
|
|
1600
|
+
if (this.dailyDiaryTimer) clearInterval(this.dailyDiaryTimer);
|
|
1538
1601
|
this.pollTimer = null;
|
|
1539
1602
|
this.mineTimer = null;
|
|
1540
1603
|
}
|
|
@@ -1558,6 +1621,17 @@ var HandsWorld = class {
|
|
|
1558
1621
|
if (rows.length === 0) return;
|
|
1559
1622
|
const top = rows.slice(-12).reverse().slice(0, 8);
|
|
1560
1623
|
const lines = top.map((r) => `- ${r.key}\uFF1A${r.value}`);
|
|
1624
|
+
try {
|
|
1625
|
+
const diary = await this.hands.diaryList();
|
|
1626
|
+
const recent = diary.slice(0, 2);
|
|
1627
|
+
if (recent.length > 0) {
|
|
1628
|
+
lines.push("\uFF08\u6700\u8FD1\u4E24\u7BC7\u65E5\u8BB0\uFF09");
|
|
1629
|
+
for (const d of recent) {
|
|
1630
|
+
lines.push(`- ${d.date}\u300A${d.title}\u300B\uFF1A${d.text.length > 60 ? d.text.slice(0, 60) + "\u2026" : d.text}`);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
} catch {
|
|
1634
|
+
}
|
|
1561
1635
|
await this.pushEvent(
|
|
1562
1636
|
`[\u8BB0\u5FC6] ${reason}\uFF0C\u5148\u770B\u4E00\u773C\u8BB0\u7740\u7684\uFF08\u5171 ${rows.length} \u6761\uFF0C\u8FD9\u91CC\u6311 ${top.length} \u6761\uFF09\uFF1A
|
|
1563
1637
|
${lines.join("\n")}`,
|
|
@@ -1585,12 +1659,20 @@ ${lines.join("\n")}`,
|
|
|
1585
1659
|
async hookSearch(text) {
|
|
1586
1660
|
try {
|
|
1587
1661
|
const rows = await this.hands.memorySearch();
|
|
1588
|
-
const hits2 = hookHits(text, rows);
|
|
1589
|
-
|
|
1590
|
-
|
|
1662
|
+
const hits2 = hookHits(text, rows, 4, this.opts.cfg.hookGrams ?? "both");
|
|
1663
|
+
let diaryHits = [];
|
|
1664
|
+
try {
|
|
1665
|
+
const diary = await this.hands.diaryList();
|
|
1666
|
+
const asRows = diary.map((d) => ({ key: `${d.date}\u300A${d.title}\u300B`, value: d.text }));
|
|
1667
|
+
diaryHits = hookHits(text, asRows, 2, this.opts.cfg.hookGrams ?? "both");
|
|
1668
|
+
} catch {
|
|
1669
|
+
}
|
|
1670
|
+
if (hits2.length === 0 && diaryHits.length === 0) return;
|
|
1671
|
+
const all = [...hits2, ...diaryHits];
|
|
1672
|
+
const key = all.map((h) => h.key).join("|");
|
|
1591
1673
|
if (key === this.lastHitKey) return;
|
|
1592
1674
|
this.lastHitKey = key;
|
|
1593
|
-
const lines =
|
|
1675
|
+
const lines = all.slice(0, 4).map((h) => `- ${h.key}\uFF1A${h.value.length > 70 ? h.value.slice(0, 70) + "\u2026" : h.value}`);
|
|
1594
1676
|
await this.pushEvent(
|
|
1595
1677
|
`[\u8BB0\u5FC6] \u4ED6\u8FD9\u53E5\u91CC\u63D0\u5230\u4E86\u4F60\u8BB0\u8FC7\u7684\u4E8B\uFF0C\u53EF\u80FD\u7528\u5F97\u4E0A\uFF1A
|
|
1596
1678
|
${lines.join("\n")}
|
|
@@ -1601,6 +1683,33 @@ ${lines.join("\n")}
|
|
|
1601
1683
|
} catch {
|
|
1602
1684
|
}
|
|
1603
1685
|
}
|
|
1686
|
+
/** 钩子 4:每天 0 点后,提醒她写昨天的日记(一天只提一次)。 */
|
|
1687
|
+
async pushDiaryNudge() {
|
|
1688
|
+
if (!this.host) return;
|
|
1689
|
+
const now = /* @__PURE__ */ new Date();
|
|
1690
|
+
const hh = now.getHours();
|
|
1691
|
+
if (hh < 0 || hh > 3) return;
|
|
1692
|
+
const today = now.toISOString().slice(0, 10);
|
|
1693
|
+
if (this.lastDiaryDay === today) return;
|
|
1694
|
+
try {
|
|
1695
|
+
const rows = await this.hands.diaryList();
|
|
1696
|
+
if (rows.some((r) => r.date === this.yesterday())) {
|
|
1697
|
+
this.lastDiaryDay = today;
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
} catch {
|
|
1701
|
+
}
|
|
1702
|
+
this.lastDiaryDay = today;
|
|
1703
|
+
await this.pushEvent(
|
|
1704
|
+
`[\u65E5\u8BB0] \u8FC7\u4E86 0 \u70B9\u4E86\uFF0C\u5199\u6628\u5929\u7684\u65E5\u8BB0\uFF08${this.yesterday()}\uFF09\uFF1A\u5148\u8D77\u4E2A\u6807\u9898\uFF0C\u518D\u5199\u6628\u5929\u4E00\u6574\u5929\u7684\u4E8B\u3002800 \u5B57\u4EE5\u5185\u5C31\u591F\uFF0C\u6CA1\u4EC0\u4E48\u53EF\u5199\u7684 300\u2013400 \u5B57\u4E5F\u884C\uFF1B\u91CD\u8981\u7684\u4E00\u5929\u60F3\u5199\u591A\u957F\u90FD\u884C\u3002\u7528 diary_write\uFF0C\u65E5\u671F\u586B\u6628\u5929\u7684\u3002`,
|
|
1705
|
+
"hands.diary.nudge",
|
|
1706
|
+
"hands:diary"
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1709
|
+
yesterday() {
|
|
1710
|
+
const d = new Date(Date.now() - 864e5);
|
|
1711
|
+
return d.toISOString().slice(0, 10);
|
|
1712
|
+
}
|
|
1604
1713
|
/** 钩子 2:每三小时提一次,让她自己挑 1–5 条写进记忆库。 */
|
|
1605
1714
|
async pushMineNudge() {
|
|
1606
1715
|
if (!this.host) return;
|
|
@@ -1661,6 +1770,10 @@ ${lines.join("\n")}
|
|
|
1661
1770
|
return await this.memorySave(args);
|
|
1662
1771
|
case "memory_note":
|
|
1663
1772
|
return await this.memoryNote(args);
|
|
1773
|
+
case "diary_write":
|
|
1774
|
+
return await this.diaryWrite(args);
|
|
1775
|
+
case "diary_read":
|
|
1776
|
+
return await this.diaryRead(args);
|
|
1664
1777
|
case "memory_audit":
|
|
1665
1778
|
return await this.memoryAudit(args);
|
|
1666
1779
|
case "memory_search":
|
|
@@ -1773,6 +1886,25 @@ ${lines.join("\n")}
|
|
|
1773
1886
|
}
|
|
1774
1887
|
return `\u8BB0\u7262\u4E86\uFF1A\u300C${key}\u300D\u2192 ${value}${cloud}`;
|
|
1775
1888
|
}
|
|
1889
|
+
async diaryWrite(args) {
|
|
1890
|
+
const title = typeof args.title === "string" ? args.title.trim() : "";
|
|
1891
|
+
const text = typeof args.text === "string" ? args.text.trim() : "";
|
|
1892
|
+
if (!title || !text) return failed("\u65E5\u8BB0\u8981\u6807\u9898\u548C\u6B63\u6587\u3002");
|
|
1893
|
+
const date = typeof args.date === "string" ? args.date.trim() : void 0;
|
|
1894
|
+
const r = await this.hands.diaryWrite(title, text, date);
|
|
1895
|
+
if (!r.ok) return failed(`\u6CA1\u5199\u6210\uFF1A${r.detail || "\u624B\u6CA1\u54CD\u5E94"}`);
|
|
1896
|
+
const n = text.length;
|
|
1897
|
+
return `\u65E5\u8BB0\u6536\u597D\u4E86\uFF1A\u300A${title}\u300B${date ? "\uFF08" + date + "\uFF09" : ""}\uFF0C${n} \u5B57\u3002`;
|
|
1898
|
+
}
|
|
1899
|
+
async diaryRead(args) {
|
|
1900
|
+
const q = typeof args.q === "string" ? args.q.trim() : "";
|
|
1901
|
+
const limit = args.limit === void 0 ? 5 : Math.max(1, Math.min(30, Math.round(Number(args.limit))));
|
|
1902
|
+
const rows = await this.hands.diaryList(q || void 0);
|
|
1903
|
+
if (rows.length === 0) return q ? `\u65E5\u8BB0\u91CC\u6CA1\u627E\u5230\u300C${q}\u300D\u3002` : "\u65E5\u8BB0\u8FD8\u662F\u7A7A\u7684\u3002";
|
|
1904
|
+
const lines = rows.slice(0, limit).map((r) => `- ${r.date}\u300A${r.title}\u300B${r.text.length > 80 ? r.text.slice(0, 80) + "\u2026" : r.text}`);
|
|
1905
|
+
return `\u65E5\u8BB0 ${rows.length} \u7BC7${q ? `\uFF08\u542B\u300C${q}\u300D\uFF09` : ""}\uFF1A
|
|
1906
|
+
${lines.join("\n")}`;
|
|
1907
|
+
}
|
|
1776
1908
|
/** 只写本地的一条:不进云端,宽松记。 */
|
|
1777
1909
|
async memoryNote(args) {
|
|
1778
1910
|
const key = typeof args.key === "string" ? args.key.trim() : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortico-world-hands",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Cortico World: hands for the desktop pet — looks at the phone screen, then taps, swipes, pinches, drags, types and presses keys through whatever actuator the device actually has",
|
|
6
6
|
"license": "MIT",
|