cortico-world-hands 0.1.0 → 0.1.2
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 +158 -11
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var HANDS_DEFAULTS = {
|
|
|
4
4
|
enabled: false,
|
|
5
5
|
actuator: "auto",
|
|
6
6
|
eyesPort: 17898,
|
|
7
|
+
handPort: 0,
|
|
7
8
|
sensePort: 17899,
|
|
8
9
|
rootCommand: "su",
|
|
9
10
|
shellCommand: "",
|
|
@@ -82,6 +83,14 @@ var HANDS_CONFIG_GROUP = {
|
|
|
82
83
|
description: "\u622A\u5C4F\u3001\u8BFB\u5B57\u548C\u624B\u52BF\u7AEF\u70B9\u90FD\u5728\u8FD9\u4E2A\u53E3\u4E0A\uFF0C\u968F\u5305\u7684\u65E0\u969C\u788D\u670D\u52A1\u7528 17898\u3002",
|
|
83
84
|
"x-hot": false
|
|
84
85
|
},
|
|
86
|
+
[`${K}.handPort`]: {
|
|
87
|
+
type: "integer",
|
|
88
|
+
title: "\u624B\u52BF\u7AEF\u53E3",
|
|
89
|
+
minimum: 0,
|
|
90
|
+
maximum: 65535,
|
|
91
|
+
description: "\u72EC\u7ACB\u624B\u52BF App\uFF08\u624B\uFF09\u7684\u7AEF\u53E3\uFF0C\u9ED8\u8BA4 17897\u3002\u586B 0 \u8868\u793A\u548C\u773C\u775B\u540C\u7AEF\u53E3\u2014\u2014\u90A3\u662F\u628A /hand/* \u8865\u8FDB Coopanin \u65E0\u969C\u788D\u670D\u52A1\u7684\u88C5\u6CD5\u3002",
|
|
92
|
+
"x-hot": false
|
|
93
|
+
},
|
|
85
94
|
[`${K}.sensePort`]: {
|
|
86
95
|
type: "integer",
|
|
87
96
|
title: "\u611F\u89C9\u7AEF\u53E3",
|
|
@@ -240,6 +249,10 @@ function inputCommand(command) {
|
|
|
240
249
|
return command.ms >= 400 ? `input swipe ${command.x} ${command.y} ${command.x} ${command.y} ${command.ms}` : `input tap ${command.x} ${command.y}`;
|
|
241
250
|
case "swipe":
|
|
242
251
|
return `input swipe ${command.x1} ${command.y1} ${command.x2} ${command.y2} ${command.ms}`;
|
|
252
|
+
case "pinch":
|
|
253
|
+
return `input swipe ${command.cx - 220} ${command.cy} ${command.cx + 220} ${command.cy} ${command.ms}`;
|
|
254
|
+
case "drag":
|
|
255
|
+
return `input draganddrop ${command.x1} ${command.y1} ${command.x2} ${command.y2} ${command.ms}`;
|
|
243
256
|
case "key":
|
|
244
257
|
return `input keyevent ${command.code}`;
|
|
245
258
|
// 文本在拼命令这一刻才转义:只有 input 这条路吃不下非 ASCII,无障碍那条能原样收
|
|
@@ -375,6 +388,10 @@ var EyesActuator = class {
|
|
|
375
388
|
return this.send(`/hand/tap?x=${command.x}&y=${command.y}&ms=${command.ms}`);
|
|
376
389
|
case "swipe":
|
|
377
390
|
return this.send(`/hand/swipe?x1=${command.x1}&y1=${command.y1}&x2=${command.x2}&y2=${command.y2}&ms=${command.ms}`);
|
|
391
|
+
case "pinch":
|
|
392
|
+
return this.send(`/hand/pinch?cx=${command.cx}&cy=${command.cy}&scale=${command.scale}&ms=${command.ms}`);
|
|
393
|
+
case "drag":
|
|
394
|
+
return this.send(`/hand/drag?x1=${command.x1}&y1=${command.y1}&x2=${command.x2}&y2=${command.y2}&ms=${command.ms}`);
|
|
378
395
|
case "key":
|
|
379
396
|
return this.send(`/hand/key?name=${encodeURIComponent(command.name)}&code=${command.code}`);
|
|
380
397
|
case "text":
|
|
@@ -444,6 +461,10 @@ var describeCommand = (command) => {
|
|
|
444
461
|
return `\u70B9 (${command.x},${command.y})`;
|
|
445
462
|
case "swipe":
|
|
446
463
|
return `\u4ECE (${command.x1},${command.y1}) \u6ED1\u5230 (${command.x2},${command.y2})`;
|
|
464
|
+
case "pinch":
|
|
465
|
+
return `\u5728 (${command.cx},${command.cy}) ${command.scale >= 1 ? "\u653E\u5927" : "\u7F29\u5C0F"} ${command.scale} \u500D`;
|
|
466
|
+
case "drag":
|
|
467
|
+
return `\u628A (${command.x1},${command.y1}) \u62D6\u5230 (${command.x2},${command.y2})`;
|
|
447
468
|
case "key":
|
|
448
469
|
return `\u6309 ${command.name}`;
|
|
449
470
|
case "text":
|
|
@@ -469,11 +490,18 @@ var Hands = class {
|
|
|
469
490
|
this.eyes = new EyesClient(() => this.port(this.cfg().eyesPort));
|
|
470
491
|
this.rootActuator = new RootActuator(() => this.cfg().rootCommand, () => this.log);
|
|
471
492
|
this.shellActuator = new ShellActuator(() => this.cfg().shellCommand, () => this.log);
|
|
472
|
-
this.eyesActuator = new EyesActuator(() => this.
|
|
493
|
+
this.eyesActuator = new EyesActuator(() => this.handPort(), () => this.readToken(), () => this.log);
|
|
473
494
|
}
|
|
474
|
-
port(value) {
|
|
495
|
+
port(value, fallback = 17898) {
|
|
475
496
|
const n = Math.round(Number(value));
|
|
476
|
-
return Number.isFinite(n) && n >= 1024 && n <= 65535 ? n :
|
|
497
|
+
return Number.isFinite(n) && n >= 1024 && n <= 65535 ? n : fallback;
|
|
498
|
+
}
|
|
499
|
+
/** 手势端点在哪:handPort 为 0 时和眼睛同一个口。 */
|
|
500
|
+
handPort() {
|
|
501
|
+
const cfg = this.cfg();
|
|
502
|
+
const explicit = Math.round(Number(cfg.handPort));
|
|
503
|
+
if (Number.isFinite(explicit) && explicit >= 1024 && explicit <= 65535) return explicit;
|
|
504
|
+
return this.port(cfg.eyesPort);
|
|
477
505
|
}
|
|
478
506
|
/** 世界挂上以后把日志接进来;探测和动作都写在这里。 */
|
|
479
507
|
attachLog(log) {
|
|
@@ -558,21 +586,18 @@ var Hands = class {
|
|
|
558
586
|
if (!picked) throw new HandError(`\u624B\u73B0\u5728\u52A8\u4E0D\u4E86\uFF1A${state.detail}`);
|
|
559
587
|
return picked;
|
|
560
588
|
}
|
|
561
|
-
/**
|
|
589
|
+
/**
|
|
590
|
+
* 屏幕尺寸。只认手势端点报的(眼睛报的是上一次截图的尺寸,常被 max= 缩过,拿来判越界会误拒)。
|
|
591
|
+
* 拿不到就不校验边界。
|
|
592
|
+
*/
|
|
562
593
|
async screenSize() {
|
|
563
594
|
if (this.screen && Date.now() - this.lastScreenAt < 6e4) return this.screen;
|
|
564
|
-
const health = await this.
|
|
595
|
+
const health = await getJson(this.handPort(), "/health", 4e3);
|
|
565
596
|
if (health?.ok && health.frameWidth && health.frameHeight) {
|
|
566
597
|
this.screen = { w: health.frameWidth, h: health.frameHeight };
|
|
567
598
|
this.lastScreenAt = Date.now();
|
|
568
599
|
return this.screen;
|
|
569
600
|
}
|
|
570
|
-
const frame = await this.eyes.frame(240, 30);
|
|
571
|
-
if (frame && frame.width > 0 && frame.height > 0) {
|
|
572
|
-
this.screen = { w: frame.width, h: frame.height };
|
|
573
|
-
this.lastScreenAt = Date.now();
|
|
574
|
-
return this.screen;
|
|
575
|
-
}
|
|
576
601
|
return null;
|
|
577
602
|
}
|
|
578
603
|
prune() {
|
|
@@ -628,6 +653,39 @@ var Hands = class {
|
|
|
628
653
|
const ms = milliseconds(opts.ms, 300);
|
|
629
654
|
return this.perform({ kind: "swipe", x1: a, y1: b, x2: c, y2: d, ms });
|
|
630
655
|
}
|
|
656
|
+
async pinch(cx, cy, scale, opts = {}) {
|
|
657
|
+
const x = coord(cx);
|
|
658
|
+
const y = coord(cy);
|
|
659
|
+
const ms = milliseconds(opts.ms, 400);
|
|
660
|
+
const factor = Math.max(0.25, Math.min(4, Number(scale) || 1));
|
|
661
|
+
return this.perform({ kind: "pinch", cx: x, cy: y, scale: factor, ms });
|
|
662
|
+
}
|
|
663
|
+
async drag(x1, y1, x2, y2, opts = {}) {
|
|
664
|
+
const a = coord(x1);
|
|
665
|
+
const b = coord(y1);
|
|
666
|
+
const c = coord(x2);
|
|
667
|
+
const d = coord(y2);
|
|
668
|
+
await this.bounds(a, b);
|
|
669
|
+
await this.bounds(c, d);
|
|
670
|
+
const ms = milliseconds(opts.ms, 900);
|
|
671
|
+
return this.perform({ kind: "drag", x1: a, y1: b, x2: c, y2: d, ms });
|
|
672
|
+
}
|
|
673
|
+
/** 一屏屏翻着找文字(只查不点)。 */
|
|
674
|
+
async findItem(text, opts = {}) {
|
|
675
|
+
const cfg = this.cfg();
|
|
676
|
+
const tries = Math.max(0, Math.min(12, Math.round(Number(opts.tries ?? 5))));
|
|
677
|
+
const scroll = opts.scroll === false ? "0" : "1";
|
|
678
|
+
const token = cfg.handToken ? `&t=${encodeURIComponent(cfg.handToken)}` : "";
|
|
679
|
+
const path = `/hand/find?text=${encodeURIComponent(text)}&tries=${tries}&scroll=${scroll}${token}`;
|
|
680
|
+
const res = await get(this.port(cfg.handPort), path, ACTION_TIMEOUT_MS);
|
|
681
|
+
let body = {};
|
|
682
|
+
try {
|
|
683
|
+
body = JSON.parse(res.body.toString("utf8"));
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
686
|
+
if (res.status === 200 && body.ok) return { ok: true, x: body.x, y: body.y, detail: flat(body.detail, 80) };
|
|
687
|
+
return { ok: false, detail: flat(body.error || `\u627E\u4E0D\u5230\u300C${text}\u300D`, 120) };
|
|
688
|
+
}
|
|
631
689
|
async press(key) {
|
|
632
690
|
const name = String(key ?? "").trim().toLowerCase().replace(/[\s_-]/g, "");
|
|
633
691
|
const code = keyCode(name);
|
|
@@ -778,6 +836,51 @@ var HANDS_TOOL_DECLS = [
|
|
|
778
836
|
required: ["x1", "y1", "x2", "y2"]
|
|
779
837
|
}
|
|
780
838
|
},
|
|
839
|
+
{
|
|
840
|
+
name: "hand_pinch",
|
|
841
|
+
tags: ["act"],
|
|
842
|
+
description: "\u53CC\u6307\u7F29\u653E\uFF1A\u653E\u5927\u6216\u7F29\u5C0F\u56FE\u7247\u3001\u5730\u56FE\u3001\u7F51\u9875\u3002scale>1 \u653E\u5927\uFF0C<1 \u7F29\u5C0F\uFF1B\u4E0D\u7ED9 cx/cy \u5C31\u4EE5\u5C4F\u5E55\u4E2D\u5FC3\u4E3A\u57FA\u51C6\u3002",
|
|
843
|
+
parameters: {
|
|
844
|
+
type: "object",
|
|
845
|
+
properties: {
|
|
846
|
+
scale: { type: "number", minimum: 0.25, maximum: 4, description: "\u500D\u6570\uFF1A1.5 \u653E\u5927\u4E00\u534A\uFF0C0.6 \u7F29\u5C0F\u3002" },
|
|
847
|
+
cx: { type: "integer", description: "\u4E2D\u5FC3\u6A2A\u5750\u6807\uFF08\u53EF\u7701\u7565\uFF09\u3002" },
|
|
848
|
+
cy: { type: "integer", description: "\u4E2D\u5FC3\u7EB5\u5750\u6807\uFF08\u53EF\u7701\u7565\uFF09\u3002" },
|
|
849
|
+
ms: { type: "integer", minimum: 120, maximum: 2e3, description: "\u624B\u52BF\u65F6\u957F\uFF0C\u9ED8\u8BA4 400 \u6BEB\u79D2\u3002" }
|
|
850
|
+
},
|
|
851
|
+
required: ["scale"]
|
|
852
|
+
}
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
name: "hand_drag",
|
|
856
|
+
tags: ["act"],
|
|
857
|
+
description: "\u957F\u6309\u540E\u62D6\u62FD\uFF1A\u62D6\u5217\u8868\u9879\u3001\u62D6\u6ED1\u5757\u3001\u62D6\u56FE\u6807\u3001\u62D6\u8FDB\u5EA6\u3002\u548C swipe \u7684\u533A\u522B\u662F\u5B83\u6309\u4F4F\u4E0D\u653E\u518D\u62D6\uFF0C\u9002\u5408\u6709\u62D6\u62FD\u8BED\u4E49\u7684\u63A7\u4EF6\u3002",
|
|
858
|
+
parameters: {
|
|
859
|
+
type: "object",
|
|
860
|
+
properties: {
|
|
861
|
+
x1: { type: "integer", description: "\u8D77\u70B9\u6A2A\u5750\u6807\u3002" },
|
|
862
|
+
y1: { type: "integer", description: "\u8D77\u70B9\u7EB5\u5750\u6807\u3002" },
|
|
863
|
+
x2: { type: "integer", description: "\u7EC8\u70B9\u6A2A\u5750\u6807\u3002" },
|
|
864
|
+
y2: { type: "integer", description: "\u7EC8\u70B9\u7EB5\u5750\u6807\u3002" },
|
|
865
|
+
ms: { type: "integer", minimum: 300, maximum: 4e3, description: "\u62D6\u591A\u4E45\uFF0C\u9ED8\u8BA4 900 \u6BEB\u79D2\u3002" }
|
|
866
|
+
},
|
|
867
|
+
required: ["x1", "y1", "x2", "y2"]
|
|
868
|
+
}
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
name: "hand_find",
|
|
872
|
+
tags: ["read"],
|
|
873
|
+
description: "\u4E00\u5C4F\u5C4F\u5F80\u4E0B\u7FFB\uFF0C\u627E\u5C4F\u5E55\u4E0A\u542B\u6709\u67D0\u6BB5\u6587\u5B57\u7684\u4E1C\u897F\uFF0C\u8FD4\u56DE\u5B83\u7684\u5750\u6807\uFF08\u53EA\u627E\u4E0D\u70B9\uFF09\u3002\u8868\u683C\u3001\u957F\u5217\u8868\u3001\u8BBE\u7F6E\u9879\u91CC\u627E\u4E1C\u897F\u7528\u5B83\uFF0C\u6BD4\u8089\u773C\u4E00\u9875\u9875\u770B\u5FEB\u3002",
|
|
874
|
+
parameters: {
|
|
875
|
+
type: "object",
|
|
876
|
+
properties: {
|
|
877
|
+
text: { type: "string", description: "\u8981\u627E\u7684\u6587\u5B57\uFF08\u5305\u542B\u5339\u914D\uFF0C\u4E0D\u533A\u5206\u5927\u5C0F\u5199\uFF09\u3002" },
|
|
878
|
+
tries: { type: "integer", minimum: 0, maximum: 12, description: "\u6700\u591A\u7FFB\u51E0\u5C4F\uFF0C\u9ED8\u8BA4 5\u3002" },
|
|
879
|
+
scroll: { type: "boolean", description: "\u627E\u4E0D\u5230\u65F6\u662F\u5426\u81EA\u52A8\u6EDA\u52A8\uFF0C\u9ED8\u8BA4 true\u3002" }
|
|
880
|
+
},
|
|
881
|
+
required: ["text"]
|
|
882
|
+
}
|
|
883
|
+
},
|
|
781
884
|
{
|
|
782
885
|
name: "hand_type",
|
|
783
886
|
tags: ["act"],
|
|
@@ -886,6 +989,12 @@ var HandsWorld = class {
|
|
|
886
989
|
return await this.long(args);
|
|
887
990
|
case "hand_swipe":
|
|
888
991
|
return await this.swipe(args);
|
|
992
|
+
case "hand_pinch":
|
|
993
|
+
return await this.pinch(args);
|
|
994
|
+
case "hand_drag":
|
|
995
|
+
return await this.drag(args);
|
|
996
|
+
case "hand_find":
|
|
997
|
+
return await this.findItem(args);
|
|
889
998
|
case "hand_type":
|
|
890
999
|
return await this.type(args);
|
|
891
1000
|
case "hand_key":
|
|
@@ -922,6 +1031,44 @@ var HandsWorld = class {
|
|
|
922
1031
|
return { text: report.text, ...blobs.length ? { blobs } : {} };
|
|
923
1032
|
}
|
|
924
1033
|
/** 给文字就先在屏幕上找,找到唯一一处才点;给坐标就直接点。 */
|
|
1034
|
+
async pinch(args) {
|
|
1035
|
+
const scale = Number(args.scale);
|
|
1036
|
+
if (!Number.isFinite(scale) || scale <= 0) return failed("scale \u8981\u662F\u6B63\u6570\uFF0C\u6BD4\u5982 1.5 \u653E\u5927\u30010.6 \u7F29\u5C0F\u3002");
|
|
1037
|
+
const cx = args.cx === void 0 ? void 0 : Number(args.cx);
|
|
1038
|
+
const cy = args.cy === void 0 ? void 0 : Number(args.cy);
|
|
1039
|
+
const ms = args.ms === void 0 ? void 0 : Number(args.ms);
|
|
1040
|
+
const size = await this.hands.screenSize();
|
|
1041
|
+
const x = cx ?? Math.round((size?.w ?? 1080) / 2);
|
|
1042
|
+
const y = cy ?? Math.round((size?.h ?? 1920) / 2);
|
|
1043
|
+
const receipt = await this.hands.pinch(x, y, scale, ms === void 0 ? {} : { ms });
|
|
1044
|
+
this.note(receipt);
|
|
1045
|
+
return receipt.text;
|
|
1046
|
+
}
|
|
1047
|
+
async drag(args) {
|
|
1048
|
+
const need = ["x1", "y1", "x2", "y2"];
|
|
1049
|
+
if (need.some((k) => args[k] === void 0)) return failed("\u62D6\u62FD\u8981\u7ED9\u9F50 x1\u3001y1\u3001x2\u3001y2\u3002");
|
|
1050
|
+
const receipt = await this.hands.drag(
|
|
1051
|
+
Number(args.x1),
|
|
1052
|
+
Number(args.y1),
|
|
1053
|
+
Number(args.x2),
|
|
1054
|
+
Number(args.y2),
|
|
1055
|
+
args.ms === void 0 ? {} : { ms: Number(args.ms) }
|
|
1056
|
+
);
|
|
1057
|
+
this.note(receipt);
|
|
1058
|
+
return receipt.text;
|
|
1059
|
+
}
|
|
1060
|
+
async findItem(args) {
|
|
1061
|
+
const text = typeof args.text === "string" ? args.text.trim() : "";
|
|
1062
|
+
if (!text) return failed("\u8981\u627E\u4EC0\u4E48\u6587\u5B57\uFF1F\u7ED9 text\u3002");
|
|
1063
|
+
const tries = args.tries === void 0 ? void 0 : Number(args.tries);
|
|
1064
|
+
const scroll = args.scroll === void 0 ? void 0 : Boolean(args.scroll);
|
|
1065
|
+
const found = await this.hands.findItem(text, {
|
|
1066
|
+
...tries === void 0 ? {} : { tries },
|
|
1067
|
+
...scroll === void 0 ? {} : { scroll }
|
|
1068
|
+
});
|
|
1069
|
+
if (!found.ok) return failed(found.detail);
|
|
1070
|
+
return `\u627E\u5230\u4E86\u300C${text}\u300D@(${found.x},${found.y})${found.detail ? `\uFF08${found.detail}\uFF09` : ""}\uFF1B\u8981\u52A8\u624B\u5C31 hand_tap \u4F20\u8FD9\u7EC4\u5750\u6807\u3002`;
|
|
1071
|
+
}
|
|
925
1072
|
async tap(args) {
|
|
926
1073
|
const text = typeof args.text === "string" ? args.text.trim() : "";
|
|
927
1074
|
if (text) {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortico-world-hands",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Cortico World: hands for the desktop pet — looks at the phone screen, then taps, swipes, types and presses keys through whatever actuator the device actually has",
|
|
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",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"cortico-world",
|
|
@@ -28,4 +28,4 @@
|
|
|
28
28
|
"README.md"
|
|
29
29
|
],
|
|
30
30
|
"scripts": {}
|
|
31
|
-
}
|
|
31
|
+
}
|