@honor-claw/yoyo 1.6.1-beta.2 → 1.6.1-beta.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/gateway-client/protocol-client.mjs +38 -37
- package/dist/hooks/index.mjs +9 -3
- package/dist/index.mjs +3 -3
- package/dist/modules/configs/config-manager.mjs +43 -43
- package/dist/modules/device/registry.mjs +12 -12
- package/dist/utils/home-dir.mjs +6 -6
- package/dist/utils/version.mjs +44 -12
- package/package.json +1 -1
- package/skills/yoyo-control/SKILL.md +0 -2
- package/skills/yoyo-control/references/flight-monitor-create.md +0 -233
- package/skills/yoyo-control/references/flight-monitor-search.md +0 -229
- package/skills/yoyo-control/scripts/time_infer.py +0 -99
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { useClawLogger as e } from "../utils/logger.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { getProtocolVersion as t } from "../utils/version.mjs";
|
|
3
|
+
import { loadOrCreateDeviceIdentity as n } from "../modules/device/identity.mjs";
|
|
4
|
+
import { buildDeviceAuthCredential as r } from "../modules/device/credential-builder.mjs";
|
|
5
|
+
import { GatewayClient as i } from "./client.mjs";
|
|
6
|
+
import { ErrorCodes as a, GATEWAY_CLIENT_IDS as o, GATEWAY_CLIENT_MODES as s, WebSocketClientError as c } from "./types/client.mjs";
|
|
7
|
+
import { randomUUID as l } from "node:crypto";
|
|
7
8
|
//#region src/gateway-client/protocol-client.ts
|
|
8
|
-
var
|
|
9
|
+
var u = {
|
|
9
10
|
role: "operator",
|
|
10
11
|
scopes: [],
|
|
11
|
-
clientId:
|
|
12
|
+
clientId: o.CLI,
|
|
12
13
|
displayName: "Protocol Client",
|
|
13
|
-
clientMode:
|
|
14
|
-
},
|
|
14
|
+
clientMode: s.CLI
|
|
15
|
+
}, d = 3e4, f = "1.0.0", p = "server", m = 2, h = 1e3, g = class extends i {
|
|
15
16
|
protocolOpts;
|
|
16
17
|
config;
|
|
17
18
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
@@ -27,7 +28,7 @@ var l = {
|
|
|
27
28
|
isReconnecting = !1;
|
|
28
29
|
constructor(e = {}, t = {}) {
|
|
29
30
|
super(e), this.protocolOpts = e, this.config = {
|
|
30
|
-
...
|
|
31
|
+
...u,
|
|
31
32
|
...t
|
|
32
33
|
}, this.debug = process.env.DEBUG_GATEWAY === "true";
|
|
33
34
|
}
|
|
@@ -67,15 +68,15 @@ var l = {
|
|
|
67
68
|
if (t) if (this.logDebug("response", e), this.pendingRequests.delete(e.id), this.clearTimer(e.id), e.ok) t.resolve(e.payload);
|
|
68
69
|
else {
|
|
69
70
|
let n = e.error || {
|
|
70
|
-
code:
|
|
71
|
+
code: a.UNKNOWN,
|
|
71
72
|
message: "Unknown error"
|
|
72
73
|
};
|
|
73
|
-
t.reject(new
|
|
74
|
+
t.reject(new c(this.toErrorCode(n.code), n.message, n.details, n.retryable));
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
async sendConnect() {
|
|
77
|
-
let e = this.config.clientMode ??
|
|
78
|
-
deviceIdentity:
|
|
78
|
+
let e = this.config.clientMode ?? s.CLI, i = await n(), a = this.connectNonce ? r({
|
|
79
|
+
deviceIdentity: i,
|
|
79
80
|
clientName: this.config.clientId,
|
|
80
81
|
clientMode: e,
|
|
81
82
|
role: this.config.role,
|
|
@@ -83,16 +84,16 @@ var l = {
|
|
|
83
84
|
authToken: this.protocolOpts.token,
|
|
84
85
|
nonce: this.connectNonce,
|
|
85
86
|
platform: process.platform,
|
|
86
|
-
deviceFamily:
|
|
87
|
-
}) : void 0,
|
|
88
|
-
minProtocol:
|
|
89
|
-
maxProtocol:
|
|
87
|
+
deviceFamily: p
|
|
88
|
+
}) : void 0, o = {
|
|
89
|
+
minProtocol: t(),
|
|
90
|
+
maxProtocol: t(),
|
|
90
91
|
client: {
|
|
91
92
|
id: this.config.clientId,
|
|
92
93
|
displayName: this.config.displayName,
|
|
93
|
-
version:
|
|
94
|
+
version: f,
|
|
94
95
|
platform: process.platform,
|
|
95
|
-
deviceFamily:
|
|
96
|
+
deviceFamily: p,
|
|
96
97
|
mode: e
|
|
97
98
|
},
|
|
98
99
|
role: this.config.role,
|
|
@@ -101,21 +102,21 @@ var l = {
|
|
|
101
102
|
token: this.protocolOpts.token,
|
|
102
103
|
password: this.protocolOpts.password
|
|
103
104
|
},
|
|
104
|
-
device:
|
|
105
|
+
device: a
|
|
105
106
|
};
|
|
106
|
-
this.sendRequest("connect",
|
|
107
|
+
this.sendRequest("connect", o).then((e) => {
|
|
107
108
|
this.helloOk = e, this.connected = !0, this.authenticated = !0, this.protocolOpts.onAuthenticated?.();
|
|
108
109
|
}).catch((e) => {
|
|
109
|
-
this.authenticated = !1, e instanceof
|
|
110
|
+
this.authenticated = !1, e instanceof c ? this.protocolOpts.onClose?.(`auth failed: [${e.code}] ${e.message}`) : this.protocolOpts.onClose?.(`auth failed: ${e.message}`);
|
|
110
111
|
});
|
|
111
112
|
}
|
|
112
113
|
async sendRequest(e, t) {
|
|
113
|
-
let n =
|
|
114
|
+
let n = l();
|
|
114
115
|
return this.logDebug("request", {
|
|
115
116
|
id: n,
|
|
116
117
|
method: e,
|
|
117
118
|
params: t
|
|
118
|
-
}), new Promise((r,
|
|
119
|
+
}), new Promise((r, i) => {
|
|
119
120
|
let o = {
|
|
120
121
|
type: "req",
|
|
121
122
|
id: n,
|
|
@@ -124,22 +125,22 @@ var l = {
|
|
|
124
125
|
};
|
|
125
126
|
this.pendingRequests.set(n, {
|
|
126
127
|
resolve: r,
|
|
127
|
-
reject:
|
|
128
|
+
reject: i
|
|
128
129
|
});
|
|
129
130
|
try {
|
|
130
131
|
this.send(JSON.stringify(o));
|
|
131
132
|
} catch (e) {
|
|
132
|
-
this.pendingRequests.delete(n),
|
|
133
|
+
this.pendingRequests.delete(n), i(new c(a.CONNECTION_FAILED, `WebSocket is not connected, ${JSON.stringify(e)}`));
|
|
133
134
|
return;
|
|
134
135
|
}
|
|
135
|
-
let
|
|
136
|
+
let s = setTimeout(() => {
|
|
136
137
|
if (this.pendingRequests.has(n)) {
|
|
137
138
|
this.pendingRequests.delete(n);
|
|
138
139
|
let t = this.pendingTimers.get(n);
|
|
139
|
-
t && clearTimeout(t), this.pendingTimers.delete(n),
|
|
140
|
+
t && clearTimeout(t), this.pendingTimers.delete(n), i(new c(a.TIMEOUT, `Request timeout: ${e}`));
|
|
140
141
|
}
|
|
141
|
-
},
|
|
142
|
-
this.pendingTimers.set(n,
|
|
142
|
+
}, d);
|
|
143
|
+
this.pendingTimers.set(n, s);
|
|
143
144
|
});
|
|
144
145
|
}
|
|
145
146
|
isAuthenticated() {
|
|
@@ -149,7 +150,7 @@ var l = {
|
|
|
149
150
|
return this.connected;
|
|
150
151
|
}
|
|
151
152
|
toErrorCode(e) {
|
|
152
|
-
return Object.values(
|
|
153
|
+
return Object.values(a).includes(e) ? e : a.UNKNOWN;
|
|
153
154
|
}
|
|
154
155
|
clearTimer(e) {
|
|
155
156
|
let t = this.pendingTimers.get(e);
|
|
@@ -170,15 +171,15 @@ var l = {
|
|
|
170
171
|
}
|
|
171
172
|
scheduleReconnect() {
|
|
172
173
|
if (!(this.closed || this.isReconnecting)) {
|
|
173
|
-
if (this.reconnectAttempts >=
|
|
174
|
-
e().error(`[protocol-gateway] max reconnect attempts (${
|
|
174
|
+
if (this.reconnectAttempts >= m) {
|
|
175
|
+
e().error(`[protocol-gateway] max reconnect attempts (${m}) reached, giving up`), this.protocolOpts.onReconnectFailed?.();
|
|
175
176
|
return;
|
|
176
177
|
}
|
|
177
|
-
this.reconnectAttempts++, this.isReconnecting = !0, e().info(`[protocol-gateway] attempting to reconnect (attempt ${this.reconnectAttempts}/${
|
|
178
|
+
this.reconnectAttempts++, this.isReconnecting = !0, e().info(`[protocol-gateway] attempting to reconnect (attempt ${this.reconnectAttempts}/${m})`), this.reconnectTimer = setTimeout(() => {
|
|
178
179
|
this.isReconnecting = !1, this.closed || super.connect();
|
|
179
|
-
},
|
|
180
|
+
}, h);
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
};
|
|
183
184
|
//#endregion
|
|
184
|
-
export {
|
|
185
|
+
export { g as ProtocolGatewayClient };
|
package/dist/hooks/index.mjs
CHANGED
|
@@ -22,15 +22,21 @@ function c(e) {
|
|
|
22
22
|
}
|
|
23
23
|
function l(e) {
|
|
24
24
|
e.on("after_tool_call", async (t) => {
|
|
25
|
-
if (!(t.toolName !== "read" || t.error || !
|
|
25
|
+
if (!(t.toolName !== "read" || t.error || !d(t.params))) try {
|
|
26
26
|
await r(o, "after_tool_call:read_yoyo_control_skill"), e.logger.info("[yoyoclaw-skill-refresh] yoyo-control skill read marker updated");
|
|
27
27
|
} catch (t) {
|
|
28
28
|
e.logger.warn(`[yoyoclaw-skill-refresh] failed to update skill read marker: ${String(t)}`);
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
var u = [
|
|
33
|
+
"path",
|
|
34
|
+
"file_path",
|
|
35
|
+
"filePath",
|
|
36
|
+
"file"
|
|
37
|
+
];
|
|
38
|
+
function d(e) {
|
|
39
|
+
return u.some((t) => n(e[t]));
|
|
34
40
|
}
|
|
35
41
|
//#endregion
|
|
36
42
|
export { s as registerHooks };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { setClawLogger as e } from "./utils/logger.mjs";
|
|
2
|
+
import { setYoyoRuntime as t } from "./runtime.mjs";
|
|
3
3
|
import { registerCommands as n } from "./commands/index.mjs";
|
|
4
4
|
import { registerHooks as r } from "./hooks/index.mjs";
|
|
5
5
|
import { YoyoPluginConfigSchema as i } from "./schemas.mjs";
|
|
@@ -12,7 +12,7 @@ var o = {
|
|
|
12
12
|
description: "OpenClaw Honor Yoyo connection plugin",
|
|
13
13
|
configSchema: i,
|
|
14
14
|
register(i) {
|
|
15
|
-
|
|
15
|
+
t(i.runtime), e(i.logger), i.registerService(a(i)), r(i), n(i);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import { areStringArraysEqual as e } from "../../utils/array.mjs";
|
|
2
|
+
import { getEnvFromProcessEnv as t } from "../../utils/env.mjs";
|
|
3
|
+
import { wrapError as n } from "../../utils/error.mjs";
|
|
4
|
+
import { useClawLogger as r } from "../../utils/logger.mjs";
|
|
5
|
+
import { detectProvidersToRename as i, updateProviderReferences as a } from "./provider.mjs";
|
|
6
|
+
import { STATE_FLAG as o, hasPersistedStateFlag as s, markPersistedStateFlag as c } from "./state-flags.mjs";
|
|
7
|
+
import { getYoyoRuntime as l } from "../../runtime.mjs";
|
|
8
|
+
import { isBetaVersion as u } from "../../utils/version.mjs";
|
|
9
9
|
//#region src/modules/configs/config-manager.ts
|
|
10
10
|
var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,alarm.enable,alarm.query,alarm.update,app.close,app.open,call.phone,call.search,capture-screenshot,contact.search,file-upload,hotspot,local-search,message.search,message.send,mobile-data,no-disturb,quiet-mode,ringing-mode,schedule.create,schedule.delete,schedule.search,schedule.update,screen-record,vibration-mode,volume.operate,wlan,bluetooth,location-service,nfc,usb-shared-network,eyecomfort,status-bar-show,brightness,autoscreen-onnotice,dark-mode,device-operation,camera,app.uninstall,audio-record,battery,gui.create,gui.pause,gui.terminate,mcp.tool.call,task_result_query".split(","), p = class {
|
|
11
11
|
loadConfig() {
|
|
12
12
|
try {
|
|
13
|
-
let e =
|
|
14
|
-
return typeof
|
|
13
|
+
let e = l().config, t = e.current;
|
|
14
|
+
return typeof t == "function" ? structuredClone(t()) : structuredClone(e.loadConfig());
|
|
15
15
|
} catch (e) {
|
|
16
|
-
throw
|
|
16
|
+
throw n(e, "Failed to load config");
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
async saveConfig(e) {
|
|
20
20
|
try {
|
|
21
|
-
let
|
|
22
|
-
if (typeof
|
|
23
|
-
await
|
|
21
|
+
let t = l().config, n = t.replaceConfigFile;
|
|
22
|
+
if (typeof n == "function") {
|
|
23
|
+
await n({
|
|
24
24
|
nextConfig: e,
|
|
25
25
|
afterWrite: { mode: "auto" }
|
|
26
26
|
});
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
await
|
|
29
|
+
await t.writeConfigFile(e);
|
|
30
30
|
} catch (e) {
|
|
31
|
-
throw
|
|
31
|
+
throw n(e, "Failed to save config");
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
getGatewayAuthConfig() {
|
|
@@ -62,14 +62,14 @@ var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,ala
|
|
|
62
62
|
}
|
|
63
63
|
getEnvInfo() {
|
|
64
64
|
try {
|
|
65
|
-
let e = this.loadConfig().plugins?.entries?.[d]?.config,
|
|
66
|
-
if (
|
|
67
|
-
let
|
|
68
|
-
if (
|
|
69
|
-
env:
|
|
65
|
+
let e = this.loadConfig().plugins?.entries?.[d]?.config, n = e?.envInfo;
|
|
66
|
+
if (n?.env) return n;
|
|
67
|
+
let r = e?.env;
|
|
68
|
+
if (r) return {
|
|
69
|
+
env: r,
|
|
70
70
|
source: "manual"
|
|
71
71
|
};
|
|
72
|
-
let i =
|
|
72
|
+
let i = t();
|
|
73
73
|
return i ? {
|
|
74
74
|
env: i,
|
|
75
75
|
source: "env"
|
|
@@ -124,7 +124,7 @@ var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,ala
|
|
|
124
124
|
};
|
|
125
125
|
await this.saveConfig(r);
|
|
126
126
|
} catch (e) {
|
|
127
|
-
throw
|
|
127
|
+
throw n(e, "Failed to update device config");
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
async updateEnv(e) {
|
|
@@ -152,7 +152,7 @@ var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,ala
|
|
|
152
152
|
};
|
|
153
153
|
await this.saveConfig(n);
|
|
154
154
|
} catch (e) {
|
|
155
|
-
throw
|
|
155
|
+
throw n(e, "Failed to update env config");
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
async resetEnv() {
|
|
@@ -181,7 +181,7 @@ var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,ala
|
|
|
181
181
|
};
|
|
182
182
|
await this.saveConfig(n);
|
|
183
183
|
} catch (e) {
|
|
184
|
-
throw
|
|
184
|
+
throw n(e, "Failed to reset env config");
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
async updateGrayTag(e) {
|
|
@@ -205,7 +205,7 @@ var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,ala
|
|
|
205
205
|
};
|
|
206
206
|
await this.saveConfig(n);
|
|
207
207
|
} catch (e) {
|
|
208
|
-
throw
|
|
208
|
+
throw n(e, "Failed to update gray tag config");
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
async updateUserConfig(e) {
|
|
@@ -229,7 +229,7 @@ var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,ala
|
|
|
229
229
|
};
|
|
230
230
|
await this.saveConfig(n);
|
|
231
231
|
} catch (e) {
|
|
232
|
-
throw
|
|
232
|
+
throw n(e, "Failed to update user config");
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
async clearUserConfig() {
|
|
@@ -252,38 +252,38 @@ var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,ala
|
|
|
252
252
|
};
|
|
253
253
|
await this.saveConfig(t);
|
|
254
254
|
} catch (e) {
|
|
255
|
-
throw
|
|
255
|
+
throw n(e, "Failed to clear user config");
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
|
-
async initializePluginConfig(
|
|
258
|
+
async initializePluginConfig(l) {
|
|
259
259
|
try {
|
|
260
|
-
let
|
|
261
|
-
env: E || w || (
|
|
260
|
+
let n = this.loadConfig(), d = n.skills || {}, p = d.load || {}, m = p.watch === !0, h = !m || await s(o.DISABLE_SKILLS_LOAD_WATCH_MIGRATION_DONE), g = n.plugins?.allow || [], _ = !g.includes(l), v = g.includes(l) ? g : [...g, l], y = n.gateway?.nodes?.allowCommands || [], b = Array.from(new Set([...y, ...f])), x = !e(y, b), S = m && !h, C = n.plugins?.entries?.[l]?.config?.envInfo, w = n.plugins?.entries?.[l]?.config?.env, T = !!(C?.env || w), E = t(), D = !!C?.env && C.source === "env", O = !C?.env && !!w || D && E && C.env !== E, k = !T || O, A = {
|
|
261
|
+
env: E || w || (u() ? "test" : "production"),
|
|
262
262
|
source: "env"
|
|
263
|
-
}, j =
|
|
263
|
+
}, j = n.plugins?.entries?.[l]?.config || {}, M = k ? {
|
|
264
264
|
...j,
|
|
265
265
|
envInfo: A,
|
|
266
266
|
env: void 0
|
|
267
|
-
} : j, N =
|
|
268
|
-
if (F &&
|
|
267
|
+
} : j, N = n.plugins?.entries?.[l]?.enabled !== !0, P = i(n), F = Object.keys(P).length > 0;
|
|
268
|
+
if (F && r().info(`[claw-configs] Found ${Object.keys(P).length} providers to rename: ${Object.entries(P).map(([e, t]) => `${e} -> ${t}`).join(", ")}`), !_ && !x && !S && !k && !N && !F) return;
|
|
269
269
|
let I = {
|
|
270
|
-
...
|
|
270
|
+
...n,
|
|
271
271
|
plugins: {
|
|
272
|
-
...
|
|
272
|
+
...n.plugins,
|
|
273
273
|
allow: v,
|
|
274
274
|
entries: {
|
|
275
|
-
...
|
|
276
|
-
[
|
|
277
|
-
...
|
|
275
|
+
...n.plugins?.entries,
|
|
276
|
+
[l]: {
|
|
277
|
+
...n.plugins?.entries?.[l],
|
|
278
278
|
enabled: !0,
|
|
279
279
|
config: M
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
},
|
|
283
283
|
gateway: {
|
|
284
|
-
...
|
|
284
|
+
...n.gateway,
|
|
285
285
|
nodes: {
|
|
286
|
-
...
|
|
286
|
+
...n.gateway?.nodes,
|
|
287
287
|
allowCommands: b
|
|
288
288
|
}
|
|
289
289
|
}
|
|
@@ -297,9 +297,9 @@ var d = "yoyo", f = /* @__PURE__ */ "alarm.create,alarm.delete,alarm.disable,ala
|
|
|
297
297
|
watch: !1
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
-
},
|
|
300
|
+
}, r().info("[claw-configs] Disabled skills.load.watch for yoyo migration")), F && (I = a(I, P), r().info("[claw-configs] Provider renaming completed")), await this.saveConfig(I), S && await c(o.DISABLE_SKILLS_LOAD_WATCH_MIGRATION_DONE);
|
|
301
301
|
} catch (e) {
|
|
302
|
-
throw
|
|
302
|
+
throw n(e, "failed to initialize plugin config");
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
}, m = null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useClawLogger as e } from "../../utils/logger.mjs";
|
|
2
|
+
import { getConfigManager as t } from "../configs/config-manager.mjs";
|
|
3
|
+
import { getYoyoPluginVersionCode as n } from "../../utils/version.mjs";
|
|
4
4
|
import { isOKResponse as r } from "../../apis/helpers.mjs";
|
|
5
5
|
import { createClawCloudClient as i } from "../../apis/claw-cloud.mjs";
|
|
6
6
|
import { getPersistedIdentity as a, updatePersistedIdentity as o } from "../configs/identity-persist.mjs";
|
|
@@ -9,28 +9,28 @@ import "../../apis/index.mjs";
|
|
|
9
9
|
import { formatHashForLog as s } from "../../utils/hash.mjs";
|
|
10
10
|
import { createGatewayAuthMd5 as c, isSameGatewayAuthMd5 as l } from "./gateway-auth.mjs";
|
|
11
11
|
//#region src/modules/device/registry.ts
|
|
12
|
-
async function u(
|
|
13
|
-
if (!
|
|
14
|
-
let o =
|
|
12
|
+
async function u(e, a) {
|
|
13
|
+
if (!e || !a) throw Error("设备信息或用户信息缺失");
|
|
14
|
+
let o = t().getGatewayAuthConfig(), s = c(o), l = n(), u = await i().registerDevice(e, a, o);
|
|
15
15
|
if (!r(u)) throw Error(`注册失败:${u.data?.cnMessage}`);
|
|
16
16
|
await f(s, l);
|
|
17
17
|
}
|
|
18
18
|
async function d(r, i) {
|
|
19
|
-
let o = c(
|
|
19
|
+
let o = c(t().getGatewayAuthConfig()), d = n(), f = await a(), p = f?.gatewayAuthMd5, m = f?.pluginVersionCode, h = e();
|
|
20
20
|
if (h.info(`[yoyoclaw-registry] checking registration state: gatewayAuth current=${s(o)}, persisted=${s(p)}; pluginVersionCode current=${d}, persisted=${m ?? "none"}`), l(p, o) && m === d) {
|
|
21
21
|
h.info("[yoyoclaw-registry] registration state unchanged, skipping device registration");
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
h.info("[yoyoclaw-registry] registration state changed, registering device"), await u(r, i), h.info(`[yoyoclaw-registry] device registration succeeded, pluginVersionCode=${d}`);
|
|
25
25
|
}
|
|
26
|
-
async function f(
|
|
26
|
+
async function f(t, n) {
|
|
27
27
|
try {
|
|
28
28
|
await o({
|
|
29
|
-
gatewayAuthMd5:
|
|
29
|
+
gatewayAuthMd5: t,
|
|
30
30
|
pluginVersionCode: n
|
|
31
|
-
}),
|
|
32
|
-
} catch (
|
|
33
|
-
|
|
31
|
+
}), e().info("[yoyoclaw-registry] registration state persisted after device registration");
|
|
32
|
+
} catch (t) {
|
|
33
|
+
e().warn(`[yoyoclaw-registry] failed to persist registration state: ${String(t)}`);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
package/dist/utils/home-dir.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { findPackageRoot as e } from "./package-json.mjs";
|
|
2
2
|
import t from "node:path";
|
|
3
|
-
import
|
|
4
|
-
import r from "node:
|
|
3
|
+
import n from "node:os";
|
|
4
|
+
import { fileURLToPath as r } from "node:url";
|
|
5
5
|
//#region src/utils/home-dir.ts
|
|
6
6
|
function i(e) {
|
|
7
7
|
return e?.trim() || void 0;
|
|
8
8
|
}
|
|
9
|
-
function a(
|
|
10
|
-
return e(
|
|
9
|
+
function a(n = import.meta.url) {
|
|
10
|
+
return e(n) ?? t.parse(r(n)).root;
|
|
11
11
|
}
|
|
12
|
-
function o(e = process.env,
|
|
13
|
-
let i = s(e,
|
|
12
|
+
function o(e = process.env, r = n.homedir) {
|
|
13
|
+
let i = s(e, r);
|
|
14
14
|
return i ? t.resolve(i) : void 0;
|
|
15
15
|
}
|
|
16
16
|
function s(e, t) {
|
package/dist/utils/version.mjs
CHANGED
|
@@ -1,27 +1,59 @@
|
|
|
1
1
|
import { findPackageRoot as e, readPackageJson as t } from "./package-json.mjs";
|
|
2
|
+
import { getYoyoRuntime as n } from "../runtime.mjs";
|
|
2
3
|
//#region src/utils/version.ts
|
|
3
|
-
var
|
|
4
|
-
function
|
|
5
|
-
|
|
4
|
+
var r = "@honor-claw/yoyo", i = 3;
|
|
5
|
+
function a() {
|
|
6
|
+
try {
|
|
7
|
+
let e = n().version;
|
|
8
|
+
return o(e);
|
|
9
|
+
} catch {
|
|
10
|
+
return i;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function o(e) {
|
|
14
|
+
let t = s(e);
|
|
15
|
+
return t && c(t, [
|
|
16
|
+
2026,
|
|
17
|
+
5,
|
|
18
|
+
12
|
|
19
|
+
]) >= 0 ? 4 : i;
|
|
20
|
+
}
|
|
21
|
+
function s(e) {
|
|
22
|
+
if (!e) return null;
|
|
23
|
+
let t = e.split("-", 1)[0].split(".");
|
|
24
|
+
if (t.length === 0) return null;
|
|
25
|
+
let n = t.map((e) => Number.parseInt(e, 10));
|
|
26
|
+
return n.some((e) => !Number.isFinite(e) || e < 0) ? null : n;
|
|
27
|
+
}
|
|
28
|
+
function c(e, t) {
|
|
29
|
+
let n = Math.max(e.length, t.length);
|
|
30
|
+
for (let r = 0; r < n; r += 1) {
|
|
31
|
+
let n = e[r] ?? 0, i = t[r] ?? 0;
|
|
32
|
+
if (n !== i) return n - i;
|
|
33
|
+
}
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
function l() {
|
|
37
|
+
let e = f();
|
|
6
38
|
return e.includes("beta") || e.includes("alpha");
|
|
7
39
|
}
|
|
8
|
-
function
|
|
40
|
+
function u(e) {
|
|
9
41
|
if (!e) return 0;
|
|
10
42
|
let t = Number.parseInt(e, 10);
|
|
11
43
|
return Number.isFinite(t) && t >= 0 ? t : 0;
|
|
12
44
|
}
|
|
13
|
-
function
|
|
14
|
-
let [e, t, n] =
|
|
15
|
-
return r * 1e6 +
|
|
45
|
+
function d() {
|
|
46
|
+
let [e, t, n] = f().split("-", 1)[0].split("."), r = u(e), i = u(t), a = u(n);
|
|
47
|
+
return r * 1e6 + i * 1e3 + a;
|
|
16
48
|
}
|
|
17
|
-
function
|
|
49
|
+
function f() {
|
|
18
50
|
try {
|
|
19
|
-
let
|
|
20
|
-
if (!
|
|
21
|
-
let i = t(
|
|
51
|
+
let n = e(import.meta.url, r);
|
|
52
|
+
if (!n) return "0.0.0";
|
|
53
|
+
let i = t(n)?.version;
|
|
22
54
|
if (typeof i == "string") return i;
|
|
23
55
|
} catch {}
|
|
24
56
|
return "0.0.0";
|
|
25
57
|
}
|
|
26
58
|
//#endregion
|
|
27
|
-
export { a as getYoyoPluginVersionCode,
|
|
59
|
+
export { a as getProtocolVersion, d as getYoyoPluginVersionCode, l as isBetaVersion };
|
package/package.json
CHANGED
|
@@ -140,8 +140,6 @@ metadata: { "openclaw": { "emoji": "📱", "always": true } }
|
|
|
140
140
|
| `flashlight` | `references/flashlight.md` | 手电筒控制 |
|
|
141
141
|
| `airplane-mode` | `references/airplane-mode.md` | 飞行模式管理 |
|
|
142
142
|
| `clean-dirty` | `references/clean-dirty.md` | 扬声器清理 |
|
|
143
|
-
| `flight-monitor` | `references/flight-monitor-create.md` | 创建机票价格监控 |
|
|
144
|
-
| `flight-monitor` | `references/flight-monitor-search.md` | 查询机票监控任务 |
|
|
145
143
|
| `express` | `references/express-logistics-search.md` | 快递查询 |
|
|
146
144
|
| `countdown-timer` | `references/countdown-timer.md` | 倒计时计时器管理 |
|
|
147
145
|
| `desktop` | `references/desktop.md` | 手机桌面管理,支持查询桌面布局、打开桌面设置页,并对风格、应用角标、桌面图标、快捷方式、搜索入口、应用名称、通透模式与布局等进行配置。 |
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: flight-monitor.create
|
|
3
|
-
description: >
|
|
4
|
-
创建机票价格监控任务。支持设置目的地、出发地、监控时间段与目标价格,缺省或低于最低监控价时给出价格区间与建议价。
|
|
5
|
-
只支持飞机票价的监控任务创建,其余票务类型(如酒店、机票联程、火车票等)暂不支持。
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Create Flight Monitor Task 创建机票监控任务
|
|
9
|
-
|
|
10
|
-
## Tool Provider
|
|
11
|
-
```bash
|
|
12
|
-
com.hihonor.magicvoice
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Tool Command
|
|
16
|
-
```bash
|
|
17
|
-
create_flight_monitor_task
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## 3. 业务规则
|
|
21
|
-
|
|
22
|
-
### 适用场景及操作流程
|
|
23
|
-
1. 解析并提取目的地城市、出发地城市、监控时间段与目标价格
|
|
24
|
-
2. 未提供出发地时,默认使用用户当前位置城市
|
|
25
|
-
3. 未提供目的地时,提醒用户指定目的地
|
|
26
|
-
4. 对价格进行币种识别与人民币换算,并向下取整
|
|
27
|
-
5. 当未指定监控价格或设定价格低于最低可监控价时,工具返回可监控价格区间与建议价
|
|
28
|
-
6. 创建机票价格监控任务并开始持续监控
|
|
29
|
-
|
|
30
|
-
### 特定交互
|
|
31
|
-
- 币种默认人民币;如检测到外币(如 USD、美元),按实时汇率换算为人民币后取整
|
|
32
|
-
- 城市名必须为具体城市中文名,不支持省份或国家级地名
|
|
33
|
-
- 监控时间段使用用户原始的自然语言日期表述(如“下周二到下周三”“10月1日-10月7日”),不做改写
|
|
34
|
-
- 价格仅提取数值部分,取整为不大于该值的整数;“以内/不高于/低于”等等价于上限价
|
|
35
|
-
|
|
36
|
-
## 4. 集成工作流
|
|
37
|
-
|
|
38
|
-
### Step 1: 意图解析与参数组装
|
|
39
|
-
```
|
|
40
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
41
|
-
│ 意图识别与参数提取 │
|
|
42
|
-
│ - 识别操作意图: 创建 + 机票价格监控任务 │
|
|
43
|
-
│ - 提取目的地城市: 城市中文名(不含省/国家) │
|
|
44
|
-
│ - 提取出发地城市: 城市中文名(缺省为当前位置) │
|
|
45
|
-
│ - 提取监控时间段: 自然语言日期描述(直接保留) │
|
|
46
|
-
│ - 提取目标价格: 数字,币种识别与换算,向下取整 │
|
|
47
|
-
│ - 特殊处理: "飞/去/到" 等同义词统一识别为目的地 │
|
|
48
|
-
└─────────────────────────────────────────────────────────────┘
|
|
49
|
-
↓
|
|
50
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
51
|
-
│ 消歧与槽位填充 │
|
|
52
|
-
│ - 未指定出发地时,默认为用户当前位置城市 │
|
|
53
|
-
│ - 未指定监控价格时,返回价格区间与建议价 │
|
|
54
|
-
│ - 价格低于最低可监控价时,提示范围并给出建议价 │
|
|
55
|
-
│ - date_time 仅当 query 含时间表述时填写 │
|
|
56
|
-
│ - 验证规则: 目的地与出发地不能相同 │
|
|
57
|
-
│ - "从/出发地" → 映射为出发地槽位 │
|
|
58
|
-
└─────────────────────────────────────────────────────────────┘
|
|
59
|
-
↓
|
|
60
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
61
|
-
│ 生成工具调用 │
|
|
62
|
-
│ - 组装 destination_city 及可选参数(departure_city, │
|
|
63
|
-
│ date_time, price) │
|
|
64
|
-
│ - 生成平台特定的命令格式 │
|
|
65
|
-
└─────────────────────────────────────────────────────────────┘
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Step 2: 执行调用
|
|
69
|
-
- 根据操作系统选择对应的命令格式
|
|
70
|
-
- Windows: Cmd 格式(双引号需转义)
|
|
71
|
-
- Linux: Bash 格式(单引号包裹)
|
|
72
|
-
|
|
73
|
-
## 5. 参数定义
|
|
74
|
-
### 工具输入参数说明
|
|
75
|
-
```json
|
|
76
|
-
{
|
|
77
|
-
"destination_city": {
|
|
78
|
-
"type": "string",
|
|
79
|
-
"description": "必填参数,机票监控的目的地城市中文名称,不支持省份或国家。如:深圳、北京。"
|
|
80
|
-
},
|
|
81
|
-
"departure_city": {
|
|
82
|
-
"type": "string",
|
|
83
|
-
"description": "机票监控的出发地城市中文名称,默认为用户当前位置所在城市,不支持省份或国家。如:深圳、北京。"
|
|
84
|
-
},
|
|
85
|
-
"date_time": {
|
|
86
|
-
"type": "string",
|
|
87
|
-
"description": "监控时间段的自然语言描述,直接提取用户输入中日期的描述。如:下周、下周二到下周三、周一下午。"
|
|
88
|
-
},
|
|
89
|
-
"price": {
|
|
90
|
-
"type": "integer",
|
|
91
|
-
"description": "用户设定的监控目标最高价格。直接提取价格数字,向下取整。默认币种人民币,其他币种需换算人民币。如果没有指定币种,按照人民币处理。"
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### 工具输出参数说明
|
|
97
|
-
```json
|
|
98
|
-
{
|
|
99
|
-
"result_code": {
|
|
100
|
-
"type": "integer",
|
|
101
|
-
"description": "结果状态码。100:执行成功。201:用户没有指定价格时返回,追问用户需要设置的监控价格,同时返回价格区间(suggested_price_range)与推荐价格(suggested_price)。202:追问,没有出发地城市,同时当前城市查询失败时追问。304:未授权三方账号,用户拒绝授权三方。406:授权三方失败。500:出发地不在支持列表范围内。501:目的地不在支持列表范围内。502:监控日期超出监控范围。503:查询监控价格失败,当前航线未查询到直飞航班报价。504:用户设置的监控价格过低,低于最低可监控价格,同时返回价格区间(suggested_price_range)与推荐价格(suggested_price)。505:监控任务创建失败。506:已存在重复监控任务。507:监控任务已达上限。"
|
|
102
|
-
},
|
|
103
|
-
"result_content": {
|
|
104
|
-
"type": "string",
|
|
105
|
-
"description": "针对不同状态码场景下,详细的执行结果,包含错误原因等。"
|
|
106
|
-
},
|
|
107
|
-
"suggested_price_range": {
|
|
108
|
-
"type": "string",
|
|
109
|
-
"description": "工具根据历史数据建议的价格区间,货币单位为人民币,格式为最低可监控价格-最高监控价格,如:500-1000。"
|
|
110
|
-
},
|
|
111
|
-
"suggested_price": {
|
|
112
|
-
"type": "integer",
|
|
113
|
-
"description": "工具建议的监控目标价格,货币单位为人民币。"
|
|
114
|
-
},
|
|
115
|
-
"departure_city": {
|
|
116
|
-
"type": "string",
|
|
117
|
-
"description": "出发地城市中文名称。如:深圳、北京。"
|
|
118
|
-
},
|
|
119
|
-
"destination_city": {
|
|
120
|
-
"type": "string",
|
|
121
|
-
"description": "目的地城市中文名称。如:深圳、北京。"
|
|
122
|
-
},
|
|
123
|
-
"date_range": {
|
|
124
|
-
"type": "string",
|
|
125
|
-
"description": "监控的日期范围,格式为 YYYY-MM-DD~YYYY-MM-DD。"
|
|
126
|
-
},
|
|
127
|
-
"price": {
|
|
128
|
-
"type": "integer",
|
|
129
|
-
"description": "用户设置的监控价格,货币单位为人民币,未提供或小于最低可监控价格则设为0。"
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
### 参数注意事项
|
|
136
|
-
1. **默认值或缺省行为**: 未提供`departure_city`时默认取用户当前位置城市;未提供`price`时由工具返回可监控价格区间与建议价;未标明币种时按人民币处理。
|
|
137
|
-
2. **参数间关联/验证**: `destination_city`为必填且不得与`departure_city`相同;外币价格需先换算为人民币后再对`price`进行向下取整。
|
|
138
|
-
3. **可选参数填写条件**: `date_time`仅在用户明确提及时间/日期时填写,不做推断;`departure_city`仅在用户未使用默认定位或明确指定时填写。
|
|
139
|
-
|
|
140
|
-
### date_time 参数格式转换
|
|
141
|
-
1. 当用户输入包含时间表述时,工具会直接保留原始的自然语言日期表述,然后调用`scripts/time_infer.py`时间推理模型进行转换。具体执行指令如下:
|
|
142
|
-
```bash
|
|
143
|
-
python3 scripts/time_infer.py --query <date_time>
|
|
144
|
-
```
|
|
145
|
-
2. 拿到json-string格式的转换结果,重新改写`date_time`参数,并调用`openclaw nodes invoke`命令执行工具。
|
|
146
|
-
3. 注意:time_infer模型返回的结果格式如下:`{"data_time": {...}}`,用`{...}`JSONstring的完整内容重写`date_time`参数。**不能拆解{...}的内容**
|
|
147
|
-
|
|
148
|
-
## 6. Query 示例及输出
|
|
149
|
-
|
|
150
|
-
### 示例 1: 指定出发地、时间段与人民币价格上限
|
|
151
|
-
**用户输入**: "帮我监控一下去上海的机票,出发地是北京,时间是十月一号到十月七号,价格设为1000元以内"
|
|
152
|
-
|
|
153
|
-
**工具**: "create_flight_monitor_task"
|
|
154
|
-
|
|
155
|
-
**JSON 参数**:
|
|
156
|
-
```json
|
|
157
|
-
{
|
|
158
|
-
"destination_city": "上海",
|
|
159
|
-
"departure_city": "北京",
|
|
160
|
-
"date_time": "十月一号到十月七号",
|
|
161
|
-
"price": 1000
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
**调用时间推理模型**:
|
|
165
|
-
```bash
|
|
166
|
-
python3 scripts/time_infer.py --query "十月一号到十月七号"
|
|
167
|
-
```
|
|
168
|
-
返回结果: {"data_time": <json-string-data>} (注意字符串转义的问题!!!)
|
|
169
|
-
|
|
170
|
-
**Windows (Cmd) 执行命令**:
|
|
171
|
-
```bash
|
|
172
|
-
cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"create_flight_monitor_task\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{\"destination_city\":\"上海\",\"departure_city\":\"北京\",\"date_time\":\"<json-string-data>\",\"price\":1000}}"'
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**Linux (Bash) 执行命令**:
|
|
176
|
-
```bash
|
|
177
|
-
openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"create_flight_monitor_task","appPkg":"com.hihonor.magicvoice","arguments":{"destination_city":"上海","departure_city":"北京","date_time":"<json-string-data>","price":1000}}'
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
### 示例 2: 仅目的地与美元价格(自动换算为人民币)
|
|
183
|
-
**用户输入**: "帮我监控飞广州的机票,价格低于200美元就提醒"
|
|
184
|
-
|
|
185
|
-
**工具**: "create_flight_monitor_task"
|
|
186
|
-
|
|
187
|
-
**JSON 参数**:
|
|
188
|
-
```json
|
|
189
|
-
{
|
|
190
|
-
"destination_city": "广州",
|
|
191
|
-
"price": 1440
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
**Windows (Cmd) 执行命令**:
|
|
196
|
-
```bash
|
|
197
|
-
cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"create_flight_monitor_task\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{\"destination_city\":\"广州\",\"price\":1440}}"'
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
**Linux (Bash) 执行命令**:
|
|
201
|
-
```bash
|
|
202
|
-
openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"create_flight_monitor_task","appPkg":"com.hihonor.magicvoice","arguments":{"destination_city":"广州","price":1440}}'
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
### 示例 3: 指定目的地与出发时段,不设价格(工具返回建议价)
|
|
208
|
-
**用户输入**: "帮我关注去杭州的机票,下周三下午出发"
|
|
209
|
-
|
|
210
|
-
**工具**: "create_flight_monitor_task"
|
|
211
|
-
|
|
212
|
-
**JSON 参数**:
|
|
213
|
-
```json
|
|
214
|
-
{
|
|
215
|
-
"destination_city": "杭州",
|
|
216
|
-
"date_time": "下周三下午"
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
**调用时间推理模型**:
|
|
220
|
-
```bash
|
|
221
|
-
python3 scripts/time_infer.py --query "下周三下午"
|
|
222
|
-
```
|
|
223
|
-
返回结果: {"data_time": <json-string-data>} (注意字符串转义的问题!!!)
|
|
224
|
-
|
|
225
|
-
**Windows (Cmd) 执行命令**:
|
|
226
|
-
```bash
|
|
227
|
-
cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"create_flight_monitor_task\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{\"destination_city\":\"杭州\",\"date_time\":\"<json-string-data>\"}}"'
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
**Linux (Bash) 执行命令**:
|
|
231
|
-
```bash
|
|
232
|
-
openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"create_flight_monitor_task","appPkg":"com.hihonor.magicvoice","arguments":{"destination_city":"杭州","date_time":"<json-string-data>"}}'
|
|
233
|
-
```
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: flight-monitor.query
|
|
3
|
-
description: >
|
|
4
|
-
查询用户已创建的机票价格监控任务,支持按出发地、目的地、监控日期与任务状态筛选。
|
|
5
|
-
只支持飞机票价的监控任务创建,其余票务类型(如酒店、机票联程、火车票等)暂不支持。
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Query Flight Monitor Task 机票监控任务查询
|
|
9
|
-
|
|
10
|
-
## Tool Provider
|
|
11
|
-
```bash
|
|
12
|
-
com.hihonor.magicvoice
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Tool Command
|
|
16
|
-
```bash
|
|
17
|
-
query_flight_monitor_task
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## 集成工作流
|
|
21
|
-
|
|
22
|
-
### Step 1: 意图解析与参数组装
|
|
23
|
-
```
|
|
24
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
25
|
-
│ 意图识别与参数提取 │
|
|
26
|
-
│ - 识别操作意图: 查询 + 机票价格监控任务 │
|
|
27
|
-
│ - 提取目的地: 城市名/机场名/别称 │
|
|
28
|
-
│ - 提取出发地: 城市名/机场名/别称 │
|
|
29
|
-
│ - 提取监控日期: 具体日期/日期范围/自然语言时间 │
|
|
30
|
-
│ - 提取任务状态: 已过期/监控中/监控成功 │
|
|
31
|
-
│ - 特殊处理: "监控中的/正在监控" → 映射为监控中 │
|
|
32
|
-
│ - 特殊处理: "过期/失效" → 已过期;"成功/达成" → 监控成功 │
|
|
33
|
-
└─────────────────────────────────────────────────────────────┘
|
|
34
|
-
↓
|
|
35
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
36
|
-
│ 消歧与槽位填充 │
|
|
37
|
-
│ - 未指定任何筛选条件时,默认查询全部监控任务 │
|
|
38
|
-
│ - destination_city/departure_city/date_time/status 缺失时, │
|
|
39
|
-
│ 不对该项筛选 │
|
|
40
|
-
│ - date_time 仅当 query 含时间表述时填写 │
|
|
41
|
-
│ - "监控中的/进行中/在监控" → 映射为监控中 │
|
|
42
|
-
│ - "已失效/过期的" → 已过期;"已触发/成功了" → 监控成功 │
|
|
43
|
-
│ - 城市名需为有效地名,避免与机场简称冲突 │
|
|
44
|
-
└─────────────────────────────────────────────────────────────┘
|
|
45
|
-
↓
|
|
46
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
47
|
-
│ 生成工具调用 │
|
|
48
|
-
│ - 组装 status 及可选参数(destination_city、departure_city、 │
|
|
49
|
-
│ date_time) │
|
|
50
|
-
│ - 生成平台特定的命令格式 │
|
|
51
|
-
└─────────────────────────────────────────────────────────────┘
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Step 2: 执行调用
|
|
55
|
-
- 根据操作系统选择对应的命令格式
|
|
56
|
-
- Windows: Cmd 格式(双引号需转义)
|
|
57
|
-
- Linux: Bash 格式(单引号包裹)
|
|
58
|
-
|
|
59
|
-
## 参数定义
|
|
60
|
-
### 工具输入参数说明
|
|
61
|
-
```json
|
|
62
|
-
{
|
|
63
|
-
"destination_city": {
|
|
64
|
-
"type": "string",
|
|
65
|
-
"description": "监控任务的目的地城市名称。"
|
|
66
|
-
},
|
|
67
|
-
"departure_city": {
|
|
68
|
-
"type": "string",
|
|
69
|
-
"description": "监控任务的出发地城市名称。"
|
|
70
|
-
},
|
|
71
|
-
"date_time": {
|
|
72
|
-
"type": "string",
|
|
73
|
-
"description": "监控时间段的自然语言描述,直接提取用户输入中日期的描述。如:下周、下周二到下周三、周一下午。"
|
|
74
|
-
},
|
|
75
|
-
"status": {
|
|
76
|
-
"type": "string",
|
|
77
|
-
"enum": ["已过期", "监控中", "监控成功"],
|
|
78
|
-
"description": "监控任务的当前生效状态。"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### 工具输出参数说明
|
|
84
|
-
```json
|
|
85
|
-
{
|
|
86
|
-
"result_code":{
|
|
87
|
-
"type":"integer",
|
|
88
|
-
"description":"结果状态码,100:执行成功。304:未授权三方账号,用户拒绝授权三方。402:三方接口调用出错。406:授权三方失败。500:无监控任务。"
|
|
89
|
-
},
|
|
90
|
-
"result_content":{
|
|
91
|
-
"type":"string",
|
|
92
|
-
"description":"针对不同状态码场景下,详细的执行结果,包含错误原因等。"
|
|
93
|
-
},
|
|
94
|
-
"task_count":{
|
|
95
|
-
"type":"integer",
|
|
96
|
-
"description":"查询到的监控任务总数量。"
|
|
97
|
-
},
|
|
98
|
-
"task_list":{
|
|
99
|
-
"type":"string",
|
|
100
|
-
"description":"监控任务的详细列表数据。每条数据包含序号、出发地、目的地、监控日期段、监控状态。查询失败或无监控任务(500)时返回空字符串。",
|
|
101
|
-
"examples":[
|
|
102
|
-
"{\"监控任务列表\":[{\"序号\":1,\"出发地\":\"北京\",\"目的地\":\"上海\",\"开始日期\":\"2023-12-01\",\"结束日期\":\"2023-12-03\",\"状态\":\"监控中\"},{\"序号\":2,\"出发地\":\"上海\",\"目的地\":\"广州\",\"开始日期\":\"2023-12-05\",\"状态\":\"监控成功\"}]}"
|
|
103
|
-
]
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### 参数注意事项
|
|
109
|
-
1. **默认值或缺省行为**: 未提供任一筛选条件时,默认返回全部监控任务;某参数未提供则不作为筛选条件参与过滤。
|
|
110
|
-
2. **可选参数填写条件**: `date_time` 仅在用户输入包含时间表述时填写;`destination_city`、`departure_city` 仅在出现明确城市名时填写;`status` 仅在用户明确提及时填写。
|
|
111
|
-
3. **格式/原样保留要求**: `date_time` 必须保留用户的自然语言原始表述,不做标准化或改写。
|
|
112
|
-
|
|
113
|
-
### date_time 参数格式转换
|
|
114
|
-
1. 当用户输入包含时间表述时,工具会直接保留原始的自然语言日期表述,然后调用`scripts/time_infer.py`时间推理模型进行转换。具体执行指令如下:
|
|
115
|
-
```bash
|
|
116
|
-
python3 scripts/time_infer.py --query <date_time>
|
|
117
|
-
```
|
|
118
|
-
2. 拿到json-string格式的转换结果,重新改写`date_time`参数,并调用`openclaw nodes invoke`命令执行工具。
|
|
119
|
-
3. 注意:time_infer模型返回的结果格式如下:`{"data_time": {...}}`,用`{...}`JSONstring的完整内容重写`date_time`参数。**不能拆解{...}的内容**
|
|
120
|
-
|
|
121
|
-
## Query 示例及输出
|
|
122
|
-
|
|
123
|
-
### 示例 1: 按目的地筛选
|
|
124
|
-
**用户输入**: "帮我查一下去上海的监控任务"
|
|
125
|
-
|
|
126
|
-
**工具**: "query_flight_monitor_task"
|
|
127
|
-
|
|
128
|
-
**JSON 参数**:
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"destination_city": "上海"
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
**Windows (Cmd) 执行命令**:
|
|
136
|
-
```bash
|
|
137
|
-
cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"query_flight_monitor_task\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{\"destination_city\":\"上海\"}}"'
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
**Linux (Bash) 执行命令**:
|
|
141
|
-
```bash
|
|
142
|
-
openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"query_flight_monitor_task","appPkg":"com.hihonor.magicvoice","arguments":{"destination_city":"上海"}}'
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
### 示例 2: 按任务状态筛选
|
|
148
|
-
**用户输入**: "查询所有监控中的任务"
|
|
149
|
-
|
|
150
|
-
**工具**: "query_flight_monitor_task"
|
|
151
|
-
|
|
152
|
-
**JSON 参数**:
|
|
153
|
-
```json
|
|
154
|
-
{
|
|
155
|
-
"status": "监控中"
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
**Windows (Cmd) 执行命令**:
|
|
160
|
-
```bash
|
|
161
|
-
cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"query_flight_monitor_task\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{\"status\":\"监控中\"}}"'
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
**Linux (Bash) 执行命令**:
|
|
165
|
-
```bash
|
|
166
|
-
openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"query_flight_monitor_task","appPkg":"com.hihonor.magicvoice","arguments":{"status":"监控中"}}'
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
### 示例 3: 按时间与状态组合筛选(自然语言时间)
|
|
172
|
-
**用户输入**: "查询下周监控中的任务"
|
|
173
|
-
|
|
174
|
-
**工具**: "query_flight_monitor_task"
|
|
175
|
-
|
|
176
|
-
**JSON 参数**:
|
|
177
|
-
```json
|
|
178
|
-
{
|
|
179
|
-
"date_time": "下周",
|
|
180
|
-
"status": "监控中"
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
**调用时间推理模型**:
|
|
184
|
-
```bash
|
|
185
|
-
python3 scripts/time_infer.py --query "下周"
|
|
186
|
-
```
|
|
187
|
-
返回结果:{"data_time": <json-string-data>} (注意字符串转义的问题!!!)
|
|
188
|
-
|
|
189
|
-
**Windows (Cmd) 执行命令**:
|
|
190
|
-
```bash
|
|
191
|
-
cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"query_flight_monitor_task\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{\"date_time\":\"<json-string-data>\",\"status\":\"监控中\"}}"'
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
**Linux (Bash) 执行命令**:
|
|
195
|
-
```bash
|
|
196
|
-
openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"query_flight_monitor_task","appPkg":"com.hihonor.magicvoice","arguments":{"date_time":"<json-string-data>","status":"监控中"}}'
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
### 示例 4: 按时间范围与状态组合筛选
|
|
202
|
-
**用户输入**: "查询十月一号到十月七号监控中的任务"
|
|
203
|
-
|
|
204
|
-
**工具**: "query_flight_monitor_task"
|
|
205
|
-
|
|
206
|
-
**JSON 参数**:
|
|
207
|
-
```json
|
|
208
|
-
{
|
|
209
|
-
"date_time": "十月一号到十月七号",
|
|
210
|
-
"status": "监控中"
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
**调用时间推理模型**:
|
|
214
|
-
```bash
|
|
215
|
-
python3 scripts/time_infer.py --query "十月一号到十月七号"
|
|
216
|
-
```
|
|
217
|
-
返回结果:{"data_time": <json-string-data>} (注意字符串转义的问题!!!)
|
|
218
|
-
|
|
219
|
-
**Windows (Cmd) 执行命令**:
|
|
220
|
-
```bash
|
|
221
|
-
cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"query_flight_monitor_task\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{\"date_time\":\"<json-string-data>\",\"status\":\"监控中\"}}"'
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
**Linux (Bash) 执行命令**:
|
|
225
|
-
```bash
|
|
226
|
-
openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"query_flight_monitor_task","appPkg":"com.hihonor.magicvoice","arguments":{date_time":"<json-string-data>","status":"监控中"}}'
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
---
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
import hmac
|
|
3
|
-
import hashlib
|
|
4
|
-
import base64
|
|
5
|
-
import json
|
|
6
|
-
import requests
|
|
7
|
-
from datetime import datetime
|
|
8
|
-
import argparse
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def gen_headers():
|
|
12
|
-
# ak / sk 建议线上不要写死在代码里,放到环境变量或配置文件中
|
|
13
|
-
ak = "DB1178355DDE4CD2"
|
|
14
|
-
sk = "4764F7B34264E8F4343AC7363AA6D4"
|
|
15
|
-
|
|
16
|
-
# 对应:var ts = Math.floor(Date.now()).toString();
|
|
17
|
-
# Date.now() 是毫秒,这里同样用毫秒时间戳
|
|
18
|
-
ts = str(int(time.time() * 1000))
|
|
19
|
-
|
|
20
|
-
# 对应:CryptoJS.HmacSHA256(ts + ak, sk).toString().toUpperCase()
|
|
21
|
-
msg = (ts + ak).encode("utf-8")
|
|
22
|
-
key = sk.encode("utf-8")
|
|
23
|
-
digest = hmac.new(key, msg, hashlib.sha256).hexdigest().upper()
|
|
24
|
-
|
|
25
|
-
# 对应:new Buffer(...).toString('base64');
|
|
26
|
-
# 即对十六进制字符串做 base64 编码
|
|
27
|
-
sign = base64.b64encode(digest.encode("utf-8")).decode("utf-8")
|
|
28
|
-
|
|
29
|
-
headers = {
|
|
30
|
-
"accessKey": ak,
|
|
31
|
-
"ts": ts,
|
|
32
|
-
"sign": sign,
|
|
33
|
-
"Content-Type": "application/json",
|
|
34
|
-
}
|
|
35
|
-
return headers
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def get_current_local_time_str():
|
|
39
|
-
"""
|
|
40
|
-
生成形如:2025-12-23 12:48:00.000 的时间字符串
|
|
41
|
-
使用当前系统时间
|
|
42
|
-
"""
|
|
43
|
-
now = datetime.now()
|
|
44
|
-
# %f 是微秒,取前 3 位当毫秒
|
|
45
|
-
return now.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def parse_args():
|
|
49
|
-
parser = argparse.ArgumentParser(
|
|
50
|
-
description="调用 time-agent 接口的命令行工具"
|
|
51
|
-
)
|
|
52
|
-
parser.add_argument(
|
|
53
|
-
"--query",
|
|
54
|
-
# required=True,
|
|
55
|
-
default="大小周下周六休息7点",
|
|
56
|
-
help="语义查询内容(必填)",
|
|
57
|
-
)
|
|
58
|
-
parser.add_argument(
|
|
59
|
-
"--type",
|
|
60
|
-
dest="req_type",
|
|
61
|
-
default="time",
|
|
62
|
-
help="payload.data.type,默认值为 'time'",
|
|
63
|
-
)
|
|
64
|
-
parser.add_argument(
|
|
65
|
-
"--timeZone",
|
|
66
|
-
dest="time_zone",
|
|
67
|
-
default="+0800",
|
|
68
|
-
help="payload.data.timeZone,默认值为 '+0800'",
|
|
69
|
-
)
|
|
70
|
-
return parser.parse_args()
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def main():
|
|
74
|
-
url = "https://ai-model-access-drcn.rnd.honor.com/time-agent/v1/models/model/predict"
|
|
75
|
-
args = parse_args()
|
|
76
|
-
|
|
77
|
-
payload = {
|
|
78
|
-
"data": {
|
|
79
|
-
"type": args.req_type,
|
|
80
|
-
"timeZone": args.time_zone,
|
|
81
|
-
"query": args.query,
|
|
82
|
-
"localTime": get_current_local_time_str()
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
headers = gen_headers()
|
|
87
|
-
|
|
88
|
-
resp = requests.post(url, headers=headers, data=json.dumps(payload))
|
|
89
|
-
print("Status code:", resp.status_code)
|
|
90
|
-
try:
|
|
91
|
-
datas = resp.json()
|
|
92
|
-
res = json.dumps(datas.get("data", {}), separators=(",", ":"), ensure_ascii=False)
|
|
93
|
-
print({"data_time": res})
|
|
94
|
-
except Exception:
|
|
95
|
-
print("Response text:", resp.text)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if __name__ == "__main__":
|
|
99
|
-
main()
|