cortico-world-hands 0.20.2 → 0.20.4
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 +223 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -233,9 +233,9 @@ var HANDS_LOOK_CONFIG_GROUP = {
|
|
|
233
233
|
};
|
|
234
234
|
|
|
235
235
|
// src/world.ts
|
|
236
|
-
import { writeFileSync as
|
|
236
|
+
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
237
237
|
import { fileURLToPath } from "node:url";
|
|
238
|
-
import { join as
|
|
238
|
+
import { join as join4 } from "node:path";
|
|
239
239
|
|
|
240
240
|
// src/policy.ts
|
|
241
241
|
var PASS_RULES = [
|
|
@@ -513,9 +513,167 @@ var Honcho = class {
|
|
|
513
513
|
}
|
|
514
514
|
};
|
|
515
515
|
|
|
516
|
-
// src/
|
|
517
|
-
import { existsSync,
|
|
516
|
+
// src/sync.ts
|
|
517
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
518
518
|
import { join } from "node:path";
|
|
519
|
+
var START = "<!--yixin:sync:start-->";
|
|
520
|
+
var END = "<!--yixin:sync:end-->";
|
|
521
|
+
function readMaybe(path) {
|
|
522
|
+
try {
|
|
523
|
+
return existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
524
|
+
} catch {
|
|
525
|
+
return "";
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function mergeMemorySection(file, rows) {
|
|
529
|
+
const body = rows.filter((r) => r && r.key && r.value).map((r) => `- **${r.key}**\uFF1A${r.value.replace(/\s+/g, " ").trim()}`).join("\n");
|
|
530
|
+
const section = `${START}
|
|
531
|
+
## \u4ECE\u61FF\u5FC3\u540C\u6B65\u8FC7\u6765\u7684\u8BB0\u5FC6\uFF08${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}\uFF09
|
|
532
|
+
${body}
|
|
533
|
+
${END}`;
|
|
534
|
+
const old = readMaybe(file);
|
|
535
|
+
let next;
|
|
536
|
+
if (old.includes(START) && old.includes(END)) {
|
|
537
|
+
next = old.slice(0, old.indexOf(START)) + section + old.slice(old.indexOf(END) + END.length);
|
|
538
|
+
} else {
|
|
539
|
+
next = (old ? old.trimEnd() + "\n\n" : "") + section + "\n";
|
|
540
|
+
}
|
|
541
|
+
if (next === old) return { lines: rows.length, wrote: false };
|
|
542
|
+
writeFileSync(file, next);
|
|
543
|
+
return { lines: rows.length, wrote: true };
|
|
544
|
+
}
|
|
545
|
+
function readPersona(dir) {
|
|
546
|
+
for (const name of ["CONSTITUTION.md", "constitution.md"]) {
|
|
547
|
+
const text = readMaybe(join(dir, name));
|
|
548
|
+
if (text.trim()) return text;
|
|
549
|
+
}
|
|
550
|
+
return "";
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/skin.ts
|
|
554
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, readdirSync, statSync, unlinkSync, writeFileSync as writeFileSync2, copyFileSync } from "node:fs";
|
|
555
|
+
import { dirname, join as join2, resolve } from "node:path";
|
|
556
|
+
function findWhaleDir(roots) {
|
|
557
|
+
for (const root of roots) {
|
|
558
|
+
const candidates = [
|
|
559
|
+
join2(root, "web", "whale"),
|
|
560
|
+
join2(root, "pet", "web", "whale"),
|
|
561
|
+
join2(root, "vendor", "pet", "web", "whale")
|
|
562
|
+
];
|
|
563
|
+
for (const dir of candidates) {
|
|
564
|
+
if (existsSync2(join2(dir, "model.json"))) return dir;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
async function fetchBytes(url) {
|
|
570
|
+
const res = await fetch(url);
|
|
571
|
+
if (!res.ok) throw new Error(`\u4E0B\u8F7D\u5931\u8D25 ${res.status} ${url}`);
|
|
572
|
+
return Buffer.from(await res.arrayBuffer());
|
|
573
|
+
}
|
|
574
|
+
async function installSkinPack(baseUrl, roots, log) {
|
|
575
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
576
|
+
let manifest;
|
|
577
|
+
try {
|
|
578
|
+
manifest = JSON.parse((await fetchBytes(`${base}/manifest.json`)).toString("utf8"));
|
|
579
|
+
} catch (err) {
|
|
580
|
+
return { ok: false, detail: `\u62FF\u4E0D\u5230 manifest\uFF1A${err.message}` };
|
|
581
|
+
}
|
|
582
|
+
if (!manifest?.id || !Array.isArray(manifest.files)) {
|
|
583
|
+
return { ok: false, detail: "manifest \u683C\u5F0F\u4E0D\u5BF9\uFF1A\u8981 id \u548C files" };
|
|
584
|
+
}
|
|
585
|
+
const whaleDir = findWhaleDir(roots);
|
|
586
|
+
if (!whaleDir) {
|
|
587
|
+
return { ok: false, detail: `\u6CA1\u627E\u5230 web/whale/model.json\uFF0C\u627E\u8FC7\uFF1A${roots.join(" / ")}` };
|
|
588
|
+
}
|
|
589
|
+
try {
|
|
590
|
+
for (const file of manifest.files) {
|
|
591
|
+
const target = resolve(whaleDir, file.path);
|
|
592
|
+
if (!target.startsWith(resolve(whaleDir))) {
|
|
593
|
+
return { ok: false, detail: `\u8DEF\u5F84\u8D8A\u754C\uFF1A${file.path}` };
|
|
594
|
+
}
|
|
595
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
596
|
+
writeFileSync2(target, await fetchBytes(`${base}/${file.path}`));
|
|
597
|
+
}
|
|
598
|
+
const modelPath = join2(whaleDir, "model.json");
|
|
599
|
+
const model = JSON.parse(readFileSync2(modelPath, "utf8"));
|
|
600
|
+
const outfits = Array.isArray(model.outfits) ? model.outfits : [];
|
|
601
|
+
const idx = outfits.findIndex((o) => o?.id === manifest.id);
|
|
602
|
+
if (idx >= 0) outfits[idx] = manifest.outfit;
|
|
603
|
+
else outfits.push(manifest.outfit);
|
|
604
|
+
model.outfits = outfits;
|
|
605
|
+
copyFileSync(modelPath, `${modelPath}.bak-${Date.now()}`);
|
|
606
|
+
writeFileSync2(modelPath, JSON.stringify(model, null, 2));
|
|
607
|
+
log?.info(`\u76AE\u80A4\u88C5\u597D\u4E86\uFF1A${manifest.id}\uFF08web \u76EE\u5F55 ${whaleDir}\uFF09`);
|
|
608
|
+
return { ok: true, detail: `\u88C5\u597D\u4E86\u300C${manifest.name || manifest.id}\u300D\uFF0C\u5171 ${manifest.files.length} \u4E2A\u6587\u4EF6`, webDir: whaleDir };
|
|
609
|
+
} catch (err) {
|
|
610
|
+
log?.warn(`\u76AE\u80A4\u5B89\u88C5\u5931\u8D25\uFF1A${err.message}`);
|
|
611
|
+
return { ok: false, detail: `\u88C5\u5230\u4E00\u534A\u5931\u8D25\uFF1A${err.message}` };
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function cleanMedia(mediaDir, opts = {}) {
|
|
615
|
+
const keepDays = opts.keepDays ?? 1;
|
|
616
|
+
const maxMB = opts.maxMB ?? 0;
|
|
617
|
+
const out = { removed: 0, freedMB: 0, kept: 0, dir: mediaDir };
|
|
618
|
+
let names = [];
|
|
619
|
+
try {
|
|
620
|
+
names = readdirSync(mediaDir);
|
|
621
|
+
} catch {
|
|
622
|
+
return out;
|
|
623
|
+
}
|
|
624
|
+
const files = [];
|
|
625
|
+
for (const name of names) {
|
|
626
|
+
const full = join2(mediaDir, name);
|
|
627
|
+
try {
|
|
628
|
+
const st = statSync(full);
|
|
629
|
+
if (!st.isFile()) continue;
|
|
630
|
+
if (!/\.(jpg|jpeg|png|webp)$/i.test(name)) continue;
|
|
631
|
+
files.push({ name, full, size: st.size, mtime: st.mtimeMs });
|
|
632
|
+
} catch {
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
const cutoff = Date.now() - keepDays * 864e5;
|
|
636
|
+
let totalMB = files.reduce((sum, f) => sum + f.size, 0) / 1024 / 1024;
|
|
637
|
+
for (const f of files) {
|
|
638
|
+
if (f.mtime >= cutoff) continue;
|
|
639
|
+
if (!opts.dryRun) {
|
|
640
|
+
try {
|
|
641
|
+
unlinkSync(f.full);
|
|
642
|
+
} catch {
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
out.removed += 1;
|
|
647
|
+
out.freedMB += f.size / 1024 / 1024;
|
|
648
|
+
totalMB -= f.size / 1024 / 1024;
|
|
649
|
+
}
|
|
650
|
+
if (maxMB > 0 && totalMB > maxMB) {
|
|
651
|
+
const rest = files.filter((f) => f.mtime >= cutoff).sort((a, b) => a.mtime - b.mtime);
|
|
652
|
+
for (const f of rest) {
|
|
653
|
+
if (totalMB <= maxMB) break;
|
|
654
|
+
if (!opts.dryRun) {
|
|
655
|
+
try {
|
|
656
|
+
unlinkSync(f.full);
|
|
657
|
+
} catch {
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
out.removed += 1;
|
|
662
|
+
out.freedMB += f.size / 1024 / 1024;
|
|
663
|
+
totalMB -= f.size / 1024 / 1024;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
out.kept = readdirSync(mediaDir).filter((n) => /\.(jpg|jpeg|png|webp)$/i.test(n)).length;
|
|
668
|
+
} catch {
|
|
669
|
+
}
|
|
670
|
+
out.freedMB = Math.round(out.freedMB * 10) / 10;
|
|
671
|
+
return out;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/stores.ts
|
|
675
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
676
|
+
import { join as join3 } from "node:path";
|
|
519
677
|
var JsonStore = class {
|
|
520
678
|
file;
|
|
521
679
|
fallback;
|
|
@@ -523,16 +681,16 @@ var JsonStore = class {
|
|
|
523
681
|
constructor(dir, name, fallback) {
|
|
524
682
|
this.fallback = fallback;
|
|
525
683
|
try {
|
|
526
|
-
if (!
|
|
684
|
+
if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
|
|
527
685
|
} catch {
|
|
528
686
|
}
|
|
529
|
-
this.file =
|
|
687
|
+
this.file = join3(dir, name);
|
|
530
688
|
}
|
|
531
689
|
read() {
|
|
532
690
|
if (this.cached) return this.cached;
|
|
533
691
|
try {
|
|
534
|
-
if (
|
|
535
|
-
const raw = JSON.parse(
|
|
692
|
+
if (existsSync3(this.file)) {
|
|
693
|
+
const raw = JSON.parse(readFileSync3(this.file, "utf8"));
|
|
536
694
|
this.cached = raw ?? this.fallback;
|
|
537
695
|
return this.cached;
|
|
538
696
|
}
|
|
@@ -544,7 +702,7 @@ var JsonStore = class {
|
|
|
544
702
|
write(value) {
|
|
545
703
|
this.cached = value;
|
|
546
704
|
try {
|
|
547
|
-
|
|
705
|
+
writeFileSync3(this.file, JSON.stringify(value, null, 2));
|
|
548
706
|
} catch {
|
|
549
707
|
}
|
|
550
708
|
}
|
|
@@ -552,7 +710,7 @@ var JsonStore = class {
|
|
|
552
710
|
|
|
553
711
|
// src/hands.ts
|
|
554
712
|
import { execFile } from "node:child_process";
|
|
555
|
-
import { readFileSync as
|
|
713
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
556
714
|
import { get as httpGet, request as httpRequest } from "node:http";
|
|
557
715
|
var ACTION_TIMEOUT_MS = 6e3;
|
|
558
716
|
var PROBE_TTL_MS = 2e4;
|
|
@@ -562,12 +720,12 @@ var MAX_GESTURE_MS = 2e4;
|
|
|
562
720
|
var HandError = class extends Error {
|
|
563
721
|
};
|
|
564
722
|
function get(port, path, timeoutMs) {
|
|
565
|
-
return new Promise((
|
|
723
|
+
return new Promise((resolve2, reject) => {
|
|
566
724
|
const req = httpGet({ host: "127.0.0.1", port, path, timeout: timeoutMs }, (res) => {
|
|
567
725
|
const chunks = [];
|
|
568
726
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
569
727
|
res.on("end", () => {
|
|
570
|
-
|
|
728
|
+
resolve2({
|
|
571
729
|
status: res.statusCode ?? 0,
|
|
572
730
|
body: Buffer.concat(chunks),
|
|
573
731
|
header: (name) => {
|
|
@@ -582,7 +740,7 @@ function get(port, path, timeoutMs) {
|
|
|
582
740
|
});
|
|
583
741
|
}
|
|
584
742
|
async function post(port, path, body, timeoutMs) {
|
|
585
|
-
return new Promise((
|
|
743
|
+
return new Promise((resolve2, reject) => {
|
|
586
744
|
const data = Buffer.from(body, "utf8");
|
|
587
745
|
const req = httpRequest(
|
|
588
746
|
{
|
|
@@ -597,7 +755,7 @@ async function post(port, path, body, timeoutMs) {
|
|
|
597
755
|
const chunks = [];
|
|
598
756
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
599
757
|
res.on("end", () => {
|
|
600
|
-
|
|
758
|
+
resolve2({
|
|
601
759
|
status: res.statusCode ?? 0,
|
|
602
760
|
body: Buffer.concat(chunks),
|
|
603
761
|
header: (name) => {
|
|
@@ -690,10 +848,10 @@ var RootActuator = class {
|
|
|
690
848
|
this.log = log;
|
|
691
849
|
}
|
|
692
850
|
run(argv, timeoutMs = ACTION_TIMEOUT_MS) {
|
|
693
|
-
return new Promise((
|
|
851
|
+
return new Promise((resolve2) => {
|
|
694
852
|
execFile(argv[0], argv.slice(1), { timeout: timeoutMs, maxBuffer: 1 << 20 }, (err, stdout, stderr) => {
|
|
695
853
|
const code = err ? typeof err.code === "number" ? err.code : 1 : 0;
|
|
696
|
-
|
|
854
|
+
resolve2({ code, out: String(stdout ?? ""), err: String(stderr ?? "") });
|
|
697
855
|
});
|
|
698
856
|
});
|
|
699
857
|
}
|
|
@@ -739,10 +897,10 @@ var ShellActuator = class {
|
|
|
739
897
|
if (!template) return { ok: false, detail: "shell \u901A\u9053\u6CA1\u914D\u6A21\u677F" };
|
|
740
898
|
if (!template.includes("{cmd}")) return { ok: false, detail: "shell \u6A21\u677F\u91CC\u6CA1\u6709 {cmd}\uFF0C\u4E0D\u77E5\u9053\u628A\u547D\u4EE4\u653E\u54EA" };
|
|
741
899
|
const probe = template.replace(/\{cmd\}/g, "id");
|
|
742
|
-
const r = await new Promise((
|
|
900
|
+
const r = await new Promise((resolve2) => {
|
|
743
901
|
execFile("sh", ["-c", probe], { timeout: 8e3, maxBuffer: 1 << 20 }, (err, stdout, stderr) => {
|
|
744
902
|
const code = err ? typeof err.code === "number" ? err.code : 1 : 0;
|
|
745
|
-
|
|
903
|
+
resolve2({ code, out: String(stdout ?? ""), err: String(stderr ?? "") });
|
|
746
904
|
});
|
|
747
905
|
});
|
|
748
906
|
if (r.code !== 0) return { ok: false, detail: flat(r.err || r.out, 140) || "\u6A21\u677F\u8DD1\u4E0D\u8D77\u6765" };
|
|
@@ -751,10 +909,10 @@ var ShellActuator = class {
|
|
|
751
909
|
}
|
|
752
910
|
async apply(command) {
|
|
753
911
|
const argv = this.argv(command);
|
|
754
|
-
const r = await new Promise((
|
|
912
|
+
const r = await new Promise((resolve2) => {
|
|
755
913
|
execFile(argv[0], argv.slice(1), { timeout: ACTION_TIMEOUT_MS, maxBuffer: 1 << 20 }, (err, stdout, stderr) => {
|
|
756
914
|
const code = err ? typeof err.code === "number" ? err.code : 1 : 0;
|
|
757
|
-
|
|
915
|
+
resolve2({ code, out: String(stdout ?? ""), err: String(stderr ?? "") });
|
|
758
916
|
});
|
|
759
917
|
});
|
|
760
918
|
if (r.code !== 0) throw new HandError(`\u901A\u9053\u547D\u4EE4\u6CA1\u6210\u529F\uFF08${flat(r.err || r.out, 160) || `\u9000\u51FA\u7801 ${r.code}`}\uFF09\u3002`);
|
|
@@ -858,6 +1016,20 @@ var EyesClient = class {
|
|
|
858
1016
|
}
|
|
859
1017
|
return { ok: body.ok !== false, pkg: flat(body.pkg, 120), items };
|
|
860
1018
|
}
|
|
1019
|
+
/** 只截一块并放大:x,y,w,h 是真实像素。 */
|
|
1020
|
+
async zoom(x, y, w, h, maxWidth = 1080, quality = 80) {
|
|
1021
|
+
const cfg = this.cfg();
|
|
1022
|
+
const path = `/screen.jpg?x=${Math.max(0, Math.round(x))}&y=${Math.max(0, Math.round(y))}&w=${Math.max(8, Math.round(w))}&h=${Math.max(8, Math.round(h))}&max=${Math.round(maxWidth)}&q=${Math.round(quality)}`;
|
|
1023
|
+
const res = await get(this.handPort(), path, 12e3);
|
|
1024
|
+
if (res.status !== 200 || res.body.length === 0) return null;
|
|
1025
|
+
return {
|
|
1026
|
+
bytes: new Uint8Array(res.body),
|
|
1027
|
+
width: Number.parseInt(res.header("x-width"), 10) || 0,
|
|
1028
|
+
height: Number.parseInt(res.header("x-height"), 10) || 0,
|
|
1029
|
+
pkg: "",
|
|
1030
|
+
ageMs: 0
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
861
1033
|
async frame(maxWidth, quality) {
|
|
862
1034
|
try {
|
|
863
1035
|
const w = Math.max(160, Math.min(2160, Math.round(maxWidth)));
|
|
@@ -953,7 +1125,7 @@ var Hands = class {
|
|
|
953
1125
|
if (Date.now() - this.tokenCache.at < 3e4) return this.tokenCache.value;
|
|
954
1126
|
let value = "";
|
|
955
1127
|
try {
|
|
956
|
-
const text =
|
|
1128
|
+
const text = readFileSync4(file, "utf8").trim();
|
|
957
1129
|
if (text && text.length <= 200) value = text;
|
|
958
1130
|
} catch {
|
|
959
1131
|
value = "";
|
|
@@ -1964,6 +2136,21 @@ var HANDS_TOOL_DECLS = [
|
|
|
1964
2136
|
},
|
|
1965
2137
|
required: ["content"]
|
|
1966
2138
|
}
|
|
2139
|
+
},
|
|
2140
|
+
{
|
|
2141
|
+
name: "hand_zoom",
|
|
2142
|
+
tags: ["read"],
|
|
2143
|
+
description: "\u53EA\u622A\u5C4F\u5E55\u7684\u4E00\u5C0F\u5757\u5E76\u653E\u5927\u2014\u2014\u5C0F\u5B57\u3001\u5C0F\u6309\u94AE\u770B\u4E0D\u6E05\u65F6\u7528\u5B83\u3002\u5148 hand_see \u62FF\u5230\u5750\u6807\uFF0C\u518D\u628A\u8FD9\u5757\u7684 x/y/\u5BBD/\u9AD8 \u4EA4\u7ED9\u5B83\u3002",
|
|
2144
|
+
parameters: {
|
|
2145
|
+
type: "object",
|
|
2146
|
+
properties: {
|
|
2147
|
+
x: { type: "integer", description: "\u5DE6\u4E0A\u89D2\u6A2A\u5750\u6807\uFF08\u771F\u5B9E\u50CF\u7D20\uFF09" },
|
|
2148
|
+
y: { type: "integer", description: "\u5DE6\u4E0A\u89D2\u7EB5\u5750\u6807" },
|
|
2149
|
+
w: { type: "integer", description: "\u5BBD" },
|
|
2150
|
+
h: { type: "integer", description: "\u9AD8" }
|
|
2151
|
+
},
|
|
2152
|
+
required: ["x", "y", "w", "h"]
|
|
2153
|
+
}
|
|
1967
2154
|
}
|
|
1968
2155
|
];
|
|
1969
2156
|
|
|
@@ -2085,8 +2272,8 @@ var HandsWorld = class {
|
|
|
2085
2272
|
try {
|
|
2086
2273
|
const old = readPersona(dir);
|
|
2087
2274
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
2088
|
-
if (old)
|
|
2089
|
-
|
|
2275
|
+
if (old) writeFileSync4(join4(dir, `CONSTITUTION.bak.${stamp}.md`), old);
|
|
2276
|
+
writeFileSync4(join4(dir, "CONSTITUTION.md"), content);
|
|
2090
2277
|
return `\u4EBA\u683C\u6362\u597D\u4E86\uFF08\u65E7\u7248\u5907\u4EFD\u6210 CONSTITUTION.bak.${stamp}.md\uFF09`;
|
|
2091
2278
|
} catch (err) {
|
|
2092
2279
|
return failed("\u5199\u4E0D\u8FDB\u53BB\uFF1A" + String(err).slice(0, 120));
|
|
@@ -2101,7 +2288,7 @@ var HandsWorld = class {
|
|
|
2101
2288
|
try {
|
|
2102
2289
|
const rows = await this.hands.memorySearch();
|
|
2103
2290
|
lines = rows.length;
|
|
2104
|
-
const target =
|
|
2291
|
+
const target = join4(dir, "\u8BB0\u5FC6\u5E93.md");
|
|
2105
2292
|
const r = mergeMemorySection(target, rows);
|
|
2106
2293
|
if (r.wrote) wrote.push("\u8BB0\u5FC6\u5E93.md");
|
|
2107
2294
|
const persona = readPersona(dir);
|
|
@@ -2272,7 +2459,7 @@ ${lines.join("\n")}
|
|
|
2272
2459
|
mediaDir() {
|
|
2273
2460
|
const base = this.opts.dataDir;
|
|
2274
2461
|
if (!base) return null;
|
|
2275
|
-
const dir = base.endsWith("/data") ?
|
|
2462
|
+
const dir = base.endsWith("/data") ? join4(base, "media") : join4(base, "data", "media");
|
|
2276
2463
|
return dir;
|
|
2277
2464
|
}
|
|
2278
2465
|
/** 钩子 5:QQ 在后台收到的消息,原样递给她;谁来的一眼能看清。 */
|
|
@@ -2372,6 +2559,17 @@ ${lines.join("\n")}
|
|
|
2372
2559
|
return await this.runSync(true);
|
|
2373
2560
|
case "sync_status":
|
|
2374
2561
|
return this.lastSync ? `\u4E0A\u6B21\u540C\u6B65 ${this.lastSync.at.slice(0, 19)}\uFF0C\u8BB0\u5FC6 ${this.lastSync.lines} \u6761\uFF0C\u4EBA\u683C ${this.lastSync.persona} \u5B57` : "\u8FD8\u6CA1\u540C\u6B65\u8FC7";
|
|
2562
|
+
case "hand_zoom": {
|
|
2563
|
+
const n = (v) => Math.round(Number(v));
|
|
2564
|
+
const x = n(args.x), y = n(args.y), w = n(args.w), h = n(args.h);
|
|
2565
|
+
if (![x, y, w, h].every(Number.isFinite) || w <= 0 || h <= 0) return failed("\u8981\u7ED9 x/y/w/h\uFF08\u771F\u5B9E\u50CF\u7D20\uFF09");
|
|
2566
|
+
const f = await this.hands.zoom(x, y, w, h);
|
|
2567
|
+
if (!f) return failed("\u8FD9\u4E00\u5757\u6CA1\u622A\u5230\uFF08\u5750\u6807\u53EF\u80FD\u8D85\u51FA\u5C4F\u5E55\uFF09");
|
|
2568
|
+
return {
|
|
2569
|
+
text: `\u653E\u5927\u770B @(${x},${y}) ${w}\xD7${h}`,
|
|
2570
|
+
blobs: [{ bytes: f.bytes, mime: "image/jpeg", name: "zoom.jpg", fallbackText: "\uFF08\u8FD9\u662F\u4ED6\u6307\u5B9A\u90A3\u4E00\u5757\u653E\u5927\u540E\u7684\u622A\u56FE\uFF09" }]
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2375
2573
|
case "hand_see":
|
|
2376
2574
|
return await this.see();
|
|
2377
2575
|
case "hand_look":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortico-world-hands",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.4",
|
|
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",
|