@lingyao037/openclaw-lingyao-cli 0.9.5 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { L as LingyaoRuntime, g as LingyaoAccount, c as DeviceToken, D as DeviceInfo } from './types-Zbv12l39.js';
1
+ import { L as LingyaoRuntime, g as LingyaoAccount, c as DeviceToken, D as DeviceInfo } from './types-LFC6Wpqo.js';
2
2
 
3
3
  /**
4
4
  * Account storage and management
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LingyaoRuntime, H as HealthStatus } from './types-Zbv12l39.js';
2
- import { A as AccountManager } from './accounts-CzjBLXMH.js';
1
+ import { L as LingyaoRuntime, H as HealthStatus } from './types-LFC6Wpqo.js';
2
+ import { A as AccountManager } from './accounts-BykE02r0.js';
3
3
 
4
4
  /**
5
5
  * Probe status levels
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { R as ResolvedAccount, L as LingyaoProbeResult } from './status-DdIuhIHE.js';
1
+ import { R as ResolvedAccount, L as LingyaoProbeResult } from './status-CVwSoPKi.js';
2
2
  import * as openclaw_plugin_sdk from 'openclaw/plugin-sdk';
3
3
  import { PluginRuntime, ChannelPlugin } from 'openclaw/plugin-sdk';
4
- import { L as LingyaoRuntime, S as SyncRequest, a as SyncResponse, b as LingyaoMessage, D as DeviceInfo, c as DeviceToken, d as LingyaoConfig, N as NotifyPayload, H as HealthStatus } from './types-Zbv12l39.js';
5
- export { A as AckRequest, e as DiarySyncPayload, F as FailedEntry, f as LINGYAO_SERVER_URL, g as LingyaoAccount, h as LingyaoAccountConfig, M as MemorySyncPayload, i as MessageType, j as NotifyAction, k as NotifyRequest, P as PairingCode, l as PairingConfirmRequest, m as PairingConfirmResponse, n as PollRequest, o as PollResponse, Q as QueuedMessage, T as TokenRefreshRequest, p as TokenRefreshResponse, W as WebSocketConnection } from './types-Zbv12l39.js';
6
- import { A as AccountManager } from './accounts-CzjBLXMH.js';
4
+ import { L as LingyaoRuntime, S as SyncRequest, a as SyncResponse, b as LingyaoMessage, D as DeviceInfo, c as DeviceToken, d as LingyaoConfig, N as NotifyPayload, H as HealthStatus } from './types-LFC6Wpqo.js';
5
+ export { A as AckRequest, e as DiarySyncPayload, F as FailedEntry, f as LINGYAO_SERVER_URL, g as LingyaoAccount, h as LingyaoAccountConfig, M as MemorySyncPayload, i as MessageType, j as NotifyAction, k as NotifyRequest, P as PairingCode, l as PairingConfirmRequest, m as PairingConfirmResponse, n as PollRequest, o as PollResponse, Q as QueuedMessage, T as TokenRefreshRequest, p as TokenRefreshResponse, W as WebSocketConnection, q as getLingyaoGatewayWsUrl } from './types-LFC6Wpqo.js';
6
+ import { A as AccountManager } from './accounts-BykE02r0.js';
7
7
 
8
8
  /**
9
9
  * 灵爻服务器 HTTP 客户端
@@ -134,6 +134,11 @@ declare class ServerHttpClient {
134
134
  * 检查是否已注册
135
135
  */
136
136
  isReady(): boolean;
137
+ /**
138
+ * Clear local gateway token/session (storage + in-memory). Used when WS handshake
139
+ * fails with 404 or when forcing re-registration with the same gatewayId.
140
+ */
141
+ clearLocalSession(): Promise<void>;
137
142
  /**
138
143
  * 从存储恢复会话
139
144
  */
@@ -164,18 +169,17 @@ declare class ServerHttpClient {
164
169
  */
165
170
  type ConnectionState = "connecting" | "connected" | "disconnected" | "error";
166
171
  /**
167
- * WebSocket 消息类型
172
+ * WebSocket 消息类型(与 `lingyao/server/src/server.ts` 中 Gateway 协议一致)
168
173
  */
169
174
  declare enum WSMessageType {
170
- REGISTER = "register",
171
- HEARTBEAT = "heartbeat",
172
- SEND_MESSAGE = "send_message",
173
- ACK = "ack",
174
- REGISTERED = "registered",
175
- HEARTBEAT_ACK = "heartbeat_ack",
175
+ GATEWAY_REGISTER = "gateway_register",
176
+ GATEWAY_HEARTBEAT = "gateway_heartbeat",
177
+ GATEWAY_SEND_MESSAGE = "gateway_send_message",
178
+ GATEWAY_REGISTERED = "gateway_registered",
179
+ GATEWAY_HEARTBEAT_ACK = "gateway_heartbeat_ack",
176
180
  MESSAGE_DELIVERED = "message_delivered",
177
181
  MESSAGE_FAILED = "message_failed",
178
- APP_MESSAGE = "app_message",// 来自 App 的消息
182
+ APP_MESSAGE = "app_message",
179
183
  DEVICE_ONLINE = "device_online",
180
184
  PAIRING_COMPLETED = "pairing_completed",
181
185
  ERROR = "error"
@@ -218,6 +222,9 @@ type WSClientEvent = {
218
222
  } | {
219
223
  type: "error";
220
224
  error: Error;
225
+ } | {
226
+ type: "fatal_handshake";
227
+ reason: "http_404";
221
228
  } | {
222
229
  type: "message";
223
230
  message: WSMessage;
@@ -259,6 +266,8 @@ declare class LingyaoWSClient {
259
266
  private connectionId;
260
267
  private heartbeatTimer;
261
268
  private reconnectTimer;
269
+ /** When set, close handler will not schedule reconnect (e.g. HTTP 404 on upgrade). */
270
+ private suppressReconnect;
262
271
  private messageHandlers;
263
272
  private logger;
264
273
  constructor(runtime: LingyaoRuntime, config: WSClientConfig);
@@ -330,6 +339,10 @@ declare class LingyaoWSClient {
330
339
  * 处理消息发送成功
331
340
  */
332
341
  private handleMessageDelivered;
342
+ /**
343
+ * 设备上线(服务器可选推送)
344
+ */
345
+ private handleDeviceOnline;
333
346
  /**
334
347
  * 处理配对完成通知(来自 lingyao.live 服务器)
335
348
  */
package/dist/index.js CHANGED
@@ -409,6 +409,9 @@ import { createHash as createHash2 } from "crypto";
409
409
 
410
410
  // src/types.ts
411
411
  var LINGYAO_SERVER_URL = "https://api.lingyao.live";
412
+ function getLingyaoGatewayWsUrl() {
413
+ return LINGYAO_SERVER_URL.replace(/^https:/i, "wss:") + "/lyoc/gateway/ws";
414
+ }
412
415
  var MessageType = /* @__PURE__ */ ((MessageType2) => {
413
416
  MessageType2["SYNC_DIARY"] = "sync_diary";
414
417
  MessageType2["SYNC_MEMORY"] = "sync_memory";
@@ -445,7 +448,8 @@ var ServerHttpClient = class {
445
448
  this.storagePrefix = storagePrefix;
446
449
  this.config = {
447
450
  baseURL: serverConfig.baseURL || "https://api.lingyao.live",
448
- apiBase: serverConfig.apiBase || "/v1",
451
+ // Public API (api.lingyao.live) serves gateway HTTP under /lyoc; local relay also accepts /lyoc (see server getApiPathSuffix).
452
+ apiBase: serverConfig.apiBase || "/lyoc",
449
453
  timeout: serverConfig.timeout || 3e4,
450
454
  connectionTimeout: serverConfig.connectionTimeout || 5e3
451
455
  };
@@ -525,6 +529,10 @@ var ServerHttpClient = class {
525
529
  throw new Error("Gateway already registered");
526
530
  } else if (status === 400) {
527
531
  throw new Error(`Invalid request: ${data?.details || "Unknown error"}`);
532
+ } else if (status === 404) {
533
+ throw new Error(
534
+ "Lingyao gateway register returned 404. Check server URL and /lyoc/gateway/register; the gatewayId may be invalid or the API may not be deployed on this host."
535
+ );
528
536
  }
529
537
  throw new Error(`Registration failed: ${axiosError.message}`);
530
538
  }
@@ -659,6 +667,26 @@ var ServerHttpClient = class {
659
667
  isReady() {
660
668
  return this.isRegistered && !!this.gatewayToken;
661
669
  }
670
+ /**
671
+ * Clear local gateway token/session (storage + in-memory). Used when WS handshake
672
+ * fails with 404 or when forcing re-registration with the same gatewayId.
673
+ */
674
+ async clearLocalSession() {
675
+ this.stopHeartbeat();
676
+ this.gatewayToken = null;
677
+ this.webhookSecret = null;
678
+ this.tokenExpiresAt = 0;
679
+ this.isRegistered = false;
680
+ const keys = ["gatewayToken", "webhookSecret", "tokenExpiresAt", "serverConfig"];
681
+ for (const k of keys) {
682
+ try {
683
+ await this.runtime.storage.delete(this.storageKey(k));
684
+ } catch (e) {
685
+ this.runtime.logger.warn(`Failed to delete storage key ${k}`, e);
686
+ }
687
+ }
688
+ this.runtime.logger.info("Cleared local Lingyao gateway session");
689
+ }
662
690
  /**
663
691
  * 从存储恢复会话
664
692
  */
@@ -705,6 +733,23 @@ var ServerHttpClient = class {
705
733
 
706
734
  // src/websocket-client.ts
707
735
  import WebSocket from "ws";
736
+ function isWebsocketUpgradeNotFoundError(message) {
737
+ return /Unexpected server response:\s*404/i.test(message) || /\b404\b/.test(message);
738
+ }
739
+ function normalizeIncomingGatewayMessageType(type) {
740
+ switch (type) {
741
+ case "registered":
742
+ return "gateway_registered" /* GATEWAY_REGISTERED */;
743
+ case "heartbeat_ack":
744
+ return "gateway_heartbeat_ack" /* GATEWAY_HEARTBEAT_ACK */;
745
+ case "gateway_registered":
746
+ return "gateway_registered" /* GATEWAY_REGISTERED */;
747
+ case "gateway_heartbeat_ack":
748
+ return "gateway_heartbeat_ack" /* GATEWAY_HEARTBEAT_ACK */;
749
+ default:
750
+ return type;
751
+ }
752
+ }
708
753
  var LingyaoWSClient = class {
709
754
  config;
710
755
  ws = null;
@@ -712,16 +757,19 @@ var LingyaoWSClient = class {
712
757
  connectionId = null;
713
758
  heartbeatTimer = null;
714
759
  reconnectTimer = null;
760
+ /** When set, close handler will not schedule reconnect (e.g. HTTP 404 on upgrade). */
761
+ suppressReconnect = false;
715
762
  messageHandlers = /* @__PURE__ */ new Map();
716
763
  logger;
717
764
  constructor(runtime, config) {
718
765
  this.logger = runtime.logger;
719
766
  this.config = { ...config };
720
- this.registerMessageHandler("registered" /* REGISTERED */, this.handleRegistered.bind(this));
721
- this.registerMessageHandler("heartbeat_ack" /* HEARTBEAT_ACK */, this.handleHeartbeatAck.bind(this));
767
+ this.registerMessageHandler("gateway_registered" /* GATEWAY_REGISTERED */, this.handleRegistered.bind(this));
768
+ this.registerMessageHandler("gateway_heartbeat_ack" /* GATEWAY_HEARTBEAT_ACK */, this.handleHeartbeatAck.bind(this));
722
769
  this.registerMessageHandler("message_delivered" /* MESSAGE_DELIVERED */, this.handleMessageDelivered.bind(this));
723
770
  this.registerMessageHandler("message_failed" /* MESSAGE_FAILED */, this.handleMessageFailed.bind(this));
724
771
  this.registerMessageHandler("app_message" /* APP_MESSAGE */, this.handleAppMessage.bind(this));
772
+ this.registerMessageHandler("device_online" /* DEVICE_ONLINE */, this.handleDeviceOnline.bind(this));
725
773
  this.registerMessageHandler("pairing_completed" /* PAIRING_COMPLETED */, this.handlePairingCompleted.bind(this));
726
774
  this.registerMessageHandler("error" /* ERROR */, this.handleError.bind(this));
727
775
  }
@@ -734,6 +782,7 @@ var LingyaoWSClient = class {
734
782
  return;
735
783
  }
736
784
  this.state = "connecting";
785
+ this.suppressReconnect = false;
737
786
  this.emitEvent({ type: "disconnected", code: 0, reason: "Reconnecting" });
738
787
  try {
739
788
  this.logger.info(`Connecting to Lingyao server: ${this.config.url}`);
@@ -794,12 +843,14 @@ var LingyaoWSClient = class {
794
843
  async handleMessage(data) {
795
844
  try {
796
845
  const message = JSON.parse(data.toString());
797
- this.logger.debug("Received message from server", { type: message.type });
798
- const handler = this.messageHandlers.get(message.type);
846
+ const rawType = String(message.type);
847
+ const handlerKey = normalizeIncomingGatewayMessageType(rawType);
848
+ this.logger.debug("Received message from server", { type: rawType, handlerKey });
849
+ const handler = this.messageHandlers.get(handlerKey);
799
850
  if (handler) {
800
851
  handler(message);
801
852
  } else {
802
- this.logger.warn("No handler for message type", { type: message.type });
853
+ this.logger.warn("No handler for message type", { type: rawType });
803
854
  }
804
855
  this.emitEvent({ type: "message", message });
805
856
  } catch (error) {
@@ -810,7 +861,16 @@ var LingyaoWSClient = class {
810
861
  * 处理连接错误
811
862
  */
812
863
  handleErrorEvent(error) {
813
- this.logger.error("WebSocket error", error);
864
+ const msg = error?.message ?? String(error);
865
+ if (isWebsocketUpgradeNotFoundError(msg)) {
866
+ this.suppressReconnect = true;
867
+ this.logger.error(
868
+ "WebSocket handshake failed with HTTP 404 \u2014 stopping reconnect loop. If the gateway was removed server-side, remove `gatewayId` from channels.lingyao.accounts.* or use a new account id; if the path is wrong, verify wss://\u2026/lyoc/gateway/ws is deployed on api.lingyao.live.",
869
+ { gatewayId: this.config.gatewayId, message: msg }
870
+ );
871
+ } else {
872
+ this.logger.error("WebSocket error", error);
873
+ }
814
874
  this.state = "error";
815
875
  this.emitEvent({ type: "error", error });
816
876
  }
@@ -827,6 +887,11 @@ var LingyaoWSClient = class {
827
887
  this.logger.error("WebSocket closed with 1008 (Invalid Token). Stopping reconnect loop.");
828
888
  return;
829
889
  }
890
+ if (this.suppressReconnect) {
891
+ this.suppressReconnect = false;
892
+ this.emitEvent({ type: "fatal_handshake", reason: "http_404" });
893
+ return;
894
+ }
830
895
  if (code !== 1e3) {
831
896
  this.scheduleReconnect();
832
897
  }
@@ -836,7 +901,7 @@ var LingyaoWSClient = class {
836
901
  */
837
902
  sendRegister() {
838
903
  const message = {
839
- type: "register" /* REGISTER */,
904
+ type: "gateway_register" /* GATEWAY_REGISTER */,
840
905
  id: this.generateMessageId(),
841
906
  timestamp: Date.now(),
842
907
  payload: {
@@ -865,7 +930,7 @@ var LingyaoWSClient = class {
865
930
  */
866
931
  sendHeartbeat() {
867
932
  const message = {
868
- type: "heartbeat" /* HEARTBEAT */,
933
+ type: "gateway_heartbeat" /* GATEWAY_HEARTBEAT */,
869
934
  id: this.generateMessageId(),
870
935
  timestamp: Date.now(),
871
936
  payload: {
@@ -936,7 +1001,7 @@ var LingyaoWSClient = class {
936
1001
  */
937
1002
  sendNotification(deviceId, notification) {
938
1003
  const message = {
939
- type: "send_message" /* SEND_MESSAGE */,
1004
+ type: "gateway_send_message" /* GATEWAY_SEND_MESSAGE */,
940
1005
  id: this.generateMessageId(),
941
1006
  timestamp: Date.now(),
942
1007
  payload: {
@@ -975,6 +1040,14 @@ var LingyaoWSClient = class {
975
1040
  messageId: message.id
976
1041
  });
977
1042
  }
1043
+ /**
1044
+ * 设备上线(服务器可选推送)
1045
+ */
1046
+ handleDeviceOnline(message) {
1047
+ this.logger.debug("Device online (server push)", {
1048
+ payload: message.payload
1049
+ });
1050
+ }
978
1051
  /**
979
1052
  * 处理配对完成通知(来自 lingyao.live 服务器)
980
1053
  */
@@ -2683,7 +2756,7 @@ var MultiAccountOrchestrator = class {
2683
2756
  }
2684
2757
  await this.registerToServer(state);
2685
2758
  const wsClient = new LingyaoWSClient(this.runtime, {
2686
- url: `${LINGYAO_SERVER_URL}/v1/gateway/ws`,
2759
+ url: getLingyaoGatewayWsUrl(),
2687
2760
  gatewayId,
2688
2761
  token: httpClient.getGatewayToken() ?? void 0,
2689
2762
  reconnectInterval: 5e3,
@@ -2905,6 +2978,11 @@ var MultiAccountOrchestrator = class {
2905
2978
  });
2906
2979
  state.errorHandler.handleError(event.error);
2907
2980
  break;
2981
+ case "fatal_handshake":
2982
+ if (event.reason === "http_404") {
2983
+ void this.handleWsHandshake404(state);
2984
+ }
2985
+ break;
2908
2986
  case "pairing_completed":
2909
2987
  this.handlePairingCompleted(state, event);
2910
2988
  break;
@@ -2928,6 +3006,45 @@ var MultiAccountOrchestrator = class {
2928
3006
  this.runtime.logger.error(`[${state.accountId}] Failed to auto-bind device: ${deviceId}`, error);
2929
3007
  }
2930
3008
  }
3009
+ /**
3010
+ * After HTTP 404 on WebSocket upgrade: clear local tokens, re-register once, reconnect.
3011
+ * Does not fix wrong server URL or permanently deleted gateway rows (user must fix config).
3012
+ */
3013
+ async handleWsHandshake404(state) {
3014
+ if (state.wsHandshake404RecoveryAttempted) {
3015
+ this.runtime.logger.error(
3016
+ `[${state.accountId}] Lingyao WebSocket still failing after one recovery attempt. Check that wss://\u2026/lyoc/gateway/ws is deployed; if the gateway was removed, delete \`channels.lingyao.accounts.${state.accountId}.gatewayId\` or use a new account id, then restart.`
3017
+ );
3018
+ return;
3019
+ }
3020
+ state.wsHandshake404RecoveryAttempted = true;
3021
+ const { httpClient, wsClient, accountId } = state;
3022
+ if (!httpClient || !wsClient) {
3023
+ return;
3024
+ }
3025
+ this.runtime.logger.info(`[${accountId}] WS HTTP 404 \u2014 clearing local session and re-registering...`);
3026
+ try {
3027
+ await httpClient.clearLocalSession();
3028
+ const response = await httpClient.register({
3029
+ websocket: true,
3030
+ compression: false,
3031
+ maxMessageSize: 1048576
3032
+ });
3033
+ wsClient.updateToken(response.gatewayToken);
3034
+ await wsClient.connect();
3035
+ this.runtime.logger.info(`[${accountId}] Re-register OK; WebSocket reconnect issued.`);
3036
+ } catch (error) {
3037
+ const msg = error instanceof Error ? error.message : String(error);
3038
+ if (msg.includes("already registered")) {
3039
+ this.runtime.logger.error(
3040
+ `[${accountId}] Re-register failed: gateway still registered on server but local session was cleared. Remove or change \`gatewayId\` under this account, or reset the gateway on lingyao.live, then restart OpenClaw.`,
3041
+ error
3042
+ );
3043
+ } else {
3044
+ this.runtime.logger.error(`[${accountId}] WS 404 recovery (clear + register) failed`, error);
3045
+ }
3046
+ }
3047
+ }
2931
3048
  /**
2932
3049
  * Handle invalid token by re-registering the gateway and reconnecting WS
2933
3050
  */
@@ -3164,7 +3281,7 @@ var LingyaoChannel = class {
3164
3281
  try {
3165
3282
  await this.registerToServer();
3166
3283
  this.wsClient = new LingyaoWSClient(this.runtime, {
3167
- url: `${LINGYAO_SERVER_URL}/v1/gateway/ws`,
3284
+ url: getLingyaoGatewayWsUrl(),
3168
3285
  gatewayId: this.gatewayId,
3169
3286
  token: this.serverClient?.getGatewayToken() ?? void 0,
3170
3287
  reconnectInterval: 5e3,
@@ -3313,6 +3430,11 @@ var LingyaoChannel = class {
3313
3430
  )
3314
3431
  );
3315
3432
  break;
3433
+ case "fatal_handshake":
3434
+ this.runtime.logger.error(
3435
+ "Lingyao WebSocket upgrade failed (HTTP 404). Use the OpenClaw plugin path for automatic recovery, or clear gateway session / gatewayId in config."
3436
+ );
3437
+ break;
3316
3438
  }
3317
3439
  }
3318
3440
  /**
@@ -3702,7 +3824,7 @@ async function createPlugin(runtime, config = {}) {
3702
3824
  }
3703
3825
  var pluginMetadata = {
3704
3826
  name: "lingyao",
3705
- version: "0.9.1",
3827
+ version: "0.9.7",
3706
3828
  description: "Lingyao Channel Plugin - bidirectional sync via lingyao.live server relay",
3707
3829
  type: "channel",
3708
3830
  capabilities: {
@@ -3738,6 +3860,7 @@ export {
3738
3860
  createPlugin,
3739
3861
  index_default as default,
3740
3862
  getDefaultConfig,
3863
+ getLingyaoGatewayWsUrl,
3741
3864
  initializeLingyaoRuntime,
3742
3865
  lingyaoPlugin,
3743
3866
  pluginMetadata,