@honor-claw/yoyo 1.5.1 → 1.6.0-beta.1
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/cloud-channel/authorization-refresh-manager.mjs +89 -0
- package/dist/cloud-channel/channel.mjs +20 -10
- package/dist/cloud-channel/client.mjs +27 -23
- package/dist/cloud-channel/message-handler.mjs +5 -2
- package/dist/cloud-channel/types.mjs +13 -0
- package/dist/commands/env/impl.mjs +1 -1
- package/dist/commands/login/impl.mjs +2 -3
- package/dist/commands/logout/impl.mjs +1 -1
- package/dist/commands/status/index.mjs +1 -1
- package/dist/honor-auth/browser.mjs +4 -1
- package/dist/honor-auth/token-manager.mjs +40 -36
- package/dist/modules/configs/identity-persist.mjs +52 -30
- package/dist/services/connection/impl.mjs +1 -1
- package/package.json +1 -2
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useClawLogger as e } from "../utils/logger.mjs";
|
|
2
|
+
import { getConfigManager as t } from "../modules/configs/config-manager.mjs";
|
|
3
|
+
import { cacheAuthorizationToken as n, getPersistedIdentity as r, parseAuthorizationExpireMs as i } from "../modules/configs/identity-persist.mjs";
|
|
4
|
+
import "../modules/configs/index.mjs";
|
|
5
|
+
//#region src/cloud-channel/authorization-refresh-manager.ts
|
|
6
|
+
var a = "[yoyoclaw-auth-refresh]", o = .3, s = .1, c = 1e3, l = 6e4, u = class {
|
|
7
|
+
timer = null;
|
|
8
|
+
config;
|
|
9
|
+
scheduleVersion = 0;
|
|
10
|
+
destroyed = !1;
|
|
11
|
+
pausedForExpiredToken = !1;
|
|
12
|
+
constructor(e) {
|
|
13
|
+
this.config = e;
|
|
14
|
+
}
|
|
15
|
+
async schedule() {
|
|
16
|
+
if (this.destroyed || this.pausedForExpiredToken) return;
|
|
17
|
+
let t = ++this.scheduleVersion;
|
|
18
|
+
this.clearTimer();
|
|
19
|
+
let n = await this.loadRefreshState();
|
|
20
|
+
if (this.destroyed || t !== this.scheduleVersion || !n) return;
|
|
21
|
+
let r = Date.now(), i = n.refreshImmediately ? r : n.receivedAtMs + n.expireMs * o, c = n.receivedAtMs + n.expireMs;
|
|
22
|
+
if (r < i) {
|
|
23
|
+
this.scheduleTimer(i - r), e().debug?.(`${a} scheduled refresh, receivedAtMs: ${n.receivedAtMs}, refreshAtMs: ${Math.floor(i)}, expireAtMs: ${Math.floor(c)}`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (r >= c) {
|
|
27
|
+
e().warn(`${a} token is expired, skip refresh, receivedAtMs: ${n.receivedAtMs}, expireAtMs: ${Math.floor(c)}`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this.sendUpdateAuthorizationRequest(), this.scheduleTimer(n.expireMs * s);
|
|
31
|
+
}
|
|
32
|
+
async handleAuthorizationUpdate(t) {
|
|
33
|
+
let r = t.bizExtInfo, i = r?.jwtToken;
|
|
34
|
+
if (typeof i != "string" || !i.trim()) {
|
|
35
|
+
e().warn(`${a} updateAuthorization missing valid jwtToken`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
let o = Date.now(), s = (await n(i.trim(), r?.expire, o))?.authorizationCache?.expireMs ?? 0;
|
|
40
|
+
e().info(`${a} cached new token, expireMs: ${s}, receivedAtMs: ${o}, sourceDeviceId: ${t.sourceDeviceId || "unknown"}, sourceRole: ${t.sourceRole || "unknown"}`), this.pausedForExpiredToken = !1, await this.schedule();
|
|
41
|
+
} catch (t) {
|
|
42
|
+
e().error(`${a} failed to cache new token: ${String(t)}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
destroy() {
|
|
46
|
+
this.destroyed = !0, this.scheduleVersion++, this.clearTimer();
|
|
47
|
+
}
|
|
48
|
+
pauseForExpiredToken() {
|
|
49
|
+
this.destroyed || this.pausedForExpiredToken || !this.timer || (this.pausedForExpiredToken = !0, this.scheduleVersion++, this.clearTimer(), e().warn(`${a} paused refresh because cloud reported current token is expired`));
|
|
50
|
+
}
|
|
51
|
+
async loadRefreshState() {
|
|
52
|
+
let o = t().getUserConfig()?.token;
|
|
53
|
+
if (!o) return e().debug?.(`${a} skip refresh, no current token`), null;
|
|
54
|
+
let s = (await r())?.authorizationCache, c = i(s?.expireMs), u = s?.receivedAtMs ?? 0;
|
|
55
|
+
if (!c || !Number.isFinite(u) || u <= 0) {
|
|
56
|
+
let t = Date.now();
|
|
57
|
+
return await n(o, l, t), e().warn(`${a} missing cached authorization timing, persisted legacy token timing and refreshing immediately`), {
|
|
58
|
+
receivedAtMs: t,
|
|
59
|
+
expireMs: l,
|
|
60
|
+
refreshImmediately: !0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
receivedAtMs: u,
|
|
65
|
+
expireMs: c
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
sendUpdateAuthorizationRequest() {
|
|
69
|
+
let { deviceInfo: t } = this.config, n = {
|
|
70
|
+
msgType: "updateAuthorization",
|
|
71
|
+
sourceRole: "yoyoclaw",
|
|
72
|
+
sourceDeviceId: t.deviceId,
|
|
73
|
+
targetRole: "yoyoclawservice",
|
|
74
|
+
port: t.port
|
|
75
|
+
};
|
|
76
|
+
this.config.sendMessage(n) ? e().info(`${a} sent updateAuthorization request`) : e().warn(`${a} failed to send updateAuthorization request`);
|
|
77
|
+
}
|
|
78
|
+
scheduleTimer(e) {
|
|
79
|
+
let t = Math.max(c, Math.floor(e));
|
|
80
|
+
this.timer = setTimeout(() => {
|
|
81
|
+
this.schedule();
|
|
82
|
+
}, t);
|
|
83
|
+
}
|
|
84
|
+
clearTimer() {
|
|
85
|
+
this.timer &&= (clearTimeout(this.timer), null);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
//#endregion
|
|
89
|
+
export { u as AuthorizationRefreshManager };
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import { useClawLogger as e } from "../utils/logger.mjs";
|
|
2
2
|
import { AdminClientManager as t } from "./admin-client-manager.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { AuthorizationRefreshManager as n } from "./authorization-refresh-manager.mjs";
|
|
4
|
+
import { ClawCloudSocketClient as r } from "./client.mjs";
|
|
5
|
+
import { MessageHandler as i } from "./message-handler.mjs";
|
|
6
|
+
import { SessionManager as a } from "./session-manager.mjs";
|
|
6
7
|
//#region src/cloud-channel/channel.ts
|
|
7
|
-
var
|
|
8
|
+
var o = class {
|
|
8
9
|
cloudClient;
|
|
9
10
|
sessionManager;
|
|
10
11
|
adminClientManager;
|
|
12
|
+
authorizationRefreshManager;
|
|
11
13
|
messageHandler;
|
|
12
14
|
config;
|
|
13
15
|
constructor(e) {
|
|
14
|
-
this.config = e, this.sessionManager = new
|
|
16
|
+
this.config = e, this.sessionManager = new a(), this.adminClientManager = new t({ onStatusEvent: this.handleStatusEvent }), this.authorizationRefreshManager = new n({
|
|
17
|
+
deviceInfo: e.deviceInfo,
|
|
18
|
+
sendMessage: (e) => this.cloudClient.send(e)
|
|
19
|
+
}), this.messageHandler = new i({
|
|
15
20
|
sessionManager: this.sessionManager,
|
|
16
21
|
adminClientManager: this.adminClientManager,
|
|
17
22
|
config: {
|
|
18
23
|
deviceInfo: e.deviceInfo,
|
|
19
24
|
onStatusEvent: this.handleStatusEvent,
|
|
20
|
-
onReply: (e) => this.cloudClient.send(e)
|
|
25
|
+
onReply: (e) => this.cloudClient.send(e),
|
|
26
|
+
onAuthorizationUpdate: (e) => this.authorizationRefreshManager.handleAuthorizationUpdate(e)
|
|
21
27
|
}
|
|
22
|
-
}), this.cloudClient = new
|
|
28
|
+
}), this.cloudClient = new r({
|
|
23
29
|
deviceInfo: e.deviceInfo,
|
|
24
30
|
userInfo: e.userInfo,
|
|
25
31
|
onMessage: this.messageHandler.handleCloudMessage,
|
|
@@ -28,6 +34,7 @@ var a = class {
|
|
|
28
34
|
onRemoteDeviceOffline: this.handleRemoteDeviceOffline,
|
|
29
35
|
onDeviceNotRegistered: this.handleDeviceNotRegistered,
|
|
30
36
|
onUnauthorized: this.handleUnauthorized,
|
|
37
|
+
onCloudTokenExpired: this.handleCloudTokenExpired,
|
|
31
38
|
onStatusEvent: this.handleStatusEvent
|
|
32
39
|
}), this.adminClientManager.init();
|
|
33
40
|
}
|
|
@@ -35,10 +42,10 @@ var a = class {
|
|
|
35
42
|
e().info("[yoyoclaw-channel] starting connection"), this.adminClientManager.ensureConnected(), this.cloudClient.connect();
|
|
36
43
|
}
|
|
37
44
|
destroy() {
|
|
38
|
-
e().info("[yoyoclaw-channel] closing connection"), this.sessionManager.closeAllNodeGatewayClients(), this.adminClientManager.destroy(), this.cloudClient.close();
|
|
45
|
+
e().info("[yoyoclaw-channel] closing connection"), this.authorizationRefreshManager.destroy(), this.sessionManager.closeAllNodeGatewayClients(), this.adminClientManager.destroy(), this.cloudClient.close();
|
|
39
46
|
}
|
|
40
47
|
handleCloudOpen = () => {
|
|
41
|
-
e().info("[yoyoclaw-channel] cloud connection established"), this.config.onOpen?.();
|
|
48
|
+
e().info("[yoyoclaw-channel] cloud connection established"), this.authorizationRefreshManager.schedule(), this.config.onOpen?.();
|
|
42
49
|
};
|
|
43
50
|
handleStatusEvent = (e) => {
|
|
44
51
|
this.config.onStatusEvent?.(e);
|
|
@@ -55,6 +62,9 @@ var a = class {
|
|
|
55
62
|
handleUnauthorized = () => {
|
|
56
63
|
e().info("[yoyoclaw-channel] unauthorized connection, notifying connection layer"), this.config.onUnauthorized?.();
|
|
57
64
|
};
|
|
65
|
+
handleCloudTokenExpired = () => {
|
|
66
|
+
this.authorizationRefreshManager.pauseForExpiredToken();
|
|
67
|
+
};
|
|
58
68
|
};
|
|
59
69
|
//#endregion
|
|
60
|
-
export {
|
|
70
|
+
export { o as ClawChannel };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useClawLogger as e } from "../utils/logger.mjs";
|
|
2
2
|
import { StatusEventType as t } from "../services/connection/status-tracker/events.mjs";
|
|
3
3
|
import { rawDataToString as n } from "../utils/ws.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import i from "
|
|
4
|
+
import { YOYO_CLAW_SERVICE_CODES as r } from "./types.mjs";
|
|
5
|
+
import { buildCloudSocketConnectParams as i } from "./utils.mjs";
|
|
6
|
+
import a from "ws";
|
|
6
7
|
//#region src/cloud-channel/client.ts
|
|
7
|
-
var
|
|
8
|
+
var o = 3e4, s = 4e3, c = 1e3, l = 2, u = 999, d = class {
|
|
8
9
|
ws = null;
|
|
9
10
|
options;
|
|
10
11
|
retryCount = 0;
|
|
@@ -17,14 +18,14 @@ var a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
|
|
|
17
18
|
this.options = e;
|
|
18
19
|
}
|
|
19
20
|
connect(e = !1) {
|
|
20
|
-
if (this.ws?.readyState ===
|
|
21
|
+
if (this.ws?.readyState === a.OPEN) return;
|
|
21
22
|
this.clearRetryTimer();
|
|
22
|
-
let { deviceInfo: n, userInfo:
|
|
23
|
+
let { deviceInfo: n, userInfo: r } = this.options, { url: o, traceId: s, wsOptions: c } = i(n, r);
|
|
23
24
|
this.currentTraceId = s, this.options.onStatusEvent?.({
|
|
24
25
|
type: t.CLOUD_SOCKET_CONNECTING,
|
|
25
26
|
timestamp: Date.now(),
|
|
26
27
|
data: { url: o }
|
|
27
|
-
}), this.ws = new
|
|
28
|
+
}), this.ws = new a(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
|
}
|
|
29
30
|
handleOpen = (n, r) => {
|
|
30
31
|
let i = r ? "reconnected" : "connected";
|
|
@@ -63,7 +64,7 @@ var a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
|
|
|
63
64
|
});
|
|
64
65
|
};
|
|
65
66
|
send(t) {
|
|
66
|
-
if (!this.ws || this.ws.readyState !==
|
|
67
|
+
if (!this.ws || this.ws.readyState !== a.OPEN) return e().error("[claw-cloud-socket] cannot send message: connection not open"), !1;
|
|
67
68
|
try {
|
|
68
69
|
let e = JSON.stringify(t);
|
|
69
70
|
return this.ws.send(e), !0;
|
|
@@ -80,22 +81,25 @@ var a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
|
|
|
80
81
|
}
|
|
81
82
|
onMessage = async (t) => {
|
|
82
83
|
try {
|
|
83
|
-
let
|
|
84
|
-
if (e().debug?.(`[
|
|
85
|
-
if (
|
|
86
|
-
|
|
84
|
+
let i = n(t), a = JSON.parse(i), o = a.wsOutputEvent;
|
|
85
|
+
if (e().debug?.(`[claw-cloud-socket] received cloud socket message, code: ${a.code}, msgType: ${o?.msgType || "none"}, sourceRole: ${o?.sourceRole || "cloud"}, sourceSession: ${o?.sourceDeviceId || "none"}, sourceDevice: ${o?.sourceDeviceInfo?.deviceId || "none"}, ${i.slice(0, 3e3)}`), a.code === r.SUCCESS) {
|
|
86
|
+
if (a.wsOutputEvent) {
|
|
87
|
+
a.sessionInfo && (a.wsOutputEvent.sessionInfo = a.sessionInfo), this.options.onMessage?.(a.wsOutputEvent);
|
|
87
88
|
return;
|
|
88
89
|
}
|
|
89
|
-
} else if (
|
|
90
|
-
if (
|
|
91
|
-
e().info(`[claw-cloud-socket] remote device offline, session: ${
|
|
90
|
+
} else if (a.code === r.TARGET_DEVICE_OFFLINE) {
|
|
91
|
+
if (a.extData?.offlineSocketId) {
|
|
92
|
+
e().info(`[claw-cloud-socket] remote device offline, session: ${a.extData.offlineSocketId}`), this.options.onRemoteDeviceOffline?.(a.extData.offlineSocketId);
|
|
92
93
|
return;
|
|
93
94
|
}
|
|
94
|
-
} else if (
|
|
95
|
+
} else if (a.code === r.DEVICE_NOT_REGISTERED) {
|
|
95
96
|
this.pauseRetry(), e().info("[claw-cloud-socket] device not registered, closing connection"), this.options.onDeviceNotRegistered?.();
|
|
96
97
|
return;
|
|
98
|
+
} else if (a.code === r.TOKEN_EXPIRED) {
|
|
99
|
+
e().warn(`[claw-cloud-socket] token expired when sending message to cloud, waiting for lazy refresh: ${a.cnMessage}, code: ${a.code}`), this.options.onCloudTokenExpired?.();
|
|
100
|
+
return;
|
|
97
101
|
}
|
|
98
|
-
e().warn(`[claw-cloud-socket] unexpected message: ${
|
|
102
|
+
e().warn(`[claw-cloud-socket] unexpected message: ${a.cnMessage}, code: ${a.code}`);
|
|
99
103
|
} catch (t) {
|
|
100
104
|
e().error(`[claw-cloud-socket] failed to parse message: ${t instanceof Error ? t.message : String(t)}`);
|
|
101
105
|
}
|
|
@@ -107,7 +111,7 @@ var a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
|
|
|
107
111
|
}
|
|
108
112
|
this.clearRetryTimer();
|
|
109
113
|
let n = this.calculateRetryDelay();
|
|
110
|
-
this.retryCount = Math.min(this.retryCount + 1,
|
|
114
|
+
this.retryCount = Math.min(this.retryCount + 1, u), e().info(`[claw-cloud-socket] scheduling reconnect attempt ${this.retryCount} in ${n}ms`), this.options.onStatusEvent?.({
|
|
111
115
|
type: t.CLOUD_SOCKET_RETRY,
|
|
112
116
|
timestamp: Date.now(),
|
|
113
117
|
data: {
|
|
@@ -122,12 +126,12 @@ var a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
|
|
|
122
126
|
this.retryTimer &&= (clearTimeout(this.retryTimer), null);
|
|
123
127
|
}
|
|
124
128
|
calculateRetryDelay() {
|
|
125
|
-
let e =
|
|
126
|
-
return Math.min(e,
|
|
129
|
+
let e = c * l ** +Math.min(this.retryCount, 2);
|
|
130
|
+
return Math.min(e, s);
|
|
127
131
|
}
|
|
128
132
|
startPingTimer() {
|
|
129
133
|
this.pingTimer = setInterval(() => {
|
|
130
|
-
if (!this.ws || this.ws.readyState !==
|
|
134
|
+
if (!this.ws || this.ws.readyState !== a.OPEN) {
|
|
131
135
|
this.stopPingTimer();
|
|
132
136
|
return;
|
|
133
137
|
}
|
|
@@ -144,7 +148,7 @@ var a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
|
|
|
144
148
|
} catch {
|
|
145
149
|
e().error("[claw-cloud-socket] failed to send pingMessage");
|
|
146
150
|
}
|
|
147
|
-
},
|
|
151
|
+
}, o);
|
|
148
152
|
}
|
|
149
153
|
stopPingTimer() {
|
|
150
154
|
this.pingTimer &&= (clearInterval(this.pingTimer), null);
|
|
@@ -153,8 +157,8 @@ var a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
|
|
|
153
157
|
this.isRetryPaused || (this.isRetryPaused = !0, this.clearRetryTimer(), e().info("[claw-cloud-socket] retry paused"));
|
|
154
158
|
}
|
|
155
159
|
resumeRetry() {
|
|
156
|
-
this.isRetryPaused && (this.isRetryPaused = !1, e().info("[claw-cloud-socket] retry resumed"), (!this.ws || this.ws.readyState !==
|
|
160
|
+
this.isRetryPaused && (this.isRetryPaused = !1, e().info("[claw-cloud-socket] retry resumed"), (!this.ws || this.ws.readyState !== a.OPEN) && (this.retryCount = 0, this.connect(!0)));
|
|
157
161
|
}
|
|
158
162
|
};
|
|
159
163
|
//#endregion
|
|
160
|
-
export {
|
|
164
|
+
export { d as ClawCloudSocketClient };
|
|
@@ -37,6 +37,9 @@ var r = { style: "SOUL.md" }, i = "[yoyoclaw-channel]", a = class {
|
|
|
37
37
|
case "fetchContexts":
|
|
38
38
|
this.handleContextRequest(e);
|
|
39
39
|
break;
|
|
40
|
+
case "updateAuthorization":
|
|
41
|
+
this.config.onAuthorizationUpdate?.(e);
|
|
42
|
+
break;
|
|
40
43
|
}
|
|
41
44
|
};
|
|
42
45
|
handlePairMessage(n) {
|
|
@@ -275,7 +278,7 @@ var r = { style: "SOUL.md" }, i = "[yoyoclaw-channel]", a = class {
|
|
|
275
278
|
ok: !1,
|
|
276
279
|
error: "Admin gateway client not ready"
|
|
277
280
|
};
|
|
278
|
-
let
|
|
281
|
+
let n = typeof t?.content == "string" ? t.content : "";
|
|
279
282
|
if (!n) return {
|
|
280
283
|
ok: !1,
|
|
281
284
|
error: "Missing content in context payload"
|
|
@@ -294,7 +297,7 @@ var r = { style: "SOUL.md" }, i = "[yoyoclaw-channel]", a = class {
|
|
|
294
297
|
ok: !1,
|
|
295
298
|
error: "Admin gateway client not ready"
|
|
296
299
|
};
|
|
297
|
-
let
|
|
300
|
+
let r = typeof n?.provider == "string" ? n.provider : "", i = typeof n?.id == "string" ? n.id : "";
|
|
298
301
|
if (!r || !i) return {
|
|
299
302
|
ok: !1,
|
|
300
303
|
error: "Missing provider or id in context payload"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "../services/connection/status-tracker/events.mjs";
|
|
2
|
+
//#region src/cloud-channel/types.ts
|
|
3
|
+
var e = {
|
|
4
|
+
SUCCESS: "YOYO_CLAW_100000",
|
|
5
|
+
DEVICE_NOT_REGISTERED: "YOYO_CLAW_100001",
|
|
6
|
+
TARGET_DEVICE_OFFLINE: "YOYO_CLAW_100002",
|
|
7
|
+
NOT_PAIRED: "YOYO_CLAW_100003",
|
|
8
|
+
INVALID_PARAMS: "YOYO_CLAW_100004",
|
|
9
|
+
INTERNAL_ERROR: "YOYO_CLAW_100005",
|
|
10
|
+
TOKEN_EXPIRED: "YOYO_CLAW_100012"
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { e as YOYO_CLAW_SERVICE_CODES };
|
|
@@ -6,7 +6,7 @@ var n = {
|
|
|
6
6
|
manual: "manual config"
|
|
7
7
|
};
|
|
8
8
|
function r(r, i) {
|
|
9
|
-
|
|
9
|
+
i.command("env").description("Manage runtime environment (dev/test/production)").option("--set <env>", "Set environment: dev, test or production").option("--gray <tag>", "Set gray header").option("--reset", "Reset env source to environment variable").action(async (r) => {
|
|
10
10
|
let { set: i, gray: a, reset: o } = r, s = e();
|
|
11
11
|
try {
|
|
12
12
|
let e = t();
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { performLogin as e } from "../../modules/login/impl.mjs";
|
|
2
2
|
//#region src/commands/login/impl.ts
|
|
3
3
|
function t(t, n) {
|
|
4
|
-
|
|
5
|
-
return r = r.option("-u, --uid <userId>", "user ID for direct login").option("--token <token>", "token for direct login").action(async (n) => {
|
|
4
|
+
n.command("login").description("login to yoyoclaw and register devices").option("-u, --uid <userId>", "user ID for direct login").option("--token <token>", "token for direct login").action(async (n) => {
|
|
6
5
|
let { uid: r, token: i } = n;
|
|
7
6
|
t.logger.debug?.("honor login CLI command called"), await e({
|
|
8
7
|
userId: r,
|
|
9
8
|
token: i
|
|
10
9
|
});
|
|
11
|
-
})
|
|
10
|
+
});
|
|
12
11
|
}
|
|
13
12
|
//#endregion
|
|
14
13
|
export { t as registerLoginCommand };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { performLogout as e } from "../../honor-auth/cloud.mjs";
|
|
2
2
|
//#region src/commands/logout/impl.ts
|
|
3
3
|
function t(t, n) {
|
|
4
|
-
|
|
4
|
+
n.command("logout").description("Logout and clear user configuration").action(async () => {
|
|
5
5
|
t.logger.info("logout CLI command called");
|
|
6
6
|
try {
|
|
7
7
|
await e(), console.log("✅ Logout successful, gateway will automatically restart to handle new configuration");
|
|
@@ -5,7 +5,7 @@ import { StatusStorage as r } from "../../services/connection/status-tracker/sto
|
|
|
5
5
|
import "../../services/connection/status-tracker/index.mjs";
|
|
6
6
|
//#region src/commands/status/index.ts
|
|
7
7
|
function i(i, o) {
|
|
8
|
-
|
|
8
|
+
o.command("status").description("Show YOYOClaw connection status").action(async () => {
|
|
9
9
|
if (i.logger.debug?.("YOYOClaw status CLI command called"), !await n()) {
|
|
10
10
|
console.log("❌ You need to login first. Please run: openclaw honor login");
|
|
11
11
|
return;
|
|
@@ -38,7 +38,10 @@ async function c(c, l) {
|
|
|
38
38
|
authConfig: u
|
|
39
39
|
});
|
|
40
40
|
if (!e?.jwtToken) throw Error("failed to get jwt token");
|
|
41
|
-
return await o({
|
|
41
|
+
return await o({
|
|
42
|
+
jwtToken: e.jwtToken,
|
|
43
|
+
expire: e.expire
|
|
44
|
+
}, u.saveToFile !== !1), { token: e.jwtToken };
|
|
42
45
|
} catch (t) {
|
|
43
46
|
throw e(t, "get token failed");
|
|
44
47
|
}
|
|
@@ -3,62 +3,66 @@ import { wrapError as t } from "../utils/error.mjs";
|
|
|
3
3
|
import { useClawLogger as n } from "../utils/logger.mjs";
|
|
4
4
|
import { getConfigManager as r } from "../modules/configs/config-manager.mjs";
|
|
5
5
|
import { createClawCloudClient as i } from "../apis/claw-cloud.mjs";
|
|
6
|
+
import { cacheAuthorizationToken as a, clearCachedAuthorizationToken as o, getPersistedIdentity as s } from "../modules/configs/identity-persist.mjs";
|
|
6
7
|
import "../modules/configs/index.mjs";
|
|
7
|
-
import { getDeviceInfo as
|
|
8
|
+
import { getDeviceInfo as c } from "../modules/device/device-info.mjs";
|
|
8
9
|
import "../modules/device/index.mjs";
|
|
9
10
|
//#region src/honor-auth/token-manager.ts
|
|
10
|
-
async function
|
|
11
|
+
async function l(e, a = !0) {
|
|
11
12
|
try {
|
|
12
|
-
let t = r(), s = e.
|
|
13
|
-
if (!
|
|
14
|
-
let t = e.deviceInfo || await
|
|
15
|
-
|
|
13
|
+
let t = r(), o = e.jwtToken, s = e.expire;
|
|
14
|
+
if (!o && e.userId) {
|
|
15
|
+
let t = e.deviceInfo || await c(), n = await i().exchangeToken(t, { userId: e.userId });
|
|
16
|
+
o = n.jwtToken, s = n.expire;
|
|
16
17
|
}
|
|
17
|
-
if (!
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
userId: void 0,
|
|
23
|
-
expired: c
|
|
24
|
-
}), { token: s };
|
|
18
|
+
if (!o) throw Error("no token available");
|
|
19
|
+
return a ? (await t.updateUserConfig({
|
|
20
|
+
token: o,
|
|
21
|
+
userId: void 0
|
|
22
|
+
}), await p(o, s), { token: o }) : (n().info("💾 token got, but not to save"), { token: o });
|
|
25
23
|
} catch (e) {
|
|
26
24
|
throw t(e, "保存Token失败");
|
|
27
25
|
}
|
|
28
26
|
}
|
|
29
|
-
async function
|
|
30
|
-
let t = await
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
userName: void 0,
|
|
37
|
-
expired: e
|
|
38
|
-
}), { token: n.jwtToken };
|
|
39
|
-
}
|
|
40
|
-
return await r().clearUserConfig(), null;
|
|
27
|
+
async function u(e) {
|
|
28
|
+
let t = await c(), n = await i().exchangeToken(t, { userId: e });
|
|
29
|
+
return n.jwtToken ? (await r().updateUserConfig({
|
|
30
|
+
token: n.jwtToken,
|
|
31
|
+
userId: void 0,
|
|
32
|
+
userName: void 0
|
|
33
|
+
}), await p(n.jwtToken, n.expire), { token: n.jwtToken }) : (await r().clearUserConfig(), null);
|
|
41
34
|
}
|
|
42
|
-
async function
|
|
43
|
-
let
|
|
35
|
+
async function d(i = {}) {
|
|
36
|
+
let a = n();
|
|
44
37
|
try {
|
|
45
|
-
let t = r(), n = t.getUserConfig(),
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
38
|
+
let t = r(), n = t.getUserConfig(), c = e();
|
|
39
|
+
if (a.debug?.(`[yoyoclaw-auth] env userId: ${c ? "present" : "absent"}, config userId: ${n?.userId ? "present" : "absent"}, config token: ${n?.token ? "present" : "absent"}`), i.migrateCachedAuthorization) {
|
|
40
|
+
let e = (await s())?.authorizationCache?.jwtToken;
|
|
41
|
+
if (e) return a.info("[yoyoclaw-auth] migrating cached token to plugin config"), await t.updateUserConfig({
|
|
42
|
+
...n ?? {},
|
|
43
|
+
token: e,
|
|
44
|
+
userId: void 0,
|
|
45
|
+
expired: void 0
|
|
46
|
+
}), await o(), n = t.getUserConfig(), { token: e };
|
|
50
47
|
}
|
|
51
|
-
return
|
|
48
|
+
return n?.userId ? await u(c || n.userId) : n?.token ? { token: n.token } : c ? (a.debug?.("[yoyoclaw-auth] no token, using env userId to exchange token"), await u(c)) : null;
|
|
52
49
|
} catch (e) {
|
|
53
50
|
throw t(e, "加载Token失败");
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
|
-
async function
|
|
53
|
+
async function f() {
|
|
57
54
|
try {
|
|
58
55
|
await r().clearUserConfig(), n().info("[yoyoclaw-auth] token cleared");
|
|
59
56
|
} catch (e) {
|
|
60
57
|
throw t(e, "清除Token失败");
|
|
61
58
|
}
|
|
62
59
|
}
|
|
60
|
+
async function p(e, t) {
|
|
61
|
+
try {
|
|
62
|
+
await a(e, t);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
n().warn(`[yoyoclaw-auth] failed to cache token metadata: ${String(e)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
63
67
|
//#endregion
|
|
64
|
-
export {
|
|
68
|
+
export { f as clearToken, d as loadToken, l as saveToken };
|
|
@@ -1,51 +1,73 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import i from "
|
|
1
|
+
import { wrapError as e } from "../../utils/error.mjs";
|
|
2
|
+
import { useClawLogger as t } from "../../utils/logger.mjs";
|
|
3
|
+
import { safeReadFile as n, safeWriteFile as r } from "../../utils/fs-safe.mjs";
|
|
4
|
+
import { resolveEffectiveHomeDir as i } from "../../utils/home-dir.mjs";
|
|
5
|
+
import a from "node:path";
|
|
5
6
|
//#region src/modules/configs/identity-persist.ts
|
|
6
|
-
var
|
|
7
|
+
var o = /* @__PURE__ */ function(e) {
|
|
7
8
|
return e[e.LEGACY = 1] = "LEGACY", e[e.NEW = 2] = "NEW", e;
|
|
8
|
-
}({}),
|
|
9
|
-
function
|
|
10
|
-
return
|
|
9
|
+
}({}), s = ".openclaw", c = "yoyo", l = "identity.json", u = 6e4;
|
|
10
|
+
function d() {
|
|
11
|
+
return a.join(s, c, l);
|
|
11
12
|
}
|
|
12
|
-
async function
|
|
13
|
-
let r =
|
|
13
|
+
async function f(t) {
|
|
14
|
+
let r = d();
|
|
14
15
|
try {
|
|
15
|
-
let e = (await
|
|
16
|
-
rootDir:
|
|
16
|
+
let e = (await n({
|
|
17
|
+
rootDir: t,
|
|
17
18
|
relativePath: r,
|
|
18
19
|
maxBytes: 10 * 1024
|
|
19
20
|
})).buffer.toString("utf8"), i = JSON.parse(e);
|
|
20
|
-
return typeof i != "object" || !i ?
|
|
21
|
-
...
|
|
21
|
+
return typeof i != "object" || !i ? v() : {
|
|
22
|
+
...v(),
|
|
22
23
|
...i
|
|
23
24
|
};
|
|
24
25
|
} catch (t) {
|
|
25
|
-
return e(
|
|
26
|
+
return e(t, "[yoyo-identity] failed to read identity"), null;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
async function
|
|
29
|
-
let
|
|
30
|
-
return
|
|
29
|
+
async function p() {
|
|
30
|
+
let e = i();
|
|
31
|
+
return e ? await f(e) || null : (t().warn("[yoyo-identity] failed to find home dir"), null);
|
|
31
32
|
}
|
|
32
|
-
async function
|
|
33
|
-
let
|
|
34
|
-
if (!
|
|
35
|
-
let
|
|
36
|
-
...
|
|
37
|
-
version:
|
|
33
|
+
async function m(e) {
|
|
34
|
+
let n = i();
|
|
35
|
+
if (!n) return t().warn("[yoyo-identity] failed to find home dir"), null;
|
|
36
|
+
let a = await f(n), s = {
|
|
37
|
+
...a,
|
|
38
|
+
version: a?.version || o.NEW
|
|
38
39
|
};
|
|
39
|
-
return
|
|
40
|
-
rootDir:
|
|
41
|
-
relativePath:
|
|
40
|
+
return e.legacyDeviceId && (s.legacyDeviceId = e.legacyDeviceId), e.deviceId && (s.deviceId = e.deviceId, s.publicKeyPem = e.publicKeyPem, s.privateKeyPem = e.privateKeyPem, s.createdAtMs = e.createdAtMs), e.version && (s.version = e.version), "gatewayAuthMd5" in e && (s.gatewayAuthMd5 = e.gatewayAuthMd5), "pluginVersionCode" in e && (s.pluginVersionCode = e.pluginVersionCode), "authorizationCache" in e && (s.authorizationCache = e.authorizationCache), await r({
|
|
41
|
+
rootDir: n,
|
|
42
|
+
relativePath: d(),
|
|
42
43
|
data: JSON.stringify(s, null, 2),
|
|
43
44
|
encoding: "utf8",
|
|
44
45
|
mkdir: !0
|
|
45
46
|
}), s;
|
|
46
47
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
48
|
+
function h(e) {
|
|
49
|
+
let t = typeof e == "number" ? e : typeof e == "string" && e.trim() ? Number(e) : 0;
|
|
50
|
+
return !Number.isFinite(t) || t < u ? 0 : Math.floor(t);
|
|
51
|
+
}
|
|
52
|
+
async function g(e, t, n = Date.now()) {
|
|
53
|
+
let r = await m({ authorizationCache: {
|
|
54
|
+
jwtToken: e,
|
|
55
|
+
receivedAtMs: n,
|
|
56
|
+
expireMs: h(t)
|
|
57
|
+
} });
|
|
58
|
+
if (!r) throw Error("failed to write authorization cache");
|
|
59
|
+
return r;
|
|
60
|
+
}
|
|
61
|
+
async function _() {
|
|
62
|
+
let e = await p(), t = e?.authorizationCache;
|
|
63
|
+
return t?.jwtToken ? m({ authorizationCache: {
|
|
64
|
+
jwtToken: void 0,
|
|
65
|
+
receivedAtMs: t.receivedAtMs,
|
|
66
|
+
expireMs: h(t.expireMs)
|
|
67
|
+
} }) : e;
|
|
68
|
+
}
|
|
69
|
+
function v() {
|
|
70
|
+
return { version: o.NEW };
|
|
49
71
|
}
|
|
50
72
|
//#endregion
|
|
51
|
-
export {
|
|
73
|
+
export { o as IdentityVersion, g as cacheAuthorizationToken, _ as clearCachedAuthorizationToken, p as getPersistedIdentity, h as parseAuthorizationExpireMs, m as updatePersistedIdentity };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@honor-claw/yoyo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0-beta.1",
|
|
4
4
|
"description": "OpenClaw Honor Yoyo connection plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
"@types/node": "^25.0.10",
|
|
50
50
|
"@types/ws": "^8.5.13",
|
|
51
51
|
"@vitest/coverage-v8": "^2.1.8",
|
|
52
|
-
"commander": "^14.0.3",
|
|
53
52
|
"typescript": "^6.0.2",
|
|
54
53
|
"vite": "^8.0.10",
|
|
55
54
|
"vitest": "^2.1.8"
|