@honor-claw/yoyo 1.5.1-beta.9 → 1.6.0-alpha.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.
@@ -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 { ClawCloudSocketClient as n } from "./client.mjs";
4
- import { MessageHandler as r } from "./message-handler.mjs";
5
- import { SessionManager as i } from "./session-manager.mjs";
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 a = class {
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 i(), this.adminClientManager = new t({ onStatusEvent: this.handleStatusEvent }), this.messageHandler = new r({
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 n({
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 { a as ClawChannel };
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 { buildCloudSocketConnectParams as r } from "./utils.mjs";
5
- import i from "ws";
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 a = 3e4, o = 4e3, s = 1e3, c = 2, l = 999, u = class {
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 === i.OPEN) return;
21
+ if (this.ws?.readyState === a.OPEN) return;
21
22
  this.clearRetryTimer();
22
- let { deviceInfo: n, userInfo: a } = this.options, { url: o, traceId: s, wsOptions: c } = r(n, a);
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 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
+ }), 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 !== i.OPEN) return e().error("[claw-cloud-socket] cannot send message: connection not open"), !1;
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 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);
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 (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);
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 (i.code === "YOYO_CLAW_100001") {
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: ${i.cnMessage}, code: ${i.code}`);
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, l), e().info(`[claw-cloud-socket] scheduling reconnect attempt ${this.retryCount} in ${n}ms`), this.options.onStatusEvent?.({
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 = s * c ** +Math.min(this.retryCount, 2);
126
- return Math.min(e, o);
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 !== i.OPEN) {
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
- }, a);
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 !== i.OPEN) && (this.retryCount = 0, this.connect(!0)));
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 { u as ClawCloudSocketClient };
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 { content: n } = t || {};
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 { provider: r, id: i } = n;
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
- return 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) => {
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
- let r = n.command("login").description("login to yoyoclaw and register devices");
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
- }), r;
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
- return n.command("logout").description("Logout and clear user configuration").action(async () => {
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
- return o.command("status").description("Show YOYOClaw connection status").action(async () => {
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({ jwtToken: e.jwtToken }, u.saveToFile !== !1), { token: e.jwtToken };
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 a } from "../modules/device/device-info.mjs";
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 o(e, o = !0) {
11
+ async function l(e, a = !0) {
11
12
  try {
12
- let t = r(), s = e.jwtToken;
13
- if (!s && e.userId) {
14
- let t = e.deviceInfo || await a();
15
- s = (await i().exchangeToken(t, { userId: e.userId })).jwtToken;
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 (!s) throw Error("no token available");
18
- if (!o) return n().info("💾 token got, but not to save"), { token: s };
19
- let c = Math.floor(Date.now() / 1e3) + 720 * 60 * 60;
20
- return await t.updateUserConfig({
21
- token: s,
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 s(e) {
30
- let t = await a(), n = await i().exchangeToken(t, { userId: e });
31
- if (n.jwtToken) {
32
- let e = Math.floor(Date.now() / 1e3) + 720 * 60 * 60;
33
- return await r().updateUserConfig({
34
- token: n.jwtToken,
35
- userId: void 0,
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 c() {
43
- let i = n();
35
+ async function d(i = {}) {
36
+ let a = n();
44
37
  try {
45
- let t = r(), n = t.getUserConfig(), a = e();
46
- if (i.debug?.(`[yoyoclaw-auth] env userId: ${a ? "present" : "absent"}, config userId: ${n?.userId ? "present" : "absent"}, config token: ${n?.token ? "present" : "absent"}`), n?.userId) return await s(a || n.userId);
47
- if (n?.token) {
48
- if (!n.expired || n.expired >= Math.floor(Date.now() / 1e3)) return { token: n.token };
49
- i.debug?.("[yoyoclaw-auth] cached token expired");
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 a ? (i.debug?.("[yoyoclaw-auth] token expired, using env userId to exchange token"), await s(a)) : (n?.token && (i.debug?.("[yoyoclaw-auth] clearing expired token"), await t.clearUserConfig()), null);
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 l() {
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 { l as clearToken, c as loadToken, o as saveToken };
68
+ export { f as clearToken, d as loadToken, l as saveToken };
@@ -1,51 +1,73 @@
1
- import { useClawLogger as e } from "../../utils/logger.mjs";
2
- import { safeReadFile as t, safeWriteFile as n } from "../../utils/fs-safe.mjs";
3
- import { resolveEffectiveHomeDir as r } from "../../utils/home-dir.mjs";
4
- import i from "node:path";
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 a = /* @__PURE__ */ function(e) {
7
+ var o = /* @__PURE__ */ function(e) {
7
8
  return e[e.LEGACY = 1] = "LEGACY", e[e.NEW = 2] = "NEW", e;
8
- }({}), o = ".openclaw", s = "yoyo", c = "identity.json";
9
- function l() {
10
- return i.join(o, s, c);
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 u(n) {
13
- let r = l();
13
+ async function f(t) {
14
+ let r = d();
14
15
  try {
15
- let e = (await t({
16
- rootDir: n,
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 ? p() : {
21
- ...p(),
21
+ return typeof i != "object" || !i ? v() : {
22
+ ...v(),
22
23
  ...i
23
24
  };
24
25
  } catch (t) {
25
- return e().warn(`[yoyo-identity] failed to read identity: ${t.message}`), null;
26
+ return e(t, "[yoyo-identity] failed to read identity"), null;
26
27
  }
27
28
  }
28
- async function d() {
29
- let t = r();
30
- return t ? await u(t) || null : (e().warn("[yoyo-identity] failed to find home dir"), null);
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 f(t) {
33
- let i = r();
34
- if (!i) return e().warn("[yoyo-identity] failed to find home dir"), null;
35
- let o = await u(i), s = {
36
- ...o,
37
- version: o?.version || a.NEW
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 t.legacyDeviceId && (s.legacyDeviceId = t.legacyDeviceId), t.deviceId && (s.deviceId = t.deviceId, s.publicKeyPem = t.publicKeyPem, s.privateKeyPem = t.privateKeyPem, s.createdAtMs = t.createdAtMs), t.version && (s.version = t.version), "gatewayAuthMd5" in t && (s.gatewayAuthMd5 = t.gatewayAuthMd5), "pluginVersionCode" in t && (s.pluginVersionCode = t.pluginVersionCode), await n({
40
- rootDir: i,
41
- relativePath: l(),
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 p() {
48
- return { version: a.NEW };
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 { a as IdentityVersion, d as getPersistedIdentity, f as updatePersistedIdentity };
73
+ export { o as IdentityVersion, g as cacheAuthorizationToken, _ as clearCachedAuthorizationToken, p as getPersistedIdentity, h as parseAuthorizationExpireMs, m as updatePersistedIdentity };
@@ -50,7 +50,7 @@ async function _() {
50
50
  }
51
51
  async function v() {
52
52
  try {
53
- let t = await o();
53
+ let t = await o({ migrateCachedAuthorization: !0 });
54
54
  if (t?.token) {
55
55
  e().info("[yoyoclaw-conn] token found, creating channel"), await y(await i(), t);
56
56
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honor-claw/yoyo",
3
- "version": "1.5.1-beta.9",
3
+ "version": "1.6.0-alpha.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"