@yoooclaw/phone-notifications 1.11.12 → 1.11.13
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.cjs +110 -62
- package/dist/index.cjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5574,7 +5574,7 @@ function readBuildInjectedVersion() {
|
|
|
5574
5574
|
if (false) {
|
|
5575
5575
|
return void 0;
|
|
5576
5576
|
}
|
|
5577
|
-
const version = "1.11.
|
|
5577
|
+
const version = "1.11.13".trim();
|
|
5578
5578
|
return version || void 0;
|
|
5579
5579
|
}
|
|
5580
5580
|
function readPluginVersionFromPackageJson() {
|
|
@@ -12303,19 +12303,72 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
12303
12303
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
12304
12304
|
var wrapper_default = import_websocket.default;
|
|
12305
12305
|
|
|
12306
|
-
// src/tunnel/
|
|
12307
|
-
function previewText(text, max =
|
|
12306
|
+
// src/tunnel/utils.ts
|
|
12307
|
+
function previewText(text, max = 200) {
|
|
12308
|
+
if (!text) return "";
|
|
12308
12309
|
return text.length <= max ? text : `${text.substring(0, max)}\u2026`;
|
|
12309
12310
|
}
|
|
12310
|
-
var HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
12311
|
-
var CONNECT_WATCHDOG_MS = 2e4;
|
|
12312
12311
|
function maskSecret(value) {
|
|
12313
12312
|
if (!value) return "empty";
|
|
12314
12313
|
if (value.length <= 8) {
|
|
12315
|
-
return `${value.slice(0, 2)}
|
|
12314
|
+
return `${value.slice(0, 2)}...${value.slice(-2)}`;
|
|
12316
12315
|
}
|
|
12317
|
-
return `${value.slice(0, 4)}
|
|
12316
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
12318
12317
|
}
|
|
12318
|
+
function redactUrlSecrets(rawUrl) {
|
|
12319
|
+
try {
|
|
12320
|
+
const url = new URL(rawUrl);
|
|
12321
|
+
for (const key of ["apiKey", "token", "access_token"]) {
|
|
12322
|
+
const value = url.searchParams.get(key);
|
|
12323
|
+
if (value) {
|
|
12324
|
+
url.searchParams.set(key, maskSecret(value));
|
|
12325
|
+
}
|
|
12326
|
+
}
|
|
12327
|
+
return url.toString();
|
|
12328
|
+
} catch {
|
|
12329
|
+
return rawUrl.replace(
|
|
12330
|
+
/([?&](?:apiKey|token|access_token)=)([^&]+)/gi,
|
|
12331
|
+
(_match, prefix, value) => {
|
|
12332
|
+
let decoded = value;
|
|
12333
|
+
try {
|
|
12334
|
+
decoded = decodeURIComponent(value);
|
|
12335
|
+
} catch {
|
|
12336
|
+
}
|
|
12337
|
+
return `${prefix}${maskSecret(decoded)}`;
|
|
12338
|
+
}
|
|
12339
|
+
);
|
|
12340
|
+
}
|
|
12341
|
+
}
|
|
12342
|
+
function findHeaderValue(headers, key) {
|
|
12343
|
+
if (!headers) return void 0;
|
|
12344
|
+
const lowerKey = key.toLowerCase();
|
|
12345
|
+
for (const [headerKey, headerValue] of Object.entries(headers)) {
|
|
12346
|
+
if (headerKey.toLowerCase() === lowerKey) {
|
|
12347
|
+
return headerValue;
|
|
12348
|
+
}
|
|
12349
|
+
}
|
|
12350
|
+
return void 0;
|
|
12351
|
+
}
|
|
12352
|
+
function summarizeRequestHeaders(headers) {
|
|
12353
|
+
const contentType = findHeaderValue(headers, "content-type");
|
|
12354
|
+
const requestId = findHeaderValue(headers, "x-request-id");
|
|
12355
|
+
const parts = [];
|
|
12356
|
+
if (contentType) {
|
|
12357
|
+
parts.push(`contentType=${contentType}`);
|
|
12358
|
+
}
|
|
12359
|
+
if (requestId) {
|
|
12360
|
+
parts.push(`xRequestId=${previewText(requestId, 120)}`);
|
|
12361
|
+
}
|
|
12362
|
+
return parts.length ? `, ${parts.join(", ")}` : "";
|
|
12363
|
+
}
|
|
12364
|
+
|
|
12365
|
+
// src/tunnel/relay-client.ts
|
|
12366
|
+
function previewText2(text, max = 500) {
|
|
12367
|
+
return text.length <= max ? text : `${text.substring(0, max)}\u2026`;
|
|
12368
|
+
}
|
|
12369
|
+
var HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
12370
|
+
var CONNECT_WATCHDOG_MS = 2e4;
|
|
12371
|
+
var SEND_SKIPPED_LOG_INTERVAL_MS = 3e4;
|
|
12319
12372
|
var RelayClient = class {
|
|
12320
12373
|
constructor(opts) {
|
|
12321
12374
|
this.opts = opts;
|
|
@@ -12329,6 +12382,8 @@ var RelayClient = class {
|
|
|
12329
12382
|
aborted = false;
|
|
12330
12383
|
lastInboundAt = 0;
|
|
12331
12384
|
stopPromise = null;
|
|
12385
|
+
skippedSendLogLastAt = null;
|
|
12386
|
+
skippedSendLogSuppressed = 0;
|
|
12332
12387
|
/** 写连接状态到磁盘 */
|
|
12333
12388
|
writeStatus(state, lastDisconnectReason) {
|
|
12334
12389
|
if (!this.opts.statusFilePath) return;
|
|
@@ -12432,9 +12487,7 @@ var RelayClient = class {
|
|
|
12432
12487
|
);
|
|
12433
12488
|
this.ws.send(payload);
|
|
12434
12489
|
} else {
|
|
12435
|
-
this.
|
|
12436
|
-
`Relay tunnel: \u25B6 send skipped, ws not open (readyState=${this.ws?.readyState ?? "null"}), frame type=${frame.type}`
|
|
12437
|
-
);
|
|
12490
|
+
this.logSendSkipped("send", `frame type=${frame.type}`);
|
|
12438
12491
|
}
|
|
12439
12492
|
}
|
|
12440
12493
|
/** 原样透传文本到 Relay(用于 Gateway WS 响应直接回传) */
|
|
@@ -12445,9 +12498,7 @@ var RelayClient = class {
|
|
|
12445
12498
|
);
|
|
12446
12499
|
this.ws.send(text);
|
|
12447
12500
|
} else {
|
|
12448
|
-
this.
|
|
12449
|
-
`Relay tunnel: \u25B6 sendRaw skipped, ws not open (readyState=${this.ws?.readyState ?? "null"})`
|
|
12450
|
-
);
|
|
12501
|
+
this.logSendSkipped("sendRaw");
|
|
12451
12502
|
}
|
|
12452
12503
|
}
|
|
12453
12504
|
/** 启动连接,带自动重连,直到 abortSignal 触发 */
|
|
@@ -12473,7 +12524,7 @@ var RelayClient = class {
|
|
|
12473
12524
|
this.cleanup(true);
|
|
12474
12525
|
const rawApiKey = this.opts.apiKey.startsWith("Bearer ") ? this.opts.apiKey.slice("Bearer ".length) : this.opts.apiKey;
|
|
12475
12526
|
this.opts.logger.info(
|
|
12476
|
-
`Relay tunnel: connecting to ${this.opts.tunnelUrl} (attempt=${this.reconnectAttempt}, heartbeat=${this.opts.heartbeatSec}s, apiKey=${maskSecret(rawApiKey)})`
|
|
12527
|
+
`Relay tunnel: connecting to ${redactUrlSecrets(this.opts.tunnelUrl)} (attempt=${this.reconnectAttempt}, heartbeat=${this.opts.heartbeatSec}s, apiKey=${maskSecret(rawApiKey)})`
|
|
12477
12528
|
);
|
|
12478
12529
|
this.writeStatus("connecting");
|
|
12479
12530
|
return new Promise((resolve) => {
|
|
@@ -12567,7 +12618,7 @@ var RelayClient = class {
|
|
|
12567
12618
|
const reasonStr = reason.toString();
|
|
12568
12619
|
const lastInboundAgoMs = this.lastInboundAt ? Date.now() - this.lastInboundAt : null;
|
|
12569
12620
|
const isCurrentSocket = this.ws === ws;
|
|
12570
|
-
const logMessage = `Relay tunnel: disconnected (code=${code}, reason=${
|
|
12621
|
+
const logMessage = `Relay tunnel: disconnected (code=${code}, reason=${previewText2(reasonStr, 200)}, lastInboundAgoMs=${lastInboundAgoMs ?? "N/A"}, reconnectAttempt=${this.reconnectAttempt})`;
|
|
12571
12622
|
if (this.aborted || !isCurrentSocket) {
|
|
12572
12623
|
this.opts.logger.info(logMessage);
|
|
12573
12624
|
} else {
|
|
@@ -12582,11 +12633,12 @@ var RelayClient = class {
|
|
|
12582
12633
|
settle();
|
|
12583
12634
|
});
|
|
12584
12635
|
ws.on("error", (err2) => {
|
|
12636
|
+
clearConnectWatchdog();
|
|
12585
12637
|
this.opts.logger.error(
|
|
12586
|
-
`Relay tunnel: WebSocket error: ${err2.message} (readyState=${ws.readyState}, reconnectAttempt=${this.reconnectAttempt}, url=${wsUrl.toString()})`
|
|
12638
|
+
`Relay tunnel: WebSocket error: ${err2.message} (readyState=${ws.readyState}, reconnectAttempt=${this.reconnectAttempt}, url=${redactUrlSecrets(wsUrl.toString())})`
|
|
12587
12639
|
);
|
|
12588
12640
|
if (this.ws === ws) {
|
|
12589
|
-
this.
|
|
12641
|
+
this.forceReconnectFromSocket(ws, `error: ${err2.message}`);
|
|
12590
12642
|
}
|
|
12591
12643
|
settle();
|
|
12592
12644
|
});
|
|
@@ -12608,14 +12660,14 @@ var RelayClient = class {
|
|
|
12608
12660
|
return;
|
|
12609
12661
|
}
|
|
12610
12662
|
this.opts.logger.info(
|
|
12611
|
-
`Relay tunnel: \u2605 received message (${text.length} chars): ${
|
|
12663
|
+
`Relay tunnel: \u2605 received message (${text.length} chars): ${previewText2(text)}`
|
|
12612
12664
|
);
|
|
12613
12665
|
let frame;
|
|
12614
12666
|
try {
|
|
12615
12667
|
frame = JSON.parse(text);
|
|
12616
12668
|
} catch {
|
|
12617
12669
|
this.opts.logger.warn(
|
|
12618
|
-
`Relay tunnel: received invalid frame, ignoring (preview=${
|
|
12670
|
+
`Relay tunnel: received invalid frame, ignoring (preview=${previewText2(text, 200)})`
|
|
12619
12671
|
);
|
|
12620
12672
|
return;
|
|
12621
12673
|
}
|
|
@@ -12651,7 +12703,8 @@ var RelayClient = class {
|
|
|
12651
12703
|
return this.opts.heartbeatSec * 3 * 1e3;
|
|
12652
12704
|
}
|
|
12653
12705
|
sendHeartbeat() {
|
|
12654
|
-
|
|
12706
|
+
const ws = this.ws;
|
|
12707
|
+
if (ws?.readyState !== wrapper_default.OPEN) {
|
|
12655
12708
|
return;
|
|
12656
12709
|
}
|
|
12657
12710
|
const idleMs = Date.now() - this.lastInboundAt;
|
|
@@ -12660,19 +12713,16 @@ var RelayClient = class {
|
|
|
12660
12713
|
this.opts.logger.warn(
|
|
12661
12714
|
`Relay tunnel: heartbeat timeout, no inbound activity for ${idleMs}ms (threshold=${timeoutMs}ms)`
|
|
12662
12715
|
);
|
|
12663
|
-
|
|
12664
|
-
this.ws.terminate();
|
|
12665
|
-
} catch {
|
|
12666
|
-
}
|
|
12716
|
+
this.forceReconnectFromSocket(ws, `heartbeat-timeout idleMs=${idleMs}`);
|
|
12667
12717
|
return;
|
|
12668
12718
|
}
|
|
12669
12719
|
this.opts.logger.info('Relay tunnel: \u2192 heartbeat "ping"');
|
|
12670
12720
|
try {
|
|
12671
|
-
|
|
12721
|
+
ws.send("ping");
|
|
12672
12722
|
} catch {
|
|
12673
12723
|
}
|
|
12674
12724
|
try {
|
|
12675
|
-
|
|
12725
|
+
ws.ping();
|
|
12676
12726
|
} catch {
|
|
12677
12727
|
}
|
|
12678
12728
|
}
|
|
@@ -12700,6 +12750,34 @@ var RelayClient = class {
|
|
|
12700
12750
|
this.ws = null;
|
|
12701
12751
|
}
|
|
12702
12752
|
}
|
|
12753
|
+
forceReconnectFromSocket(ws, reason) {
|
|
12754
|
+
if (this.aborted || this.ws !== ws) return;
|
|
12755
|
+
this.stopHeartbeat();
|
|
12756
|
+
this.ws = null;
|
|
12757
|
+
this.writeStatus("disconnected", reason);
|
|
12758
|
+
this.scheduleReconnect();
|
|
12759
|
+
try {
|
|
12760
|
+
if (ws.readyState !== wrapper_default.CLOSED) {
|
|
12761
|
+
ws.terminate();
|
|
12762
|
+
}
|
|
12763
|
+
} catch {
|
|
12764
|
+
}
|
|
12765
|
+
}
|
|
12766
|
+
logSendSkipped(kind, detail) {
|
|
12767
|
+
const now = Date.now();
|
|
12768
|
+
const shouldLog = this.skippedSendLogLastAt === null || now - this.skippedSendLogLastAt >= SEND_SKIPPED_LOG_INTERVAL_MS;
|
|
12769
|
+
if (!shouldLog) {
|
|
12770
|
+
this.skippedSendLogSuppressed++;
|
|
12771
|
+
return;
|
|
12772
|
+
}
|
|
12773
|
+
const suppressedSuffix = this.skippedSendLogSuppressed > 0 ? `, suppressed=${this.skippedSendLogSuppressed}` : "";
|
|
12774
|
+
const detailSuffix = detail ? `, ${detail}` : "";
|
|
12775
|
+
this.opts.logger.warn(
|
|
12776
|
+
`Relay tunnel: \u25B6 ${kind} skipped, ws not open (readyState=${this.ws?.readyState ?? "null"}${detailSuffix}${suppressedSuffix})`
|
|
12777
|
+
);
|
|
12778
|
+
this.skippedSendLogLastAt = now;
|
|
12779
|
+
this.skippedSendLogSuppressed = 0;
|
|
12780
|
+
}
|
|
12703
12781
|
scheduleReconnect() {
|
|
12704
12782
|
if (this.aborted) return;
|
|
12705
12783
|
if (this.reconnectTimer) {
|
|
@@ -12727,36 +12805,6 @@ var RelayClient = class {
|
|
|
12727
12805
|
|
|
12728
12806
|
// src/tunnel/proxy.ts
|
|
12729
12807
|
var import_node_crypto5 = require("crypto");
|
|
12730
|
-
|
|
12731
|
-
// src/tunnel/utils.ts
|
|
12732
|
-
function previewText2(text, max = 200) {
|
|
12733
|
-
if (!text) return "";
|
|
12734
|
-
return text.length <= max ? text : `${text.substring(0, max)}\u2026`;
|
|
12735
|
-
}
|
|
12736
|
-
function findHeaderValue(headers, key) {
|
|
12737
|
-
if (!headers) return void 0;
|
|
12738
|
-
const lowerKey = key.toLowerCase();
|
|
12739
|
-
for (const [headerKey, headerValue] of Object.entries(headers)) {
|
|
12740
|
-
if (headerKey.toLowerCase() === lowerKey) {
|
|
12741
|
-
return headerValue;
|
|
12742
|
-
}
|
|
12743
|
-
}
|
|
12744
|
-
return void 0;
|
|
12745
|
-
}
|
|
12746
|
-
function summarizeRequestHeaders(headers) {
|
|
12747
|
-
const contentType = findHeaderValue(headers, "content-type");
|
|
12748
|
-
const requestId = findHeaderValue(headers, "x-request-id");
|
|
12749
|
-
const parts = [];
|
|
12750
|
-
if (contentType) {
|
|
12751
|
-
parts.push(`contentType=${contentType}`);
|
|
12752
|
-
}
|
|
12753
|
-
if (requestId) {
|
|
12754
|
-
parts.push(`xRequestId=${previewText2(requestId, 120)}`);
|
|
12755
|
-
}
|
|
12756
|
-
return parts.length ? `, ${parts.join(", ")}` : "";
|
|
12757
|
-
}
|
|
12758
|
-
|
|
12759
|
-
// src/tunnel/proxy.ts
|
|
12760
12808
|
init_host();
|
|
12761
12809
|
|
|
12762
12810
|
// src/tunnel/device-identity.ts
|
|
@@ -12995,7 +13043,7 @@ async function handleHttpRequest(opts, frame) {
|
|
|
12995
13043
|
localHeaders[RELAY_INTERNAL_HTTP_HEADER] = "1";
|
|
12996
13044
|
const authAttempts = buildLocalGatewayAuthAttempts(opts, localHeaders);
|
|
12997
13045
|
opts.logger.info(
|
|
12998
|
-
`TunnelProxy: HTTP id=${frame.id} ${frame.method} ${frame.path} \u2192 ${url.toString()}${summarizeRequestHeaders(frame.headers)}, authAttempts=${authAttempts.map((a) => a.label).join(" -> ")}, body=${
|
|
13046
|
+
`TunnelProxy: HTTP id=${frame.id} ${frame.method} ${frame.path} \u2192 ${url.toString()}${summarizeRequestHeaders(frame.headers)}, authAttempts=${authAttempts.map((a) => a.label).join(" -> ")}, body=${previewText(frame.body)}`
|
|
12999
13047
|
);
|
|
13000
13048
|
try {
|
|
13001
13049
|
for (let attemptIndex = 0; attemptIndex < authAttempts.length; attemptIndex++) {
|
|
@@ -13012,7 +13060,7 @@ async function handleHttpRequest(opts, frame) {
|
|
|
13012
13060
|
if (res.status === 401 && hasFallback) {
|
|
13013
13061
|
const body = await res.text();
|
|
13014
13062
|
opts.logger.warn(
|
|
13015
|
-
`TunnelProxy: HTTP id=${frame.id} local gateway auth via ${attempt.label} returned 401 after ${Date.now() - startedAtMs}ms, retrying next credential${body ? `, body=${
|
|
13063
|
+
`TunnelProxy: HTTP id=${frame.id} local gateway auth via ${attempt.label} returned 401 after ${Date.now() - startedAtMs}ms, retrying next credential${body ? `, body=${previewText(body)}` : ""}`
|
|
13016
13064
|
);
|
|
13017
13065
|
continue;
|
|
13018
13066
|
}
|
|
@@ -13053,7 +13101,7 @@ async function sendHttpResponse(opts, params) {
|
|
|
13053
13101
|
}
|
|
13054
13102
|
const body = await res.text();
|
|
13055
13103
|
opts.logger.info(
|
|
13056
|
-
`TunnelProxy: HTTP id=${frameId} response body=${
|
|
13104
|
+
`TunnelProxy: HTTP id=${frameId} response body=${previewText(body)}`
|
|
13057
13105
|
);
|
|
13058
13106
|
const headers = {};
|
|
13059
13107
|
res.headers.forEach((value, key) => {
|
|
@@ -13678,7 +13726,7 @@ var TunnelProxy = class {
|
|
|
13678
13726
|
return;
|
|
13679
13727
|
}
|
|
13680
13728
|
this.opts.logger.error(
|
|
13681
|
-
`TunnelProxy: RPC WS handshake failed (pending=${this.gatewayWsPending.length}): ${
|
|
13729
|
+
`TunnelProxy: RPC WS handshake failed (pending=${this.gatewayWsPending.length}): ${previewText(JSON.stringify(frame.error), 500)}`
|
|
13682
13730
|
);
|
|
13683
13731
|
ws.close();
|
|
13684
13732
|
return;
|
|
@@ -13854,7 +13902,7 @@ function createTunnelService(opts) {
|
|
|
13854
13902
|
JSON.stringify({
|
|
13855
13903
|
pid: process.pid,
|
|
13856
13904
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13857
|
-
tunnelUrl: opts.tunnelUrl
|
|
13905
|
+
tunnelUrl: redactUrlSecrets(opts.tunnelUrl)
|
|
13858
13906
|
}) + "\n"
|
|
13859
13907
|
);
|
|
13860
13908
|
lockFilePath = filePath;
|
|
@@ -13920,7 +13968,7 @@ function createTunnelService(opts) {
|
|
|
13920
13968
|
const { logger } = opts;
|
|
13921
13969
|
const baseStateDir = (0, import_node_path24.join)(ctx.stateDir, "plugins", "phone-notifications");
|
|
13922
13970
|
logger.info(
|
|
13923
|
-
`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})`
|
|
13971
|
+
`Relay tunnel: starting (pid=${process.pid}, url=${redactUrlSecrets(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})`
|
|
13924
13972
|
);
|
|
13925
13973
|
const statusFilePath = (0, import_node_path24.join)(baseStateDir, "tunnel-status.json");
|
|
13926
13974
|
const lockPath = (0, import_node_path24.join)(baseStateDir, "relay-tunnel.lock");
|