cortico-world-hands 0.1.1 → 0.1.3

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.
Files changed (2) hide show
  1. package/dist/index.js +223 -0
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -249,6 +249,10 @@ function inputCommand(command) {
249
249
  return command.ms >= 400 ? `input swipe ${command.x} ${command.y} ${command.x} ${command.y} ${command.ms}` : `input tap ${command.x} ${command.y}`;
250
250
  case "swipe":
251
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}`;
252
256
  case "key":
253
257
  return `input keyevent ${command.code}`;
254
258
  // 文本在拼命令这一刻才转义:只有 input 这条路吃不下非 ASCII,无障碍那条能原样收
@@ -384,6 +388,10 @@ var EyesActuator = class {
384
388
  return this.send(`/hand/tap?x=${command.x}&y=${command.y}&ms=${command.ms}`);
385
389
  case "swipe":
386
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}`);
387
395
  case "key":
388
396
  return this.send(`/hand/key?name=${encodeURIComponent(command.name)}&code=${command.code}`);
389
397
  case "text":
@@ -453,6 +461,10 @@ var describeCommand = (command) => {
453
461
  return `\u70B9 (${command.x},${command.y})`;
454
462
  case "swipe":
455
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})`;
456
468
  case "key":
457
469
  return `\u6309 ${command.name}`;
458
470
  case "text":
@@ -641,6 +653,71 @@ var Hands = class {
641
653
  const ms = milliseconds(opts.ms, 300);
642
654
  return this.perform({ kind: "swipe", x1: a, y1: b, x2: c, y2: d, ms });
643
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 noteAdd(text) {
675
+ const cfg = this.cfg();
676
+ const token = cfg.handToken ? `&t=${encodeURIComponent(cfg.handToken)}` : "";
677
+ const path = `/hand/note?text=${encodeURIComponent(text)}${token}`;
678
+ const res = await get(this.port(cfg.handPort), path, ACTION_TIMEOUT_MS);
679
+ let body = {};
680
+ try {
681
+ body = JSON.parse(res.body.toString("utf8"));
682
+ } catch {
683
+ }
684
+ return { ok: res.status === 200 && body.ok !== false, detail: flat(body.detail || body.error || "", 80) };
685
+ }
686
+ /** 留言板:全部留言。 */
687
+ async noteList() {
688
+ const cfg = this.cfg();
689
+ const token = cfg.handToken ? `?t=${encodeURIComponent(cfg.handToken)}` : "";
690
+ const res = await get(this.port(cfg.handPort), `/hand/notes${token}`, ACTION_TIMEOUT_MS);
691
+ try {
692
+ const body = JSON.parse(res.body.toString("utf8"));
693
+ return Array.isArray(body.notes) ? body.notes : [];
694
+ } catch {
695
+ return [];
696
+ }
697
+ }
698
+ /** 留言板:划掉一条。 */
699
+ async noteDone(id) {
700
+ const cfg = this.cfg();
701
+ const token = cfg.handToken ? `&t=${encodeURIComponent(cfg.handToken)}` : "";
702
+ const res = await get(this.port(cfg.handPort), `/hand/note/done?id=${encodeURIComponent(id)}${token}`, ACTION_TIMEOUT_MS);
703
+ return res.status === 200;
704
+ }
705
+ /** 一屏屏翻着找文字(只查不点)。 */
706
+ async findItem(text, opts = {}) {
707
+ const cfg = this.cfg();
708
+ const tries = Math.max(0, Math.min(12, Math.round(Number(opts.tries ?? 5))));
709
+ const scroll = opts.scroll === false ? "0" : "1";
710
+ const token = cfg.handToken ? `&t=${encodeURIComponent(cfg.handToken)}` : "";
711
+ const path = `/hand/find?text=${encodeURIComponent(text)}&tries=${tries}&scroll=${scroll}${token}`;
712
+ const res = await get(this.port(cfg.handPort), path, ACTION_TIMEOUT_MS);
713
+ let body = {};
714
+ try {
715
+ body = JSON.parse(res.body.toString("utf8"));
716
+ } catch {
717
+ }
718
+ if (res.status === 200 && body.ok) return { ok: true, x: body.x, y: body.y, detail: flat(body.detail, 80) };
719
+ return { ok: false, detail: flat(body.error || `\u627E\u4E0D\u5230\u300C${text}\u300D`, 120) };
720
+ }
644
721
  async press(key) {
645
722
  const name = String(key ?? "").trim().toLowerCase().replace(/[\s_-]/g, "");
646
723
  const code = keyCode(name);
@@ -791,6 +868,51 @@ var HANDS_TOOL_DECLS = [
791
868
  required: ["x1", "y1", "x2", "y2"]
792
869
  }
793
870
  },
871
+ {
872
+ name: "hand_pinch",
873
+ tags: ["act"],
874
+ 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",
875
+ parameters: {
876
+ type: "object",
877
+ properties: {
878
+ scale: { type: "number", minimum: 0.25, maximum: 4, description: "\u500D\u6570\uFF1A1.5 \u653E\u5927\u4E00\u534A\uFF0C0.6 \u7F29\u5C0F\u3002" },
879
+ cx: { type: "integer", description: "\u4E2D\u5FC3\u6A2A\u5750\u6807\uFF08\u53EF\u7701\u7565\uFF09\u3002" },
880
+ cy: { type: "integer", description: "\u4E2D\u5FC3\u7EB5\u5750\u6807\uFF08\u53EF\u7701\u7565\uFF09\u3002" },
881
+ ms: { type: "integer", minimum: 120, maximum: 2e3, description: "\u624B\u52BF\u65F6\u957F\uFF0C\u9ED8\u8BA4 400 \u6BEB\u79D2\u3002" }
882
+ },
883
+ required: ["scale"]
884
+ }
885
+ },
886
+ {
887
+ name: "hand_drag",
888
+ tags: ["act"],
889
+ 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",
890
+ parameters: {
891
+ type: "object",
892
+ properties: {
893
+ x1: { type: "integer", description: "\u8D77\u70B9\u6A2A\u5750\u6807\u3002" },
894
+ y1: { type: "integer", description: "\u8D77\u70B9\u7EB5\u5750\u6807\u3002" },
895
+ x2: { type: "integer", description: "\u7EC8\u70B9\u6A2A\u5750\u6807\u3002" },
896
+ y2: { type: "integer", description: "\u7EC8\u70B9\u7EB5\u5750\u6807\u3002" },
897
+ ms: { type: "integer", minimum: 300, maximum: 4e3, description: "\u62D6\u591A\u4E45\uFF0C\u9ED8\u8BA4 900 \u6BEB\u79D2\u3002" }
898
+ },
899
+ required: ["x1", "y1", "x2", "y2"]
900
+ }
901
+ },
902
+ {
903
+ name: "hand_find",
904
+ tags: ["read"],
905
+ 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",
906
+ parameters: {
907
+ type: "object",
908
+ properties: {
909
+ text: { type: "string", description: "\u8981\u627E\u7684\u6587\u5B57\uFF08\u5305\u542B\u5339\u914D\uFF0C\u4E0D\u533A\u5206\u5927\u5C0F\u5199\uFF09\u3002" },
910
+ tries: { type: "integer", minimum: 0, maximum: 12, description: "\u6700\u591A\u7FFB\u51E0\u5C4F\uFF0C\u9ED8\u8BA4 5\u3002" },
911
+ scroll: { type: "boolean", description: "\u627E\u4E0D\u5230\u65F6\u662F\u5426\u81EA\u52A8\u6EDA\u52A8\uFF0C\u9ED8\u8BA4 true\u3002" }
912
+ },
913
+ required: ["text"]
914
+ }
915
+ },
794
916
  {
795
917
  name: "hand_type",
796
918
  tags: ["act"],
@@ -825,6 +947,36 @@ var HANDS_TOOL_DECLS = [
825
947
  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",
826
948
  parameters: { type: "object", properties: {}, required: [] }
827
949
  },
950
+ {
951
+ name: "note_add",
952
+ tags: ["act"],
953
+ description: "\u5728\u7559\u8A00\u677F\u4E0A\u5199\u4E00\u6761\uFF1A\u60F3\u8BF4\u7684\u8BDD\u3001\u60F3\u8BB0\u7684\u4E8B\u3001\u5F85\u529E\u3001\u7ED9\u81EA\u5DF1\u7684\u63D0\u9192\u3002\u60F3\u8BB0\u5C31\u8BB0\uFF0C\u4E0D\u7528\u7B49\u5BF9\u65B9\u540C\u610F\uFF1B\u4E00\u6761\u522B\u592A\u957F\uFF08\u4E00\u53E5\u8BDD\u6700\u597D\uFF09\u3002",
954
+ parameters: {
955
+ type: "object",
956
+ properties: {
957
+ text: { type: "string", description: "\u8981\u5199\u4E0B\u7684\u5185\u5BB9\u3002" }
958
+ },
959
+ required: ["text"]
960
+ }
961
+ },
962
+ {
963
+ name: "note_list",
964
+ tags: ["read"],
965
+ description: "\u770B\u7559\u8A00\u677F\uFF1A\u6709\u54EA\u4E9B\u6CA1\u5212\u6389\u7684\u3001\u90FD\u5199\u4E86\u4EC0\u4E48\u3002\u5BF9\u65B9\u95EE\u300C\u4F60\u8BB0\u4E86\u4EC0\u4E48\u300D\u300C\u7559\u8A00\u677F\u4E0A\u6709\u5565\u300D\u65F6\u7528\u5B83\u3002",
966
+ parameters: { type: "object", properties: {}, required: [] }
967
+ },
968
+ {
969
+ name: "note_done",
970
+ tags: ["act"],
971
+ description: "\u628A\u7559\u8A00\u677F\u4E0A\u67D0\u4E00\u6761\u5212\u6389\uFF08\u505A\u5B8C\u4E86/\u4E0D\u7528\u4E86\uFF09\u3002\u5148\u7528 note_list \u62FF\u5230 id\u3002",
972
+ parameters: {
973
+ type: "object",
974
+ properties: {
975
+ id: { type: "string", description: "\u7559\u8A00\u7684 id\uFF08note_list \u91CC\u80FD\u770B\u5230\uFF09\u3002" }
976
+ },
977
+ required: ["id"]
978
+ }
979
+ },
828
980
  {
829
981
  name: "hand_stop",
830
982
  tags: ["flow"],
@@ -899,12 +1051,24 @@ var HandsWorld = class {
899
1051
  return await this.long(args);
900
1052
  case "hand_swipe":
901
1053
  return await this.swipe(args);
1054
+ case "hand_pinch":
1055
+ return await this.pinch(args);
1056
+ case "hand_drag":
1057
+ return await this.drag(args);
1058
+ case "hand_find":
1059
+ return await this.findItem(args);
902
1060
  case "hand_type":
903
1061
  return await this.type(args);
904
1062
  case "hand_key":
905
1063
  return await this.key(args);
906
1064
  case "hand_status":
907
1065
  return this.statusText();
1066
+ case "note_add":
1067
+ return await this.noteAdd(args);
1068
+ case "note_list":
1069
+ return await this.noteList();
1070
+ case "note_done":
1071
+ return await this.noteDone(args);
908
1072
  case "hand_stop":
909
1073
  return this.stopHands();
910
1074
  default:
@@ -935,6 +1099,65 @@ var HandsWorld = class {
935
1099
  return { text: report.text, ...blobs.length ? { blobs } : {} };
936
1100
  }
937
1101
  /** 给文字就先在屏幕上找,找到唯一一处才点;给坐标就直接点。 */
1102
+ async noteAdd(args) {
1103
+ const text = typeof args.text === "string" ? args.text.trim() : "";
1104
+ if (!text) return failed("\u8981\u5199\u4EC0\u4E48\uFF1F\u7ED9 text\u3002");
1105
+ const result = await this.hands.noteAdd(text);
1106
+ if (!result.ok) return failed(`\u6CA1\u5199\u8FDB\u7559\u8A00\u677F\uFF1A${result.detail || "\u624B\u6CA1\u54CD\u5E94"}`);
1107
+ return `\u5199\u5728\u7559\u8A00\u677F\u4E0A\u4E86\uFF1A\u300C${text}\u300D`;
1108
+ }
1109
+ async noteList() {
1110
+ const notes = await this.hands.noteList();
1111
+ if (notes.length === 0) return "\u7559\u8A00\u677F\u662F\u7A7A\u7684\u3002";
1112
+ const open = notes.filter((n) => !n.done);
1113
+ const lines = open.map((n) => `- [${n.id}] ${n.ts} ${n.text}`).join("\n");
1114
+ return `\u7559\u8A00\u677F\u4E0A\u6709 ${notes.length} \u6761\uFF0C\u672A\u5B8C ${open.length} \u6761\uFF1A
1115
+ ${lines || "\uFF08\u90FD\u5212\u6389\u4E86\uFF09"}`;
1116
+ }
1117
+ async noteDone(args) {
1118
+ const id = typeof args.id === "string" ? args.id.trim() : "";
1119
+ if (!id) return failed("\u8981\u5212\u6389\u54EA\u6761\uFF1F\u7ED9 id\uFF08note_list \u91CC\u80FD\u770B\u5230\uFF09\u3002");
1120
+ const ok = await this.hands.noteDone(id);
1121
+ return ok ? `\u5212\u6389\u4E86 ${id}\u3002` : failed(`\u6CA1\u627E\u5230 id=${id} \u7684\u7559\u8A00\u3002`);
1122
+ }
1123
+ async pinch(args) {
1124
+ const scale = Number(args.scale);
1125
+ 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");
1126
+ const cx = args.cx === void 0 ? void 0 : Number(args.cx);
1127
+ const cy = args.cy === void 0 ? void 0 : Number(args.cy);
1128
+ const ms = args.ms === void 0 ? void 0 : Number(args.ms);
1129
+ const size = await this.hands.screenSize();
1130
+ const x = cx ?? Math.round((size?.w ?? 1080) / 2);
1131
+ const y = cy ?? Math.round((size?.h ?? 1920) / 2);
1132
+ const receipt = await this.hands.pinch(x, y, scale, ms === void 0 ? {} : { ms });
1133
+ this.note(receipt);
1134
+ return receipt.text;
1135
+ }
1136
+ async drag(args) {
1137
+ const need = ["x1", "y1", "x2", "y2"];
1138
+ if (need.some((k) => args[k] === void 0)) return failed("\u62D6\u62FD\u8981\u7ED9\u9F50 x1\u3001y1\u3001x2\u3001y2\u3002");
1139
+ const receipt = await this.hands.drag(
1140
+ Number(args.x1),
1141
+ Number(args.y1),
1142
+ Number(args.x2),
1143
+ Number(args.y2),
1144
+ args.ms === void 0 ? {} : { ms: Number(args.ms) }
1145
+ );
1146
+ this.note(receipt);
1147
+ return receipt.text;
1148
+ }
1149
+ async findItem(args) {
1150
+ const text = typeof args.text === "string" ? args.text.trim() : "";
1151
+ if (!text) return failed("\u8981\u627E\u4EC0\u4E48\u6587\u5B57\uFF1F\u7ED9 text\u3002");
1152
+ const tries = args.tries === void 0 ? void 0 : Number(args.tries);
1153
+ const scroll = args.scroll === void 0 ? void 0 : Boolean(args.scroll);
1154
+ const found = await this.hands.findItem(text, {
1155
+ ...tries === void 0 ? {} : { tries },
1156
+ ...scroll === void 0 ? {} : { scroll }
1157
+ });
1158
+ if (!found.ok) return failed(found.detail);
1159
+ 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`;
1160
+ }
938
1161
  async tap(args) {
939
1162
  const text = typeof args.text === "string" ? args.text.trim() : "";
940
1163
  if (text) {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "cortico-world-hands",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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
+ }