@yoooclaw/phone-notifications 1.7.0 → 1.7.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 +154 -45
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -3648,7 +3648,7 @@ var require_websocket_server = __commonJS({
|
|
|
3648
3648
|
|
|
3649
3649
|
// src/index.ts
|
|
3650
3650
|
import { readFileSync as readFileSync14 } from "fs";
|
|
3651
|
-
import { basename as basename2, dirname as
|
|
3651
|
+
import { basename as basename2, dirname as dirname6 } from "path";
|
|
3652
3652
|
|
|
3653
3653
|
// src/light/protocol.ts
|
|
3654
3654
|
var MAX_LIGHT_SEGMENTS = 12;
|
|
@@ -3794,11 +3794,11 @@ import { randomUUID } from "crypto";
|
|
|
3794
3794
|
|
|
3795
3795
|
// src/env.ts
|
|
3796
3796
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
3797
|
-
import { dirname } from "path";
|
|
3797
|
+
import { dirname as dirname2 } from "path";
|
|
3798
3798
|
|
|
3799
3799
|
// src/host.ts
|
|
3800
3800
|
import { existsSync, readFileSync } from "fs";
|
|
3801
|
-
import { join } from "path";
|
|
3801
|
+
import { dirname, join } from "path";
|
|
3802
3802
|
function trimToUndefined(value) {
|
|
3803
3803
|
if (typeof value !== "string") {
|
|
3804
3804
|
return void 0;
|
|
@@ -3809,6 +3809,18 @@ function trimToUndefined(value) {
|
|
|
3809
3809
|
function homeDir() {
|
|
3810
3810
|
return process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
3811
3811
|
}
|
|
3812
|
+
function expandUserPath(value) {
|
|
3813
|
+
if (!value) {
|
|
3814
|
+
return void 0;
|
|
3815
|
+
}
|
|
3816
|
+
if (value === "~") {
|
|
3817
|
+
return homeDir();
|
|
3818
|
+
}
|
|
3819
|
+
if (value.startsWith("~/")) {
|
|
3820
|
+
return join(homeDir(), value.slice(2));
|
|
3821
|
+
}
|
|
3822
|
+
return value;
|
|
3823
|
+
}
|
|
3812
3824
|
function candidateMetaPaths() {
|
|
3813
3825
|
const home = homeDir();
|
|
3814
3826
|
return [
|
|
@@ -3824,8 +3836,8 @@ function loadQClawMeta() {
|
|
|
3824
3836
|
try {
|
|
3825
3837
|
const parsed = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
3826
3838
|
return {
|
|
3827
|
-
stateDir: trimToUndefined(parsed?.stateDir),
|
|
3828
|
-
configPath: trimToUndefined(parsed?.configPath)
|
|
3839
|
+
stateDir: expandUserPath(trimToUndefined(parsed?.stateDir)),
|
|
3840
|
+
configPath: expandUserPath(trimToUndefined(parsed?.configPath))
|
|
3829
3841
|
};
|
|
3830
3842
|
} catch {
|
|
3831
3843
|
}
|
|
@@ -3833,21 +3845,42 @@ function loadQClawMeta() {
|
|
|
3833
3845
|
return void 0;
|
|
3834
3846
|
}
|
|
3835
3847
|
function resolveStateDirFromEnv() {
|
|
3836
|
-
return
|
|
3848
|
+
return expandUserPath(
|
|
3849
|
+
trimToUndefined(process.env.OPENCLAW_STATE_DIR) ?? trimToUndefined(process.env.QCLAW_STATE_DIR) ?? trimToUndefined(process.env.OPENCLAW_ROOT) ?? trimToUndefined(process.env.OPENCLAW_HOME) ?? trimToUndefined(process.env.QCLAW_HOME)
|
|
3850
|
+
);
|
|
3851
|
+
}
|
|
3852
|
+
function resolveConfigPathFromEnv() {
|
|
3853
|
+
return expandUserPath(
|
|
3854
|
+
trimToUndefined(process.env.OPENCLAW_CONFIG_PATH) ?? trimToUndefined(process.env.QCLAW_CONFIG_PATH)
|
|
3855
|
+
);
|
|
3856
|
+
}
|
|
3857
|
+
function hasOpenClawMarkers() {
|
|
3858
|
+
const baseDir = join(homeDir(), ".openclaw");
|
|
3859
|
+
return [
|
|
3860
|
+
join(baseDir, "openclaw.json"),
|
|
3861
|
+
join(baseDir, "credentials.json"),
|
|
3862
|
+
join(baseDir, "extensions")
|
|
3863
|
+
].some((candidate) => existsSync(candidate));
|
|
3837
3864
|
}
|
|
3838
3865
|
function resolveStateDir() {
|
|
3839
3866
|
const envDir = resolveStateDirFromEnv();
|
|
3840
3867
|
if (envDir) {
|
|
3841
3868
|
return envDir;
|
|
3842
3869
|
}
|
|
3870
|
+
if (hasOpenClawMarkers()) {
|
|
3871
|
+
return join(homeDir(), ".openclaw");
|
|
3872
|
+
}
|
|
3843
3873
|
const meta = loadQClawMeta();
|
|
3844
3874
|
if (meta?.stateDir) {
|
|
3845
3875
|
return meta.stateDir;
|
|
3846
3876
|
}
|
|
3877
|
+
if (meta?.configPath) {
|
|
3878
|
+
return dirname(meta.configPath);
|
|
3879
|
+
}
|
|
3847
3880
|
return join(homeDir(), ".openclaw");
|
|
3848
3881
|
}
|
|
3849
3882
|
function resolveConfigPath(stateDir = resolveStateDir()) {
|
|
3850
|
-
const envConfigPath =
|
|
3883
|
+
const envConfigPath = resolveConfigPathFromEnv();
|
|
3851
3884
|
if (envConfigPath) {
|
|
3852
3885
|
return envConfigPath;
|
|
3853
3886
|
}
|
|
@@ -3893,7 +3926,7 @@ function saveEnvName(env) {
|
|
|
3893
3926
|
throw new Error(`\u65E0\u6548\u7684\u73AF\u5883\u540D\u79F0: ${env}\uFF0C\u53EF\u9009\u503C: ${[...VALID_ENVS].join(", ")}`);
|
|
3894
3927
|
}
|
|
3895
3928
|
const filePath = envFilePath();
|
|
3896
|
-
mkdirSync(
|
|
3929
|
+
mkdirSync(dirname2(filePath), { recursive: true, mode: 448 });
|
|
3897
3930
|
writeFileSync(filePath, JSON.stringify({ env }, null, 2), {
|
|
3898
3931
|
encoding: "utf-8",
|
|
3899
3932
|
mode: 384
|
|
@@ -3964,7 +3997,7 @@ import {
|
|
|
3964
3997
|
writeFileSync as writeFileSync2,
|
|
3965
3998
|
watch
|
|
3966
3999
|
} from "fs";
|
|
3967
|
-
import { dirname as
|
|
4000
|
+
import { dirname as dirname3, basename } from "path";
|
|
3968
4001
|
function credentialsPath() {
|
|
3969
4002
|
return resolveStateFile("credentials.json");
|
|
3970
4003
|
}
|
|
@@ -3979,7 +4012,7 @@ function readCredentials() {
|
|
|
3979
4012
|
}
|
|
3980
4013
|
function writeCredentials(creds) {
|
|
3981
4014
|
const path2 = credentialsPath();
|
|
3982
|
-
mkdirSync2(
|
|
4015
|
+
mkdirSync2(dirname3(path2), { recursive: true, mode: 448 });
|
|
3983
4016
|
writeFileSync2(path2, JSON.stringify(creds, null, 2), {
|
|
3984
4017
|
encoding: "utf-8",
|
|
3985
4018
|
mode: 384
|
|
@@ -4000,7 +4033,7 @@ function requireApiKey() {
|
|
|
4000
4033
|
}
|
|
4001
4034
|
function watchCredentials(onChange) {
|
|
4002
4035
|
const path2 = credentialsPath();
|
|
4003
|
-
const dir =
|
|
4036
|
+
const dir = dirname3(path2);
|
|
4004
4037
|
const filename = basename(path2);
|
|
4005
4038
|
let debounceTimer = null;
|
|
4006
4039
|
const delayMs = 200;
|
|
@@ -5447,11 +5480,11 @@ import {
|
|
|
5447
5480
|
unlinkSync,
|
|
5448
5481
|
writeFileSync as writeFileSync9
|
|
5449
5482
|
} from "fs";
|
|
5450
|
-
import { dirname as
|
|
5483
|
+
import { dirname as dirname5, join as join10 } from "path";
|
|
5451
5484
|
|
|
5452
5485
|
// src/tunnel/relay-client.ts
|
|
5453
5486
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
5454
|
-
import { dirname as
|
|
5487
|
+
import { dirname as dirname4 } from "path";
|
|
5455
5488
|
|
|
5456
5489
|
// node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
|
|
5457
5490
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -5462,6 +5495,16 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
5462
5495
|
var wrapper_default = import_websocket.default;
|
|
5463
5496
|
|
|
5464
5497
|
// src/tunnel/relay-client.ts
|
|
5498
|
+
function previewText(text, max = 500) {
|
|
5499
|
+
return text.length <= max ? text : `${text.substring(0, max)}\u2026`;
|
|
5500
|
+
}
|
|
5501
|
+
function maskSecret(value) {
|
|
5502
|
+
if (!value) return "empty";
|
|
5503
|
+
if (value.length <= 8) {
|
|
5504
|
+
return `${value.slice(0, 2)}\u2026${value.slice(-2)}`;
|
|
5505
|
+
}
|
|
5506
|
+
return `${value.slice(0, 4)}\u2026${value.slice(-4)}`;
|
|
5507
|
+
}
|
|
5465
5508
|
var RelayClient = class {
|
|
5466
5509
|
constructor(opts) {
|
|
5467
5510
|
this.opts = opts;
|
|
@@ -5483,7 +5526,7 @@ var RelayClient = class {
|
|
|
5483
5526
|
lastDisconnectReason
|
|
5484
5527
|
};
|
|
5485
5528
|
try {
|
|
5486
|
-
mkdirSync6(
|
|
5529
|
+
mkdirSync6(dirname4(this.opts.statusFilePath), { recursive: true });
|
|
5487
5530
|
writeFileSync8(this.opts.statusFilePath, JSON.stringify(info, null, 2));
|
|
5488
5531
|
} catch {
|
|
5489
5532
|
}
|
|
@@ -5542,8 +5585,9 @@ var RelayClient = class {
|
|
|
5542
5585
|
async connect() {
|
|
5543
5586
|
if (this.aborted) return;
|
|
5544
5587
|
this.cleanup(true);
|
|
5588
|
+
const rawApiKey = this.opts.apiKey.startsWith("Bearer ") ? this.opts.apiKey.slice("Bearer ".length) : this.opts.apiKey;
|
|
5545
5589
|
this.opts.logger.info(
|
|
5546
|
-
`Relay tunnel: connecting to ${this.opts.tunnelUrl} (attempt=${this.reconnectAttempt}, heartbeat=${this.opts.heartbeatSec}s)`
|
|
5590
|
+
`Relay tunnel: connecting to ${this.opts.tunnelUrl} (attempt=${this.reconnectAttempt}, heartbeat=${this.opts.heartbeatSec}s, apiKey=${maskSecret(rawApiKey)})`
|
|
5547
5591
|
);
|
|
5548
5592
|
this.writeStatus("connecting");
|
|
5549
5593
|
return new Promise((resolve) => {
|
|
@@ -5554,7 +5598,6 @@ var RelayClient = class {
|
|
|
5554
5598
|
resolve();
|
|
5555
5599
|
}
|
|
5556
5600
|
};
|
|
5557
|
-
const rawApiKey = this.opts.apiKey.startsWith("Bearer ") ? this.opts.apiKey.slice("Bearer ".length) : this.opts.apiKey;
|
|
5558
5601
|
const wsUrl = new URL(this.opts.tunnelUrl);
|
|
5559
5602
|
if (!wsUrl.searchParams.get("apiKey")) {
|
|
5560
5603
|
wsUrl.searchParams.set("apiKey", rawApiKey);
|
|
@@ -5598,8 +5641,9 @@ var RelayClient = class {
|
|
|
5598
5641
|
});
|
|
5599
5642
|
ws.on("close", (code, reason) => {
|
|
5600
5643
|
const reasonStr = reason.toString();
|
|
5644
|
+
const lastInboundAgoMs = this.lastInboundAt ? Date.now() - this.lastInboundAt : null;
|
|
5601
5645
|
this.opts.logger.warn(
|
|
5602
|
-
`Relay tunnel: disconnected (code=${code}, reason=${reasonStr})`
|
|
5646
|
+
`Relay tunnel: disconnected (code=${code}, reason=${previewText(reasonStr, 200)}, lastInboundAgoMs=${lastInboundAgoMs ?? "N/A"}, reconnectAttempt=${this.reconnectAttempt})`
|
|
5603
5647
|
);
|
|
5604
5648
|
if (this.ws === ws) {
|
|
5605
5649
|
this.stopHeartbeat();
|
|
@@ -5610,7 +5654,9 @@ var RelayClient = class {
|
|
|
5610
5654
|
settle();
|
|
5611
5655
|
});
|
|
5612
5656
|
ws.on("error", (err) => {
|
|
5613
|
-
this.opts.logger.error(
|
|
5657
|
+
this.opts.logger.error(
|
|
5658
|
+
`Relay tunnel: WebSocket error: ${err.message} (readyState=${ws.readyState}, reconnectAttempt=${this.reconnectAttempt}, url=${wsUrl.toString()})`
|
|
5659
|
+
);
|
|
5614
5660
|
settle();
|
|
5615
5661
|
});
|
|
5616
5662
|
});
|
|
@@ -5621,13 +5667,16 @@ var RelayClient = class {
|
|
|
5621
5667
|
if (text === "pong") {
|
|
5622
5668
|
return;
|
|
5623
5669
|
}
|
|
5624
|
-
|
|
5625
|
-
|
|
5670
|
+
this.opts.logger.info(
|
|
5671
|
+
`Relay tunnel: \u2605 received message (${text.length} chars): ${previewText(text)}`
|
|
5672
|
+
);
|
|
5626
5673
|
let frame;
|
|
5627
5674
|
try {
|
|
5628
5675
|
frame = JSON.parse(text);
|
|
5629
5676
|
} catch {
|
|
5630
|
-
this.opts.logger.warn(
|
|
5677
|
+
this.opts.logger.warn(
|
|
5678
|
+
`Relay tunnel: received invalid frame, ignoring (preview=${previewText(text, 200)})`
|
|
5679
|
+
);
|
|
5631
5680
|
return;
|
|
5632
5681
|
}
|
|
5633
5682
|
this.opts.logger.info(`Relay tunnel: parsed frame type=${frame.type}, id=${"id" in frame ? frame.id : "N/A"}`);
|
|
@@ -5742,6 +5791,32 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
5742
5791
|
import crypto from "crypto";
|
|
5743
5792
|
import fs from "fs";
|
|
5744
5793
|
import path from "path";
|
|
5794
|
+
function previewText2(text, max = 200) {
|
|
5795
|
+
if (!text) return "";
|
|
5796
|
+
return text.length <= max ? text : `${text.substring(0, max)}\u2026`;
|
|
5797
|
+
}
|
|
5798
|
+
function findHeaderValue(headers, key) {
|
|
5799
|
+
if (!headers) return void 0;
|
|
5800
|
+
const lowerKey = key.toLowerCase();
|
|
5801
|
+
for (const [headerKey, headerValue] of Object.entries(headers)) {
|
|
5802
|
+
if (headerKey.toLowerCase() === lowerKey) {
|
|
5803
|
+
return headerValue;
|
|
5804
|
+
}
|
|
5805
|
+
}
|
|
5806
|
+
return void 0;
|
|
5807
|
+
}
|
|
5808
|
+
function summarizeRequestHeaders(headers) {
|
|
5809
|
+
const contentType = findHeaderValue(headers, "content-type");
|
|
5810
|
+
const requestId = findHeaderValue(headers, "x-request-id");
|
|
5811
|
+
const parts = [];
|
|
5812
|
+
if (contentType) {
|
|
5813
|
+
parts.push(`contentType=${contentType}`);
|
|
5814
|
+
}
|
|
5815
|
+
if (requestId) {
|
|
5816
|
+
parts.push(`xRequestId=${previewText2(requestId, 120)}`);
|
|
5817
|
+
}
|
|
5818
|
+
return parts.length ? `, ${parts.join(", ")}` : "";
|
|
5819
|
+
}
|
|
5745
5820
|
var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
5746
5821
|
function base64UrlEncode(buf) {
|
|
5747
5822
|
return buf.toString("base64").replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/g, "");
|
|
@@ -5840,6 +5915,12 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
5840
5915
|
gatewayWsPending = [];
|
|
5841
5916
|
/** 设备身份,用于 Gateway connect 握手 */
|
|
5842
5917
|
deviceIdentity;
|
|
5918
|
+
pushGatewayPending(payload, reason) {
|
|
5919
|
+
this.gatewayWsPending.push(payload);
|
|
5920
|
+
this.opts.logger.info(
|
|
5921
|
+
`TunnelProxy: gateway WS pending queue size=${this.gatewayWsPending.length} (${reason})`
|
|
5922
|
+
);
|
|
5923
|
+
}
|
|
5843
5924
|
resolveGatewayConnectAuth() {
|
|
5844
5925
|
const token = this.opts.gatewayToken?.trim() || void 0;
|
|
5845
5926
|
const password = this.opts.gatewayPassword?.trim() || void 0;
|
|
@@ -5888,19 +5969,21 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
5888
5969
|
}
|
|
5889
5970
|
return attempts;
|
|
5890
5971
|
}
|
|
5891
|
-
async sendHttpResponse(
|
|
5972
|
+
async sendHttpResponse(params) {
|
|
5973
|
+
const { frameId, method, path: path2, authLabel, startedAtMs, res } = params;
|
|
5892
5974
|
const contentType = res.headers.get("content-type") ?? "";
|
|
5893
5975
|
const isStreaming = contentType.includes("text/event-stream");
|
|
5976
|
+
const elapsedMs = Date.now() - startedAtMs;
|
|
5894
5977
|
this.opts.logger.info(
|
|
5895
|
-
`TunnelProxy:
|
|
5978
|
+
`TunnelProxy: HTTP id=${frameId} ${method} ${path2} <= ${res.status} (${elapsedMs}ms, auth=${authLabel}, content-type=${contentType}, streaming=${isStreaming})`
|
|
5896
5979
|
);
|
|
5897
5980
|
if (isStreaming && res.body) {
|
|
5898
|
-
await this.streamResponse(frameId, res);
|
|
5981
|
+
await this.streamResponse(frameId, res, startedAtMs);
|
|
5899
5982
|
return;
|
|
5900
5983
|
}
|
|
5901
5984
|
const body = await res.text();
|
|
5902
5985
|
this.opts.logger.info(
|
|
5903
|
-
`TunnelProxy:
|
|
5986
|
+
`TunnelProxy: HTTP id=${frameId} response body=${previewText2(body)}`
|
|
5904
5987
|
);
|
|
5905
5988
|
const headers = {};
|
|
5906
5989
|
res.headers.forEach((value, key) => {
|
|
@@ -5949,7 +6032,7 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
5949
6032
|
if (this.gatewayWsReady && this.gatewayWs?.readyState === wrapper_default.OPEN) {
|
|
5950
6033
|
this.gatewayWs.send(payload);
|
|
5951
6034
|
} else {
|
|
5952
|
-
this.
|
|
6035
|
+
this.pushGatewayPending(payload, "raw frame queued before gateway WS ready");
|
|
5953
6036
|
}
|
|
5954
6037
|
}
|
|
5955
6038
|
/** 清理所有代理的 WebSocket 连接 */
|
|
@@ -5983,12 +6066,14 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
5983
6066
|
this.gatewayWsConnecting = true;
|
|
5984
6067
|
this.gatewayWsReady = false;
|
|
5985
6068
|
const wsUrl = this.opts.gatewayBaseUrl.replace(/^http/, "ws");
|
|
5986
|
-
this.opts.logger.info(
|
|
6069
|
+
this.opts.logger.info(
|
|
6070
|
+
`TunnelProxy: RPC WS connecting to gateway ${wsUrl} (pending=${this.gatewayWsPending.length})`
|
|
6071
|
+
);
|
|
5987
6072
|
const ws = new wrapper_default(wsUrl);
|
|
5988
6073
|
ws.on("open", () => {
|
|
5989
6074
|
this.gatewayWs = ws;
|
|
5990
6075
|
this.opts.logger.info(
|
|
5991
|
-
|
|
6076
|
+
`TunnelProxy: RPC WS tcp connected, waiting for connect.challenge (pending=${this.gatewayWsPending.length})`
|
|
5992
6077
|
);
|
|
5993
6078
|
});
|
|
5994
6079
|
ws.on("message", (data) => {
|
|
@@ -6015,15 +6100,16 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6015
6100
|
}
|
|
6016
6101
|
if (frame.type === "event" && frame.event === "connect.challenge") {
|
|
6017
6102
|
const challengeNonce = frame.payload?.nonce ?? "";
|
|
6103
|
+
const connectRequestId = `tunnel-connect-${randomUUID2()}`;
|
|
6104
|
+
const gatewayAuth = this.resolveGatewayConnectAuth();
|
|
6018
6105
|
this.opts.logger.info(
|
|
6019
|
-
`TunnelProxy: received connect.challenge (nonce=${challengeNonce}), sending connect request with device identity`
|
|
6106
|
+
`TunnelProxy: received connect.challenge (nonce=${challengeNonce}, connectReqId=${connectRequestId}, hasToken=${!!gatewayAuth?.token}, hasPassword=${!!gatewayAuth?.password}), sending connect request with device identity`
|
|
6020
6107
|
);
|
|
6021
6108
|
const role = "operator";
|
|
6022
6109
|
const scopes = ["operator.admin"];
|
|
6023
6110
|
const signedAtMs = Date.now();
|
|
6024
6111
|
const clientId = "gateway-client";
|
|
6025
6112
|
const clientMode = "backend";
|
|
6026
|
-
const gatewayAuth = this.resolveGatewayConnectAuth();
|
|
6027
6113
|
const authPayload = buildDeviceAuthPayload({
|
|
6028
6114
|
deviceId: this.deviceIdentity.deviceId,
|
|
6029
6115
|
clientId,
|
|
@@ -6040,7 +6126,7 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6040
6126
|
);
|
|
6041
6127
|
const connectReq = {
|
|
6042
6128
|
type: "req",
|
|
6043
|
-
id:
|
|
6129
|
+
id: connectRequestId,
|
|
6044
6130
|
method: "connect",
|
|
6045
6131
|
params: {
|
|
6046
6132
|
minProtocol: 3,
|
|
@@ -6082,7 +6168,7 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6082
6168
|
}
|
|
6083
6169
|
if (frame.type === "res" && frame.ok === false && !this.gatewayWsReady) {
|
|
6084
6170
|
this.opts.logger.error(
|
|
6085
|
-
`TunnelProxy: RPC WS handshake failed: ${JSON.stringify(frame.error)}`
|
|
6171
|
+
`TunnelProxy: RPC WS handshake failed (pending=${this.gatewayWsPending.length}): ${previewText2(JSON.stringify(frame.error), 500)}`
|
|
6086
6172
|
);
|
|
6087
6173
|
ws.close();
|
|
6088
6174
|
return;
|
|
@@ -6090,8 +6176,10 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6090
6176
|
this.opts.client.sendRaw(text);
|
|
6091
6177
|
});
|
|
6092
6178
|
ws.on("close", (code, reason) => {
|
|
6179
|
+
const wasReady = this.gatewayWsReady;
|
|
6180
|
+
const pendingCount = this.gatewayWsPending.length;
|
|
6093
6181
|
this.opts.logger.info(
|
|
6094
|
-
`TunnelProxy: RPC WS closed by gateway (code=${code}, reason=${reason.toString()})`
|
|
6182
|
+
`TunnelProxy: RPC WS closed by gateway (code=${code}, reason=${reason.toString()}, ready=${wasReady}, pending=${pendingCount}, activeWs=${this.wsConnections.size})`
|
|
6095
6183
|
);
|
|
6096
6184
|
if (this.gatewayWs === ws) {
|
|
6097
6185
|
this.gatewayWs = null;
|
|
@@ -6100,7 +6188,9 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6100
6188
|
this.gatewayWsConnecting = false;
|
|
6101
6189
|
});
|
|
6102
6190
|
ws.on("error", (err) => {
|
|
6103
|
-
this.opts.logger.warn(
|
|
6191
|
+
this.opts.logger.warn(
|
|
6192
|
+
`TunnelProxy: RPC WS error: ${err.message} (ready=${this.gatewayWsReady}, pending=${this.gatewayWsPending.length}, activeWs=${this.wsConnections.size})`
|
|
6193
|
+
);
|
|
6104
6194
|
this.gatewayWsConnecting = false;
|
|
6105
6195
|
if (this.gatewayWs === ws) {
|
|
6106
6196
|
this.gatewayWs = null;
|
|
@@ -6121,7 +6211,10 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6121
6211
|
this.opts.logger.info(
|
|
6122
6212
|
`TunnelProxy: req id=${frame.id} queued, gateway WS not ready yet`
|
|
6123
6213
|
);
|
|
6124
|
-
this.
|
|
6214
|
+
this.pushGatewayPending(
|
|
6215
|
+
payload,
|
|
6216
|
+
`req id=${frame.id} queued before gateway WS handshake`
|
|
6217
|
+
);
|
|
6125
6218
|
}
|
|
6126
6219
|
}
|
|
6127
6220
|
// ─── 路径映射 ───
|
|
@@ -6133,7 +6226,9 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6133
6226
|
}
|
|
6134
6227
|
// ─── HTTP 请求代理 ───
|
|
6135
6228
|
async handleHttpRequest(frame) {
|
|
6136
|
-
const
|
|
6229
|
+
const mappedPath = this.mapPath(frame.path);
|
|
6230
|
+
const url = new URL(mappedPath, this.opts.gatewayBaseUrl);
|
|
6231
|
+
const startedAtMs = Date.now();
|
|
6137
6232
|
const localHeaders = {};
|
|
6138
6233
|
for (const [k, v] of Object.entries(frame.headers ?? {})) {
|
|
6139
6234
|
const lower = k.toLowerCase();
|
|
@@ -6143,11 +6238,14 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6143
6238
|
}
|
|
6144
6239
|
const authAttempts = this.buildLocalGatewayAuthAttempts(localHeaders);
|
|
6145
6240
|
this.opts.logger.info(
|
|
6146
|
-
`TunnelProxy: HTTP ${frame.method} ${frame.path} \u2192 ${url.toString()}, body=${frame.body
|
|
6241
|
+
`TunnelProxy: HTTP id=${frame.id} ${frame.method} ${frame.path} \u2192 ${url.toString()}${summarizeRequestHeaders(frame.headers)}, authAttempts=${authAttempts.map((attempt) => attempt.label).join(" -> ")}, body=${previewText2(frame.body)}`
|
|
6147
6242
|
);
|
|
6148
6243
|
try {
|
|
6149
6244
|
for (let attemptIndex = 0; attemptIndex < authAttempts.length; attemptIndex++) {
|
|
6150
6245
|
const attempt = authAttempts[attemptIndex];
|
|
6246
|
+
this.opts.logger.info(
|
|
6247
|
+
`TunnelProxy: HTTP id=${frame.id} attempt ${attemptIndex + 1}/${authAttempts.length} auth=${attempt.label}`
|
|
6248
|
+
);
|
|
6151
6249
|
const res = await fetch(url.toString(), {
|
|
6152
6250
|
method: frame.method,
|
|
6153
6251
|
headers: attempt.headers,
|
|
@@ -6157,23 +6255,34 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6157
6255
|
if (res.status === 401 && hasFallback) {
|
|
6158
6256
|
const body = await res.text();
|
|
6159
6257
|
this.opts.logger.warn(
|
|
6160
|
-
`TunnelProxy: local gateway auth via ${attempt.label} returned 401, retrying next credential${body ? `, body=${body
|
|
6258
|
+
`TunnelProxy: HTTP id=${frame.id} local gateway auth via ${attempt.label} returned 401 after ${Date.now() - startedAtMs}ms, retrying next credential${body ? `, body=${previewText2(body)}` : ""}`
|
|
6161
6259
|
);
|
|
6162
6260
|
continue;
|
|
6163
6261
|
}
|
|
6164
|
-
await this.sendHttpResponse(
|
|
6262
|
+
await this.sendHttpResponse({
|
|
6263
|
+
frameId: frame.id,
|
|
6264
|
+
method: frame.method,
|
|
6265
|
+
path: mappedPath,
|
|
6266
|
+
authLabel: attempt.label,
|
|
6267
|
+
startedAtMs,
|
|
6268
|
+
res
|
|
6269
|
+
});
|
|
6165
6270
|
return;
|
|
6166
6271
|
}
|
|
6167
6272
|
} catch (err) {
|
|
6273
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6274
|
+
this.opts.logger.error(
|
|
6275
|
+
`TunnelProxy: HTTP id=${frame.id} ${frame.method} ${mappedPath} failed after ${Date.now() - startedAtMs}ms: ${message}`
|
|
6276
|
+
);
|
|
6168
6277
|
this.opts.client.send({
|
|
6169
6278
|
type: "proxy_error",
|
|
6170
6279
|
id: frame.id,
|
|
6171
6280
|
status: 502,
|
|
6172
|
-
message: `gateway unreachable: ${
|
|
6281
|
+
message: `gateway unreachable: ${message}`
|
|
6173
6282
|
});
|
|
6174
6283
|
}
|
|
6175
6284
|
}
|
|
6176
|
-
async streamResponse(requestId, res) {
|
|
6285
|
+
async streamResponse(requestId, res, startedAtMs) {
|
|
6177
6286
|
const reader = res.body.getReader();
|
|
6178
6287
|
const decoder = new TextDecoder();
|
|
6179
6288
|
let chunkCount = 0;
|
|
@@ -6195,7 +6304,7 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6195
6304
|
});
|
|
6196
6305
|
}
|
|
6197
6306
|
this.opts.logger.info(
|
|
6198
|
-
`TunnelProxy: stream end id=${requestId}, total chunks=${chunkCount}`
|
|
6307
|
+
`TunnelProxy: stream end id=${requestId}, total chunks=${chunkCount}, totalElapsedMs=${Date.now() - startedAtMs}`
|
|
6199
6308
|
);
|
|
6200
6309
|
this.opts.client.send({
|
|
6201
6310
|
type: "stream",
|
|
@@ -6205,7 +6314,7 @@ var TunnelProxy = class _TunnelProxy {
|
|
|
6205
6314
|
});
|
|
6206
6315
|
} catch (err) {
|
|
6207
6316
|
this.opts.logger.error(
|
|
6208
|
-
`TunnelProxy: stream error id=${requestId} after ${chunkCount} chunks: ${err instanceof Error ? err.message : String(err)}`
|
|
6317
|
+
`TunnelProxy: stream error id=${requestId} after ${chunkCount} chunks and ${Date.now() - startedAtMs}ms: ${err instanceof Error ? err.message : String(err)}`
|
|
6209
6318
|
);
|
|
6210
6319
|
this.opts.client.send({
|
|
6211
6320
|
type: "proxy_error",
|
|
@@ -6360,7 +6469,7 @@ function createTunnelService(opts) {
|
|
|
6360
6469
|
}
|
|
6361
6470
|
}
|
|
6362
6471
|
function acquireLock(filePath) {
|
|
6363
|
-
mkdirSync7(
|
|
6472
|
+
mkdirSync7(dirname5(filePath), { recursive: true });
|
|
6364
6473
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
6365
6474
|
try {
|
|
6366
6475
|
const fd = openSync(filePath, "wx", 384);
|
|
@@ -6826,7 +6935,7 @@ var index_default = {
|
|
|
6826
6935
|
if (openclawDir) return openclawDir;
|
|
6827
6936
|
if (!workspaceDir) return void 0;
|
|
6828
6937
|
if (basename2(workspaceDir) !== "workspace") return void 0;
|
|
6829
|
-
return
|
|
6938
|
+
return dirname6(workspaceDir);
|
|
6830
6939
|
};
|
|
6831
6940
|
api.registerCli(
|
|
6832
6941
|
(ctx) => {
|