pinggy 0.5.3 → 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-BBPR2LJB.js → chunk-NBR2L6LW.js} +21 -9
- package/dist/{chunk-7G6SJEEA.js → chunk-P7WNBN7F.js} +1 -1
- package/dist/{chunk-DLNUDW6G.js → chunk-PQSLSIDA.js} +67 -13
- package/dist/{chunk-GK2PQ7KX.js → chunk-UX64XCC2.js} +214 -74
- package/dist/{chunk-5YHD6M35.js → chunk-YTJEV27N.js} +2 -2
- package/dist/{chunk-WM7I57AA.js → chunk-YWXADADB.js} +1 -1
- package/dist/{configStore-HP3NXPH4.js → configStore-2AZRILVC.js} +2 -2
- package/dist/{daemonChild-WIRWKBKB.js → daemonChild-ZACX7V6J.js} +6 -6
- package/dist/index.cjs +389 -114
- package/dist/index.d.cts +24 -17
- package/dist/index.d.ts +24 -17
- package/dist/index.js +6 -6
- package/dist/{ipcClient-Z2RXDQUQ.js → ipcClient-XRL2WUUM.js} +1 -1
- package/dist/{main-B2EBHFN2.js → main-KG3Q4LXI.js} +6 -6
- package/dist/workers/file_serve_worker.js +1 -1
- package/jest.config.cjs +1 -0
- package/package.json +2 -2
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
IPCClient,
|
|
4
4
|
SessionMode,
|
|
5
5
|
ShutdownStatus
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YTJEV27N.js";
|
|
7
7
|
import {
|
|
8
8
|
TunnelAlreadyRunningError,
|
|
9
9
|
TunnelManager,
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
getVersion,
|
|
13
13
|
isValidPort,
|
|
14
14
|
printer_default
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-PQSLSIDA.js";
|
|
16
16
|
import {
|
|
17
17
|
logger
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-P7WNBN7F.js";
|
|
19
19
|
import {
|
|
20
20
|
getDaemonInfoPath,
|
|
21
21
|
getDaemonLogPath
|
|
@@ -129,7 +129,7 @@ var RemoteManagementStatus = {
|
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
// src/remote_management/remote_schema.ts
|
|
132
|
-
import { TunnelType } from "@pinggy/pinggy";
|
|
132
|
+
import { HaProxyVersion, TunnelType } from "@pinggy/pinggy";
|
|
133
133
|
import { z } from "zod";
|
|
134
134
|
var HeaderModificationSchema = z.object({
|
|
135
135
|
key: z.string(),
|
|
@@ -209,7 +209,7 @@ var UpdateConfigSchema = z.object({
|
|
|
209
209
|
var ForwardingEntryV2Schema = z.object({
|
|
210
210
|
listenAddress: z.string().optional(),
|
|
211
211
|
address: z.string(),
|
|
212
|
-
type: z.enum([TunnelType.Http, TunnelType.Tcp, TunnelType.Udp, TunnelType.Tls, TunnelType.TlsTcp])
|
|
212
|
+
type: z.enum([TunnelType.Http, TunnelType.Tcp, TunnelType.Udp, TunnelType.Tls, TunnelType.TlsTcp]).default(TunnelType.Http)
|
|
213
213
|
});
|
|
214
214
|
var OptionalSchema = z.object({
|
|
215
215
|
sniServerName: z.string().optional(),
|
|
@@ -253,7 +253,9 @@ var TunnelConfigV1Schema = z.object({
|
|
|
253
253
|
httpsOnly: z.boolean().optional(),
|
|
254
254
|
originalRequestUrl: z.boolean().optional(),
|
|
255
255
|
allowPreflight: z.boolean().optional(),
|
|
256
|
-
|
|
256
|
+
haProxy: z.enum([HaProxyVersion.V1, HaProxyVersion.V2]).optional(),
|
|
257
|
+
serve: z.string().optional(),
|
|
258
|
+
optional: z.record(z.string(), z.unknown()).optional()
|
|
257
259
|
});
|
|
258
260
|
var TUNNEL_CONFIG_V1_KEYS = Object.keys(
|
|
259
261
|
TunnelConfigV1Schema.shape
|
|
@@ -286,6 +288,7 @@ function pinggyOptionsToTunnelConfigV1(opts, configStoredInCli) {
|
|
|
286
288
|
httpsOnly: opts.httpsOnly ?? false,
|
|
287
289
|
originalRequestUrl: opts.originalRequestUrl ?? false,
|
|
288
290
|
allowPreflight: opts.allowPreflight ?? false,
|
|
291
|
+
haProxy: opts.haProxy,
|
|
289
292
|
optional: opts.optional || {}
|
|
290
293
|
};
|
|
291
294
|
}
|
|
@@ -506,11 +509,11 @@ var TunnelOperations = class {
|
|
|
506
509
|
startPromise.catch((err) => {
|
|
507
510
|
logger.error("No-wait startTunnel failed", { tunnelid, err: String(err) });
|
|
508
511
|
});
|
|
509
|
-
return this.buildPendingTunnelResponseV2(tunnelid, tunnelConfig, config, config.configId, config.name, config.
|
|
512
|
+
return this.buildPendingTunnelResponseV2(tunnelid, tunnelConfig, config, config.configId, config.name, config.serve);
|
|
510
513
|
}
|
|
511
514
|
await startPromise;
|
|
512
515
|
const tunnelPconfig = await this.tunnelManager.getTunnelConfig("", tunnelid);
|
|
513
|
-
return this.buildTunnelResponseV2(tunnelid, tunnelPconfig, config, config.configId, config.name, config.
|
|
516
|
+
return this.buildTunnelResponseV2(tunnelid, tunnelPconfig, config, config.configId, config.name, config.serve);
|
|
514
517
|
} catch (err) {
|
|
515
518
|
if (err instanceof TunnelAlreadyRunningError) {
|
|
516
519
|
return this.error(ErrorCode.TunnelAlreadyRunningError, err, err.message);
|
|
@@ -532,10 +535,12 @@ var TunnelOperations = class {
|
|
|
532
535
|
if (noWait) {
|
|
533
536
|
const existing = this.tunnelManager.getManagedTunnel(config.configid);
|
|
534
537
|
if (!existing.tunnelConfig) throw new Error("Invalid tunnel state before configuration update");
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
538
|
+
if (!existing.isStopped) {
|
|
539
|
+
this.tunnelManager.updateConfig(updateOpts).catch((err) => {
|
|
540
|
+
logger.error("No-wait updateConfig failed", { configid: config.configid, err: String(err) });
|
|
541
|
+
});
|
|
542
|
+
return this.buildPendingTunnelResponse(existing.tunnelid, existing.tunnelConfig, config.configid, existing.tunnelName, existing.serve);
|
|
543
|
+
}
|
|
539
544
|
}
|
|
540
545
|
const tunnel = await this.tunnelManager.updateConfig(updateOpts);
|
|
541
546
|
if (!tunnel.instance || !tunnel.tunnelConfig)
|
|
@@ -550,10 +555,12 @@ var TunnelOperations = class {
|
|
|
550
555
|
if (noWait) {
|
|
551
556
|
const existing = this.tunnelManager.getManagedTunnel(config.configId);
|
|
552
557
|
if (!existing.tunnelConfig) throw new Error("Invalid tunnel state before configuration update");
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
558
|
+
if (!existing.isStopped) {
|
|
559
|
+
this.tunnelManager.updateConfig(config).catch((err) => {
|
|
560
|
+
logger.error("No-wait updateConfigV2 failed", { configId: config.configId, err: String(err) });
|
|
561
|
+
});
|
|
562
|
+
return this.buildPendingTunnelResponseV2(existing.tunnelid, existing.tunnelConfig, config, config.configId, existing.tunnelName, existing.serve);
|
|
563
|
+
}
|
|
557
564
|
}
|
|
558
565
|
const tunnel = await this.tunnelManager.updateConfig(config);
|
|
559
566
|
if (!tunnel.instance || !tunnel.tunnelConfig)
|
|
@@ -700,13 +707,71 @@ import WebSocket from "ws";
|
|
|
700
707
|
|
|
701
708
|
// src/remote_management/websocket_printer.ts
|
|
702
709
|
import pico from "picocolors";
|
|
703
|
-
var
|
|
704
|
-
var
|
|
710
|
+
var PENDING_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
711
|
+
var PendingTracker = class {
|
|
705
712
|
constructor() {
|
|
706
|
-
this.
|
|
713
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
714
|
+
}
|
|
715
|
+
queue(entry) {
|
|
716
|
+
this.latestConfigId = entry.configId;
|
|
717
|
+
this.entries.set(entry.configId, entry);
|
|
718
|
+
}
|
|
719
|
+
/** Store an entry without claiming the latest cursor. */
|
|
720
|
+
upsert(entry) {
|
|
721
|
+
this.entries.set(entry.configId, entry);
|
|
722
|
+
}
|
|
723
|
+
get(configId) {
|
|
724
|
+
return this.entries.get(configId);
|
|
725
|
+
}
|
|
726
|
+
find(configId, tunnelId) {
|
|
727
|
+
const byConfigId = this.entries.get(configId);
|
|
728
|
+
if (byConfigId) {
|
|
729
|
+
return byConfigId;
|
|
730
|
+
}
|
|
731
|
+
if (!tunnelId) {
|
|
732
|
+
return void 0;
|
|
733
|
+
}
|
|
734
|
+
for (const entry of this.entries.values()) {
|
|
735
|
+
if (entry.tunnelId === tunnelId) {
|
|
736
|
+
return entry;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return void 0;
|
|
740
|
+
}
|
|
741
|
+
delete(configId) {
|
|
742
|
+
this.entries.delete(configId);
|
|
743
|
+
if (this.latestConfigId === configId) {
|
|
744
|
+
this.latestConfigId = void 0;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
get latest() {
|
|
748
|
+
return this.latestConfigId;
|
|
749
|
+
}
|
|
750
|
+
get hasLatest() {
|
|
751
|
+
return this.entries.size > 0 && this.latestConfigId !== void 0;
|
|
752
|
+
}
|
|
753
|
+
isLatest(configId) {
|
|
754
|
+
return configId === this.latestConfigId;
|
|
755
|
+
}
|
|
756
|
+
cleanupExpired(onTimeout) {
|
|
757
|
+
const now = Date.now();
|
|
758
|
+
for (const [configId, entry] of this.entries) {
|
|
759
|
+
if (now - entry.queuedAt <= PENDING_TIMEOUT_MS) {
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const wasLatest = this.isLatest(configId);
|
|
763
|
+
this.delete(configId);
|
|
764
|
+
onTimeout(entry, wasLatest);
|
|
765
|
+
}
|
|
707
766
|
}
|
|
708
|
-
|
|
709
|
-
|
|
767
|
+
};
|
|
768
|
+
var RemoteManagementWebSocketPrinter = class {
|
|
769
|
+
constructor() {
|
|
770
|
+
this.pendingStarts = new PendingTracker();
|
|
771
|
+
this.pendingActions = new PendingTracker();
|
|
772
|
+
// tunnelId -> details, fed by monitorList. Stop/restart requests only carry
|
|
773
|
+
// a tunnelId; without this the spinner shows the raw id.
|
|
774
|
+
this.tunnelDetailsCache = /* @__PURE__ */ new Map();
|
|
710
775
|
}
|
|
711
776
|
queueStart(config) {
|
|
712
777
|
this.cleanupExpiredPendingStarts();
|
|
@@ -715,24 +780,23 @@ var RemoteManagementWebSocketPrinter = class {
|
|
|
715
780
|
configName: this.getConfigNameFromRequest(config),
|
|
716
781
|
queuedAt: Date.now()
|
|
717
782
|
};
|
|
718
|
-
this.
|
|
719
|
-
this.pendingStarts.set(entry.configId, entry);
|
|
783
|
+
this.pendingStarts.queue(entry);
|
|
720
784
|
printer_default.startSpinner("Starting tunnel with config name: " + entry.configName);
|
|
721
785
|
}
|
|
722
786
|
failQueuedStart(config, reason) {
|
|
723
787
|
const configId = this.getConfigIdFromRequest(config);
|
|
724
788
|
const pending = this.pendingStarts.get(configId);
|
|
725
789
|
const configName = pending?.configName || this.getConfigNameFromRequest(config);
|
|
790
|
+
const wasLatest = this.pendingStarts.isLatest(configId);
|
|
726
791
|
this.pendingStarts.delete(configId);
|
|
727
|
-
if (
|
|
728
|
-
this.latestPendingConfigId = void 0;
|
|
792
|
+
if (wasLatest) {
|
|
729
793
|
printer_default.stopSpinnerFail(`Failed to start tunnel with config name: ${configName}. ${reason}`);
|
|
730
794
|
}
|
|
731
795
|
}
|
|
732
796
|
handleStartResult(config, result) {
|
|
733
797
|
this.cleanupExpiredPendingStarts();
|
|
734
798
|
const requestedConfigId = this.getConfigIdFromRequest(config);
|
|
735
|
-
if (this.
|
|
799
|
+
if (this.pendingStarts.latest && !this.pendingStarts.isLatest(requestedConfigId)) {
|
|
736
800
|
this.pendingStarts.delete(requestedConfigId);
|
|
737
801
|
return;
|
|
738
802
|
}
|
|
@@ -740,20 +804,20 @@ var RemoteManagementWebSocketPrinter = class {
|
|
|
740
804
|
this.failQueuedStart(config, result.message);
|
|
741
805
|
return;
|
|
742
806
|
}
|
|
743
|
-
const configId = this.getConfigIdFromTunnel(result);
|
|
744
807
|
const pending = this.pendingStarts.get(requestedConfigId) || {
|
|
745
808
|
configId: requestedConfigId,
|
|
746
809
|
configName: this.getConfigNameFromRequest(config),
|
|
747
810
|
queuedAt: Date.now()
|
|
748
811
|
};
|
|
749
812
|
pending.tunnelId = result.tunnelid;
|
|
750
|
-
this.pendingStarts.
|
|
813
|
+
this.pendingStarts.upsert(pending);
|
|
751
814
|
if (result.remoteurls.length > 0) {
|
|
752
815
|
this.completePendingStart(pending, result.remoteurls);
|
|
753
816
|
}
|
|
754
817
|
}
|
|
755
818
|
printStopRequested(tunnelId) {
|
|
756
819
|
const details = this.resolveTunnelDetails(tunnelId);
|
|
820
|
+
this.clearPendingAction(details.configId, tunnelId);
|
|
757
821
|
printer_default.startSpinner("Stopping tunnel with config name: " + details.configName);
|
|
758
822
|
}
|
|
759
823
|
handleStopResult(tunnelId, result) {
|
|
@@ -767,6 +831,7 @@ var RemoteManagementWebSocketPrinter = class {
|
|
|
767
831
|
}
|
|
768
832
|
printRestartRequested(tunnelId) {
|
|
769
833
|
const details = this.resolveTunnelDetails(tunnelId);
|
|
834
|
+
this.clearPendingAction(details.configId, tunnelId);
|
|
770
835
|
printer_default.startSpinner("Restarting tunnel with config name: " + details.configName);
|
|
771
836
|
}
|
|
772
837
|
handleRestartResult(tunnelId, result) {
|
|
@@ -776,48 +841,101 @@ var RemoteManagementWebSocketPrinter = class {
|
|
|
776
841
|
printer_default.stopSpinnerFail("Failed to restart tunnel with config name: " + details.configName);
|
|
777
842
|
return;
|
|
778
843
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
844
|
+
this.queuePendingAction("restart", details.configId, details.configName, result.tunnelid);
|
|
845
|
+
}
|
|
846
|
+
printUpdateConfigRequested(config) {
|
|
847
|
+
const configName = this.getConfigNameFromRequest(config);
|
|
848
|
+
this.clearPendingAction(this.getConfigIdFromRequest(config));
|
|
849
|
+
printer_default.startSpinner("Updating tunnel configuration for: " + configName);
|
|
850
|
+
}
|
|
851
|
+
handleUpdateConfigResult(config, result) {
|
|
852
|
+
const configId = this.getConfigIdFromRequest(config);
|
|
853
|
+
const configName = this.getConfigNameFromRequest(config);
|
|
854
|
+
if (isErrorResponse(result)) {
|
|
855
|
+
printer_default.stopSpinnerFail(`Failed to update tunnel configuration for: ${configName}. ${result.message}`);
|
|
856
|
+
return;
|
|
785
857
|
}
|
|
858
|
+
if (result.status.state === "exited" /* Exited */) {
|
|
859
|
+
printer_default.stopSpinnerSuccess(`Tunnel configuration updated: ${configName}.`);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
this.queuePendingAction("update", configId, configName, result.tunnelid);
|
|
863
|
+
}
|
|
864
|
+
clearPendingAction(configId, tunnelId) {
|
|
865
|
+
const entry = this.pendingActions.find(configId, tunnelId);
|
|
866
|
+
if (!entry) {
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
this.pendingActions.delete(entry.configId);
|
|
870
|
+
}
|
|
871
|
+
queuePendingAction(action, configId, configName, tunnelId) {
|
|
872
|
+
this.cleanupExpiredPendingActions();
|
|
873
|
+
this.pendingActions.queue({ action, configId, configName, queuedAt: Date.now(), tunnelId });
|
|
874
|
+
}
|
|
875
|
+
printActionComplete(action, configName, urls) {
|
|
876
|
+
const message = action === "restart" ? `Restarted tunnel with config name: ${configName}` : `Tunnel configuration updated and restarted: ${configName}`;
|
|
877
|
+
printer_default.stopSpinnerSuccess(message);
|
|
878
|
+
printer_default.info(pico.cyanBright("Remote URLs:"));
|
|
879
|
+
urls.forEach((url) => printer_default.print(" " + pico.magentaBright(url)));
|
|
786
880
|
}
|
|
787
881
|
monitorList(result) {
|
|
788
882
|
this.cleanupExpiredPendingStarts();
|
|
789
|
-
|
|
883
|
+
this.cleanupExpiredPendingActions();
|
|
884
|
+
if (!Array.isArray(result)) {
|
|
790
885
|
return;
|
|
791
886
|
}
|
|
792
887
|
for (const tunnel of result) {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
888
|
+
this.tunnelDetailsCache.set(tunnel.tunnelid, {
|
|
889
|
+
configId: this.getConfigIdFromTunnel(tunnel),
|
|
890
|
+
configName: this.getConfigNameFromTunnel(tunnel)
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
this.resolvePendingFromList(
|
|
894
|
+
result,
|
|
895
|
+
this.pendingStarts,
|
|
896
|
+
(entry, urls) => this.completePendingStart(entry, urls),
|
|
897
|
+
(entry, reason) => printer_default.stopSpinnerFail(`Tunnel start did not complete for config name: ${entry.configName}. ${reason}`)
|
|
898
|
+
);
|
|
899
|
+
this.resolvePendingFromList(
|
|
900
|
+
result,
|
|
901
|
+
this.pendingActions,
|
|
902
|
+
(entry, urls) => {
|
|
903
|
+
this.pendingActions.delete(entry.configId);
|
|
904
|
+
this.printActionComplete(entry.action, entry.configName, urls);
|
|
905
|
+
},
|
|
906
|
+
(entry, reason) => {
|
|
907
|
+
const verb = entry.action === "restart" ? "Restart" : "Configuration update";
|
|
908
|
+
printer_default.stopSpinnerFail(`${verb} did not complete for config name: ${entry.configName}. ${reason}`);
|
|
796
909
|
}
|
|
797
|
-
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
resolvePendingFromList(tunnels, tracker, onComplete, onExited) {
|
|
913
|
+
if (!tracker.hasLatest) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
for (const tunnel of tunnels) {
|
|
917
|
+
const entry = tracker.find(this.getConfigIdFromTunnel(tunnel), tunnel.tunnelid);
|
|
918
|
+
if (!entry || !tracker.isLatest(entry.configId)) {
|
|
798
919
|
continue;
|
|
799
920
|
}
|
|
800
|
-
|
|
801
|
-
|
|
921
|
+
entry.tunnelId = tunnel.tunnelid;
|
|
922
|
+
tracker.upsert(entry);
|
|
802
923
|
if (tunnel.remoteurls.length > 0) {
|
|
803
|
-
|
|
924
|
+
onComplete(entry, tunnel.remoteurls);
|
|
804
925
|
continue;
|
|
805
926
|
}
|
|
806
927
|
if (tunnel.status.state === "exited" /* Exited */) {
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
this.latestPendingConfigId = void 0;
|
|
810
|
-
printer_default.stopSpinnerFail(`Tunnel start did not complete for config name: ${pending.configName}. ${reason}`);
|
|
928
|
+
tracker.delete(entry.configId);
|
|
929
|
+
onExited(entry, tunnel.status.errormsg || "Tunnel exited before a public URL was assigned");
|
|
811
930
|
}
|
|
812
931
|
}
|
|
813
932
|
}
|
|
814
933
|
completePendingStart(entry, urls) {
|
|
815
|
-
if (this.
|
|
934
|
+
if (this.pendingStarts.latest && !this.pendingStarts.isLatest(entry.configId)) {
|
|
816
935
|
this.pendingStarts.delete(entry.configId);
|
|
817
936
|
return;
|
|
818
937
|
}
|
|
819
938
|
this.pendingStarts.delete(entry.configId);
|
|
820
|
-
this.latestPendingConfigId = void 0;
|
|
821
939
|
printer_default.stopSpinnerSuccess(`Tunnel started with config name: ${entry.configName}.`);
|
|
822
940
|
printer_default.info(pico.cyanBright("Remote URLs:"));
|
|
823
941
|
(urls ?? []).forEach(
|
|
@@ -825,28 +943,27 @@ var RemoteManagementWebSocketPrinter = class {
|
|
|
825
943
|
);
|
|
826
944
|
}
|
|
827
945
|
cleanupExpiredPendingStarts() {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
if (
|
|
831
|
-
|
|
946
|
+
this.pendingStarts.cleanupExpired((entry, wasLatest) => {
|
|
947
|
+
const message = `Timed out while waiting for tunnel URL for config name: ${entry.configName}`;
|
|
948
|
+
if (wasLatest) {
|
|
949
|
+
printer_default.stopSpinnerFail(message);
|
|
950
|
+
} else {
|
|
951
|
+
printer_default.warn(message);
|
|
832
952
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
logger.warn("Pending websocket start entry expired", { configId, tunnelId: entry.tunnelId });
|
|
836
|
-
}
|
|
953
|
+
logger.warn("Pending websocket start entry expired", { configId: entry.configId, tunnelId: entry.tunnelId });
|
|
954
|
+
});
|
|
837
955
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
return entry;
|
|
956
|
+
cleanupExpiredPendingActions() {
|
|
957
|
+
this.pendingActions.cleanupExpired((entry, wasLatest) => {
|
|
958
|
+
const verb = entry.action === "restart" ? "restart" : "configuration update";
|
|
959
|
+
const message = `Timed out while waiting for tunnel URL after ${verb} for config name: ${entry.configName}`;
|
|
960
|
+
if (wasLatest) {
|
|
961
|
+
printer_default.stopSpinnerFail(message);
|
|
962
|
+
} else {
|
|
963
|
+
printer_default.warn(message);
|
|
847
964
|
}
|
|
848
|
-
|
|
849
|
-
|
|
965
|
+
logger.warn("Pending websocket action entry expired", { configId: entry.configId, tunnelId: entry.tunnelId, action: entry.action });
|
|
966
|
+
});
|
|
850
967
|
}
|
|
851
968
|
resolveTunnelDetails(tunnelId, result) {
|
|
852
969
|
if (result && !isErrorResponse(result)) {
|
|
@@ -855,7 +972,7 @@ var RemoteManagementWebSocketPrinter = class {
|
|
|
855
972
|
configName: this.getConfigNameFromTunnel(result)
|
|
856
973
|
};
|
|
857
974
|
}
|
|
858
|
-
return {
|
|
975
|
+
return this.tunnelDetailsCache.get(tunnelId) ?? {
|
|
859
976
|
configId: tunnelId,
|
|
860
977
|
configName: tunnelId
|
|
861
978
|
};
|
|
@@ -892,7 +1009,6 @@ var WsCommand = {
|
|
|
892
1009
|
var WebSocketCommandHandler = class {
|
|
893
1010
|
constructor(handler) {
|
|
894
1011
|
this.tunnelHandler = handler ?? new TunnelOperations();
|
|
895
|
-
remoteManagementWebSocketPrinter.setTunnelHandler(this.tunnelHandler);
|
|
896
1012
|
}
|
|
897
1013
|
safeParse(text) {
|
|
898
1014
|
if (!text) return void 0;
|
|
@@ -959,8 +1075,10 @@ var WebSocketCommandHandler = class {
|
|
|
959
1075
|
}
|
|
960
1076
|
}
|
|
961
1077
|
async handleStopReq(req, raw) {
|
|
1078
|
+
let tunnelID;
|
|
962
1079
|
try {
|
|
963
1080
|
const dc = StopSchema.parse(raw);
|
|
1081
|
+
tunnelID = dc.tunnelID;
|
|
964
1082
|
remoteManagementWebSocketPrinter.printStopRequested(dc.tunnelID);
|
|
965
1083
|
const result = await this.tunnelHandler.handleStop(dc.tunnelID);
|
|
966
1084
|
remoteManagementWebSocketPrinter.handleStopResult(dc.tunnelID, result);
|
|
@@ -970,6 +1088,9 @@ var WebSocketCommandHandler = class {
|
|
|
970
1088
|
printer_default.warn("Validation failed for stop request");
|
|
971
1089
|
return NewErrorResponseObject({ code: ErrorCode.InvalidBodyFormatError, message: "Validation failed" });
|
|
972
1090
|
}
|
|
1091
|
+
if (tunnelID) {
|
|
1092
|
+
remoteManagementWebSocketPrinter.handleStopResult(tunnelID, newErrorResponse({ code: ErrorCode.InternalServerError, message: errorMessage(e) }));
|
|
1093
|
+
}
|
|
973
1094
|
printer_default.warn(`Error in handleStopReq error: ${String(e)}`);
|
|
974
1095
|
return NewErrorResponseObject({ code: ErrorCode.InternalServerError, message: String(e) });
|
|
975
1096
|
}
|
|
@@ -989,8 +1110,10 @@ var WebSocketCommandHandler = class {
|
|
|
989
1110
|
}
|
|
990
1111
|
}
|
|
991
1112
|
async handleRestartReq(req, raw) {
|
|
1113
|
+
let tunnelID;
|
|
992
1114
|
try {
|
|
993
1115
|
const dc = RestartSchema.parse(raw);
|
|
1116
|
+
tunnelID = dc.tunnelID;
|
|
994
1117
|
remoteManagementWebSocketPrinter.printRestartRequested(dc.tunnelID);
|
|
995
1118
|
const result = await this.tunnelHandler.handleRestart(dc.tunnelID, true);
|
|
996
1119
|
remoteManagementWebSocketPrinter.handleRestartResult(dc.tunnelID, result);
|
|
@@ -1000,34 +1123,51 @@ var WebSocketCommandHandler = class {
|
|
|
1000
1123
|
printer_default.warn("Validation failed for restart request");
|
|
1001
1124
|
return NewErrorResponseObject({ code: ErrorCode.InvalidBodyFormatError, message: "Validation failed" });
|
|
1002
1125
|
}
|
|
1126
|
+
if (tunnelID) {
|
|
1127
|
+
remoteManagementWebSocketPrinter.handleRestartResult(tunnelID, newErrorResponse({ code: ErrorCode.InternalServerError, message: errorMessage(e) }));
|
|
1128
|
+
}
|
|
1003
1129
|
printer_default.warn(`Error in handleRestartReq error: ${String(e)}`);
|
|
1004
1130
|
return NewErrorResponseObject({ code: ErrorCode.InternalServerError, message: String(e) });
|
|
1005
1131
|
}
|
|
1006
1132
|
}
|
|
1007
1133
|
async handleUpdateConfigReq(req, raw) {
|
|
1134
|
+
let parsedConfig;
|
|
1008
1135
|
try {
|
|
1009
1136
|
const dc = UpdateConfigSchema.parse(raw);
|
|
1137
|
+
parsedConfig = dc.tunnelConfig;
|
|
1138
|
+
remoteManagementWebSocketPrinter.printUpdateConfigRequested(dc.tunnelConfig);
|
|
1010
1139
|
const result = await this.tunnelHandler.handleUpdateConfig(dc.tunnelConfig, true);
|
|
1140
|
+
remoteManagementWebSocketPrinter.handleUpdateConfigResult(dc.tunnelConfig, result);
|
|
1011
1141
|
return this.wrapResponse(result, req);
|
|
1012
1142
|
} catch (e) {
|
|
1013
1143
|
if (e instanceof z2.ZodError) {
|
|
1014
1144
|
printer_default.warn("Validation failed for updateconfig request");
|
|
1015
1145
|
return NewErrorResponseObject({ code: ErrorCode.InvalidBodyFormatError, message: "Validation failed" });
|
|
1016
1146
|
}
|
|
1147
|
+
if (parsedConfig) {
|
|
1148
|
+
remoteManagementWebSocketPrinter.handleUpdateConfigResult(parsedConfig, newErrorResponse({ code: ErrorCode.InternalServerError, message: errorMessage(e) }));
|
|
1149
|
+
}
|
|
1017
1150
|
printer_default.warn(`Error in handleUpdateConfigReq error: ${String(e)}`);
|
|
1018
1151
|
return NewErrorResponseObject({ code: ErrorCode.InternalServerError, message: String(e) });
|
|
1019
1152
|
}
|
|
1020
1153
|
}
|
|
1021
1154
|
async handleUpdateConfigV2Req(req, raw) {
|
|
1155
|
+
let parsedConfig;
|
|
1022
1156
|
try {
|
|
1023
1157
|
const dc = UpdateConfigV2Schema.parse(raw);
|
|
1158
|
+
parsedConfig = dc.tunnelConfig;
|
|
1159
|
+
remoteManagementWebSocketPrinter.printUpdateConfigRequested(dc.tunnelConfig);
|
|
1024
1160
|
const result = await this.tunnelHandler.handleUpdateConfigV2(dc.tunnelConfig, true);
|
|
1161
|
+
remoteManagementWebSocketPrinter.handleUpdateConfigResult(dc.tunnelConfig, result);
|
|
1025
1162
|
return this.wrapResponse(result, req);
|
|
1026
1163
|
} catch (e) {
|
|
1027
1164
|
if (e instanceof z2.ZodError) {
|
|
1028
1165
|
printer_default.warn("Validation failed for update-config-v2 request");
|
|
1029
1166
|
return NewErrorResponseObject({ code: ErrorCode.InvalidBodyFormatError, message: "Validation failed" });
|
|
1030
1167
|
}
|
|
1168
|
+
if (parsedConfig) {
|
|
1169
|
+
remoteManagementWebSocketPrinter.handleUpdateConfigResult(parsedConfig, newErrorResponse({ code: ErrorCode.InternalServerError, message: errorMessage(e) }));
|
|
1170
|
+
}
|
|
1031
1171
|
printer_default.warn(`Error in handleUpdateConfigV2Req error: ${String(e)}`);
|
|
1032
1172
|
return NewErrorResponseObject({ code: ErrorCode.InternalServerError, message: String(e) });
|
|
1033
1173
|
}
|
|
@@ -1507,7 +1647,7 @@ async function ensureDaemonRunning() {
|
|
|
1507
1647
|
const existing = getDaemonInfo();
|
|
1508
1648
|
if (existing) return existing;
|
|
1509
1649
|
if (process.versions.electron) {
|
|
1510
|
-
const { runDaemonChild } = await import("./daemonChild-
|
|
1650
|
+
const { runDaemonChild } = await import("./daemonChild-ZACX7V6J.js");
|
|
1511
1651
|
const handle = await runDaemonChild({
|
|
1512
1652
|
installSignalHandlers: false,
|
|
1513
1653
|
exitOnFailure: false,
|
|
@@ -1529,7 +1669,7 @@ function getInProcessDaemonHandle() {
|
|
|
1529
1669
|
async function getActiveTunnelSummaries(origin = "app") {
|
|
1530
1670
|
const info = getDaemonInfo();
|
|
1531
1671
|
if (!info) return [];
|
|
1532
|
-
const { IPCClient: IPCClient2 } = await import("./ipcClient-
|
|
1672
|
+
const { IPCClient: IPCClient2 } = await import("./ipcClient-XRL2WUUM.js");
|
|
1533
1673
|
const client = new IPCClient2(info.port, origin);
|
|
1534
1674
|
let tunnels;
|
|
1535
1675
|
try {
|
|
@@ -1576,7 +1716,7 @@ async function stopDaemon() {
|
|
|
1576
1716
|
if (!info) return { ok: false, error: "No daemon is running." };
|
|
1577
1717
|
let daemonErrors = [];
|
|
1578
1718
|
try {
|
|
1579
|
-
const { IPCClient: IPCClient2 } = await import("./ipcClient-
|
|
1719
|
+
const { IPCClient: IPCClient2 } = await import("./ipcClient-XRL2WUUM.js");
|
|
1580
1720
|
const client = new IPCClient2(info.port);
|
|
1581
1721
|
const result = await client.shutdown();
|
|
1582
1722
|
logger.debug("Sent shutdown command to daemon", { result });
|
|
@@ -2048,9 +2188,9 @@ var TunnelClient = class _TunnelClient {
|
|
|
2048
2188
|
this.assertClient();
|
|
2049
2189
|
return this.ipc.getTunnel(tunnelId);
|
|
2050
2190
|
}
|
|
2051
|
-
async handleRestart(tunnelId) {
|
|
2191
|
+
async handleRestart(tunnelId, noWait) {
|
|
2052
2192
|
this.assertClient();
|
|
2053
|
-
return this.ipc.restartTunnel(tunnelId);
|
|
2193
|
+
return this.ipc.restartTunnel(tunnelId, noWait);
|
|
2054
2194
|
}
|
|
2055
2195
|
async shutdown() {
|
|
2056
2196
|
this.assertClient();
|
|
@@ -62,8 +62,8 @@ var IPCClient = class {
|
|
|
62
62
|
async stopTunnel(tunnelid) {
|
|
63
63
|
return this.call(Route.StopTunnel, { tunnelid });
|
|
64
64
|
}
|
|
65
|
-
async restartTunnel(tunnelid) {
|
|
66
|
-
return this.call(Route.RestartTunnel, { tunnelid });
|
|
65
|
+
async restartTunnel(tunnelid, noWait) {
|
|
66
|
+
return this.call(Route.RestartTunnel, { tunnelid, noWait });
|
|
67
67
|
}
|
|
68
68
|
// v1 operations (used by remote management via daemon)
|
|
69
69
|
async startTunnelV1(config, mode, noWait) {
|
|
@@ -22,8 +22,8 @@ import {
|
|
|
22
22
|
validateName,
|
|
23
23
|
validateNameForStorage,
|
|
24
24
|
validateNameStrict
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import "./chunk-
|
|
25
|
+
} from "./chunk-YWXADADB.js";
|
|
26
|
+
import "./chunk-P7WNBN7F.js";
|
|
27
27
|
import "./chunk-GBYF2H4H.js";
|
|
28
28
|
export {
|
|
29
29
|
CONFIG_VERBS,
|
|
@@ -6,16 +6,16 @@ import {
|
|
|
6
6
|
trackIPCTunnelStart,
|
|
7
7
|
trackTunnelStart,
|
|
8
8
|
trackTunnelStop
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-NBR2L6LW.js";
|
|
10
|
+
import "./chunk-UX64XCC2.js";
|
|
11
11
|
import "./chunk-MT44NAXX.js";
|
|
12
12
|
import {
|
|
13
13
|
DaemonHost
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-YTJEV27N.js";
|
|
15
|
+
import "./chunk-YWXADADB.js";
|
|
16
|
+
import "./chunk-PQSLSIDA.js";
|
|
17
17
|
import "./chunk-UB26QJ4T.js";
|
|
18
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-P7WNBN7F.js";
|
|
19
19
|
import "./chunk-GBYF2H4H.js";
|
|
20
20
|
export {
|
|
21
21
|
DaemonHost,
|