@honor-claw/yoyo 1.4.0-beta.6 → 1.4.0-beta.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.
@@ -1,39 +1,50 @@
1
- import { safeWriteFile as e } from "../../utils/fs-safe.mjs";
2
- import { INVALID_TOOL_FILENAME_CHAR_PATTERN as t, TOOL_ARCHIVE_DIR as n } from "./consts.mjs";
3
- import r from "node:path";
1
+ import { hasNodeErrorCode as e } from "../../utils/error.mjs";
2
+ import { safeWriteFile as t } from "../../utils/fs-safe.mjs";
3
+ import { INVALID_TOOL_FILENAME_CHAR_PATTERN as n, TOOL_ARCHIVE_DIR as r } from "./consts.mjs";
4
+ import i from "node:path";
5
+ import a from "node:fs/promises";
4
6
  //#region src/modules/device-toolset/archive.ts
5
- async function i(t, i) {
6
- let s = 0, c = 0;
7
- for (let a of i) {
8
- let i = o(a.name);
9
- if (!i) {
10
- c += 1;
7
+ async function o(e, n) {
8
+ let a = 0, o = 0, u = await c(e);
9
+ for (let s of n) {
10
+ let n = l(s.name);
11
+ if (!n) {
12
+ o += 1;
11
13
  continue;
12
14
  }
13
- await e({
14
- rootDir: t,
15
- relativePath: r.join(n, i),
16
- data: JSON.stringify(a, null, 2),
15
+ await t({
16
+ rootDir: e,
17
+ relativePath: i.join(r, n),
18
+ data: JSON.stringify(s, null, 2),
17
19
  encoding: "utf8",
18
20
  mkdir: !0
19
- }), s += 1;
21
+ }), a += 1;
20
22
  }
21
23
  return {
22
- archiveDir: a(t),
23
- writtenCount: s,
24
- skippedCount: c
24
+ archiveDir: s(e),
25
+ writtenCount: a,
26
+ skippedCount: o,
27
+ createdArchiveDir: !u && a > 0
25
28
  };
26
29
  }
27
- function a(e) {
28
- return r.join(e, n);
30
+ function s(e) {
31
+ return i.join(e, r);
32
+ }
33
+ async function c(t) {
34
+ try {
35
+ return (await a.stat(s(t))).isDirectory();
36
+ } catch (t) {
37
+ if (e(t, "ENOENT", "ENOTDIR")) return !1;
38
+ throw t;
39
+ }
29
40
  }
30
- function o(e) {
31
- let n = e.trim();
32
- if (!(!n || t.test(n) || s(n))) return `${n}.json`;
41
+ function l(e) {
42
+ let t = e.trim();
43
+ if (!(!t || n.test(t) || u(t))) return `${t}.json`;
33
44
  }
34
- function s(e) {
45
+ function u(e) {
35
46
  for (let t of e) if (t.codePointAt(0) < 32) return !0;
36
47
  return !1;
37
48
  }
38
49
  //#endregion
39
- export { i as refreshToolArchive, a as resolveToolArchiveDir };
50
+ export { o as refreshToolArchive, s as resolveToolArchiveDir };
@@ -1,43 +1,43 @@
1
1
  import { useClawLogger as e } from "../../utils/logger.mjs";
2
2
  import { resolveEffectiveHomeDir as t, resolveExtensionRoot as n } from "../../utils/home-dir.mjs";
3
- import { clearYoyoControlSkillReadMarker as r } from "./skill-refresh-marker.mjs";
4
- import { refreshToolArchive as i } from "./archive.mjs";
5
- import { ensureMcpToolsMapping as a, resolveMcpToolsTargetDir as o } from "./link.mjs";
6
- import { mergeDeviceToolSetCache as s } from "./persist.mjs";
7
- import { refreshYoyoControlSkill as c } from "./skill-inject.mjs";
8
- import { ensureDeviceToolSetArtifacts as l } from "./artifacts.mjs";
9
- import { computeDeviceToolSetMd5 as u, getDeviceToolSetChangeState as d, writeToolSetMd5Entry as f } from "./md5-index.mjs";
10
- import { assertDeviceToolSetPayload as p, normalizeDeviceToolSet as m } from "./normalize.mjs";
3
+ import { clearYoyoControlSkillReadMarker as r, markYoyoControlSkillRead as i } from "./skill-refresh-marker.mjs";
4
+ import { refreshToolArchive as a } from "./archive.mjs";
5
+ import { ensureMcpToolsMapping as o, resolveMcpToolsTargetDir as s } from "./link.mjs";
6
+ import { mergeDeviceToolSetCache as c } from "./persist.mjs";
7
+ import { refreshYoyoControlSkill as l } from "./skill-inject.mjs";
8
+ import { ensureDeviceToolSetArtifacts as u } from "./artifacts.mjs";
9
+ import { computeDeviceToolSetMd5 as d, getDeviceToolSetChangeState as f, writeToolSetMd5Entry as p } from "./md5-index.mjs";
10
+ import { assertDeviceToolSetPayload as m, normalizeDeviceToolSet as h } from "./normalize.mjs";
11
11
  //#region src/modules/device-toolset/processor.ts
12
- var h = n();
13
- async function g(n) {
14
- let g = p(n), _ = t();
15
- if (!_) throw Error("Unable to resolve home directory for DeviceToolSet persistence");
16
- let v = m(g);
17
- e().info(`[yoyoclaw-device-toolset] process start, nodeId: ${v.nodeId}, tools: ${v.tools.length}, skipped: ${v.skippedCount}`);
18
- let y = u(v.tools);
19
- if (e().debug?.(`[yoyoclaw-device-toolset] computed md5, nodeId: ${v.nodeId}, md5: ${y}`), !(await d(_, v.nodeId, y)).changed) {
20
- let t = await l(_, h);
21
- return e().info(`[yoyoclaw-device-toolset] unchanged, skip refresh, nodeId: ${v.nodeId}, artifactsRestored: ${t.restored}`), {
12
+ var g = n();
13
+ async function _(n) {
14
+ let _ = m(n), v = t();
15
+ if (!v) throw Error("Unable to resolve home directory for DeviceToolSet persistence");
16
+ let y = h(_);
17
+ e().info(`[yoyoclaw-device-toolset] process start, nodeId: ${y.nodeId}, tools: ${y.tools.length}, skipped: ${y.skippedCount}`);
18
+ let b = d(y.tools);
19
+ if (e().debug?.(`[yoyoclaw-device-toolset] computed md5, nodeId: ${y.nodeId}, md5: ${b}`), !(await f(v, y.nodeId, b)).changed) {
20
+ let t = await u(v, g);
21
+ return e().info(`[yoyoclaw-device-toolset] unchanged, skip refresh, nodeId: ${y.nodeId}, artifactsRestored: ${t.restored}`), {
22
22
  changed: !1,
23
- nodeId: v.nodeId,
24
- skippedCount: v.skippedCount
23
+ nodeId: y.nodeId,
24
+ skippedCount: y.skippedCount
25
25
  };
26
26
  }
27
- let b = await s(_, v.nodeId, v.tools);
28
- e().info(`[yoyoclaw-device-toolset] cache merged, nodeId: ${v.nodeId}, supportedNodes: ${b.nodeCount}, tools: ${b.toolCount}`);
29
- let x = b.cache.tools, S = await i(_, v.tools);
30
- e().info(`[yoyoclaw-device-toolset] archive refreshed, nodeId: ${v.nodeId}, written: ${S.writtenCount}, skipped: ${S.skippedCount}`);
31
- let C = await c(h, x);
32
- e().info(`[yoyoclaw-device-toolset] skill refreshed, nodeId: ${v.nodeId}, rows: ${C.rowCount}, updated: ${C.updated}`), C.updated && await r(h, v.nodeId);
33
- let w = await a(S.archiveDir, o(h));
34
- return e().info(`[yoyoclaw-device-toolset] mapping ready, nodeId: ${v.nodeId}, mode: ${w.mode}`), await f(_, v.nodeId, y, Date.now()), e().info(`[yoyoclaw-device-toolset] process complete, nodeId: ${v.nodeId}`), {
27
+ let x = await c(v, y.nodeId, y.tools);
28
+ e().info(`[yoyoclaw-device-toolset] cache merged, nodeId: ${y.nodeId}, supportedNodes: ${x.nodeCount}, tools: ${x.toolCount}`);
29
+ let S = x.cache.tools, C = await a(v, y.tools);
30
+ e().info(`[yoyoclaw-device-toolset] archive refreshed, nodeId: ${y.nodeId}, written: ${C.writtenCount}, skipped: ${C.skippedCount}, createdArchiveDir: ${C.createdArchiveDir}`);
31
+ let w = await l(g, S);
32
+ e().info(`[yoyoclaw-device-toolset] skill refreshed, nodeId: ${y.nodeId}, rows: ${w.rowCount}, updated: ${w.updated}`), C.createdArchiveDir ? await i(g, "clean_install") : w.updated && await r(g, y.nodeId);
33
+ let T = await o(C.archiveDir, s(g));
34
+ return e().info(`[yoyoclaw-device-toolset] mapping ready, nodeId: ${y.nodeId}, mode: ${T.mode}`), await p(v, y.nodeId, b, Date.now()), e().info(`[yoyoclaw-device-toolset] process complete, nodeId: ${y.nodeId}`), {
35
35
  changed: !0,
36
- nodeId: v.nodeId,
37
- nodeCount: b.nodeCount,
38
- toolCount: b.toolCount,
39
- skippedCount: v.skippedCount
36
+ nodeId: y.nodeId,
37
+ nodeCount: x.nodeCount,
38
+ toolCount: x.toolCount,
39
+ skippedCount: y.skippedCount
40
40
  };
41
41
  }
42
42
  //#endregion
43
- export { g as processDeviceToolSet };
43
+ export { _ as processDeviceToolSet };
@@ -1,38 +1,39 @@
1
- import { MCP_TOOLS_END_MARKER as e, MCP_TOOLS_PLACEHOLDER as t, MCP_TOOLS_START_MARKER as n, YOYO_CONTROL_SKILL_RELATIVE_PATH as r, YOYO_CONTROL_SUB_SKILLS_RELATIVE_PATH as i } from "./consts.mjs";
2
- import a from "node:path";
3
- import o from "node:fs/promises";
1
+ import { hasNodeErrorCode as e } from "../../utils/error.mjs";
2
+ import { MCP_TOOLS_END_MARKER as t, MCP_TOOLS_PLACEHOLDER as n, MCP_TOOLS_START_MARKER as r, YOYO_CONTROL_SKILL_RELATIVE_PATH as i, YOYO_CONTROL_SUB_SKILLS_RELATIVE_PATH as a } from "./consts.mjs";
3
+ import o from "node:path";
4
+ import s from "node:fs/promises";
4
5
  //#region src/modules/device-toolset/skill-inject.ts
5
- async function s(e, t) {
6
- let n = a.join(e, r), i = await o.readFile(n, "utf8"), s = l(t, await c(e)), p = f(i, u(s));
7
- return p !== i && await o.writeFile(n, p, "utf8"), {
6
+ async function c(e, t) {
7
+ let n = o.join(e, i), r = await s.readFile(n, "utf8"), a = u(t, await l(e)), c = p(r, d(a));
8
+ return c !== r && await s.writeFile(n, c, "utf8"), {
8
9
  skillPath: n,
9
- rowCount: d(s).length,
10
- updated: p !== i
10
+ rowCount: f(a).length,
11
+ updated: c !== r
11
12
  };
12
13
  }
13
- async function c(e) {
14
- let t = a.join(e, i), n;
14
+ async function l(t) {
15
+ let n = o.join(t, a), r;
15
16
  try {
16
- n = await o.readFile(t, "utf8");
17
- } catch (e) {
18
- if (h(e) && e.code === "ENOENT") return /* @__PURE__ */ new Set();
19
- throw e;
17
+ r = await s.readFile(n, "utf8");
18
+ } catch (t) {
19
+ if (e(t, "ENOENT")) return /* @__PURE__ */ new Set();
20
+ throw t;
20
21
  }
21
- let r = JSON.parse(n);
22
- if (!Array.isArray(r) || r.some((e) => typeof e != "string")) throw Error("sub-skills config must be a JSON array of tool names");
23
- return new Set(r.map((e) => e.trim()).filter(Boolean));
22
+ let i = JSON.parse(r);
23
+ if (!Array.isArray(i) || i.some((e) => typeof e != "string")) throw Error("sub-skills config must be a JSON array of tool names");
24
+ return new Set(i.map((e) => e.trim()).filter(Boolean));
24
25
  }
25
- function l(e, t) {
26
+ function u(e, t) {
26
27
  return t.size === 0 ? e : Object.fromEntries(Object.entries(e).filter(([e, n]) => !t.has(e) && !t.has(n.name)));
27
28
  }
28
- function u(t) {
29
+ function d(e) {
29
30
  return [
30
- n,
31
- ...d(t).map((e) => `| ${p(e.name)} | ${p(e.description)} | ${p(e.nodeId)} |`),
32
- e
31
+ r,
32
+ ...f(e).map((e) => `| ${m(e.name)} | ${m(e.description)} | ${m(e.nodeId)} |`),
33
+ t
33
34
  ].join("\n");
34
35
  }
35
- function d(e) {
36
+ function f(e) {
36
37
  return Object.values(e).flatMap((e) => e.supportedNodeIds.map((t) => ({
37
38
  name: e.name,
38
39
  description: e.description,
@@ -42,20 +43,17 @@ function d(e) {
42
43
  return n === 0 ? e.nodeId.localeCompare(t.nodeId) : n;
43
44
  });
44
45
  }
45
- function f(r, i) {
46
- let a = RegExp(`${m(n)}[\\s\\S]*?${m(e)}`);
47
- if (a.test(r)) return r.replace(a, i);
48
- if (r.includes("{{full-mcp-tool-list}}")) return r.replace(t, i);
46
+ function p(e, i) {
47
+ let a = RegExp(`${h(r)}[\\s\\S]*?${h(t)}`);
48
+ if (a.test(e)) return e.replace(a, i);
49
+ if (e.includes("{{full-mcp-tool-list}}")) return e.replace(n, i);
49
50
  throw Error("Unable to find MCP tools placeholder or generated block in yoyo-control skill");
50
51
  }
51
- function p(e) {
52
- return e.replaceAll("|", "\\|").replace(/\r?\n/g, " ");
53
- }
54
52
  function m(e) {
55
- return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
53
+ return e.replaceAll("|", "\\|").replace(/\r?\n/g, " ");
56
54
  }
57
55
  function h(e) {
58
- return e instanceof Error && "code" in e;
56
+ return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
59
57
  }
60
58
  //#endregion
61
- export { s as refreshYoyoControlSkill };
59
+ export { c as refreshYoyoControlSkill };
@@ -1,44 +1,42 @@
1
- import { useClawLogger as e } from "../../utils/logger.mjs";
2
- import { YOYO_CONTROL_SKILL_READ_MARKER_RELATIVE_PATH as t } from "./consts.mjs";
3
- import n from "node:path";
4
- import r from "node:fs/promises";
1
+ import { hasNodeErrorCode as e } from "../../utils/error.mjs";
2
+ import { useClawLogger as t } from "../../utils/logger.mjs";
3
+ import { YOYO_CONTROL_SKILL_READ_MARKER_RELATIVE_PATH as n } from "./consts.mjs";
4
+ import r from "node:path";
5
+ import i from "node:fs/promises";
5
6
  //#region src/modules/device-toolset/skill-refresh-marker.ts
6
- var i = /(?:^|[\\/])skills[\\/]yoyo-control[\\/]SKILL\.md$/i, a, o = "## Mandatory yoyo_phone_control skill refresh\n\nThis is a one-shot instruction for the same user message that contains this notice.\n\nThe yoyo_phone_control skill changed after your cached context was built. If that same user message involves or may involve phone control:\n\n1. Your next tool call MUST read exactly skills/yoyo-control/SKILL.md.\n2. Until that read succeeds, DO NOT read skills/yoyo-control/references/*, read skills/yoyo-control/mcptools/*, run openclaw commands, or invoke any phone-control action.\n3. Ignore all previously cached yoyo_phone_control instructions, tool tables, reference paths, command formats, and parameter schemas. They are stale even if they look relevant.\n4. After reading SKILL.md, use only the freshly read workflow to decide which reference or tool definition file to read next.\n\nThis instruction expires as soon as you either successfully read skills/yoyo-control/SKILL.md or finish responding to that same user message. If you see this notice only in conversation history, and it is not included in the latest user message you are answering, ignore it and do not re-read SKILL.md because of this old notice.\n\nThinking or saying that you should re-read the skill is not enough while this one-shot instruction is active; the first phone-control tool call after the active notice must be the SKILL.md read.";
7
- function s(e) {
8
- return typeof e == "string" && i.test(e);
7
+ var a = /(?:^|[\\/])skills[\\/]yoyo-control[\\/]SKILL\.md$/i, o, s = "## Mandatory yoyo_phone_control skill refresh\n\nThis is a one-shot instruction for the same user message that contains this notice.\n\nThe yoyo_phone_control skill may have changed after your cached context was built. Your next tool call MUST read exactly skills/yoyo-control/SKILL.md, even if the current user message does not involve phone control.\n\n1. Until that read succeeds, DO NOT read skills/yoyo-control/references/*, read skills/yoyo-control/mcptools/*, run openclaw commands, or invoke any phone-control action.\n2. Ignore all previously cached yoyo_phone_control instructions, tool tables, reference paths, command formats, and parameter schemas. They are stale even if they look relevant.\n3. After reading SKILL.md, use only the freshly read workflow to decide whether any yoyo_phone_control follow-up is needed. If the user message is unrelated to phone control, continue answering normally without performing phone-control actions.\n\nThis instruction expires as soon as you either successfully read skills/yoyo-control/SKILL.md or finish responding to that same user message. If you see this notice only in conversation history, and it is not included in the latest user message you are answering, ignore it and do not re-read SKILL.md because of this old notice.\n\nThinking or saying that you should re-read the skill is not enough while this one-shot instruction is active; the first tool call after the active notice must be the SKILL.md read.";
8
+ function c(e) {
9
+ return typeof e == "string" && a.test(e);
9
10
  }
10
- async function c(t) {
11
- if (a !== void 0) return e().debug?.(`[yoyoclaw-skill-refresh] skill marker state resolved from memory, exists: ${a}`), !a;
12
- try {
13
- await r.access(d(t)), a = !0, e().debug?.("[yoyoclaw-skill-refresh] skill marker state resolved from file, exists: true");
14
- } catch (t) {
15
- if (p(t) && t.code === "ENOENT") a = !1, e().debug?.("[yoyoclaw-skill-refresh] skill marker state resolved from file, exists: false");
16
- else throw t;
11
+ async function l(n) {
12
+ if (o === !0) return t().debug?.(`[yoyoclaw-skill-refresh] skill marker state resolved from memory, exists: ${o}`), !1;
13
+ if (o === void 0) try {
14
+ return await i.access(f(n)), o = !0, t().debug?.("[yoyoclaw-skill-refresh] skill marker state resolved from file, exists: true"), !1;
15
+ } catch (n) {
16
+ if (e(n, "ENOENT")) o = !1, t().debug?.("[yoyoclaw-skill-refresh] skill marker state resolved from file, exists: false");
17
+ else throw n;
17
18
  }
18
- return !a;
19
+ return t().debug?.("[yoyoclaw-skill-refresh] skill marker missing; refresh prompt required"), !0;
19
20
  }
20
- async function l(e, t) {
21
- let i = d(e), o = {
21
+ async function u(e, t) {
22
+ let n = f(e), a = {
22
23
  updateTime: Date.now(),
23
24
  triggerReason: t
24
25
  };
25
- await r.mkdir(n.dirname(i), { recursive: !0 }), await r.writeFile(i, JSON.stringify(o, null, 2), "utf8"), a = !0;
26
+ await i.mkdir(r.dirname(n), { recursive: !0 }), await i.writeFile(n, JSON.stringify(a, null, 2), "utf8"), o = !0;
26
27
  }
27
- async function u(t, n) {
28
+ async function d(e, n) {
28
29
  try {
29
- await r.rm(d(t), { force: !0 }), a = !1, e().info(`[yoyoclaw-skill-refresh] skill read marker cleared${f(n)}`);
30
- } catch (t) {
31
- e().warn(`[yoyoclaw-skill-refresh] failed to clear skill read marker${f(n)}, error: ${String(t)}`);
30
+ await i.rm(f(e), { force: !0 }), o = !1, t().info(`[yoyoclaw-skill-refresh] skill read marker cleared${p(n)}`);
31
+ } catch (e) {
32
+ t().warn(`[yoyoclaw-skill-refresh] failed to clear skill read marker${p(n)}, error: ${String(e)}`);
32
33
  }
33
34
  }
34
- function d(e) {
35
- return n.join(e, t);
36
- }
37
35
  function f(e) {
38
- return e ? `, nodeId: ${e}` : "";
36
+ return r.join(e, n);
39
37
  }
40
38
  function p(e) {
41
- return e instanceof Error && "code" in e;
39
+ return e ? `, nodeId: ${e}` : "";
42
40
  }
43
41
  //#endregion
44
- export { o as YOYO_CONTROL_SKILL_REFRESH_PROMPT, u as clearYoyoControlSkillReadMarker, s as isYoyoControlSkillReadPath, l as markYoyoControlSkillRead, c as shouldRefreshYoyoControlSkillPrompt };
42
+ export { s as YOYO_CONTROL_SKILL_REFRESH_PROMPT, d as clearYoyoControlSkillReadMarker, c as isYoyoControlSkillReadPath, u as markYoyoControlSkillRead, l as shouldRefreshYoyoControlSkillPrompt };
@@ -3,5 +3,11 @@ function e(e, t) {
3
3
  let n = e instanceof Error ? e.message : String(e);
4
4
  return Error(`${t}: ${n}`, { cause: e });
5
5
  }
6
+ function t(e) {
7
+ return e instanceof Error && "code" in e;
8
+ }
9
+ function n(e, ...n) {
10
+ return t(e) && typeof e.code == "string" && n.includes(e.code);
11
+ }
6
12
  //#endregion
7
- export { e as wrapError };
13
+ export { n as hasNodeErrorCode, t as isNodeError, e as wrapError };
@@ -1,103 +1,101 @@
1
- import { constants as e } from "node:fs";
2
- import t from "node:path";
3
- import { randomUUID as n } from "node:crypto";
4
- import r from "node:fs/promises";
5
- import i from "node:os";
1
+ import { hasNodeErrorCode as e, isNodeError as t } from "./error.mjs";
2
+ import { constants as n } from "node:fs";
3
+ import r from "node:path";
4
+ import { randomUUID as i } from "node:crypto";
5
+ import a from "node:fs/promises";
6
+ import o from "node:os";
6
7
  //#region src/utils/fs-safe.ts
7
- var a = class extends Error {
8
+ var s = class extends Error {
8
9
  code;
9
10
  constructor(e, t, n) {
10
11
  super(t, n), this.code = e, this.name = "SafeFsError";
11
12
  }
12
- }, o = new Set(["ENOENT", "ENOTDIR"]), s = new Set([
13
+ }, c = new Set(["ENOENT", "ENOTDIR"]), l = new Set([
13
14
  "ELOOP",
14
15
  "EINVAL",
15
16
  "ENOTSUP"
16
17
  ]);
17
- function c(e) {
18
- return !!(e && typeof e == "object" && "code" in e);
18
+ function u(t) {
19
+ return e(t, ...c);
19
20
  }
20
- function l(e) {
21
- return c(e) && typeof e.code == "string" && o.has(e.code);
21
+ function d(t) {
22
+ return e(t, ...l);
22
23
  }
23
- function u(e) {
24
- return c(e) && typeof e.code == "string" && s.has(e.code);
24
+ function f(e) {
25
+ let t = r.win32.normalize(e);
26
+ return t.startsWith("\\\\?\\") && (t = t.slice(4), t.toUpperCase().startsWith("UNC\\") && (t = `\\\\${t.slice(4)}`)), t.replaceAll("/", "\\").toLowerCase();
25
27
  }
26
- function d(e) {
27
- let n = t.win32.normalize(e);
28
- return n.startsWith("\\\\?\\") && (n = n.slice(4), n.toUpperCase().startsWith("UNC\\") && (n = `\\\\${n.slice(4)}`)), n.replaceAll("/", "\\").toLowerCase();
29
- }
30
- function f(e, n) {
31
- let r = t.resolve(e), i = t.resolve(n);
28
+ function p(e, t) {
29
+ let n = r.resolve(e), i = r.resolve(t);
32
30
  if (process.platform === "win32") {
33
- let e = d(r), n = d(i), a = t.win32.relative(e, n);
34
- return a === "" || !a.startsWith("..") && !t.win32.isAbsolute(a);
31
+ let e = f(n), t = f(i), a = r.win32.relative(e, t);
32
+ return a === "" || !a.startsWith("..") && !r.win32.isAbsolute(a);
35
33
  }
36
- let a = t.relative(r, i);
37
- return a === "" || !a.startsWith("..") && !t.isAbsolute(a);
34
+ let a = r.relative(n, i);
35
+ return a === "" || !a.startsWith("..") && !r.isAbsolute(a);
38
36
  }
39
- function p(e) {
37
+ function m(e) {
40
38
  return e === 0 || e === 0n;
41
39
  }
42
- function m(e, t, n = process.platform) {
43
- return e.ino === t.ino ? e.dev === t.dev ? !0 : n === "win32" && (p(e.dev) || p(t.dev)) : !1;
40
+ function h(e, t, n = process.platform) {
41
+ return e.ino === t.ino ? e.dev === t.dev ? !0 : n === "win32" && (m(e.dev) || m(t.dev)) : !1;
44
42
  }
45
- var h = process.platform !== "win32" && "O_NOFOLLOW" in e, g = e.O_RDONLY | (h ? e.O_NOFOLLOW : 0), _ = e.O_WRONLY | e.O_CREAT | e.O_EXCL | (h ? e.O_NOFOLLOW : 0), v = e.O_WRONLY | e.O_APPEND | (h ? e.O_NOFOLLOW : 0), y = (e) => e.endsWith(t.sep) ? e : e + t.sep;
46
- function b(e, t) {
43
+ var g = process.platform !== "win32" && "O_NOFOLLOW" in n, _ = n.O_RDONLY | (g ? n.O_NOFOLLOW : 0), v = n.O_WRONLY | n.O_CREAT | n.O_EXCL | (g ? n.O_NOFOLLOW : 0), y = n.O_WRONLY | n.O_APPEND | (g ? n.O_NOFOLLOW : 0), b = (e) => e.endsWith(r.sep) ? e : e + r.sep;
44
+ function x(e, t) {
47
45
  if (!e.startsWith("~")) return e;
48
- let n = t ?? process.env.HOME ?? process.env.USERPROFILE ?? i.homedir();
46
+ let n = t ?? process.env.HOME ?? process.env.USERPROFILE ?? o.homedir();
49
47
  return n ? e.replace(/^~(?=$|[\\/])/, n) : e;
50
48
  }
51
- async function x(e) {
52
- let n;
49
+ async function S(e) {
50
+ let t;
53
51
  try {
54
- n = await r.realpath(e.rootDir);
52
+ t = await a.realpath(e.rootDir);
55
53
  } catch (e) {
56
- throw l(e) ? new a("not-found", "root dir not found") : e;
54
+ throw u(e) ? new s("not-found", "root dir not found") : e;
57
55
  }
58
- let i = y(n), o = b(e.relativePath), s = t.resolve(i, o);
59
- if (!f(i, s)) throw new a("outside-root", "file is outside workspace root");
56
+ let n = b(t), i = x(e.relativePath), o = r.resolve(n, i);
57
+ if (!p(n, o)) throw new s("outside-root", "file is outside workspace root");
60
58
  return {
61
- rootReal: n,
62
- rootWithSep: i,
63
- resolved: s
59
+ rootReal: t,
60
+ rootWithSep: n,
61
+ resolved: o
64
62
  };
65
63
  }
66
- async function S(e, t) {
64
+ async function C(e, n) {
67
65
  try {
68
- if ((await r.lstat(e)).isDirectory()) throw new a("not-file", "not a file");
66
+ if ((await a.lstat(e)).isDirectory()) throw new s("not-file", "not a file");
69
67
  } catch (e) {
70
- if (e instanceof a) throw e;
68
+ if (e instanceof s) throw e;
71
69
  }
72
- let n;
70
+ let r;
73
71
  try {
74
- n = await r.open(e, g);
72
+ r = await a.open(e, _);
75
73
  } catch (e) {
76
- throw l(e) ? new a("not-found", "file not found") : u(e) ? new a("symlink", "symlink open blocked", { cause: e }) : c(e) && e.code === "EISDIR" ? new a("not-file", "not a file") : e;
74
+ throw u(e) ? new s("not-found", "file not found") : d(e) ? new s("symlink", "symlink open blocked", { cause: e }) : t(e) && e.code === "EISDIR" ? new s("not-file", "not a file") : e;
77
75
  }
78
76
  try {
79
- let [i, o] = await Promise.all([n.stat(), r.lstat(e)]);
80
- if (o.isSymbolicLink()) throw new a("symlink", "symlink not allowed");
81
- if (!i.isFile()) throw new a("not-file", "not a file");
82
- if (t?.rejectHardlinks && i.nlink > 1) throw new a("invalid-path", "hardlinked path not allowed");
83
- if (process.platform !== "win32" && !m(i, o)) throw new a("path-mismatch", "path changed during read");
84
- let s = await r.realpath(e), c = await r.stat(s);
85
- if (t?.rejectHardlinks && c.nlink > 1) throw new a("invalid-path", "hardlinked path not allowed");
86
- if (process.platform !== "win32" && !m(i, c)) throw new a("path-mismatch", "path mismatch");
77
+ let [t, i] = await Promise.all([r.stat(), a.lstat(e)]);
78
+ if (i.isSymbolicLink()) throw new s("symlink", "symlink not allowed");
79
+ if (!t.isFile()) throw new s("not-file", "not a file");
80
+ if (n?.rejectHardlinks && t.nlink > 1) throw new s("invalid-path", "hardlinked path not allowed");
81
+ if (process.platform !== "win32" && !h(t, i)) throw new s("path-mismatch", "path changed during read");
82
+ let o = await a.realpath(e), c = await a.stat(o);
83
+ if (n?.rejectHardlinks && c.nlink > 1) throw new s("invalid-path", "hardlinked path not allowed");
84
+ if (process.platform !== "win32" && !h(t, c)) throw new s("path-mismatch", "path mismatch");
87
85
  return {
88
- handle: n,
89
- realPath: s,
90
- stat: i
86
+ handle: r,
87
+ realPath: o,
88
+ stat: t
91
89
  };
92
90
  } catch (e) {
93
- throw await n.close().catch(() => {}), e instanceof a ? e : l(e) ? new a("not-found", "file not found") : e;
91
+ throw await r.close().catch(() => {}), e instanceof s ? e : u(e) ? new s("not-found", "file not found") : e;
94
92
  }
95
93
  }
96
- async function C(e) {
97
- let { rootWithSep: t, resolved: n } = await x(e), r = await S(n);
98
- if (!f(t, r.realPath)) throw await r.handle.close().catch(() => {}), new a("outside-root", "file is outside workspace root");
94
+ async function w(e) {
95
+ let { rootWithSep: t, resolved: n } = await S(e), r = await C(n);
96
+ if (!p(t, r.realPath)) throw await r.handle.close().catch(() => {}), new s("outside-root", "file is outside workspace root");
99
97
  try {
100
- if (e.maxBytes !== void 0 && r.stat.size > e.maxBytes) throw new a("too-large", `file exceeds limit of ${e.maxBytes} bytes (got ${r.stat.size})`);
98
+ if (e.maxBytes !== void 0 && r.stat.size > e.maxBytes) throw new s("too-large", `file exceeds limit of ${e.maxBytes} bytes (got ${r.stat.size})`);
101
99
  return {
102
100
  buffer: await r.handle.readFile(),
103
101
  realPath: r.realPath,
@@ -107,101 +105,101 @@ async function C(e) {
107
105
  await r.handle.close().catch(() => {});
108
106
  }
109
107
  }
110
- function w(e) {
111
- let r = t.dirname(e), i = t.basename(e);
112
- return t.join(r, `.${i}.${process.pid}.${n()}.tmp`);
108
+ function T(e) {
109
+ let t = r.dirname(e), n = r.basename(e);
110
+ return r.join(t, `.${n}.${process.pid}.${i()}.tmp`);
113
111
  }
114
- async function T(e) {
115
- let t = await r.open(e.tempPath, _, e.mode);
112
+ async function E(e) {
113
+ let t = await a.open(e.tempPath, v, e.mode);
116
114
  try {
117
115
  return typeof e.data == "string" ? await t.writeFile(e.data, e.encoding ?? "utf8") : await t.writeFile(e.data), await t.stat();
118
116
  } finally {
119
117
  await t.close().catch(() => {});
120
118
  }
121
119
  }
122
- async function E(e) {
123
- let t = y(await r.realpath(e.rootDir)), n = await S(e.targetPath, { rejectHardlinks: !0 });
120
+ async function D(e) {
121
+ let t = b(await a.realpath(e.rootDir)), n = await C(e.targetPath, { rejectHardlinks: !0 });
124
122
  try {
125
- if (process.platform !== "win32" && !m(n.stat, e.expectedStat)) throw new a("path-mismatch", "path changed during write");
126
- if (!f(t, n.realPath)) throw new a("outside-root", "file is outside workspace root");
123
+ if (process.platform !== "win32" && !h(n.stat, e.expectedStat)) throw new s("path-mismatch", "path changed during write");
124
+ if (!p(t, n.realPath)) throw new s("outside-root", "file is outside workspace root");
127
125
  } finally {
128
126
  await n.handle.close().catch(() => {});
129
127
  }
130
128
  }
131
- async function D(e) {
132
- let { rootWithSep: n, resolved: i } = await x(e);
133
- e.mkdir !== !1 && await r.mkdir(t.dirname(i), { recursive: !0 });
134
- let o = i, s = !1;
129
+ async function O(e) {
130
+ let { rootWithSep: t, resolved: n } = await S(e);
131
+ e.mkdir !== !1 && await a.mkdir(r.dirname(n), { recursive: !0 });
132
+ let i = n, o = !1;
135
133
  try {
136
- let e = await r.realpath(i);
137
- if (!f(n, e)) throw new a("outside-root", "file is outside workspace root");
138
- o = e, s = !0;
134
+ let e = await a.realpath(n);
135
+ if (!p(t, e)) throw new s("outside-root", "file is outside workspace root");
136
+ i = e, o = !0;
139
137
  } catch (e) {
140
- if (e instanceof a || !l(e)) throw e;
138
+ if (e instanceof s || !u(e)) throw e;
141
139
  }
142
140
  let c = e.mode === "append";
143
- if (c && !s) throw new a("not-found", "cannot append to non-existent file");
144
- return c ? await O({
145
- ioPath: o,
146
- rootWithSep: n,
141
+ if (c && !o) throw new s("not-found", "cannot append to non-existent file");
142
+ return c ? await k({
143
+ ioPath: i,
144
+ rootWithSep: t,
147
145
  data: e.data,
148
146
  encoding: e.encoding,
149
147
  maxBytes: e.maxBytes
150
- }) : await k({
151
- ioPath: o,
152
- rootWithSep: n,
148
+ }) : await A({
149
+ ioPath: i,
150
+ rootWithSep: t,
153
151
  rootDir: e.rootDir,
154
152
  data: e.data,
155
153
  encoding: e.encoding,
156
- existingFile: s,
154
+ existingFile: o,
157
155
  fileMode: 384
158
156
  });
159
157
  }
160
- async function O(e) {
158
+ async function k(e) {
161
159
  let t;
162
160
  try {
163
- t = await r.open(e.ioPath, v);
161
+ t = await a.open(e.ioPath, y);
164
162
  } catch (e) {
165
- throw l(e) ? new a("not-found", "file not found") : u(e) ? new a("symlink", "symlink open blocked", { cause: e }) : e;
163
+ throw u(e) ? new s("not-found", "file not found") : d(e) ? new s("symlink", "symlink open blocked", { cause: e }) : e;
166
164
  }
167
165
  try {
168
166
  let n = await t.stat();
169
- if (!n.isFile()) throw new a("not-file", "path is not a regular file");
170
- let i = await r.lstat(e.ioPath);
171
- if (i.isSymbolicLink()) throw new a("symlink", "path is a symlink");
172
- if (!m(n, i)) throw new a("path-mismatch", "path changed during write");
173
- let o = await r.realpath(e.ioPath);
174
- if (!f(e.rootWithSep, o)) throw new a("outside-root", "file is outside workspace root");
175
- let s = typeof e.data == "string" ? Buffer.byteLength(e.data, e.encoding ?? "utf8") : e.data.length;
176
- if (e.maxBytes !== void 0 && n.size + s > e.maxBytes) throw new a("too-large", `file would exceed limit of ${e.maxBytes} bytes`);
167
+ if (!n.isFile()) throw new s("not-file", "path is not a regular file");
168
+ let r = await a.lstat(e.ioPath);
169
+ if (r.isSymbolicLink()) throw new s("symlink", "path is a symlink");
170
+ if (!h(n, r)) throw new s("path-mismatch", "path changed during write");
171
+ let i = await a.realpath(e.ioPath);
172
+ if (!p(e.rootWithSep, i)) throw new s("outside-root", "file is outside workspace root");
173
+ let o = typeof e.data == "string" ? Buffer.byteLength(e.data, e.encoding ?? "utf8") : e.data.length;
174
+ if (e.maxBytes !== void 0 && n.size + o > e.maxBytes) throw new s("too-large", `file would exceed limit of ${e.maxBytes} bytes`);
177
175
  return typeof e.data == "string" ? await t.writeFile(e.data, e.encoding ?? "utf8") : await t.writeFile(e.data), {
178
- realPath: o,
179
- bytesWritten: s,
176
+ realPath: i,
177
+ bytesWritten: o,
180
178
  created: !1
181
179
  };
182
180
  } finally {
183
181
  await t.close().catch(() => {});
184
182
  }
185
183
  }
186
- async function k(e) {
184
+ async function A(e) {
187
185
  let t = null, n = e.ioPath;
188
186
  try {
189
- t = w(n);
190
- let i = await T({
187
+ t = T(n);
188
+ let r = await E({
191
189
  tempPath: t,
192
190
  data: e.data,
193
191
  encoding: e.encoding,
194
192
  mode: e.fileMode
195
193
  });
196
- await r.rename(t, n), t = null;
194
+ await a.rename(t, n), t = null;
197
195
  try {
198
- await E({
196
+ await D({
199
197
  rootDir: e.rootDir,
200
198
  targetPath: n,
201
- expectedStat: i
199
+ expectedStat: r
202
200
  });
203
201
  } catch (e) {
204
- throw e instanceof a ? e : new a("write-failed", "atomic write verification failed", { cause: e });
202
+ throw e instanceof s ? e : new s("write-failed", "atomic write verification failed", { cause: e });
205
203
  }
206
204
  return {
207
205
  realPath: n,
@@ -209,8 +207,8 @@ async function k(e) {
209
207
  created: !e.existingFile
210
208
  };
211
209
  } finally {
212
- t && await r.rm(t, { force: !0 }).catch(() => {});
210
+ t && await a.rm(t, { force: !0 }).catch(() => {});
213
211
  }
214
212
  }
215
213
  //#endregion
216
- export { a as SafeFsError, C as safeReadFile, D as safeWriteFile };
214
+ export { s as SafeFsError, w as safeReadFile, O as safeWriteFile };
@@ -1,7 +1,7 @@
1
1
  //#region src/utils/version.ts
2
2
  var e = null;
3
3
  function t() {
4
- return e === null && (e = "1.4.0-beta.6".includes("beta") || "1.4.0-beta.6".includes("alpha")), !!e;
4
+ return e === null && (e = "1.4.0-beta.8".includes("beta") || "1.4.0-beta.8".includes("alpha")), !!e;
5
5
  }
6
6
  //#endregion
7
7
  export { t as isBetaVersion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honor-claw/yoyo",
3
- "version": "1.4.0-beta.6",
3
+ "version": "1.4.0-beta.8",
4
4
  "description": "OpenClaw Honor Yoyo connection plugin",
5
5
  "keywords": [
6
6
  "ai",
@@ -3,7 +3,6 @@ name: yoyo_phone_control
3
3
  description: >
4
4
  通过YOYO操作手机系统用机功能,包括打开/关闭/卸载应用、发送/查找短信、拨打电话、搜索联系人、搜索通话记录、创建/打开/查询/关闭/删除闹钟、创建/添加/新建日程、计时器管理、录音、截屏、屏幕录制、调节音量、屏幕亮度调节、切换响铃/静音/勿扰/护眼/深色/飞行模式、管理移动数据或热点或蓝牙或WLAN、进行本地内容搜索、文件发送、调用相机拍照/录像、电池状态/电量查询与、开启/关闭定位服务、开启/关闭NFC功能、开启/关闭自动亮屏功能、USB共享网络、设备电源状态与屏幕状态控制、状态栏显示信息管理与控制、进行扫码操作、控制手机的手电筒、清理和维护设备扬声器、机票监控、查快递等。
5
5
  支持与手机系统用机交互的相关操作。
6
- 此外,还支持完成在用户指定App应用内进行GUI操作。
7
6
  metadata: { "openclaw": { "emoji": "📱", "always": true } }
8
7
  ---
9
8
 
@@ -36,7 +35,6 @@ metadata: { "openclaw": { "emoji": "📱", "always": true } }
36
35
  - 参数必须为合法 JSON,枚举值区分大小写,数值在定义范围内
37
36
  - 当工具执行错误时,必须确认下发的指令是否存在格式问题,尤其是单双引号的使用问题
38
37
  - 如果执行失败,必须重新读取 `SKILL.md` 和对应的工具定义文件,然后重新执行
39
- - 禁止使用GUI工具作为兜底工具,除非用户明确要求在指定 App 内执行指定动作
40
38
  - 绝对不能使用任何其他设备的工具
41
39
  - 不要尝试任何其他方式完成任务,不要提供替代方案
42
40
  - **Never try a different approach**
@@ -84,7 +82,6 @@ metadata: { "openclaw": { "emoji": "📱", "always": true } }
84
82
  4. **如果 Sub-Skill 和 MCP 均无匹配**:
85
83
  - 告知用户暂不支持该功能
86
84
  - 终止本次操作
87
- - 严禁使用 GUI 工具作为备选工具
88
85
 
89
86
  #### Sub-Skill 工具参考表
90
87
 
@@ -132,9 +129,6 @@ metadata: { "openclaw": { "emoji": "📱", "always": true } }
132
129
  | `battery` | `references/battery.md` | 电量与省电模式管理 |
133
130
  | `audio-record` | `references/audio-record.md` | 录音管理 |
134
131
  | `camera` | `references/camera.md` | 相机控制(拍照/录像),不支持手电筒相关的操作。 |
135
- | `gui` | `references/gui-task-create.md` | 启动 GUI 操作任务(UI Agent),只有当用户明确要求在指定 App 内执行指定动作时才使用。如果用户没有提供应用名,严禁使用本工具。|
136
- | `gui` | `references/gui-task-terminate.md` | 取消 GUI 任务 |
137
- | `gui` | `references/gui-task-pause.md` | 暂停 GUI 任务 |
138
132
  | `scan-code` | `references/scan-code.md` | 扫码(二维码/条形码) |
139
133
  | `flashlight` | `references/flashlight.md` | 手电筒控制 |
140
134
  | `airplane-mode` | `references/airplane-mode.md` | 飞行模式管理 |