cortico-world-hands 0.1.7 → 0.1.8
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 +198 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,7 +15,13 @@ var HANDS_DEFAULTS = {
|
|
|
15
15
|
maxTextLength: 200,
|
|
16
16
|
lookAttach: true,
|
|
17
17
|
lookMaxWidth: 720,
|
|
18
|
-
lookQuality: 60
|
|
18
|
+
lookQuality: 60,
|
|
19
|
+
honchoEnabled: false,
|
|
20
|
+
honchoApiKey: "",
|
|
21
|
+
honchoBaseUrl: "https://api.honcho.dev",
|
|
22
|
+
honchoWorkspace: "dsh",
|
|
23
|
+
honchoPeerId: "\u4E3B\u4EBA",
|
|
24
|
+
honchoSessionId: "yixin"
|
|
19
25
|
};
|
|
20
26
|
var KEY_CODES = {
|
|
21
27
|
back: 4,
|
|
@@ -37,6 +43,54 @@ var KEY_CODES = {
|
|
|
37
43
|
menu: 82
|
|
38
44
|
};
|
|
39
45
|
var K = `worlds.${HANDS_ID}`;
|
|
46
|
+
var HK = `worlds.${HANDS_ID}`;
|
|
47
|
+
var HANDS_HONCHO_GROUP = {
|
|
48
|
+
id: `world:${HANDS_ID}:honcho`,
|
|
49
|
+
owner: `world:${HANDS_ID}`,
|
|
50
|
+
schema: {
|
|
51
|
+
type: "object",
|
|
52
|
+
title: "Honcho \u8BB0\u5FC6",
|
|
53
|
+
description: "\u628A\u684C\u5BA0\u7684\u957F\u671F\u8BB0\u5FC6\u540C\u6B65\u5230 Honcho\uFF08plastic-labs \u7684\u8BB0\u5FC6\u670D\u52A1\uFF09\uFF0C\u56DE\u60F3\u65F6\u628A\u4E91\u7AEF\u7ED3\u8BBA\u4E00\u8D77\u5E26\u56DE\u6765\u3002\u4E0D\u586B key \u5C31\u53EA\u7528\u672C\u5730 memory.jsonl\u3002",
|
|
54
|
+
properties: {
|
|
55
|
+
[`${HK}.honchoEnabled`]: {
|
|
56
|
+
type: "boolean",
|
|
57
|
+
title: "\u542F\u7528 Honcho \u8BB0\u5FC6",
|
|
58
|
+
description: "\u6253\u5F00\u540E memory_save \u4F1A\u540C\u65F6\u5199\u672C\u5730\u4E0E Honcho\uFF0Cmemory_search \u4F1A\u5408\u5E76\u4E91\u7AEF\u7ED3\u679C\u3002",
|
|
59
|
+
"x-hot": true
|
|
60
|
+
},
|
|
61
|
+
[`${HK}.honchoApiKey`]: {
|
|
62
|
+
type: "string",
|
|
63
|
+
title: "API Key",
|
|
64
|
+
description: "\u5728 app.honcho.dev \u7533\u8BF7\u3002\u53EA\u586B\u8FD9\u4E00\u9879\u5373\u53EF\uFF0C\u5176\u4F59\u4FDD\u6301\u9ED8\u8BA4\u3002",
|
|
65
|
+
"x-hot": true
|
|
66
|
+
},
|
|
67
|
+
[`${HK}.honchoBaseUrl`]: {
|
|
68
|
+
type: "string",
|
|
69
|
+
title: "\u670D\u52A1\u5730\u5740",
|
|
70
|
+
description: "\u9ED8\u8BA4 https://api.honcho.dev\uFF1B\u81EA\u5EFA\u5B9E\u4F8B\u586B\u81EA\u5DF1\u7684\u5730\u5740\u3002",
|
|
71
|
+
"x-hot": false
|
|
72
|
+
},
|
|
73
|
+
[`${HK}.honchoWorkspace`]: {
|
|
74
|
+
type: "string",
|
|
75
|
+
title: "\u5DE5\u4F5C\u533A",
|
|
76
|
+
description: "\u8BB0\u5FC6\u7684\u9694\u79BB\u8FB9\u754C\u3002\u60F3\u548C\u5176\u4ED6\u7AEF\uFF08claude-honcho \u7B49\uFF09\u5171\u4EAB\u8BB0\u5FC6\u5C31\u586B\u540C\u4E00\u4E2A workspace\u3002",
|
|
77
|
+
"x-hot": false
|
|
78
|
+
},
|
|
79
|
+
[`${HK}.honchoPeerId`]: {
|
|
80
|
+
type: "string",
|
|
81
|
+
title: "\u5173\u4E8E\u8C01",
|
|
82
|
+
description: "\u8BB0\u5FC6\u5F52\u5C5E\u7684 peer\uFF0C\u9ED8\u8BA4\u300C\u4E3B\u4EBA\u300D\u3002",
|
|
83
|
+
"x-hot": false
|
|
84
|
+
},
|
|
85
|
+
[`${HK}.honchoSessionId`]: {
|
|
86
|
+
type: "string",
|
|
87
|
+
title: "\u4F1A\u8BDD\u540D",
|
|
88
|
+
description: "Honcho \u91CC\u7684\u4F1A\u8BDD\u6807\u8BC6\uFF0C\u9ED8\u8BA4 yixin\u3002",
|
|
89
|
+
"x-hot": false
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
40
94
|
var HANDS_CONFIG_GROUP = {
|
|
41
95
|
id: `world:${HANDS_ID}`,
|
|
42
96
|
owner: `world:${HANDS_ID}`,
|
|
@@ -161,6 +215,98 @@ var HANDS_LOOK_CONFIG_GROUP = {
|
|
|
161
215
|
// src/world.ts
|
|
162
216
|
import { fileURLToPath } from "node:url";
|
|
163
217
|
|
|
218
|
+
// src/honcho.ts
|
|
219
|
+
var TIMEOUT_MS = 12e3;
|
|
220
|
+
var Honcho = class {
|
|
221
|
+
cfg;
|
|
222
|
+
peerReady = false;
|
|
223
|
+
sessionReady = false;
|
|
224
|
+
constructor(cfg) {
|
|
225
|
+
this.cfg = cfg;
|
|
226
|
+
}
|
|
227
|
+
get configured() {
|
|
228
|
+
return this.cfg.enabled && this.cfg.apiKey.trim().length > 0;
|
|
229
|
+
}
|
|
230
|
+
base() {
|
|
231
|
+
return this.cfg.baseUrl.replace(/\/+$/, "");
|
|
232
|
+
}
|
|
233
|
+
async call(path, body) {
|
|
234
|
+
const url = `${this.base()}/v3/workspaces/${encodeURIComponent(this.cfg.workspace)}${path}`;
|
|
235
|
+
const controller = new AbortController();
|
|
236
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
237
|
+
try {
|
|
238
|
+
const res = await fetch(url, {
|
|
239
|
+
method: "POST",
|
|
240
|
+
headers: {
|
|
241
|
+
"content-type": "application/json",
|
|
242
|
+
authorization: `Bearer ${this.cfg.apiKey.trim()}`
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify(body),
|
|
245
|
+
signal: controller.signal
|
|
246
|
+
});
|
|
247
|
+
const text = await res.text();
|
|
248
|
+
if (!res.ok) throw new Error(`honcho ${res.status}: ${text.slice(0, 160)}`);
|
|
249
|
+
return text ? JSON.parse(text) : {};
|
|
250
|
+
} finally {
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async ensurePeer() {
|
|
255
|
+
if (this.peerReady) return;
|
|
256
|
+
await this.call("/peers", { id: this.cfg.peerId });
|
|
257
|
+
this.peerReady = true;
|
|
258
|
+
}
|
|
259
|
+
async ensureSession() {
|
|
260
|
+
if (this.sessionReady) return;
|
|
261
|
+
await this.ensurePeer();
|
|
262
|
+
await this.call("/sessions", { id: this.cfg.sessionId, peers: [this.cfg.peerId] });
|
|
263
|
+
this.sessionReady = true;
|
|
264
|
+
}
|
|
265
|
+
/** 把一条事实写进 Honcho 的会话里(后台 Deriver 会据此归纳结论)。 */
|
|
266
|
+
async remember(sentence) {
|
|
267
|
+
if (!this.configured) return;
|
|
268
|
+
await this.ensureSession();
|
|
269
|
+
await this.call(`/sessions/${encodeURIComponent(this.cfg.sessionId)}/messages`, {
|
|
270
|
+
messages: [{ peer_id: this.cfg.peerId, content: sentence }]
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
/** 在 Honcho 里搜相关记忆(原始消息 + 归纳结论)。 */
|
|
274
|
+
async search(query, limit = 8) {
|
|
275
|
+
if (!this.configured) return [];
|
|
276
|
+
await this.ensurePeer();
|
|
277
|
+
const raw = await this.call(`/peers/${encodeURIComponent(this.cfg.peerId)}/search`, {
|
|
278
|
+
query,
|
|
279
|
+
limit
|
|
280
|
+
});
|
|
281
|
+
const hits = [];
|
|
282
|
+
const push = (item, source) => {
|
|
283
|
+
if (typeof item === "string") {
|
|
284
|
+
hits.push({ text: item, source });
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (!item || typeof item !== "object") return;
|
|
288
|
+
const row = item;
|
|
289
|
+
const text = [row.content, row.text, row.conclusion, row.observation].find((v) => typeof v === "string" && v.trim().length > 0);
|
|
290
|
+
if (text) hits.push({ text: text.trim(), source: String(row.session_id || row.type || source) });
|
|
291
|
+
};
|
|
292
|
+
const list = Array.isArray(raw) ? raw : raw.results || raw.messages || raw.conclusions || [];
|
|
293
|
+
for (const item of list) push(item, "\u7ED3\u8BBA");
|
|
294
|
+
return hits.slice(0, limit);
|
|
295
|
+
}
|
|
296
|
+
/** 让 Honcho 用自己的推理回答一个关于主人的问题(慢,但更“懂”)。 */
|
|
297
|
+
async ask(question) {
|
|
298
|
+
if (!this.configured) return "";
|
|
299
|
+
await this.ensurePeer();
|
|
300
|
+
const raw = await this.call(`/peers/${encodeURIComponent(this.cfg.peerId)}/chat`, {
|
|
301
|
+
query: question
|
|
302
|
+
});
|
|
303
|
+
const answer = [raw.content, raw.answer, raw.text, raw.reply].find(
|
|
304
|
+
(v) => typeof v === "string" && v.trim().length > 0
|
|
305
|
+
);
|
|
306
|
+
return answer ? answer.trim() : "";
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
164
310
|
// src/hands.ts
|
|
165
311
|
import { execFile } from "node:child_process";
|
|
166
312
|
import { readFileSync } from "node:fs";
|
|
@@ -1112,6 +1258,7 @@ var HandsWorld = class {
|
|
|
1112
1258
|
hands;
|
|
1113
1259
|
pausedUntil = 0;
|
|
1114
1260
|
recent = [];
|
|
1261
|
+
honchoClient = null;
|
|
1115
1262
|
opts;
|
|
1116
1263
|
constructor(opts) {
|
|
1117
1264
|
this.opts = opts;
|
|
@@ -1215,21 +1362,65 @@ var HandsWorld = class {
|
|
|
1215
1362
|
return { text: report.text, ...blobs.length ? { blobs } : {} };
|
|
1216
1363
|
}
|
|
1217
1364
|
/** 给文字就先在屏幕上找,找到唯一一处才点;给坐标就直接点。 */
|
|
1365
|
+
honcho() {
|
|
1366
|
+
if (!this.honchoClient) {
|
|
1367
|
+
this.honchoClient = new Honcho({
|
|
1368
|
+
enabled: Boolean(this.opts.cfg.honchoEnabled),
|
|
1369
|
+
apiKey: this.opts.cfg.honchoApiKey || "",
|
|
1370
|
+
baseUrl: this.opts.cfg.honchoBaseUrl || "https://api.honcho.dev",
|
|
1371
|
+
workspace: this.opts.cfg.honchoWorkspace || "dsh",
|
|
1372
|
+
peerId: this.opts.cfg.honchoPeerId || "\u4E3B\u4EBA",
|
|
1373
|
+
sessionId: this.opts.cfg.honchoSessionId || "yixin"
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
return this.honchoClient;
|
|
1377
|
+
}
|
|
1218
1378
|
async memorySave(args) {
|
|
1219
1379
|
const key = typeof args.key === "string" ? args.key.trim() : "";
|
|
1220
1380
|
const value = typeof args.value === "string" ? args.value.trim() : "";
|
|
1221
1381
|
if (!key || !value) return failed("\u8981 key \u548C value\u3002");
|
|
1222
1382
|
const tags = typeof args.tags === "string" ? args.tags : void 0;
|
|
1223
1383
|
const r = await this.hands.memorySave(key, value, tags);
|
|
1224
|
-
|
|
1384
|
+
if (!r.ok) return failed(`\u6CA1\u8BB0\u4F4F\uFF1A${r.detail || "\u624B\u6CA1\u54CD\u5E94"}`);
|
|
1385
|
+
let cloud = "";
|
|
1386
|
+
const honcho = this.honcho();
|
|
1387
|
+
if (honcho.configured) {
|
|
1388
|
+
try {
|
|
1389
|
+
await honcho.remember(`\u5173\u4E8E\u300C${key}\u300D\uFF1A${value}`);
|
|
1390
|
+
cloud = "\uFF08\u5DF2\u540C\u6B65 Honcho\uFF09";
|
|
1391
|
+
} catch (err) {
|
|
1392
|
+
cloud = `\uFF08Honcho \u540C\u6B65\u5931\u8D25\uFF1A${err.message.slice(0, 60)}\uFF09`;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
return `\u8BB0\u7262\u4E86\uFF1A\u300C${key}\u300D\u2192 ${value}${cloud}`;
|
|
1225
1396
|
}
|
|
1226
1397
|
async memorySearch(args) {
|
|
1227
1398
|
const query = typeof args.query === "string" ? args.query.trim() : "";
|
|
1228
1399
|
const rows = await this.hands.memorySearch(query);
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1400
|
+
const lines = rows.slice(0, 30).map((r) => `- ${r.key}\uFF1A${r.value}${r.ts ? `\uFF08${r.ts}\uFF09` : ""}`);
|
|
1401
|
+
let cloudHits = [];
|
|
1402
|
+
let cloudNote = "";
|
|
1403
|
+
const honcho = this.honcho();
|
|
1404
|
+
if (honcho.configured && query) {
|
|
1405
|
+
try {
|
|
1406
|
+
const hits = await honcho.search(query);
|
|
1407
|
+
cloudHits = hits.map((h) => `- ${h.text}`);
|
|
1408
|
+
if (cloudHits.length > 0) cloudNote = `
|
|
1409
|
+
|
|
1410
|
+
Honcho \u8865\u5145 ${cloudHits.length} \u6761\uFF1A`;
|
|
1411
|
+
} catch (err) {
|
|
1412
|
+
cloudNote = `
|
|
1413
|
+
|
|
1414
|
+
\uFF08Honcho \u67E5\u8BE2\u5931\u8D25\uFF1A${err.message.slice(0, 60)}\uFF09`;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
if (lines.length === 0 && cloudHits.length === 0) {
|
|
1418
|
+
return query ? `\u8BB0\u5FC6\u91CC\u6CA1\u6709\u548C\u300C${query}\u300D\u76F8\u5173\u7684\u3002` : "\u957F\u671F\u8BB0\u5FC6\u8FD8\u662F\u7A7A\u7684\u3002";
|
|
1419
|
+
}
|
|
1420
|
+
const local = lines.length > 0 ? `\u60F3\u8D77 ${rows.length} \u6761\uFF1A
|
|
1421
|
+
${lines.join("\n")}` : "\u672C\u5730\u8BB0\u5FC6\u6CA1\u6709\u76F4\u63A5\u547D\u4E2D\u3002";
|
|
1422
|
+
return local + cloudNote + (cloudHits.length > 0 ? `
|
|
1423
|
+
${cloudHits.join("\n")}` : "");
|
|
1233
1424
|
}
|
|
1234
1425
|
async memoryForget(args) {
|
|
1235
1426
|
const key = typeof args.key === "string" ? args.key.trim() : "";
|
|
@@ -1414,7 +1605,7 @@ ${this.recent.map((r) => `- ${r}`).join("\n")}`);
|
|
|
1414
1605
|
{ label: zh ? "\u52A8\u8FC7" : "actions", value: stats.acts, tone: "plain" },
|
|
1415
1606
|
{ label: zh ? "\u770B\u8FC7" : "looks", value: stats.looks, tone: "plain" }
|
|
1416
1607
|
],
|
|
1417
|
-
config: [HANDS_CONFIG_GROUP, HANDS_LOOK_CONFIG_GROUP],
|
|
1608
|
+
config: [HANDS_CONFIG_GROUP, HANDS_LOOK_CONFIG_GROUP, HANDS_HONCHO_GROUP],
|
|
1418
1609
|
promptDocs: [
|
|
1419
1610
|
{
|
|
1420
1611
|
key: `worlds.${HANDS_ID}.envPrompt`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortico-world-hands",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
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",
|