@yoooclaw/phone-notifications 1.9.0 → 1.9.1-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/index.js +85 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6261,6 +6261,7 @@ var RelayClient = class {
|
|
|
6261
6261
|
|
|
6262
6262
|
// src/tunnel/proxy.ts
|
|
6263
6263
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6264
|
+
import { approveDevicePairing } from "openclaw/plugin-sdk";
|
|
6264
6265
|
|
|
6265
6266
|
// src/tunnel/utils.ts
|
|
6266
6267
|
function previewText2(text, max = 200) {
|
|
@@ -6757,10 +6758,12 @@ var WsProxy = class {
|
|
|
6757
6758
|
};
|
|
6758
6759
|
|
|
6759
6760
|
// src/tunnel/proxy.ts
|
|
6761
|
+
var MAX_AUTO_PAIRING_APPROVALS = 3;
|
|
6760
6762
|
var TunnelProxy = class {
|
|
6761
6763
|
constructor(opts) {
|
|
6762
6764
|
this.opts = opts;
|
|
6763
6765
|
this.stateDir = resolveClientStateDir(opts.stateDir);
|
|
6766
|
+
this.hostStateDir = opts.hostStateDir ?? resolveStateDir();
|
|
6764
6767
|
this.deviceIdentity = loadOrCreateDeviceIdentity(this.stateDir);
|
|
6765
6768
|
opts.logger.info(
|
|
6766
6769
|
`TunnelProxy: loaded device identity (deviceId=${this.deviceIdentity.deviceId})`
|
|
@@ -6776,11 +6779,16 @@ var TunnelProxy = class {
|
|
|
6776
6779
|
gatewayWsConnecting = false;
|
|
6777
6780
|
/** 握手是否已完成(收到 hello-ok) */
|
|
6778
6781
|
gatewayWsReady = false;
|
|
6782
|
+
/** 收到本地自动配对成功后,在 close 回调里触发重连 */
|
|
6783
|
+
gatewayWsReconnectRequested = false;
|
|
6784
|
+
/** 防止配对失败时无限重连 */
|
|
6785
|
+
gatewayWsAutoPairingApprovals = 0;
|
|
6779
6786
|
/** 等待 Gateway WS 握手完成后发送的帧队列 */
|
|
6780
6787
|
gatewayWsPending = [];
|
|
6781
6788
|
/** 设备身份,用于 Gateway connect 握手 */
|
|
6782
6789
|
deviceIdentity;
|
|
6783
6790
|
stateDir;
|
|
6791
|
+
hostStateDir;
|
|
6784
6792
|
wsProxy;
|
|
6785
6793
|
// ─── Frame dispatcher ───
|
|
6786
6794
|
/** 处理从 Relay 收到的入站帧 */
|
|
@@ -6825,6 +6833,10 @@ var TunnelProxy = class {
|
|
|
6825
6833
|
}
|
|
6826
6834
|
this.gatewayWs = null;
|
|
6827
6835
|
}
|
|
6836
|
+
this.gatewayWsReady = false;
|
|
6837
|
+
this.gatewayWsConnecting = false;
|
|
6838
|
+
this.gatewayWsReconnectRequested = false;
|
|
6839
|
+
this.gatewayWsAutoPairingApprovals = 0;
|
|
6828
6840
|
this.gatewayWsPending = [];
|
|
6829
6841
|
}
|
|
6830
6842
|
// ─── Gateway RPC WebSocket ───
|
|
@@ -6913,6 +6925,53 @@ var TunnelProxy = class {
|
|
|
6913
6925
|
`TunnelProxy: cleared stale stored device token after gateway mismatch (deviceId=${this.deviceIdentity.deviceId})`
|
|
6914
6926
|
);
|
|
6915
6927
|
}
|
|
6928
|
+
async maybeAutoApproveGatewayPairing(frame) {
|
|
6929
|
+
const errorCode = typeof frame?.error?.code === "string" ? frame.error.code : void 0;
|
|
6930
|
+
const detailsCode = typeof frame?.error?.details?.code === "string" ? frame.error.details.code : void 0;
|
|
6931
|
+
if (errorCode !== "NOT_PAIRED" && detailsCode !== "PAIRING_REQUIRED") {
|
|
6932
|
+
return false;
|
|
6933
|
+
}
|
|
6934
|
+
const requestId = typeof frame?.error?.details?.requestId === "string" ? frame.error.details.requestId.trim() : "";
|
|
6935
|
+
const reason = typeof frame?.error?.details?.reason === "string" ? frame.error.details.reason.trim() : "";
|
|
6936
|
+
if (!requestId) {
|
|
6937
|
+
this.opts.logger.warn(
|
|
6938
|
+
"TunnelProxy: gateway pairing required but requestId missing; unable to auto-approve"
|
|
6939
|
+
);
|
|
6940
|
+
return false;
|
|
6941
|
+
}
|
|
6942
|
+
if (reason && reason !== "not-paired" && reason !== "scope-upgrade") {
|
|
6943
|
+
this.opts.logger.warn(
|
|
6944
|
+
`TunnelProxy: gateway pairing required with unsupported reason=${reason}; skipping auto-approve`
|
|
6945
|
+
);
|
|
6946
|
+
return false;
|
|
6947
|
+
}
|
|
6948
|
+
if (this.gatewayWsAutoPairingApprovals >= MAX_AUTO_PAIRING_APPROVALS) {
|
|
6949
|
+
this.opts.logger.warn(
|
|
6950
|
+
`TunnelProxy: reached auto-pairing retry limit (${MAX_AUTO_PAIRING_APPROVALS}); requestId=${requestId}`
|
|
6951
|
+
);
|
|
6952
|
+
return false;
|
|
6953
|
+
}
|
|
6954
|
+
try {
|
|
6955
|
+
const approved = await approveDevicePairing(requestId, this.hostStateDir);
|
|
6956
|
+
if (!approved) {
|
|
6957
|
+
this.opts.logger.warn(
|
|
6958
|
+
`TunnelProxy: gateway pairing request ${requestId} not found in host state ${this.hostStateDir}`
|
|
6959
|
+
);
|
|
6960
|
+
return false;
|
|
6961
|
+
}
|
|
6962
|
+
this.gatewayWsAutoPairingApprovals += 1;
|
|
6963
|
+
this.gatewayWsReconnectRequested = true;
|
|
6964
|
+
this.opts.logger.info(
|
|
6965
|
+
`TunnelProxy: auto-approved local gateway pairing request ${requestId} (reason=${reason || "not-paired"}, hostStateDir=${this.hostStateDir}, approval=${this.gatewayWsAutoPairingApprovals}/${MAX_AUTO_PAIRING_APPROVALS})`
|
|
6966
|
+
);
|
|
6967
|
+
return true;
|
|
6968
|
+
} catch (err) {
|
|
6969
|
+
this.opts.logger.warn(
|
|
6970
|
+
`TunnelProxy: failed to auto-approve gateway pairing request ${requestId}: ${err?.message ?? String(err)}`
|
|
6971
|
+
);
|
|
6972
|
+
return false;
|
|
6973
|
+
}
|
|
6974
|
+
}
|
|
6916
6975
|
// ─── Gateway RPC WebSocket lifecycle ───
|
|
6917
6976
|
/** 确保到本地 Gateway 的 RPC WebSocket 已连接且握手完成 */
|
|
6918
6977
|
ensureGatewayWs() {
|
|
@@ -6932,7 +6991,7 @@ var TunnelProxy = class {
|
|
|
6932
6991
|
`TunnelProxy: RPC WS tcp connected, waiting for connect.challenge (pending=${this.gatewayWsPending.length})`
|
|
6933
6992
|
);
|
|
6934
6993
|
});
|
|
6935
|
-
ws.on("message", (data) => {
|
|
6994
|
+
ws.on("message", async (data) => {
|
|
6936
6995
|
let text;
|
|
6937
6996
|
if (typeof data === "string") {
|
|
6938
6997
|
text = data;
|
|
@@ -6964,6 +7023,8 @@ var TunnelProxy = class {
|
|
|
6964
7023
|
fallbackScopes: ["operator.admin"],
|
|
6965
7024
|
authInfo: frame.payload?.auth
|
|
6966
7025
|
});
|
|
7026
|
+
this.gatewayWsAutoPairingApprovals = 0;
|
|
7027
|
+
this.gatewayWsReconnectRequested = false;
|
|
6967
7028
|
this.gatewayWsReady = true;
|
|
6968
7029
|
this.gatewayWsConnecting = false;
|
|
6969
7030
|
this.opts.logger.info(
|
|
@@ -6976,6 +7037,20 @@ var TunnelProxy = class {
|
|
|
6976
7037
|
return;
|
|
6977
7038
|
}
|
|
6978
7039
|
if (frame.type === "res" && frame.ok === false && !this.gatewayWsReady) {
|
|
7040
|
+
const autoApproved = await this.maybeAutoApproveGatewayPairing(frame);
|
|
7041
|
+
if (autoApproved) {
|
|
7042
|
+
const wsAlreadyClosed = ws.readyState === wrapper_default.CLOSING || ws.readyState === wrapper_default.CLOSED || this.gatewayWs !== ws;
|
|
7043
|
+
this.opts.logger.info(
|
|
7044
|
+
`TunnelProxy: RPC WS handshake requested local pairing approval, reconnecting${wsAlreadyClosed ? " immediately" : " after close"} (pending=${this.gatewayWsPending.length})`
|
|
7045
|
+
);
|
|
7046
|
+
if (wsAlreadyClosed) {
|
|
7047
|
+
this.gatewayWsReconnectRequested = false;
|
|
7048
|
+
queueMicrotask(() => this.ensureGatewayWs());
|
|
7049
|
+
return;
|
|
7050
|
+
}
|
|
7051
|
+
ws.close(1e3, "pairing approved, reconnecting");
|
|
7052
|
+
return;
|
|
7053
|
+
}
|
|
6979
7054
|
this.opts.logger.error(
|
|
6980
7055
|
`TunnelProxy: RPC WS handshake failed (pending=${this.gatewayWsPending.length}): ${previewText2(JSON.stringify(frame.error), 500)}`
|
|
6981
7056
|
);
|
|
@@ -6988,6 +7063,7 @@ var TunnelProxy = class {
|
|
|
6988
7063
|
const wasReady = this.gatewayWsReady;
|
|
6989
7064
|
const pendingCount = this.gatewayWsPending.length;
|
|
6990
7065
|
const reasonText = reason.toString();
|
|
7066
|
+
const shouldReconnect = this.gatewayWsReconnectRequested && pendingCount > 0;
|
|
6991
7067
|
this.opts.logger.info(
|
|
6992
7068
|
`TunnelProxy: RPC WS closed by gateway (code=${code}, reason=${reasonText}, ready=${wasReady}, pending=${pendingCount}, activeWs=${this.wsProxy.activeCount})`
|
|
6993
7069
|
);
|
|
@@ -6997,6 +7073,13 @@ var TunnelProxy = class {
|
|
|
6997
7073
|
}
|
|
6998
7074
|
this.gatewayWsConnecting = false;
|
|
6999
7075
|
this.maybeClearStoredDeviceTokenOnMismatch(code, reasonText);
|
|
7076
|
+
if (shouldReconnect) {
|
|
7077
|
+
this.gatewayWsReconnectRequested = false;
|
|
7078
|
+
this.opts.logger.info(
|
|
7079
|
+
`TunnelProxy: retrying RPC WS after local pairing approval (pending=${this.gatewayWsPending.length})`
|
|
7080
|
+
);
|
|
7081
|
+
queueMicrotask(() => this.ensureGatewayWs());
|
|
7082
|
+
}
|
|
7000
7083
|
});
|
|
7001
7084
|
ws.on("error", (err) => {
|
|
7002
7085
|
this.opts.logger.warn(
|
|
@@ -7193,6 +7276,7 @@ function createTunnelService(opts) {
|
|
|
7193
7276
|
});
|
|
7194
7277
|
proxy = new TunnelProxy({
|
|
7195
7278
|
stateDir: baseStateDir,
|
|
7279
|
+
hostStateDir: ctx.stateDir,
|
|
7196
7280
|
gatewayBaseUrl: opts.gatewayBaseUrl,
|
|
7197
7281
|
gatewayAuthMode: opts.gatewayAuthMode,
|
|
7198
7282
|
gatewayToken: opts.gatewayToken,
|