@yoooclaw/phone-notifications 1.5.1 → 1.5.2
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 +118 -63
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5209,30 +5209,9 @@ var RelayClient = class {
|
|
|
5209
5209
|
startHeartbeat() {
|
|
5210
5210
|
this.stopHeartbeat();
|
|
5211
5211
|
const intervalMs = this.opts.heartbeatSec * 1e3;
|
|
5212
|
+
this.sendHeartbeat();
|
|
5212
5213
|
this.heartbeatTimer = setInterval(() => {
|
|
5213
|
-
|
|
5214
|
-
const idleMs = Date.now() - this.lastInboundAt;
|
|
5215
|
-
const timeoutMs = this.getHeartbeatTimeoutMs();
|
|
5216
|
-
if (idleMs >= timeoutMs) {
|
|
5217
|
-
this.opts.logger.warn(
|
|
5218
|
-
`Relay tunnel: heartbeat timeout, no inbound activity for ${idleMs}ms (threshold=${timeoutMs}ms)`
|
|
5219
|
-
);
|
|
5220
|
-
try {
|
|
5221
|
-
this.ws.terminate();
|
|
5222
|
-
} catch {
|
|
5223
|
-
}
|
|
5224
|
-
return;
|
|
5225
|
-
}
|
|
5226
|
-
this.opts.logger.info('Relay tunnel: \u2192 heartbeat "ping"');
|
|
5227
|
-
try {
|
|
5228
|
-
this.ws.send("ping");
|
|
5229
|
-
} catch {
|
|
5230
|
-
}
|
|
5231
|
-
try {
|
|
5232
|
-
this.ws.ping();
|
|
5233
|
-
} catch {
|
|
5234
|
-
}
|
|
5235
|
-
}
|
|
5214
|
+
this.sendHeartbeat();
|
|
5236
5215
|
}, intervalMs);
|
|
5237
5216
|
}
|
|
5238
5217
|
stopHeartbeat() {
|
|
@@ -5244,6 +5223,32 @@ var RelayClient = class {
|
|
|
5244
5223
|
getHeartbeatTimeoutMs() {
|
|
5245
5224
|
return this.opts.heartbeatSec * 3 * 1e3;
|
|
5246
5225
|
}
|
|
5226
|
+
sendHeartbeat() {
|
|
5227
|
+
if (this.ws?.readyState !== wrapper_default.OPEN) {
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
const idleMs = Date.now() - this.lastInboundAt;
|
|
5231
|
+
const timeoutMs = this.getHeartbeatTimeoutMs();
|
|
5232
|
+
if (idleMs >= timeoutMs) {
|
|
5233
|
+
this.opts.logger.warn(
|
|
5234
|
+
`Relay tunnel: heartbeat timeout, no inbound activity for ${idleMs}ms (threshold=${timeoutMs}ms)`
|
|
5235
|
+
);
|
|
5236
|
+
try {
|
|
5237
|
+
this.ws.terminate();
|
|
5238
|
+
} catch {
|
|
5239
|
+
}
|
|
5240
|
+
return;
|
|
5241
|
+
}
|
|
5242
|
+
this.opts.logger.info('Relay tunnel: \u2192 heartbeat "ping"');
|
|
5243
|
+
try {
|
|
5244
|
+
this.ws.send("ping");
|
|
5245
|
+
} catch {
|
|
5246
|
+
}
|
|
5247
|
+
try {
|
|
5248
|
+
this.ws.ping();
|
|
5249
|
+
} catch {
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5247
5252
|
markInboundActivity() {
|
|
5248
5253
|
this.lastInboundAt = Date.now();
|
|
5249
5254
|
}
|
|
@@ -5408,17 +5413,68 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
5408
5413
|
password
|
|
5409
5414
|
};
|
|
5410
5415
|
}
|
|
5411
|
-
|
|
5416
|
+
buildLocalGatewayAuthAttempts(baseHeaders) {
|
|
5412
5417
|
const auth = this.resolveGatewayConnectAuth();
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5418
|
+
const attempts = [];
|
|
5419
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5420
|
+
const authMode = this.opts.gatewayAuthMode;
|
|
5421
|
+
const addAttempt = (kind, secret) => {
|
|
5422
|
+
if (!secret) return;
|
|
5423
|
+
const dedupeKey = `${kind}:${secret}`;
|
|
5424
|
+
if (seen.has(dedupeKey)) return;
|
|
5425
|
+
seen.add(dedupeKey);
|
|
5426
|
+
const headers = { ...baseHeaders };
|
|
5427
|
+
headers.authorization = `Bearer ${secret}`;
|
|
5428
|
+
if (kind === "password") {
|
|
5429
|
+
headers["x-openclaw-password"] = secret;
|
|
5430
|
+
} else {
|
|
5431
|
+
delete headers["x-openclaw-password"];
|
|
5432
|
+
}
|
|
5433
|
+
attempts.push({
|
|
5434
|
+
label: kind === "token" ? "gateway-token" : "gateway-password",
|
|
5435
|
+
headers
|
|
5436
|
+
});
|
|
5437
|
+
};
|
|
5438
|
+
if (authMode === "password") {
|
|
5439
|
+
addAttempt("password", auth?.password);
|
|
5440
|
+
addAttempt("token", auth?.token);
|
|
5441
|
+
} else {
|
|
5442
|
+
addAttempt("token", auth?.token);
|
|
5443
|
+
addAttempt("password", auth?.password);
|
|
5417
5444
|
}
|
|
5418
|
-
if (
|
|
5419
|
-
|
|
5420
|
-
|
|
5445
|
+
if (attempts.length === 0) {
|
|
5446
|
+
attempts.push({
|
|
5447
|
+
label: "no-auth",
|
|
5448
|
+
headers: { ...baseHeaders }
|
|
5449
|
+
});
|
|
5421
5450
|
}
|
|
5451
|
+
return attempts;
|
|
5452
|
+
}
|
|
5453
|
+
async sendHttpResponse(frameId, res) {
|
|
5454
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
5455
|
+
const isStreaming = contentType.includes("text/event-stream");
|
|
5456
|
+
this.opts.logger.info(
|
|
5457
|
+
`TunnelProxy: response status=${res.status}, content-type=${contentType}, streaming=${isStreaming}`
|
|
5458
|
+
);
|
|
5459
|
+
if (isStreaming && res.body) {
|
|
5460
|
+
await this.streamResponse(frameId, res);
|
|
5461
|
+
return;
|
|
5462
|
+
}
|
|
5463
|
+
const body = await res.text();
|
|
5464
|
+
this.opts.logger.info(
|
|
5465
|
+
`TunnelProxy: response status=${res.status}, body=${body.substring(0, 200)}`
|
|
5466
|
+
);
|
|
5467
|
+
const headers = {};
|
|
5468
|
+
res.headers.forEach((value, key) => {
|
|
5469
|
+
headers[key] = value;
|
|
5470
|
+
});
|
|
5471
|
+
this.opts.client.send({
|
|
5472
|
+
type: "proxy_response",
|
|
5473
|
+
id: frameId,
|
|
5474
|
+
status: res.status,
|
|
5475
|
+
headers,
|
|
5476
|
+
body
|
|
5477
|
+
});
|
|
5422
5478
|
}
|
|
5423
5479
|
/** 处理从 Relay 收到的入站帧 */
|
|
5424
5480
|
async handleFrame(frame) {
|
|
@@ -5647,39 +5703,28 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
5647
5703
|
localHeaders[k] = v;
|
|
5648
5704
|
}
|
|
5649
5705
|
}
|
|
5650
|
-
this.
|
|
5706
|
+
const authAttempts = this.buildLocalGatewayAuthAttempts(localHeaders);
|
|
5651
5707
|
this.opts.logger.info(
|
|
5652
5708
|
`TunnelProxy: HTTP ${frame.method} ${frame.path} \u2192 ${url.toString()}, body=${frame.body?.substring(0, 200)}`
|
|
5653
5709
|
);
|
|
5654
5710
|
try {
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
const isStreaming = contentType.includes("text/event-stream");
|
|
5662
|
-
this.opts.logger.info(
|
|
5663
|
-
`TunnelProxy: response status=${res.status}, content-type=${contentType}, streaming=${isStreaming}`
|
|
5664
|
-
);
|
|
5665
|
-
if (isStreaming && res.body) {
|
|
5666
|
-
await this.streamResponse(frame.id, res);
|
|
5667
|
-
} else {
|
|
5668
|
-
const body = await res.text();
|
|
5669
|
-
this.opts.logger.info(
|
|
5670
|
-
`TunnelProxy: response status=${res.status}, body=${body.substring(0, 200)}`
|
|
5671
|
-
);
|
|
5672
|
-
const headers = {};
|
|
5673
|
-
res.headers.forEach((value, key) => {
|
|
5674
|
-
headers[key] = value;
|
|
5675
|
-
});
|
|
5676
|
-
this.opts.client.send({
|
|
5677
|
-
type: "proxy_response",
|
|
5678
|
-
id: frame.id,
|
|
5679
|
-
status: res.status,
|
|
5680
|
-
headers,
|
|
5681
|
-
body
|
|
5711
|
+
for (let attemptIndex = 0; attemptIndex < authAttempts.length; attemptIndex++) {
|
|
5712
|
+
const attempt = authAttempts[attemptIndex];
|
|
5713
|
+
const res = await fetch(url.toString(), {
|
|
5714
|
+
method: frame.method,
|
|
5715
|
+
headers: attempt.headers,
|
|
5716
|
+
body: frame.method !== "GET" && frame.method !== "HEAD" ? frame.body : void 0
|
|
5682
5717
|
});
|
|
5718
|
+
const hasFallback = attemptIndex < authAttempts.length - 1;
|
|
5719
|
+
if (res.status === 401 && hasFallback) {
|
|
5720
|
+
const body = await res.text();
|
|
5721
|
+
this.opts.logger.warn(
|
|
5722
|
+
`TunnelProxy: local gateway auth via ${attempt.label} returned 401, retrying next credential${body ? `, body=${body.substring(0, 200)}` : ""}`
|
|
5723
|
+
);
|
|
5724
|
+
continue;
|
|
5725
|
+
}
|
|
5726
|
+
await this.sendHttpResponse(frame.id, res);
|
|
5727
|
+
return;
|
|
5683
5728
|
}
|
|
5684
5729
|
} catch (err) {
|
|
5685
5730
|
this.opts.client.send({
|
|
@@ -5832,6 +5877,8 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
5832
5877
|
};
|
|
5833
5878
|
|
|
5834
5879
|
// src/tunnel/service.ts
|
|
5880
|
+
var DEFAULT_HEARTBEAT_SEC = 10;
|
|
5881
|
+
var DEFAULT_RECONNECT_BACKOFF_MS = 2e3;
|
|
5835
5882
|
function createTunnelService(opts) {
|
|
5836
5883
|
let client = null;
|
|
5837
5884
|
let proxy = null;
|
|
@@ -5938,7 +5985,7 @@ function createTunnelService(opts) {
|
|
|
5938
5985
|
const { logger } = opts;
|
|
5939
5986
|
const baseStateDir = join9(ctx.stateDir, "plugins", "phone-notifications");
|
|
5940
5987
|
logger.info(
|
|
5941
|
-
`Relay tunnel: starting (pid=${process.pid}, url=${opts.tunnelUrl}, heartbeat=${opts.heartbeatSec ??
|
|
5988
|
+
`Relay tunnel: starting (pid=${process.pid}, url=${opts.tunnelUrl}, heartbeat=${opts.heartbeatSec ?? DEFAULT_HEARTBEAT_SEC}s, backoff=${opts.reconnectBackoffMs ?? DEFAULT_RECONNECT_BACKOFF_MS}ms, gateway=${opts.gatewayBaseUrl}, hasGatewayToken=${!!opts.gatewayToken}, hasGatewayPwd=${!!opts.gatewayPassword})`
|
|
5942
5989
|
);
|
|
5943
5990
|
const statusFilePath = join9(baseStateDir, "tunnel-status.json");
|
|
5944
5991
|
const lockPath = join9(baseStateDir, "relay-tunnel.lock");
|
|
@@ -5949,13 +5996,14 @@ function createTunnelService(opts) {
|
|
|
5949
5996
|
client = new RelayClient({
|
|
5950
5997
|
tunnelUrl: opts.tunnelUrl,
|
|
5951
5998
|
token,
|
|
5952
|
-
heartbeatSec: opts.heartbeatSec ??
|
|
5953
|
-
reconnectBackoffMs: opts.reconnectBackoffMs ??
|
|
5999
|
+
heartbeatSec: opts.heartbeatSec ?? DEFAULT_HEARTBEAT_SEC,
|
|
6000
|
+
reconnectBackoffMs: opts.reconnectBackoffMs ?? DEFAULT_RECONNECT_BACKOFF_MS,
|
|
5954
6001
|
statusFilePath,
|
|
5955
6002
|
logger
|
|
5956
6003
|
});
|
|
5957
6004
|
proxy = new TunnelProxy({
|
|
5958
6005
|
gatewayBaseUrl: opts.gatewayBaseUrl,
|
|
6006
|
+
gatewayAuthMode: opts.gatewayAuthMode,
|
|
5959
6007
|
gatewayToken: opts.gatewayToken,
|
|
5960
6008
|
gatewayPassword: opts.gatewayPassword,
|
|
5961
6009
|
client,
|
|
@@ -6047,12 +6095,17 @@ function trimToUndefined(value) {
|
|
|
6047
6095
|
function resolveLocalGatewayAuth(params) {
|
|
6048
6096
|
const envGatewayToken = trimToUndefined(process.env.OPENCLAW_GATEWAY_TOKEN) ?? trimToUndefined(process.env.CLAWDBOT_GATEWAY_TOKEN);
|
|
6049
6097
|
const envGatewayPassword = trimToUndefined(process.env.OPENCLAW_GATEWAY_PASSWORD) ?? trimToUndefined(process.env.CLAWDBOT_GATEWAY_PASSWORD);
|
|
6098
|
+
let configGatewayAuthMode;
|
|
6050
6099
|
let configGatewayToken;
|
|
6051
6100
|
let configGatewayPassword;
|
|
6052
6101
|
const configPath = params.stateDir ? `${params.stateDir}/openclaw.json` : void 0;
|
|
6053
6102
|
if (configPath) {
|
|
6054
6103
|
try {
|
|
6055
6104
|
const configData = JSON.parse(readFileSync11(configPath, "utf-8"));
|
|
6105
|
+
const rawGatewayAuthMode = trimToUndefined(configData?.gateway?.auth?.mode);
|
|
6106
|
+
if (rawGatewayAuthMode === "token" || rawGatewayAuthMode === "password") {
|
|
6107
|
+
configGatewayAuthMode = rawGatewayAuthMode;
|
|
6108
|
+
}
|
|
6056
6109
|
configGatewayToken = trimToUndefined(configData?.gateway?.auth?.token);
|
|
6057
6110
|
configGatewayPassword = trimToUndefined(configData?.gateway?.auth?.password);
|
|
6058
6111
|
} catch (err) {
|
|
@@ -6064,6 +6117,7 @@ function resolveLocalGatewayAuth(params) {
|
|
|
6064
6117
|
}
|
|
6065
6118
|
}
|
|
6066
6119
|
return {
|
|
6120
|
+
gatewayAuthMode: configGatewayAuthMode,
|
|
6067
6121
|
gatewayToken: envGatewayToken ?? configGatewayToken,
|
|
6068
6122
|
gatewayPassword: envGatewayPassword ?? configGatewayPassword
|
|
6069
6123
|
};
|
|
@@ -6097,7 +6151,7 @@ var index_default = {
|
|
|
6097
6151
|
const tunnelUrl = "wss://openclaw-service-dev.yoootek.com/message/messages/ws/plugin";
|
|
6098
6152
|
if (tunnelUrl) {
|
|
6099
6153
|
const gatewayPort = process.env.OPENCLAW_GATEWAY_PORT || "18789";
|
|
6100
|
-
const { gatewayToken, gatewayPassword } = resolveLocalGatewayAuth({
|
|
6154
|
+
const { gatewayAuthMode, gatewayToken, gatewayPassword } = resolveLocalGatewayAuth({
|
|
6101
6155
|
stateDir: openclawDir,
|
|
6102
6156
|
logger
|
|
6103
6157
|
});
|
|
@@ -6106,6 +6160,7 @@ var index_default = {
|
|
|
6106
6160
|
heartbeatSec: config.relay?.heartbeatSec,
|
|
6107
6161
|
reconnectBackoffMs: config.relay?.reconnectBackoffMs,
|
|
6108
6162
|
gatewayBaseUrl: `http://localhost:${gatewayPort}`,
|
|
6163
|
+
gatewayAuthMode,
|
|
6109
6164
|
gatewayToken,
|
|
6110
6165
|
gatewayPassword,
|
|
6111
6166
|
logger
|