pinggy 0.5.2 → 0.5.5
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/README.md +4 -0
- package/dist/{TunnelManager-OPUMAZFX.js → TunnelManager-ZSVX7U3Z.js} +2 -2
- package/dist/{TunnelTui-QZEWWH2H.js → TunnelTui-S35BNVPO.js} +85 -16
- package/dist/{chunk-LFK74BMU.js → chunk-NBR2L6LW.js} +40 -12
- package/dist/{chunk-7G6SJEEA.js → chunk-P7WNBN7F.js} +1 -1
- package/dist/{chunk-DLNUDW6G.js → chunk-PQSLSIDA.js} +67 -13
- package/dist/{chunk-C3ZMLIC4.js → chunk-UX64XCC2.js} +251 -77
- package/dist/{chunk-BFARGPGP.js → chunk-YTJEV27N.js} +12 -2
- package/dist/{chunk-WM7I57AA.js → chunk-YWXADADB.js} +1 -1
- package/dist/{configStore-HP3NXPH4.js → configStore-2AZRILVC.js} +2 -2
- package/dist/{daemonChild-6QA4AVAN.js → daemonChild-ZACX7V6J.js} +11 -6
- package/dist/index.cjs +459 -120
- package/dist/index.d.cts +65 -19
- package/dist/index.d.ts +65 -19
- package/dist/index.js +10 -6
- package/dist/{ipcClient-LZQCCNMR.js → ipcClient-XRL2WUUM.js} +1 -1
- package/dist/{main-K6ZEWZQ7.js → main-KG3Q4LXI.js} +9 -6
- package/dist/workers/file_serve_worker.js +1 -1
- package/jest.config.cjs +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -324,6 +324,7 @@ Extended options provide advanced controls. Specify them as positional values li
|
|
|
324
324
|
- x:reverseproxy Disable built-in reverse-proxy header injection.
|
|
325
325
|
- x:xff Add X-Forwarded-For.
|
|
326
326
|
- x:fullurl | x:fullrequesturl Include original request URL.
|
|
327
|
+
- x:haproxy | x:haproxy:v1 | x:haproxy:v2 Send HAProxy PROXY protocol header to the local server (default v1).
|
|
327
328
|
- w:<cidr>[,<cidr>...] Whitelist IPs (IPv4 CIDR).
|
|
328
329
|
- k:<token> Set Bearer token(s) for auth (repeatable).
|
|
329
330
|
- b:<user:pass> Add Basic Auth credentials (repeatable).
|
|
@@ -338,6 +339,9 @@ Examples:
|
|
|
338
339
|
- Allow only a local subnet:
|
|
339
340
|
pinggy w:192.168.1.0/24 -l 8080
|
|
340
341
|
|
|
342
|
+
- Send the real client IP to a PROXY-protocol-aware backend:
|
|
343
|
+
pinggy x:haproxy:v2 -l 8080
|
|
344
|
+
|
|
341
345
|
To generate advanced CLI arguments, use [Configure from Pinggy.io](https://pinggy.io/)
|
|
342
346
|
|
|
343
347
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TunnelAlreadyRunningError,
|
|
3
3
|
TunnelManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PQSLSIDA.js";
|
|
5
5
|
import "./chunk-UB26QJ4T.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-P7WNBN7F.js";
|
|
7
7
|
import "./chunk-GBYF2H4H.js";
|
|
8
8
|
export {
|
|
9
9
|
TunnelAlreadyRunningError,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TunnelManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PQSLSIDA.js";
|
|
4
4
|
import "./chunk-UB26QJ4T.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-P7WNBN7F.js";
|
|
8
8
|
import "./chunk-GBYF2H4H.js";
|
|
9
9
|
|
|
10
10
|
// src/tui/blessed/TunnelTui.ts
|
|
@@ -150,10 +150,21 @@ var asciiArtPinggyLogo = `
|
|
|
150
150
|
\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2554\u255D
|
|
151
151
|
\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551
|
|
152
152
|
\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D `;
|
|
153
|
+
var asciiArtPinggyLogoCompact = "P I N G G Y";
|
|
154
|
+
var asciiArtPinggyLogoMedium = [
|
|
155
|
+
" ___ ___ _ _ ___ _____ __",
|
|
156
|
+
"| _ \\_ _| \\| |/ __|/ __\\ \\ / /",
|
|
157
|
+
"| _/| || .` | (_ | (_ |\\ V /",
|
|
158
|
+
"|_| |___|_|\\_|\\___|\\___| |_|"
|
|
159
|
+
].join("\n");
|
|
153
160
|
|
|
154
161
|
// src/tui/blessed/components/UIComponents.ts
|
|
155
162
|
var MIN_WIDTH_WARNING = 60;
|
|
156
163
|
var SIMPLE_LAYOUT_THRESHOLD = 80;
|
|
164
|
+
var MEDIUM_LOGO_THRESHOLD = 100;
|
|
165
|
+
var FULL_LOGO_THRESHOLD = 120;
|
|
166
|
+
var MEDIUM_LOGO_HEIGHT_THRESHOLD = 26;
|
|
167
|
+
var FULL_LOGO_HEIGHT_THRESHOLD = 34;
|
|
157
168
|
function colorizeGradient(text) {
|
|
158
169
|
const colors = ["red", "yellow", "green", "cyan", "blue", "magenta"];
|
|
159
170
|
const lines = text.split("\n");
|
|
@@ -162,6 +173,54 @@ function colorizeGradient(text) {
|
|
|
162
173
|
return `{${color}-fg}${line}{/${color}-fg}`;
|
|
163
174
|
}).join("\n");
|
|
164
175
|
}
|
|
176
|
+
function colorizeGradientHorizontal(text) {
|
|
177
|
+
const colors = ["red", "yellow", "green", "cyan", "blue", "magenta"];
|
|
178
|
+
let colorIndex = 0;
|
|
179
|
+
return [...text].map((char) => {
|
|
180
|
+
if (char === " ") return char;
|
|
181
|
+
const color = colors[colorIndex % colors.length];
|
|
182
|
+
colorIndex++;
|
|
183
|
+
return `{${color}-fg}${char}{/${color}-fg}`;
|
|
184
|
+
}).join("");
|
|
185
|
+
}
|
|
186
|
+
function selectLogo(width, height) {
|
|
187
|
+
const widthTier = width >= FULL_LOGO_THRESHOLD ? 2 : width >= MEDIUM_LOGO_THRESHOLD ? 1 : 0;
|
|
188
|
+
const heightTier = height >= FULL_LOGO_HEIGHT_THRESHOLD ? 2 : height >= MEDIUM_LOGO_HEIGHT_THRESHOLD ? 1 : 0;
|
|
189
|
+
const tier = Math.min(widthTier, heightTier);
|
|
190
|
+
if (tier === 2) {
|
|
191
|
+
return { content: colorizeGradient(asciiArtPinggyLogo), boxHeight: 7 };
|
|
192
|
+
}
|
|
193
|
+
if (tier === 1) {
|
|
194
|
+
return { content: colorizeGradient(asciiArtPinggyLogoMedium), boxHeight: 5 };
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
content: `{bold}${colorizeGradientHorizontal(asciiArtPinggyLogoCompact)}{/bold}`,
|
|
198
|
+
boxHeight: 1
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function wrapText(text, width) {
|
|
202
|
+
const lines = [];
|
|
203
|
+
let current = "";
|
|
204
|
+
for (const word of text.split(/\s+/).filter(Boolean)) {
|
|
205
|
+
const candidate = current ? `${current} ${word}` : word;
|
|
206
|
+
if (candidate.length <= width) {
|
|
207
|
+
current = candidate;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (current) {
|
|
211
|
+
lines.push(current);
|
|
212
|
+
current = "";
|
|
213
|
+
}
|
|
214
|
+
let rest = word;
|
|
215
|
+
while (rest.length > width) {
|
|
216
|
+
lines.push(rest.slice(0, width));
|
|
217
|
+
rest = rest.slice(width);
|
|
218
|
+
}
|
|
219
|
+
current = rest;
|
|
220
|
+
}
|
|
221
|
+
if (current) lines.push(current);
|
|
222
|
+
return lines.length ? lines : [""];
|
|
223
|
+
}
|
|
165
224
|
function createWarningUI(screen) {
|
|
166
225
|
return blessed.box({
|
|
167
226
|
parent: screen,
|
|
@@ -188,21 +247,26 @@ function createFullUI(screen, urls, greet, tunnelConfig) {
|
|
|
188
247
|
height: "100%",
|
|
189
248
|
padding: 1
|
|
190
249
|
});
|
|
250
|
+
const { content: logoContent, boxHeight: logoHeight } = selectLogo(
|
|
251
|
+
screen.width,
|
|
252
|
+
screen.height
|
|
253
|
+
);
|
|
191
254
|
const logoBox = blessed.box({
|
|
192
255
|
parent: mainContainer,
|
|
193
256
|
top: 0,
|
|
194
257
|
left: 0,
|
|
195
258
|
width: "100%",
|
|
196
|
-
height:
|
|
197
|
-
content:
|
|
259
|
+
height: logoHeight,
|
|
260
|
+
content: logoContent,
|
|
198
261
|
tags: true
|
|
199
262
|
});
|
|
263
|
+
const contentTop = logoHeight + 1;
|
|
200
264
|
const contentBox = blessed.box({
|
|
201
265
|
parent: mainContainer,
|
|
202
|
-
top:
|
|
266
|
+
top: contentTop,
|
|
203
267
|
left: 0,
|
|
204
268
|
width: "100%-2",
|
|
205
|
-
height:
|
|
269
|
+
height: `100%-${contentTop + 2}`,
|
|
206
270
|
padding: 0,
|
|
207
271
|
border: {
|
|
208
272
|
type: "line"
|
|
@@ -215,20 +279,24 @@ function createFullUI(screen, urls, greet, tunnelConfig) {
|
|
|
215
279
|
});
|
|
216
280
|
let greetHeight = 0;
|
|
217
281
|
if (greet) {
|
|
218
|
-
const
|
|
282
|
+
const contentInnerWidth = screen.width - 6;
|
|
283
|
+
const greetWidth = Math.max(20, Math.floor(contentInnerWidth * 0.6));
|
|
284
|
+
const greetLines = wrapText(greet, greetWidth);
|
|
285
|
+
const greetBoxHeight = greetLines.length + 1;
|
|
286
|
+
blessed.box({
|
|
219
287
|
parent: contentBox,
|
|
220
288
|
top: 0,
|
|
221
289
|
left: "center",
|
|
222
|
-
width:
|
|
223
|
-
height:
|
|
224
|
-
content: `{bold}${
|
|
290
|
+
width: greetWidth,
|
|
291
|
+
height: greetBoxHeight,
|
|
292
|
+
content: greetLines.map((line) => `{bold}${line}{/bold}`).join("\n"),
|
|
225
293
|
tags: true,
|
|
226
294
|
align: "center",
|
|
227
295
|
style: {
|
|
228
296
|
fg: "green"
|
|
229
297
|
}
|
|
230
298
|
});
|
|
231
|
-
greetHeight =
|
|
299
|
+
greetHeight = greetBoxHeight;
|
|
232
300
|
}
|
|
233
301
|
const upperSectionTop = greetHeight > 0 ? greetHeight : 0;
|
|
234
302
|
const upperSection = blessed.box({
|
|
@@ -322,21 +390,22 @@ function createSimpleUI(screen, urls, greet) {
|
|
|
322
390
|
});
|
|
323
391
|
let currentTop = 0;
|
|
324
392
|
if (greet) {
|
|
393
|
+
const greetWidth = Math.max(20, Math.floor((screen.width - 2) * 0.9));
|
|
394
|
+
const greetLines = wrapText(greet, greetWidth);
|
|
325
395
|
blessed.box({
|
|
326
396
|
parent: mainContainer,
|
|
327
397
|
top: currentTop,
|
|
328
398
|
left: "center",
|
|
329
|
-
width:
|
|
330
|
-
height:
|
|
331
|
-
content: `{bold}${
|
|
399
|
+
width: greetWidth,
|
|
400
|
+
height: greetLines.length,
|
|
401
|
+
content: greetLines.map((line) => `{bold}${line}{/bold}`).join("\n"),
|
|
332
402
|
tags: true,
|
|
333
403
|
align: "center",
|
|
334
404
|
style: {
|
|
335
405
|
fg: "green"
|
|
336
406
|
}
|
|
337
407
|
});
|
|
338
|
-
|
|
339
|
-
currentTop += Math.max(lines, 1) + 1;
|
|
408
|
+
currentTop += greetLines.length + 1;
|
|
340
409
|
}
|
|
341
410
|
const urlsBox = blessed.box({
|
|
342
411
|
parent: mainContainer,
|
|
@@ -10,14 +10,16 @@ import {
|
|
|
10
10
|
startDaemon,
|
|
11
11
|
startRemoteManagement,
|
|
12
12
|
stopDaemon
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UX64XCC2.js";
|
|
14
14
|
import {
|
|
15
15
|
readDaemonConfig
|
|
16
16
|
} from "./chunk-MT44NAXX.js";
|
|
17
17
|
import {
|
|
18
|
+
DaemonHost,
|
|
18
19
|
Route,
|
|
19
|
-
SessionMode
|
|
20
|
-
|
|
20
|
+
SessionMode,
|
|
21
|
+
ShutdownStatus
|
|
22
|
+
} from "./chunk-YTJEV27N.js";
|
|
21
23
|
import {
|
|
22
24
|
ConfigVerb,
|
|
23
25
|
SUBCOMMANDS,
|
|
@@ -34,7 +36,7 @@ import {
|
|
|
34
36
|
updateConfigAutoStart,
|
|
35
37
|
updateTunnelConfig,
|
|
36
38
|
validateName
|
|
37
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-YWXADADB.js";
|
|
38
40
|
import {
|
|
39
41
|
TunnelManager,
|
|
40
42
|
detachAllTunnelLoggers,
|
|
@@ -46,14 +48,14 @@ import {
|
|
|
46
48
|
isValidPort,
|
|
47
49
|
printer_default,
|
|
48
50
|
setTunnelLoggingEnabled
|
|
49
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-PQSLSIDA.js";
|
|
50
52
|
import {
|
|
51
53
|
configureLogger,
|
|
52
54
|
enablePackageLogging,
|
|
53
55
|
getLogLevel,
|
|
54
56
|
logger,
|
|
55
57
|
setLogLevel
|
|
56
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-P7WNBN7F.js";
|
|
57
59
|
import {
|
|
58
60
|
ensurePinggyConfigDir,
|
|
59
61
|
ensurePinggyLogDir,
|
|
@@ -135,6 +137,7 @@ function printHelpMessage() {
|
|
|
135
137
|
console.log(" x:noreverseproxy Disable built-in reverse-proxy header injection");
|
|
136
138
|
console.log(" x:localservertls:host Connect to local HTTPS server with SNI");
|
|
137
139
|
console.log(" x:passpreflight Pass CORS preflight requests unchanged");
|
|
140
|
+
console.log(" x:haproxy[:v1|:v2] Enable HAProxy PROXY protocol (default v1)");
|
|
138
141
|
console.log(" a:Key:Val Add header");
|
|
139
142
|
console.log(" u:Key:Val Update header");
|
|
140
143
|
console.log(" r:Key Remove header");
|
|
@@ -272,6 +275,7 @@ var defaultOptions = {
|
|
|
272
275
|
};
|
|
273
276
|
|
|
274
277
|
// src/cli/extendedOptions.ts
|
|
278
|
+
import { HaProxyVersion } from "@pinggy/pinggy";
|
|
275
279
|
import { isIP } from "net";
|
|
276
280
|
function parseExtendedOptions(options, config, localServerTls) {
|
|
277
281
|
if (!options) return localServerTls;
|
|
@@ -298,10 +302,20 @@ function parseExtendedOptions(options, config, localServerTls) {
|
|
|
298
302
|
case "fullrequesturl":
|
|
299
303
|
config.originalRequestUrl = true;
|
|
300
304
|
break;
|
|
305
|
+
case "haproxy":
|
|
306
|
+
case "haproxy:v1":
|
|
307
|
+
config.haProxy = HaProxyVersion.V1;
|
|
308
|
+
break;
|
|
309
|
+
case "haproxy:v2":
|
|
310
|
+
config.haProxy = HaProxyVersion.V2;
|
|
311
|
+
break;
|
|
301
312
|
default: {
|
|
302
313
|
if (value && (value.startsWith("localServerTls") || value.startsWith("localservertls"))) {
|
|
303
314
|
const parts = value.split(/:(.+)/);
|
|
304
315
|
localServerTls = parts[1] ? parts[1] : "";
|
|
316
|
+
} else if (value && value.startsWith("haproxy:")) {
|
|
317
|
+
printer_default.warn(`Invalid HAProxy version in "${opt}". Use x:haproxy, x:haproxy:v1 or x:haproxy:v2`);
|
|
318
|
+
logger.warn(`Warning: Invalid HAProxy version in "${opt}". Use x:haproxy, x:haproxy:v1 or x:haproxy:v2`);
|
|
305
319
|
} else {
|
|
306
320
|
printer_default.warn(`Unknown extended option "${value}"`);
|
|
307
321
|
logger.warn(`Warning: Unknown extended option "${value}"`);
|
|
@@ -1085,7 +1099,7 @@ async function connectTui(opts) {
|
|
|
1085
1099
|
const exitMessage = opts.exitMessage || "Stopping tunnel...";
|
|
1086
1100
|
if (!noTui && process.stdin.isTTY) {
|
|
1087
1101
|
try {
|
|
1088
|
-
const { TunnelTui } = await import("./TunnelTui-
|
|
1102
|
+
const { TunnelTui } = await import("./TunnelTui-S35BNVPO.js");
|
|
1089
1103
|
const tui = new TunnelTui({
|
|
1090
1104
|
urls,
|
|
1091
1105
|
greet,
|
|
@@ -1308,7 +1322,7 @@ async function startBackgroundTunnels(configs, values, positionals) {
|
|
|
1308
1322
|
client.close();
|
|
1309
1323
|
}
|
|
1310
1324
|
async function startAutoStartTunnels() {
|
|
1311
|
-
const { getAutoStartConfigs: getAutoStartConfigs2 } = await import("./configStore-
|
|
1325
|
+
const { getAutoStartConfigs: getAutoStartConfigs2 } = await import("./configStore-2AZRILVC.js");
|
|
1312
1326
|
const configs = getAutoStartConfigs2();
|
|
1313
1327
|
if (configs.length === 0) {
|
|
1314
1328
|
printer_default.warn("No configs marked for auto-start. Use: pinggy config auto <name>");
|
|
@@ -1777,6 +1791,8 @@ async function handleDaemonStop() {
|
|
|
1777
1791
|
const result = await stopDaemon();
|
|
1778
1792
|
if (result.ok) {
|
|
1779
1793
|
printer_default.success("Daemon stopped.");
|
|
1794
|
+
} else if (result.reason === DaemonHost.APP) {
|
|
1795
|
+
printer_default.print(pico2.yellow("The Pinggy daemon is running inside the Pinggy desktop app. Please quit the Pinggy app to stop it."));
|
|
1780
1796
|
} else {
|
|
1781
1797
|
printer_default.error(result.error);
|
|
1782
1798
|
}
|
|
@@ -1797,6 +1813,7 @@ function handleDaemonStatus() {
|
|
|
1797
1813
|
printer_default.print(` Port: ${info.port}`);
|
|
1798
1814
|
printer_default.print(` Started: ${info.startedAt}`);
|
|
1799
1815
|
printer_default.print(` Uptime: ${uptimeStr}`);
|
|
1816
|
+
printer_default.print(` Host: ${info.host === DaemonHost.APP ? "Pinggy app (in-process)" : "standalone (CLI)"}`);
|
|
1800
1817
|
}
|
|
1801
1818
|
|
|
1802
1819
|
// src/cli/subcommand/handlers/psCommand.ts
|
|
@@ -2630,7 +2647,7 @@ var IPCServer = class {
|
|
|
2630
2647
|
},
|
|
2631
2648
|
[Route.RestartTunnel]: async (req) => {
|
|
2632
2649
|
if (!req.tunnelid) throw new Error("Missing 'tunnelid' field");
|
|
2633
|
-
return await this.ops.handleRestart(req.tunnelid);
|
|
2650
|
+
return await this.ops.handleRestart(req.tunnelid, req.noWait);
|
|
2634
2651
|
},
|
|
2635
2652
|
[Route.UpdateConfig]: async (req) => {
|
|
2636
2653
|
if (!req.config) throw new Error("Missing 'config' field");
|
|
@@ -2697,6 +2714,10 @@ var IPCServer = class {
|
|
|
2697
2714
|
},
|
|
2698
2715
|
[Route.Shutdown]: () => {
|
|
2699
2716
|
logger.info("Daemon shutdown requested via IPC");
|
|
2717
|
+
if (getDaemonHost() === DaemonHost.APP) {
|
|
2718
|
+
logger.info("Shutdown refused: daemon is hosted in-process by the Pinggy app");
|
|
2719
|
+
return { status: ShutdownStatus.RefusedInApp, errors: [] };
|
|
2720
|
+
}
|
|
2700
2721
|
const errors = [];
|
|
2701
2722
|
const step = (label, fn) => {
|
|
2702
2723
|
try {
|
|
@@ -2711,7 +2732,7 @@ var IPCServer = class {
|
|
|
2711
2732
|
step("clearDaemonState", clearDaemonState);
|
|
2712
2733
|
step("stopAllTunnels", () => TunnelManager.getInstance().stopAllTunnels());
|
|
2713
2734
|
setTimeout(() => process.exit(0), 200);
|
|
2714
|
-
return { status:
|
|
2735
|
+
return { status: ShutdownStatus.ShuttingDown, errors };
|
|
2715
2736
|
}
|
|
2716
2737
|
};
|
|
2717
2738
|
}
|
|
@@ -3154,6 +3175,10 @@ var SessionTracker = class {
|
|
|
3154
3175
|
|
|
3155
3176
|
// src/daemon/lifecycle/daemonChild.ts
|
|
3156
3177
|
var daemonState = { tunnels: [], lastUpdated: "" };
|
|
3178
|
+
var daemonHost = DaemonHost.CLI;
|
|
3179
|
+
function getDaemonHost() {
|
|
3180
|
+
return daemonHost;
|
|
3181
|
+
}
|
|
3157
3182
|
var sessionTrackerRef = null;
|
|
3158
3183
|
function setDaemonSessionTracker(st) {
|
|
3159
3184
|
sessionTrackerRef = st;
|
|
@@ -3283,6 +3308,7 @@ async function restoreCrashedTunnels(manager) {
|
|
|
3283
3308
|
async function runDaemonChild(opts = {}) {
|
|
3284
3309
|
const installSignalHandlers = opts.installSignalHandlers ?? true;
|
|
3285
3310
|
const exitOnFailure = opts.exitOnFailure ?? true;
|
|
3311
|
+
daemonHost = opts.host ?? DaemonHost.CLI;
|
|
3286
3312
|
ensurePinggyConfigDir();
|
|
3287
3313
|
const persistedLevel = readDaemonConfig()?.logLevel;
|
|
3288
3314
|
const envLevel = process.env.PINGGY_LOG_LEVEL;
|
|
@@ -3358,7 +3384,8 @@ async function runDaemonChild(opts = {}) {
|
|
|
3358
3384
|
const info = {
|
|
3359
3385
|
pid: process.pid,
|
|
3360
3386
|
port,
|
|
3361
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3387
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3388
|
+
host: daemonHost
|
|
3362
3389
|
};
|
|
3363
3390
|
writeDaemonInfo(info);
|
|
3364
3391
|
logger.info("Daemon info written", info);
|
|
@@ -3399,7 +3426,7 @@ async function main() {
|
|
|
3399
3426
|
const { values, positionals, hasAnyArgs } = parseCliArgs(cliOptions);
|
|
3400
3427
|
configureLogger(values);
|
|
3401
3428
|
if (values["_daemon-child"]) {
|
|
3402
|
-
const { runDaemonChild: runDaemonChild2 } = await import("./daemonChild-
|
|
3429
|
+
const { runDaemonChild: runDaemonChild2 } = await import("./daemonChild-ZACX7V6J.js");
|
|
3403
3430
|
await runDaemonChild2();
|
|
3404
3431
|
return;
|
|
3405
3432
|
}
|
|
@@ -3433,6 +3460,7 @@ if (entryFile && entryFile === currentFile) {
|
|
|
3433
3460
|
}
|
|
3434
3461
|
|
|
3435
3462
|
export {
|
|
3463
|
+
getDaemonHost,
|
|
3436
3464
|
setDaemonSessionTracker,
|
|
3437
3465
|
removeDaemonInfo,
|
|
3438
3466
|
trackTunnelStart,
|
|
@@ -16,7 +16,7 @@ function setLogLevel(level) {
|
|
|
16
16
|
}
|
|
17
17
|
import("./daemonConfig-G6S46GPJ.js").then(({ writeDaemonConfig }) => writeDaemonConfig({ logLevel: level })).catch(() => {
|
|
18
18
|
});
|
|
19
|
-
import("./TunnelManager-
|
|
19
|
+
import("./TunnelManager-ZSVX7U3Z.js").then(({ TunnelManager }) => TunnelManager.getInstance().applyLogLevelToActiveTunnels(level)).catch(() => {
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
var _logger = null;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
getLogLevel,
|
|
6
6
|
logger
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-P7WNBN7F.js";
|
|
8
8
|
import {
|
|
9
9
|
getTunnelLogPath
|
|
10
10
|
} from "./chunk-GBYF2H4H.js";
|
|
@@ -121,7 +121,7 @@ function startSpinner(name = "dots", text = "Loading") {
|
|
|
121
121
|
}
|
|
122
122
|
currentTimer = setInterval(() => {
|
|
123
123
|
const frame = spinner.frames[i = ++i % spinner.frames.length];
|
|
124
|
-
process.stdout.write(`\r${pico.cyan(frame)} ${text}`);
|
|
124
|
+
process.stdout.write(`\r${pico.cyan(frame)} ${text}\x1B[K`);
|
|
125
125
|
}, spinner.interval);
|
|
126
126
|
return () => stopSpinner();
|
|
127
127
|
}
|
|
@@ -137,7 +137,7 @@ function stopSpinnerSuccess(message) {
|
|
|
137
137
|
clearInterval(currentTimer);
|
|
138
138
|
currentTimer = null;
|
|
139
139
|
const finalMessage = message || currentText;
|
|
140
|
-
process.stdout.write(`\r${pico.green("\u2714")} ${finalMessage}
|
|
140
|
+
process.stdout.write(`\r${pico.green("\u2714")} ${finalMessage}\x1B[K
|
|
141
141
|
`);
|
|
142
142
|
}
|
|
143
143
|
}
|
|
@@ -146,7 +146,7 @@ function stopSpinnerFail(message) {
|
|
|
146
146
|
clearInterval(currentTimer);
|
|
147
147
|
currentTimer = null;
|
|
148
148
|
const finalMessage = message || currentText;
|
|
149
|
-
process.stdout.write(`\r${pico.red("\u2716")} ${finalMessage}
|
|
149
|
+
process.stdout.write(`\r${pico.red("\u2716")} ${finalMessage}\x1B[K
|
|
150
150
|
`);
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -724,6 +724,29 @@ var TunnelManager = class _TunnelManager {
|
|
|
724
724
|
}
|
|
725
725
|
return Promise.reject(new Error(`Either configId or tunnelId must be provided`));
|
|
726
726
|
}
|
|
727
|
+
/**
|
|
728
|
+
* Stop a tunnel instance and wait for the stop to settle, capped by a
|
|
729
|
+
* timeout. Failure is logged, not thrown: the caller replaces the
|
|
730
|
+
* instance either way and a hung stop must not block that forever.
|
|
731
|
+
*/
|
|
732
|
+
async stopInstanceGuarded(managed, timeoutMs = 5e3) {
|
|
733
|
+
let timer;
|
|
734
|
+
try {
|
|
735
|
+
await Promise.race([
|
|
736
|
+
managed.instance.stop(),
|
|
737
|
+
new Promise((_, reject) => {
|
|
738
|
+
timer = setTimeout(() => reject(new Error(`Stop timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
739
|
+
})
|
|
740
|
+
]);
|
|
741
|
+
} catch (e) {
|
|
742
|
+
logger.warn("Failed to stop tunnel instance before replacement", {
|
|
743
|
+
tunnelId: managed.tunnelid,
|
|
744
|
+
error: errorMessage(e)
|
|
745
|
+
});
|
|
746
|
+
} finally {
|
|
747
|
+
if (timer) clearTimeout(timer);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
727
750
|
/**
|
|
728
751
|
* Restarts a tunnel with its current configuration.
|
|
729
752
|
* This function will stop the tunnel if it's running and start it again.
|
|
@@ -745,6 +768,9 @@ var TunnelManager = class _TunnelManager {
|
|
|
745
768
|
const currentServe = existingTunnel.serve;
|
|
746
769
|
const autoReconnect = existingTunnel.autoReconnect || false;
|
|
747
770
|
const currentOrigin = existingTunnel.origin;
|
|
771
|
+
if (!existingTunnel.isStopped) {
|
|
772
|
+
await this.stopInstanceGuarded(existingTunnel);
|
|
773
|
+
}
|
|
748
774
|
this.tunnelsByTunnelId.delete(tunnelid);
|
|
749
775
|
this.tunnelsByConfigId.delete(existingTunnel.configId);
|
|
750
776
|
this.tunnelStats.delete(tunnelid);
|
|
@@ -779,9 +805,26 @@ var TunnelManager = class _TunnelManager {
|
|
|
779
805
|
throw new Error(`Failed to restart tunnel: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
780
806
|
}
|
|
781
807
|
}
|
|
808
|
+
/**
|
|
809
|
+
* Carry lifecycle state from a replaced tunnel over to its replacement:
|
|
810
|
+
* keep createdAt, start the replacement if the old tunnel was running,
|
|
811
|
+
* otherwise keep it stopped with the original timestamps.
|
|
812
|
+
*/
|
|
813
|
+
async applyPreservedLifecycle(tunnel, prior) {
|
|
814
|
+
if (prior.createdAt) {
|
|
815
|
+
tunnel.createdAt = prior.createdAt;
|
|
816
|
+
}
|
|
817
|
+
if (!prior.isStopped) {
|
|
818
|
+
await this.startTunnel(tunnel.tunnelid);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
tunnel.isStopped = true;
|
|
822
|
+
tunnel.startedAt = prior.startedAt;
|
|
823
|
+
tunnel.stoppedAt = prior.stoppedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
824
|
+
}
|
|
782
825
|
/**
|
|
783
826
|
* Updates the configuration of an existing tunnel.
|
|
784
|
-
*
|
|
827
|
+
*
|
|
785
828
|
* This method handles the process of updating a tunnel's configuration while preserving
|
|
786
829
|
* its state. If the tunnel is running, it will be stopped, updated, and restarted.
|
|
787
830
|
* In case of failure, it attempts to restore the original configuration.
|
|
@@ -808,10 +851,13 @@ var TunnelManager = class _TunnelManager {
|
|
|
808
851
|
const currentServe = existingTunnel.serve;
|
|
809
852
|
const currentAutoReconnect = existingTunnel.autoReconnect || false;
|
|
810
853
|
const currentOrigin = existingTunnel.origin;
|
|
854
|
+
const currentCreatedAt = existingTunnel.createdAt;
|
|
855
|
+
const currentStartedAt = existingTunnel.startedAt;
|
|
856
|
+
const currentStoppedAt = existingTunnel.stoppedAt;
|
|
811
857
|
const requestedServe = this.resolveServePath(newConfig);
|
|
812
858
|
try {
|
|
813
859
|
if (!isStopped) {
|
|
814
|
-
|
|
860
|
+
await this.stopInstanceGuarded(existingTunnel);
|
|
815
861
|
}
|
|
816
862
|
this.tunnelsByTunnelId.delete(currentTunnelId);
|
|
817
863
|
this.tunnelsByConfigId.delete(currentTunnelConfigId);
|
|
@@ -823,7 +869,6 @@ var TunnelManager = class _TunnelManager {
|
|
|
823
869
|
};
|
|
824
870
|
const effectiveServe = requestedServe !== void 0 ? requestedServe : currentServe;
|
|
825
871
|
const effectiveTunnelName = newTunnelName !== void 0 ? newTunnelName : currentTunnelName;
|
|
826
|
-
let configWithForwarding;
|
|
827
872
|
const newTunnel = await this._createTunnelWithProcessedConfig({
|
|
828
873
|
configId,
|
|
829
874
|
tunnelid: currentTunnelId,
|
|
@@ -833,9 +878,12 @@ var TunnelManager = class _TunnelManager {
|
|
|
833
878
|
serve: effectiveServe,
|
|
834
879
|
autoReconnect: currentAutoReconnect
|
|
835
880
|
});
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
881
|
+
await this.applyPreservedLifecycle(newTunnel, {
|
|
882
|
+
isStopped,
|
|
883
|
+
createdAt: currentCreatedAt,
|
|
884
|
+
startedAt: currentStartedAt,
|
|
885
|
+
stoppedAt: currentStoppedAt
|
|
886
|
+
});
|
|
839
887
|
logger.info("Tunnel configuration updated", {
|
|
840
888
|
tunnelId: newTunnel.tunnelid,
|
|
841
889
|
configId: newTunnel.configId,
|
|
@@ -857,9 +905,12 @@ var TunnelManager = class _TunnelManager {
|
|
|
857
905
|
serve: currentServe,
|
|
858
906
|
autoReconnect: currentAutoReconnect
|
|
859
907
|
});
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
908
|
+
await this.applyPreservedLifecycle(originalTunnel, {
|
|
909
|
+
isStopped,
|
|
910
|
+
createdAt: currentCreatedAt,
|
|
911
|
+
startedAt: currentStartedAt,
|
|
912
|
+
stoppedAt: currentStoppedAt
|
|
913
|
+
});
|
|
863
914
|
logger.warn("Restored original tunnel configuration after update failure", {
|
|
864
915
|
currentTunnelId,
|
|
865
916
|
error: errorMessage(error)
|
|
@@ -1476,6 +1527,9 @@ var TunnelManager = class _TunnelManager {
|
|
|
1476
1527
|
try {
|
|
1477
1528
|
logger.info("Tunnel reconnection completed", { tunnelId, urls });
|
|
1478
1529
|
const managedTunnel = this.tunnelsByTunnelId.get(tunnelId);
|
|
1530
|
+
if (managedTunnel?.isStopping || managedTunnel?.isStopped) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1479
1533
|
if (managedTunnel) {
|
|
1480
1534
|
managedTunnel.isStopped = false;
|
|
1481
1535
|
managedTunnel.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|