cortico-world-hands 0.6.2 → 0.8.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/dist/index.js +183 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,7 +22,8 @@ var HANDS_DEFAULTS = {
|
|
|
22
22
|
honchoWorkspace: "dsh",
|
|
23
23
|
honchoPeerId: "morse",
|
|
24
24
|
honchoSessionId: "yixin",
|
|
25
|
-
hookGrams: "three"
|
|
25
|
+
hookGrams: "three",
|
|
26
|
+
skinPackUrl: ""
|
|
26
27
|
};
|
|
27
28
|
var KEY_CODES = {
|
|
28
29
|
back: 4,
|
|
@@ -495,7 +496,7 @@ var Honcho = class {
|
|
|
495
496
|
// src/hands.ts
|
|
496
497
|
import { execFile } from "node:child_process";
|
|
497
498
|
import { readFileSync } from "node:fs";
|
|
498
|
-
import { get as httpGet } from "node:http";
|
|
499
|
+
import { get as httpGet, request as httpRequest } from "node:http";
|
|
499
500
|
var ACTION_TIMEOUT_MS = 6e3;
|
|
500
501
|
var PROBE_TTL_MS = 2e4;
|
|
501
502
|
var COORD_LIMIT = 2e4;
|
|
@@ -523,6 +524,38 @@ function get(port, path, timeoutMs) {
|
|
|
523
524
|
req.on("error", (err) => reject(err));
|
|
524
525
|
});
|
|
525
526
|
}
|
|
527
|
+
async function post(port, path, body, timeoutMs) {
|
|
528
|
+
return new Promise((resolve, reject) => {
|
|
529
|
+
const data = Buffer.from(body, "utf8");
|
|
530
|
+
const req = httpRequest(
|
|
531
|
+
{
|
|
532
|
+
host: "127.0.0.1",
|
|
533
|
+
port,
|
|
534
|
+
path,
|
|
535
|
+
method: "POST",
|
|
536
|
+
timeout: timeoutMs,
|
|
537
|
+
headers: { "content-type": "application/json", "content-length": data.length }
|
|
538
|
+
},
|
|
539
|
+
(res) => {
|
|
540
|
+
const chunks = [];
|
|
541
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
542
|
+
res.on("end", () => {
|
|
543
|
+
resolve({
|
|
544
|
+
status: res.statusCode ?? 0,
|
|
545
|
+
body: Buffer.concat(chunks),
|
|
546
|
+
header: (name) => {
|
|
547
|
+
const value = res.headers[name.toLowerCase()];
|
|
548
|
+
return Array.isArray(value) ? value[0] ?? "" : value ?? "";
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
req.on("timeout", () => req.destroy(new Error(`\u7B49\u592A\u4E45\uFF08${timeoutMs}ms\uFF09`)));
|
|
555
|
+
req.on("error", (err) => reject(err));
|
|
556
|
+
req.end(data);
|
|
557
|
+
});
|
|
558
|
+
}
|
|
526
559
|
async function getJson(port, path, timeoutMs = 6e3) {
|
|
527
560
|
try {
|
|
528
561
|
const res = await get(port, path, timeoutMs);
|
|
@@ -1030,6 +1063,31 @@ var Hands = class {
|
|
|
1030
1063
|
}
|
|
1031
1064
|
return { ok: res.status === 200 && body.ok !== false, detail: flat(body.detail || body.error || "", 80) };
|
|
1032
1065
|
}
|
|
1066
|
+
/** 画板:新开一张。 */
|
|
1067
|
+
async canvasNew(w = 512, h = 512, bg = "transparent") {
|
|
1068
|
+
const cfg = this.cfg();
|
|
1069
|
+
const credential = this.readToken();
|
|
1070
|
+
const t = credential ? `&t=${encodeURIComponent(credential)}` : "";
|
|
1071
|
+
const res = await get(this.port(cfg.handPort), `/canvas/new?w=${w}&h=${h}&bg=${encodeURIComponent(bg)}${t}`, ACTION_TIMEOUT_MS);
|
|
1072
|
+
return res.body.toString("utf8");
|
|
1073
|
+
}
|
|
1074
|
+
/** 画板:画一笔。 */
|
|
1075
|
+
async canvasStroke(points, color = "#111111", width = 4) {
|
|
1076
|
+
const cfg = this.cfg();
|
|
1077
|
+
const credential = this.readToken();
|
|
1078
|
+
const t = credential ? `t=${encodeURIComponent(credential)}` : "";
|
|
1079
|
+
const body = JSON.stringify({ points, color, width });
|
|
1080
|
+
const res = await post(this.port(cfg.handPort), `/canvas/stroke?${t}`, body, ACTION_TIMEOUT_MS);
|
|
1081
|
+
return res.body.toString("utf8");
|
|
1082
|
+
}
|
|
1083
|
+
/** 画板:撤销 / 清空 / 保存 / 列表。 */
|
|
1084
|
+
async canvasOp(op) {
|
|
1085
|
+
const cfg = this.cfg();
|
|
1086
|
+
const credential = this.readToken();
|
|
1087
|
+
const t = credential ? `t=${encodeURIComponent(credential)}` : "";
|
|
1088
|
+
const res = op === "list" || op === "state" ? await get(this.port(cfg.handPort), `/canvas/${op}?${t}`, ACTION_TIMEOUT_MS) : await post(this.port(cfg.handPort), `/canvas/${op}?${t}`, "", ACTION_TIMEOUT_MS);
|
|
1089
|
+
return res.body.toString("utf8");
|
|
1090
|
+
}
|
|
1033
1091
|
/** 日记:写一篇。 */
|
|
1034
1092
|
async diaryWrite(title, text, date) {
|
|
1035
1093
|
const cfg = this.cfg();
|
|
@@ -1384,6 +1442,68 @@ var HANDS_TOOL_DECLS = [
|
|
|
1384
1442
|
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",
|
|
1385
1443
|
parameters: { type: "object", properties: {}, required: [] }
|
|
1386
1444
|
},
|
|
1445
|
+
{
|
|
1446
|
+
name: "canvas_new",
|
|
1447
|
+
tags: ["act"],
|
|
1448
|
+
description: "\u65B0\u5F00\u4E00\u5F20\u753B\u5E03\uFF08\u9ED8\u8BA4 512\xD7512\uFF0C\u80CC\u666F\u900F\u660E\u6216\u767D\u8272\uFF09\uFF0C\u4F1A\u628A\u5F53\u524D\u8FD9\u5F20\u6E05\u6389\u3002",
|
|
1449
|
+
parameters: {
|
|
1450
|
+
type: "object",
|
|
1451
|
+
properties: {
|
|
1452
|
+
w: { type: "integer", description: "\u5BBD\uFF0C\u9ED8\u8BA4 512" },
|
|
1453
|
+
h: { type: "integer", description: "\u9AD8\uFF0C\u9ED8\u8BA4 512" },
|
|
1454
|
+
bg: { type: "string", description: "transparent \u6216 white" }
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
},
|
|
1458
|
+
{
|
|
1459
|
+
name: "canvas_stroke",
|
|
1460
|
+
tags: ["act"],
|
|
1461
|
+
description: "\u5728\u753B\u5E03\u4E0A\u753B\u4E00\u7B14\uFF1A\u7ED9\u4E00\u4E32\u5750\u6807\u70B9\u3001\u989C\u8272\u3001\u7C97\u7EC6\u3002\u60F3\u753B\u66F2\u7EBF\u5C31\u591A\u7ED9\u70B9\uFF0C\u6BCF\u7B14\u6700\u591A\u51E0\u5341\u4E2A\u70B9\uFF1B\u753B\u6B6A\u4E86\u5C31 canvas_undo\u3002",
|
|
1462
|
+
parameters: {
|
|
1463
|
+
type: "object",
|
|
1464
|
+
properties: {
|
|
1465
|
+
points: { type: "array", description: "\u5750\u6807 [[x,y],[x,y],\u2026]\uFF0C\u539F\u70B9\u662F\u753B\u5E03\u5DE6\u4E0A\u89D2" },
|
|
1466
|
+
color: { type: "string", description: "\u989C\u8272\uFF0C#RRGGBB" },
|
|
1467
|
+
width: { type: "integer", description: "\u7B14\u753B\u7C97\u7EC6\uFF0C\u9ED8\u8BA4 4" }
|
|
1468
|
+
},
|
|
1469
|
+
required: ["points"]
|
|
1470
|
+
}
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
name: "canvas_undo",
|
|
1474
|
+
tags: ["act"],
|
|
1475
|
+
description: "\u64A4\u9500\u521A\u624D\u90A3\u4E00\u7B14\uFF08\u53EF\u4EE5\u8FDE\u7740\u64A4\u9500\uFF09\u3002",
|
|
1476
|
+
parameters: { type: "object", properties: {} }
|
|
1477
|
+
},
|
|
1478
|
+
{
|
|
1479
|
+
name: "canvas_clear",
|
|
1480
|
+
tags: ["act"],
|
|
1481
|
+
description: "\u6E05\u7A7A\u6574\u5F20\u753B\u5E03\uFF08\u53EA\u6E05\u7B14\u753B\uFF0C\u4E0D\u5220\u6587\u4EF6\uFF09\u3002",
|
|
1482
|
+
parameters: { type: "object", properties: {} }
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
name: "canvas_save",
|
|
1486
|
+
tags: ["act"],
|
|
1487
|
+
description: "\u628A\u753B\u5E03\u5B58\u6210 PNG\uFF0C\u843D\u5230 blobs/drawings/\u65E5\u671F-\u65F6\u95F4.png\uFF0C\u8FD4\u56DE\u8DEF\u5F84\u3002\u53EA\u65B0\u589E\u6587\u4EF6\uFF0C\u4E0D\u8986\u76D6\u65E7\u56FE\u3002",
|
|
1488
|
+
parameters: { type: "object", properties: {} }
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
name: "canvas_list",
|
|
1492
|
+
tags: ["read"],
|
|
1493
|
+
description: "\u770B\u770B\u81EA\u5DF1\u753B\u8FC7\u54EA\u4E9B\u56FE\uFF08\u6587\u4EF6\u540D\u548C\u5730\u5740\uFF09\u3002",
|
|
1494
|
+
parameters: { type: "object", properties: {} }
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
name: "skin_install",
|
|
1498
|
+
tags: ["act"],
|
|
1499
|
+
description: "\u88C5\u4E00\u4EF6\u5916\u90E8\u670D\u88C5\u5305\uFF08\u9CB8\u9C7C\u5A18\u7684\u8863\u670D\uFF09\uFF1A\u7ED9\u4E00\u4E2A\u672C\u5730 HTTP \u5730\u5740\uFF0C\u5B83\u4F1A\u628A\u8D34\u56FE\u5199\u8FDB\u684C\u5BA0\u7684 web \u76EE\u5F55\u5E76\u5408\u5E76 model.json\uFF08\u5148\u5907\u4EFD\uFF09\u3002\u88C5\u5B8C\u5728\u88C5\u626E\u9875\u300C\u670D\u88C5\u300D\u91CC\u9009\u3002",
|
|
1500
|
+
parameters: {
|
|
1501
|
+
type: "object",
|
|
1502
|
+
properties: {
|
|
1503
|
+
url: { type: "string", description: "\u5305\u5730\u5740\uFF0C\u6BD4\u5982 http://127.0.0.1:18080/onigiri" }
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
},
|
|
1387
1507
|
{
|
|
1388
1508
|
name: "diary_write",
|
|
1389
1509
|
tags: ["write"],
|
|
@@ -1570,6 +1690,11 @@ var HandsWorld = class {
|
|
|
1570
1690
|
this.cursor = host.store.latestCursor();
|
|
1571
1691
|
const state = await this.hands.probe(true);
|
|
1572
1692
|
this.log?.info(state.ok ? `\u624B\uFF1A${state.detail}` : `\u624B\u8FD8\u52A8\u4E0D\u4E86\uFF1A${state.detail}`);
|
|
1693
|
+
if (this.opts.cfg.skinPackUrl) {
|
|
1694
|
+
void installSkinPack(this.opts.cfg.skinPackUrl, this.skinRoots(), this.log).then((r) => {
|
|
1695
|
+
this.log?.info(r.ok ? `\u670D\u88C5\u5305\uFF1A${r.detail}` : `\u670D\u88C5\u5305\u6CA1\u88C5\u4E0A\uFF1A${r.detail}`);
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1573
1698
|
void this.pushMemoryDigest("\u88AB\u53EB\u8D77\u6765");
|
|
1574
1699
|
this.startHooks();
|
|
1575
1700
|
}
|
|
@@ -1774,6 +1899,20 @@ ${lines.join("\n")}
|
|
|
1774
1899
|
return await this.diaryWrite(args);
|
|
1775
1900
|
case "diary_read":
|
|
1776
1901
|
return await this.diaryRead(args);
|
|
1902
|
+
case "skin_install":
|
|
1903
|
+
return await this.skinInstall(args);
|
|
1904
|
+
case "canvas_new":
|
|
1905
|
+
return await this.canvasNew(args);
|
|
1906
|
+
case "canvas_stroke":
|
|
1907
|
+
return await this.canvasStroke(args);
|
|
1908
|
+
case "canvas_undo":
|
|
1909
|
+
return await this.canvasSimple("undo");
|
|
1910
|
+
case "canvas_clear":
|
|
1911
|
+
return await this.canvasSimple("clear");
|
|
1912
|
+
case "canvas_save":
|
|
1913
|
+
return await this.canvasSimple("save");
|
|
1914
|
+
case "canvas_list":
|
|
1915
|
+
return await this.canvasSimple("list");
|
|
1777
1916
|
case "memory_audit":
|
|
1778
1917
|
return await this.memoryAudit(args);
|
|
1779
1918
|
case "memory_search":
|
|
@@ -1886,6 +2025,47 @@ ${lines.join("\n")}
|
|
|
1886
2025
|
}
|
|
1887
2026
|
return `\u8BB0\u7262\u4E86\uFF1A\u300C${key}\u300D\u2192 ${value}${cloud}`;
|
|
1888
2027
|
}
|
|
2028
|
+
async canvasNew(args) {
|
|
2029
|
+
const w = args.w === void 0 ? 512 : Math.max(64, Math.min(2048, Math.round(Number(args.w))));
|
|
2030
|
+
const h = args.h === void 0 ? 512 : Math.max(64, Math.min(2048, Math.round(Number(args.h))));
|
|
2031
|
+
const bg = typeof args.bg === "string" && args.bg ? args.bg : "transparent";
|
|
2032
|
+
return await this.hands.canvasNew(w, h, bg);
|
|
2033
|
+
}
|
|
2034
|
+
async canvasStroke(args) {
|
|
2035
|
+
const raw = args.points;
|
|
2036
|
+
if (!Array.isArray(raw) || raw.length === 0) return failed("\u8981 points\uFF0C\u50CF [[10,10],[20,30]]\u3002");
|
|
2037
|
+
const points = [];
|
|
2038
|
+
for (const p of raw.slice(0, 200)) {
|
|
2039
|
+
if (Array.isArray(p) && p.length >= 2) {
|
|
2040
|
+
const x = Number(p[0]);
|
|
2041
|
+
const y = Number(p[1]);
|
|
2042
|
+
if (Number.isFinite(x) && Number.isFinite(y)) points.push([x, y]);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
if (points.length === 0) return failed("points \u91CC\u6CA1\u6709\u5408\u6CD5\u5750\u6807\u3002");
|
|
2046
|
+
const color = typeof args.color === "string" && /^#[0-9a-fA-F]{6}$/.test(args.color) ? args.color : "#111111";
|
|
2047
|
+
const width = args.width === void 0 ? 4 : Math.max(1, Math.min(40, Math.round(Number(args.width))));
|
|
2048
|
+
return await this.hands.canvasStroke(points, color, width);
|
|
2049
|
+
}
|
|
2050
|
+
async canvasSimple(op) {
|
|
2051
|
+
return await this.hands.canvasOp(op);
|
|
2052
|
+
}
|
|
2053
|
+
/** 装一件外部服装包:从本地 HTTP 拿包,写进桌宠 web 目录。 */
|
|
2054
|
+
async skinInstall(args) {
|
|
2055
|
+
const url = typeof args.url === "string" && args.url.trim() ? args.url.trim() : this.opts.cfg.skinPackUrl || "";
|
|
2056
|
+
if (!url) return failed("\u8981\u4E00\u4E2A\u5305\u5730\u5740\uFF08url\uFF09\uFF0C\u6216\u8005\u5148\u914D\u7F6E skinPackUrl\u3002");
|
|
2057
|
+
const r = await installSkinPack(url, this.skinRoots(), this.log);
|
|
2058
|
+
return r.ok ? r.detail : failed(r.detail);
|
|
2059
|
+
}
|
|
2060
|
+
skinRoots() {
|
|
2061
|
+
const roots = [...this.opts.roots || []];
|
|
2062
|
+
try {
|
|
2063
|
+
roots.push(process.cwd());
|
|
2064
|
+
} catch {
|
|
2065
|
+
}
|
|
2066
|
+
roots.push("/root", "/app", "/workspace");
|
|
2067
|
+
return [...new Set(roots.filter(Boolean))];
|
|
2068
|
+
}
|
|
1889
2069
|
async diaryWrite(args) {
|
|
1890
2070
|
const title = typeof args.title === "string" ? args.title.trim() : "";
|
|
1891
2071
|
const text = typeof args.text === "string" ? args.text.trim() : "";
|
|
@@ -2171,7 +2351,7 @@ function handsDefinition() {
|
|
|
2171
2351
|
id: HANDS_ID,
|
|
2172
2352
|
label: "\u624B",
|
|
2173
2353
|
defaults: () => structuredClone(HANDS_DEFAULTS),
|
|
2174
|
-
create: (ctx) => new HandsWorld({ cfg: ctx.cfg, timezone: ctx.timezone })
|
|
2354
|
+
create: (ctx) => new HandsWorld({ cfg: ctx.cfg, timezone: ctx.timezone, roots: [ctx.botDir, ctx.dataDir, ctx.repoRoot, ctx.packageDir] })
|
|
2175
2355
|
};
|
|
2176
2356
|
}
|
|
2177
2357
|
var HANDS = handsDefinition();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortico-world-hands",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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",
|