@honor-claw/yoyo 1.3.0-beta.1 → 1.3.0-beta.10
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/apis/claw-cloud.mjs +74 -0
- package/dist/apis/helpers.mjs +6 -0
- package/dist/apis/hosts.mjs +28 -0
- package/dist/apis/http-client.mjs +97 -0
- package/dist/apis/index.mjs +3 -0
- package/dist/cloud-channel/admin-client-manager.mjs +75 -0
- package/dist/cloud-channel/channel.mjs +60 -0
- package/dist/cloud-channel/client.mjs +160 -0
- package/dist/cloud-channel/message-handler.mjs +381 -0
- package/dist/cloud-channel/session-manager.mjs +71 -0
- package/dist/cloud-channel/utils.mjs +27 -0
- package/dist/commands/env/impl.mjs +31 -0
- package/dist/commands/env/index.mjs +1 -0
- package/dist/commands/index.mjs +17 -0
- package/dist/commands/login/impl.mjs +14 -0
- package/dist/commands/login/index.mjs +1 -0
- package/dist/commands/logout/impl.mjs +15 -0
- package/dist/commands/logout/index.mjs +1 -0
- package/dist/commands/status/index.mjs +58 -0
- package/dist/gateway-client/admin-client.mjs +54 -0
- package/dist/gateway-client/client.mjs +46 -0
- package/dist/gateway-client/node-client.mjs +49 -0
- package/dist/gateway-client/protocol-client.mjs +184 -0
- package/dist/gateway-client/types/client.mjs +32 -0
- package/dist/honor-auth/assets/favicon.mjs +4 -0
- package/dist/honor-auth/auth-result-html.mjs +213 -0
- package/dist/honor-auth/browser.mjs +47 -0
- package/dist/honor-auth/callback-server.mjs +101 -0
- package/dist/honor-auth/cloud.mjs +17 -0
- package/dist/honor-auth/config.mjs +27 -0
- package/dist/honor-auth/honor-auth-client.mjs +51 -0
- package/dist/honor-auth/index.mjs +4 -0
- package/dist/honor-auth/token-manager.mjs +64 -0
- package/dist/hooks/index.mjs +33 -0
- package/dist/index.mjs +13 -3808
- package/dist/modules/configs/config-manager.mjs +298 -0
- package/dist/modules/configs/identity-persist.mjs +51 -0
- package/dist/modules/configs/index.mjs +2 -0
- package/dist/modules/configs/provider.mjs +136 -0
- package/dist/modules/device/auth.mjs +20 -0
- package/dist/modules/device/credential-builder.mjs +28 -0
- package/dist/modules/device/device-info.mjs +38 -0
- package/dist/modules/device/gateway-auth.mjs +16 -0
- package/dist/modules/device/helpers.mjs +13 -0
- package/dist/modules/device/identity.mjs +63 -0
- package/dist/modules/device/index.mjs +7 -0
- package/dist/modules/device/providers/index.mjs +28 -0
- package/dist/modules/device/providers/linux.mjs +33 -0
- package/dist/modules/device/providers/macos.mjs +25 -0
- package/dist/modules/device/providers/pad.mjs +68 -0
- package/dist/modules/device/providers/windows.mjs +79 -0
- package/dist/modules/device/registry.mjs +26 -0
- package/dist/modules/device-toolset/archive.mjs +39 -0
- package/dist/modules/device-toolset/artifacts.mjs +52 -0
- package/dist/modules/device-toolset/consts.mjs +5 -0
- package/dist/modules/device-toolset/copy-fallback.mjs +42 -0
- package/dist/modules/device-toolset/index.mjs +2 -0
- package/dist/modules/device-toolset/link.mjs +78 -0
- package/dist/modules/device-toolset/md5-index.mjs +74 -0
- package/dist/modules/device-toolset/normalize.mjs +38 -0
- package/dist/modules/device-toolset/persist.mjs +81 -0
- package/dist/modules/device-toolset/processor.mjs +43 -0
- package/dist/modules/device-toolset/skill-inject.mjs +51 -0
- package/dist/modules/device-toolset/skill-refresh-marker.mjs +44 -0
- package/dist/modules/login/impl.mjs +24 -0
- package/dist/modules/prompt/index.mjs +4 -0
- package/dist/runtime.mjs +23 -0
- package/dist/schemas.mjs +33 -0
- package/dist/services/connection/impl.mjs +148 -0
- package/dist/services/connection/index.mjs +1 -0
- package/dist/services/connection/status-tracker/events.mjs +6 -0
- package/dist/services/connection/status-tracker/index.mjs +4 -0
- package/dist/services/connection/status-tracker/storage.mjs +49 -0
- package/dist/services/connection/status-tracker/tracker.mjs +142 -0
- package/dist/services/connection/status-tracker/types.mjs +36 -0
- package/dist/utils/env.mjs +29 -0
- package/dist/utils/error.mjs +7 -0
- package/dist/utils/fs-safe.mjs +216 -0
- package/dist/utils/hash.mjs +10 -0
- package/dist/utils/home-dir.mjs +40 -0
- package/dist/utils/id.mjs +8 -0
- package/dist/utils/logger.mjs +15 -0
- package/dist/utils/proxy.mjs +18 -0
- package/dist/utils/version.mjs +7 -0
- package/dist/utils/ws.mjs +7 -0
- package/package.json +1 -1
- package/skills/yoyo-control/SKILL.md +58 -19
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { uuid as e } from "../utils/id.mjs";
|
|
2
|
+
import { isOKResponse as t } from "./helpers.mjs";
|
|
3
|
+
import { takeApiHost as n } from "./hosts.mjs";
|
|
4
|
+
import { HttpClient as r } from "./http-client.mjs";
|
|
5
|
+
//#region src/apis/claw-cloud.ts
|
|
6
|
+
var i = class {
|
|
7
|
+
httpClient;
|
|
8
|
+
constructor(e, t) {
|
|
9
|
+
this.httpClient = new r(e, t);
|
|
10
|
+
}
|
|
11
|
+
async registerDevice(t, n, r) {
|
|
12
|
+
let i = {
|
|
13
|
+
"x-trace-id": e(),
|
|
14
|
+
"x-jwt-token": n.token,
|
|
15
|
+
"x-device-id": t.deviceId
|
|
16
|
+
}, a = {
|
|
17
|
+
businessTag: "YOYO_CLAW",
|
|
18
|
+
role: "yoyoclaw",
|
|
19
|
+
deviceInfo: {
|
|
20
|
+
...t,
|
|
21
|
+
manufacture: t.manufacture,
|
|
22
|
+
brand: t.brand,
|
|
23
|
+
bizExtInfo: r
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
return this.httpClient.post("/v1/device/registry", {
|
|
27
|
+
headers: i,
|
|
28
|
+
body: a
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async logoutDevice(t, n) {
|
|
32
|
+
let r = {
|
|
33
|
+
"x-trace-id": e(),
|
|
34
|
+
"x-jwt-token": n.token,
|
|
35
|
+
"x-device-id": t.deviceId
|
|
36
|
+
}, i = {
|
|
37
|
+
businessTag: "YOYO_CLAW",
|
|
38
|
+
deviceInfo: {
|
|
39
|
+
...t,
|
|
40
|
+
manufacture: t.manufacture,
|
|
41
|
+
brand: t.brand
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
return this.httpClient.post("/v1/user/logout", {
|
|
45
|
+
headers: r,
|
|
46
|
+
body: i
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async exchangeToken(n, r) {
|
|
50
|
+
let i = e(), a = {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
"x-device-id": n.deviceId,
|
|
53
|
+
"x-trace-id": i
|
|
54
|
+
}, o = {};
|
|
55
|
+
"userId" in r ? a["x-agw-userId"] = r.userId : o = {
|
|
56
|
+
clientId: r.authConfig.clientId,
|
|
57
|
+
code: r.code,
|
|
58
|
+
redirectUri: "http://127.0.0.1:8081/deepLink"
|
|
59
|
+
};
|
|
60
|
+
let s = await this.httpClient.post("/v1/user/jwtToken", {
|
|
61
|
+
body: o,
|
|
62
|
+
headers: a,
|
|
63
|
+
timeout: 15e3
|
|
64
|
+
});
|
|
65
|
+
if (t(s) && s.data.data) return s.data.data;
|
|
66
|
+
throw Error(`failed to get token: ${JSON.stringify(s.data)}, traceId: ${i}`);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
function a() {
|
|
70
|
+
let e = n();
|
|
71
|
+
return new i(`https://${e.clawCloud}/aicloud/yoyo-claw-service`, e.grayTag ? { defaultHeaders: { "x-gray": e.grayTag } } : void 0);
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { i as ClawCloudClient, a as createClawCloudClient };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getYoyoEnvInfo as e } from "../runtime.mjs";
|
|
2
|
+
//#region src/apis/hosts.ts
|
|
3
|
+
function t() {
|
|
4
|
+
let t = e(), n;
|
|
5
|
+
switch (t.env) {
|
|
6
|
+
case "dev":
|
|
7
|
+
n = {
|
|
8
|
+
clawCloud: "omni-dev-drcn.hiboard.hihonorcloud.com",
|
|
9
|
+
ics: "api-agd-test-drcn.hiboard.hihonorcloud.com"
|
|
10
|
+
};
|
|
11
|
+
break;
|
|
12
|
+
case "test":
|
|
13
|
+
n = {
|
|
14
|
+
clawCloud: "omni-pre-drcn.hiboard.hihonorcloud.com",
|
|
15
|
+
ics: "api-agd-test-drcn.hiboard.hihonorcloud.com"
|
|
16
|
+
};
|
|
17
|
+
break;
|
|
18
|
+
default:
|
|
19
|
+
n = {
|
|
20
|
+
clawCloud: "yoyoclaw-drcn.hiboard.hihonorcloud.com",
|
|
21
|
+
ics: "api-prd-drcn.hiboard.hihonorcloud.com"
|
|
22
|
+
};
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
return t.grayTag && (n.grayTag = t.grayTag), n;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { t as takeApiHost };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { wrapError as e } from "../utils/error.mjs";
|
|
2
|
+
import { getProxyUrl as t, shouldUseProxy as n } from "../utils/proxy.mjs";
|
|
3
|
+
import { ProxyAgent as r, request as i } from "undici";
|
|
4
|
+
//#region src/apis/http-client.ts
|
|
5
|
+
var a = class extends Error {
|
|
6
|
+
constructor(e, t, n) {
|
|
7
|
+
super(n), this.status = e, this.data = t, this.name = "HttpError";
|
|
8
|
+
}
|
|
9
|
+
}, o = class {
|
|
10
|
+
baseUrl;
|
|
11
|
+
defaultHeaders;
|
|
12
|
+
defaultTimeout;
|
|
13
|
+
proxyAgent;
|
|
14
|
+
constructor(e, n) {
|
|
15
|
+
this.baseUrl = e.replace(/\/$/, ""), this.defaultHeaders = n?.defaultHeaders || {}, this.defaultTimeout = n?.defaultTimeout || 3e4;
|
|
16
|
+
let i = t(n?.proxy);
|
|
17
|
+
i && (this.proxyAgent = new r(i));
|
|
18
|
+
}
|
|
19
|
+
async request(r) {
|
|
20
|
+
let { method: o = "GET", headers: s = {}, body: c, timeout: l = this.defaultTimeout, query: u } = r, d = r.url;
|
|
21
|
+
if (!d.startsWith("http://") && !d.startsWith("https://") && (d = `${this.baseUrl}${d.startsWith("/") ? "" : "/"}${d}`), u && Object.keys(u).length > 0) {
|
|
22
|
+
let e = new URLSearchParams();
|
|
23
|
+
Object.entries(u).forEach(([t, n]) => {
|
|
24
|
+
n !== void 0 && e.append(t, String(n));
|
|
25
|
+
});
|
|
26
|
+
let t = e.toString();
|
|
27
|
+
t && (d += (d.includes("?") ? "&" : "?") + t);
|
|
28
|
+
}
|
|
29
|
+
let f = {
|
|
30
|
+
...this.defaultHeaders,
|
|
31
|
+
...s
|
|
32
|
+
};
|
|
33
|
+
c && !f["Content-Type"] && (f["Content-Type"] = "application/json");
|
|
34
|
+
let p;
|
|
35
|
+
c != null && (p = typeof c == "string" ? c : JSON.stringify(c));
|
|
36
|
+
try {
|
|
37
|
+
let e = n(t(), r.disableProxy) && this.proxyAgent ? this.proxyAgent : void 0, s = await i(d, {
|
|
38
|
+
method: o,
|
|
39
|
+
headers: f,
|
|
40
|
+
body: p,
|
|
41
|
+
headersTimeout: l,
|
|
42
|
+
bodyTimeout: l,
|
|
43
|
+
dispatcher: e
|
|
44
|
+
}), c = await s.body.text(), u;
|
|
45
|
+
try {
|
|
46
|
+
u = c ? JSON.parse(c) : {};
|
|
47
|
+
} catch {
|
|
48
|
+
u = c;
|
|
49
|
+
}
|
|
50
|
+
if (s.statusCode >= 200 && s.statusCode < 300) return {
|
|
51
|
+
data: u,
|
|
52
|
+
status: s.statusCode,
|
|
53
|
+
headers: s.headers
|
|
54
|
+
};
|
|
55
|
+
throw new a(s.statusCode, u, `HTTP request failed with status ${s.statusCode}`);
|
|
56
|
+
} catch (t) {
|
|
57
|
+
throw t instanceof a ? t : e(t, "HTTP request failed");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async get(e, t) {
|
|
61
|
+
return this.request({
|
|
62
|
+
...t,
|
|
63
|
+
url: e,
|
|
64
|
+
method: "GET"
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async post(e, t) {
|
|
68
|
+
return this.request({
|
|
69
|
+
...t,
|
|
70
|
+
url: e,
|
|
71
|
+
method: "POST"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async put(e, t) {
|
|
75
|
+
return this.request({
|
|
76
|
+
...t,
|
|
77
|
+
url: e,
|
|
78
|
+
method: "PUT"
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async delete(e, t) {
|
|
82
|
+
return this.request({
|
|
83
|
+
...t,
|
|
84
|
+
url: e,
|
|
85
|
+
method: "DELETE"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async patch(e, t) {
|
|
89
|
+
return this.request({
|
|
90
|
+
...t,
|
|
91
|
+
url: e,
|
|
92
|
+
method: "PATCH"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
export { o as HttpClient, a as HttpError };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useClawLogger as e } from "../utils/logger.mjs";
|
|
2
|
+
import { getConfigManager as t } from "../modules/configs/config-manager.mjs";
|
|
3
|
+
import { StatusEventType as n } from "../services/connection/status-tracker/events.mjs";
|
|
4
|
+
import r from "../gateway-client/admin-client.mjs";
|
|
5
|
+
//#region src/cloud-channel/admin-client-manager.ts
|
|
6
|
+
var i = class {
|
|
7
|
+
client = null;
|
|
8
|
+
ready = !1;
|
|
9
|
+
options;
|
|
10
|
+
pendingReadyRequests = /* @__PURE__ */ new Map();
|
|
11
|
+
requestIdCounter = 0;
|
|
12
|
+
constructor(e = {}) {
|
|
13
|
+
this.options = e;
|
|
14
|
+
}
|
|
15
|
+
init() {
|
|
16
|
+
if (this.client !== null) return;
|
|
17
|
+
let i = t().getGatewayAuthConfig(), a = typeof i?.token == "string" ? i.token : "", o = typeof i?.password == "string" ? i.password : "";
|
|
18
|
+
this.client = new r({
|
|
19
|
+
token: a,
|
|
20
|
+
password: o,
|
|
21
|
+
onAuthenticated: () => {
|
|
22
|
+
e().info("[yoyoclaw-channel] admin gateway client authenticated"), this.ready = !0, this.resolveAllPending(!0);
|
|
23
|
+
},
|
|
24
|
+
onClose: (t) => {
|
|
25
|
+
e().info(`[yoyoclaw-channel] admin gateway client closed: ${t}`), this.ready = !1;
|
|
26
|
+
},
|
|
27
|
+
onReconnectFailed: () => {
|
|
28
|
+
e().error("[yoyoclaw-channel] admin gateway client reconnect failed, notifying status tracker"), this.options.onStatusEvent?.({
|
|
29
|
+
type: n.ADMIN_CLIENT_RECONNECT_FAILED,
|
|
30
|
+
timestamp: Date.now(),
|
|
31
|
+
data: {}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
connect() {
|
|
37
|
+
this.client?.connect();
|
|
38
|
+
}
|
|
39
|
+
destroy() {
|
|
40
|
+
this.client &&= (this.client.stop(), null), this.ready = !1, this.cancelAllPending(), e().info("[yoyoclaw-channel] admin gateway client closed");
|
|
41
|
+
}
|
|
42
|
+
cancelAllPending() {
|
|
43
|
+
for (let [e, t] of this.pendingReadyRequests) clearTimeout(t.timeout), t.resolve(!1);
|
|
44
|
+
this.pendingReadyRequests.clear();
|
|
45
|
+
}
|
|
46
|
+
isReady() {
|
|
47
|
+
return this.ready;
|
|
48
|
+
}
|
|
49
|
+
getClient() {
|
|
50
|
+
return this.client;
|
|
51
|
+
}
|
|
52
|
+
canOperate() {
|
|
53
|
+
return this.client !== null && this.ready;
|
|
54
|
+
}
|
|
55
|
+
ensureConnected() {
|
|
56
|
+
return this.client === null && (e().info("[yoyoclaw-channel] admin gateway client is null, initializing"), this.init()), this.ready || (e().info("[yoyoclaw-channel] admin gateway client not ready, connecting"), this.connect()), this.canOperate();
|
|
57
|
+
}
|
|
58
|
+
waitForReady(t = 5e3) {
|
|
59
|
+
return this.canOperate() ? Promise.resolve(!0) : new Promise((n) => {
|
|
60
|
+
let r = `req_${++this.requestIdCounter}`, i = setTimeout(() => {
|
|
61
|
+
this.pendingReadyRequests.delete(r), this.pendingReadyRequests.size === 0 && e().warn("[yoyoclaw-channel] admin gateway client waitForReady timeout, all requests rejected"), n(!1);
|
|
62
|
+
}, t);
|
|
63
|
+
this.pendingReadyRequests.set(r, {
|
|
64
|
+
resolve: n,
|
|
65
|
+
timeout: i
|
|
66
|
+
}), this.ensureConnected();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
resolveAllPending(e) {
|
|
70
|
+
for (let [t, n] of this.pendingReadyRequests) clearTimeout(n.timeout), n.resolve(e);
|
|
71
|
+
this.pendingReadyRequests.clear();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
export { i as AdminClientManager };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useClawLogger as e } from "../utils/logger.mjs";
|
|
2
|
+
import { AdminClientManager as t } from "./admin-client-manager.mjs";
|
|
3
|
+
import { ClawCloudSocketClient as n } from "./client.mjs";
|
|
4
|
+
import { MessageHandler as r } from "./message-handler.mjs";
|
|
5
|
+
import { SessionManager as i } from "./session-manager.mjs";
|
|
6
|
+
//#region src/cloud-channel/channel.ts
|
|
7
|
+
var a = class {
|
|
8
|
+
cloudClient;
|
|
9
|
+
sessionManager;
|
|
10
|
+
adminClientManager;
|
|
11
|
+
messageHandler;
|
|
12
|
+
config;
|
|
13
|
+
constructor(e) {
|
|
14
|
+
this.config = e, this.sessionManager = new i(), this.adminClientManager = new t({ onStatusEvent: this.handleStatusEvent }), this.messageHandler = new r({
|
|
15
|
+
sessionManager: this.sessionManager,
|
|
16
|
+
adminClientManager: this.adminClientManager,
|
|
17
|
+
config: {
|
|
18
|
+
deviceInfo: e.deviceInfo,
|
|
19
|
+
onStatusEvent: this.handleStatusEvent,
|
|
20
|
+
onReply: (e) => this.cloudClient.send(e)
|
|
21
|
+
}
|
|
22
|
+
}), this.cloudClient = new n({
|
|
23
|
+
deviceInfo: e.deviceInfo,
|
|
24
|
+
userInfo: e.userInfo,
|
|
25
|
+
onMessage: this.messageHandler.handleCloudMessage,
|
|
26
|
+
onOpen: this.handleCloudOpen,
|
|
27
|
+
onClose: this.handleCloudClose,
|
|
28
|
+
onRemoteDeviceOffline: this.handleRemoteDeviceOffline,
|
|
29
|
+
onDeviceNotRegistered: this.handleDeviceNotRegistered,
|
|
30
|
+
onUnauthorized: this.handleUnauthorized,
|
|
31
|
+
onStatusEvent: this.handleStatusEvent
|
|
32
|
+
}), this.adminClientManager.init();
|
|
33
|
+
}
|
|
34
|
+
start() {
|
|
35
|
+
e().info("[yoyoclaw-channel] starting connection"), this.adminClientManager.ensureConnected(), this.cloudClient.connect();
|
|
36
|
+
}
|
|
37
|
+
destroy() {
|
|
38
|
+
e().info("[yoyoclaw-channel] closing connection"), this.sessionManager.closeAllNodeGatewayClients(), this.adminClientManager.destroy(), this.cloudClient.close();
|
|
39
|
+
}
|
|
40
|
+
handleCloudOpen = () => {
|
|
41
|
+
e().info("[yoyoclaw-channel] cloud connection established"), this.config.onOpen?.();
|
|
42
|
+
};
|
|
43
|
+
handleStatusEvent = (e) => {
|
|
44
|
+
this.config.onStatusEvent?.(e);
|
|
45
|
+
};
|
|
46
|
+
handleCloudClose = () => {
|
|
47
|
+
e().info("[yoyoclaw-channel] cloud connection closed"), this.config.onClose?.();
|
|
48
|
+
};
|
|
49
|
+
handleRemoteDeviceOffline = (e) => {
|
|
50
|
+
this.sessionManager.closeNodeGatewayClient(e);
|
|
51
|
+
};
|
|
52
|
+
handleDeviceNotRegistered = () => {
|
|
53
|
+
e().info("[yoyoclaw-channel] device not registered, notifying connection layer"), this.config.onDeviceNotRegistered?.();
|
|
54
|
+
};
|
|
55
|
+
handleUnauthorized = () => {
|
|
56
|
+
e().info("[yoyoclaw-channel] unauthorized connection, notifying connection layer"), this.config.onUnauthorized?.();
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
//#endregion
|
|
60
|
+
export { a as ClawChannel };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { useClawLogger as e } from "../utils/logger.mjs";
|
|
2
|
+
import { StatusEventType as t } from "../services/connection/status-tracker/events.mjs";
|
|
3
|
+
import { rawDataToString as n } from "../utils/ws.mjs";
|
|
4
|
+
import { buildCloudSocketConnectParams as r } from "./utils.mjs";
|
|
5
|
+
import i from "ws";
|
|
6
|
+
//#region src/cloud-channel/client.ts
|
|
7
|
+
var a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
|
|
8
|
+
ws = null;
|
|
9
|
+
options;
|
|
10
|
+
retryCount = 0;
|
|
11
|
+
retryTimer = null;
|
|
12
|
+
isManualClose = !1;
|
|
13
|
+
isRetryPaused = !1;
|
|
14
|
+
pingTimer = null;
|
|
15
|
+
currentTraceId = "";
|
|
16
|
+
constructor(e) {
|
|
17
|
+
this.options = e;
|
|
18
|
+
}
|
|
19
|
+
connect(e = !1) {
|
|
20
|
+
if (this.ws?.readyState === i.OPEN) return;
|
|
21
|
+
this.clearRetryTimer();
|
|
22
|
+
let { deviceInfo: n, userInfo: a } = this.options, { url: o, traceId: s, wsOptions: c } = r(n, a);
|
|
23
|
+
this.currentTraceId = s, this.options.onStatusEvent?.({
|
|
24
|
+
type: t.CLOUD_SOCKET_CONNECTING,
|
|
25
|
+
timestamp: Date.now(),
|
|
26
|
+
data: { url: o }
|
|
27
|
+
}), this.ws = new i(o, c), this.ws.on("open", this.handleOpen.bind(this, o, e)), this.ws.on("message", this.onMessage), this.ws.on("pong", this.handlePong), this.ws.on("close", this.handleClose), this.ws.on("error", this.handleError);
|
|
28
|
+
}
|
|
29
|
+
handleOpen = (n, r) => {
|
|
30
|
+
let i = r ? "reconnected" : "connected";
|
|
31
|
+
e().info(`[claw-cloud-socket][${this.currentTraceId}] ${i} to ${n.slice(0, 15)}`), this.clearRetryTimer(), this.isManualClose = !1, this.retryCount = 0, this.isRetryPaused = !1, this.startPingTimer(), this.options.onStatusEvent?.({
|
|
32
|
+
type: t.CLOUD_SOCKET_CONNECTED,
|
|
33
|
+
timestamp: Date.now(),
|
|
34
|
+
data: {
|
|
35
|
+
url: n,
|
|
36
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
37
|
+
}
|
|
38
|
+
}), this.options.onOpen?.();
|
|
39
|
+
};
|
|
40
|
+
handlePong = () => {
|
|
41
|
+
e().debug?.("[claw-cloud-socket] received pong from server");
|
|
42
|
+
};
|
|
43
|
+
handleClose = (n, r) => {
|
|
44
|
+
let i = r.toString();
|
|
45
|
+
e().info(`[claw-cloud-socket][${this.currentTraceId}] connection closed: ${n} - ${i}`), this.options.onStatusEvent?.({
|
|
46
|
+
type: t.CLOUD_SOCKET_DISCONNECTED,
|
|
47
|
+
timestamp: Date.now(),
|
|
48
|
+
data: {
|
|
49
|
+
reason: i,
|
|
50
|
+
code: n,
|
|
51
|
+
disconnectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
52
|
+
}
|
|
53
|
+
}), this.isManualClose ? (this.stopPingTimer(), this.options.onClose?.()) : this.scheduleReconnect();
|
|
54
|
+
};
|
|
55
|
+
handleError = (n) => {
|
|
56
|
+
e().error(`[claw-cloud-socket][${this.currentTraceId}] connect errorred: ${n.message}`), n.message.includes("401") && (e().warn(`[claw-cloud-socket][${this.currentTraceId}] unauthorized connection (401), pausing retry and notifying client`), this.pauseRetry(), this.options.onUnauthorized?.()), this.options.onStatusEvent?.({
|
|
57
|
+
type: t.CLOUD_SOCKET_ERROR,
|
|
58
|
+
timestamp: Date.now(),
|
|
59
|
+
data: {
|
|
60
|
+
error: n.message,
|
|
61
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
send(t) {
|
|
66
|
+
if (!this.ws || this.ws.readyState !== i.OPEN) return e().error("[claw-cloud-socket] cannot send message: connection not open"), !1;
|
|
67
|
+
try {
|
|
68
|
+
let e = JSON.stringify(t);
|
|
69
|
+
return this.ws.send(e), !0;
|
|
70
|
+
} catch (n) {
|
|
71
|
+
return e().error(`[claw-cloud-socket] failed to send message to cloud session ${t.targetDeviceId},
|
|
72
|
+
${n instanceof Error ? n.message : String(n)}`), !1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
close() {
|
|
76
|
+
this.isManualClose = !0, this.reset(), this.ws &&= (this.ws.close(), null);
|
|
77
|
+
}
|
|
78
|
+
reset() {
|
|
79
|
+
this.clearRetryTimer(), this.stopPingTimer();
|
|
80
|
+
}
|
|
81
|
+
onMessage = async (t) => {
|
|
82
|
+
try {
|
|
83
|
+
let r = n(t), i = JSON.parse(r);
|
|
84
|
+
if (e().debug?.(`[yoyoclaw-channel] received cloud message from session ${i.wsOutputEvent?.sourceDeviceId}, deviceId ${i.wsOutputEvent?.sourceDeviceInfo?.deviceId || "nil"}, ${r.slice(0, 3e3)}`), i.code === "YOYO_CLAW_100000") {
|
|
85
|
+
if (i.wsOutputEvent) {
|
|
86
|
+
i.sessionInfo && (i.wsOutputEvent.sessionInfo = i.sessionInfo), this.options.onMessage?.(i.wsOutputEvent);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
} else if (i.code === "YOYO_CLAW_100002") {
|
|
90
|
+
if (i.extData?.offlineSocketId) {
|
|
91
|
+
e().info(`[claw-cloud-socket] remote device offline, session: ${i.extData.offlineSocketId}`), this.options.onRemoteDeviceOffline?.(i.extData.offlineSocketId);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
} else if (i.code === "YOYO_CLAW_100001") {
|
|
95
|
+
this.pauseRetry(), e().info("[claw-cloud-socket] device not registered, closing connection"), this.options.onDeviceNotRegistered?.();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
e().warn(`[claw-cloud-socket] unexpected message: ${i.cnMessage}, code: ${i.code}`);
|
|
99
|
+
} catch (t) {
|
|
100
|
+
e().error(`[claw-cloud-socket] failed to parse message: ${t instanceof Error ? t.message : String(t)}`);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
scheduleReconnect() {
|
|
104
|
+
if (this.isRetryPaused) {
|
|
105
|
+
e().info("[claw-cloud-socket] retry paused, skipping reconnect"), this.reset(), this.options.onClose?.();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.clearRetryTimer();
|
|
109
|
+
let n = this.calculateRetryDelay();
|
|
110
|
+
this.retryCount = Math.min(this.retryCount + 1, l), e().info(`[claw-cloud-socket] scheduling reconnect attempt ${this.retryCount} in ${n}ms`), this.options.onStatusEvent?.({
|
|
111
|
+
type: t.CLOUD_SOCKET_RETRY,
|
|
112
|
+
timestamp: Date.now(),
|
|
113
|
+
data: {
|
|
114
|
+
retryCount: this.retryCount,
|
|
115
|
+
delay: n
|
|
116
|
+
}
|
|
117
|
+
}), this.retryTimer = setTimeout(() => {
|
|
118
|
+
this.connect(!0);
|
|
119
|
+
}, n);
|
|
120
|
+
}
|
|
121
|
+
clearRetryTimer() {
|
|
122
|
+
this.retryTimer &&= (clearTimeout(this.retryTimer), null);
|
|
123
|
+
}
|
|
124
|
+
calculateRetryDelay() {
|
|
125
|
+
let e = s * c ** +Math.min(this.retryCount, 2);
|
|
126
|
+
return Math.min(e, o);
|
|
127
|
+
}
|
|
128
|
+
startPingTimer() {
|
|
129
|
+
this.pingTimer = setInterval(() => {
|
|
130
|
+
if (!this.ws || this.ws.readyState !== i.OPEN) {
|
|
131
|
+
this.stopPingTimer();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
this.ws.ping(), e().debug?.("[claw-cloud-socket] sent ping to server");
|
|
135
|
+
let { deviceInfo: t } = this.options, n = {
|
|
136
|
+
msgType: "pingMessage",
|
|
137
|
+
sourceRole: "yoyoclaw",
|
|
138
|
+
sourceDeviceId: t.deviceId,
|
|
139
|
+
targetRole: "node",
|
|
140
|
+
port: t.port
|
|
141
|
+
};
|
|
142
|
+
try {
|
|
143
|
+
this.ws.send(JSON.stringify(n)), e().debug?.("[claw-cloud-socket] sent pingMessage to server");
|
|
144
|
+
} catch {
|
|
145
|
+
e().error("[claw-cloud-socket] failed to send pingMessage");
|
|
146
|
+
}
|
|
147
|
+
}, a);
|
|
148
|
+
}
|
|
149
|
+
stopPingTimer() {
|
|
150
|
+
this.pingTimer &&= (clearInterval(this.pingTimer), null);
|
|
151
|
+
}
|
|
152
|
+
pauseRetry() {
|
|
153
|
+
this.isRetryPaused || (this.isRetryPaused = !0, this.clearRetryTimer(), e().info("[claw-cloud-socket] retry paused"));
|
|
154
|
+
}
|
|
155
|
+
resumeRetry() {
|
|
156
|
+
this.isRetryPaused && (this.isRetryPaused = !1, e().info("[claw-cloud-socket] retry resumed"), (!this.ws || this.ws.readyState !== i.OPEN) && (this.retryCount = 0, this.connect(!0)));
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
//#endregion
|
|
160
|
+
export { u as ClawCloudSocketClient };
|