pinggy 0.6.1 → 0.6.3
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/ARCHITECTURE.md +334 -10
- package/CLAUDE.md +20 -2
- package/README.md +90 -0
- package/dist/TunnelManager-NGSUBZYI.js +13 -0
- package/dist/{TunnelTui-GBRJDOQD.js → TunnelTui-UCG2YSLX.js} +6 -18
- package/dist/{chunk-I4HXBP5G.js → chunk-7EQKHVQX.js} +34 -2
- package/dist/{chunk-GBYF2H4H.js → chunk-AFMTIELL.js} +4 -0
- package/dist/{chunk-QVV3MRKY.js → chunk-GESMZQHI.js} +47 -119
- package/dist/{chunk-JQAQRJPE.js → chunk-MV56U3E6.js} +2 -2
- package/dist/{chunk-MT44NAXX.js → chunk-PD7HXVQ3.js} +1 -1
- package/dist/{chunk-LV3J6TAV.js → chunk-QNUVQ6KD.js} +199 -17
- package/dist/{chunk-7F67A6LY.js → chunk-TG6LBCPG.js} +3486 -236
- package/dist/chunk-XZ64ZAGO.js +18 -0
- package/dist/{chunk-DJ5WX37D.js → chunk-Y435RMHQ.js} +7 -3
- package/dist/{configStore-XRWOTTA4.js → configStore-RR5ZU4E3.js} +3 -3
- package/dist/{daemonChild-VX7SCGVS.js → daemonChild-E73NFWKE.js} +13 -8
- package/dist/{daemonConfig-G6S46GPJ.js → daemonConfig-QVXQ47XH.js} +2 -2
- package/dist/index.cjs +3919 -277
- package/dist/index.d.cts +135 -53
- package/dist/index.d.ts +135 -53
- package/dist/index.js +13 -10
- package/dist/ipcClient-XGF2KCQO.js +10 -0
- package/dist/{main-GXPUTY6N.js → main-M6MACH7D.js} +9 -8
- package/dist/workers/file_serve_worker.js +1 -1
- package/package.json +5 -2
- package/scripts/install/install.ps1 +261 -0
- package/scripts/install/install.sh +387 -0
- package/dist/TunnelManager-EHMPIQDZ.js +0 -11
- package/dist/ipcClient-URHJKNEH.js +0 -6
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
getLogLevel,
|
|
6
6
|
logger
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MV56U3E6.js";
|
|
8
8
|
import {
|
|
9
9
|
getTunnelLogPath
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-AFMTIELL.js";
|
|
11
11
|
|
|
12
12
|
// src/tunnel_manager/TunnelManager.ts
|
|
13
13
|
import { TunnelInstance, LogLevel as SdkLogLevel } from "@pinggy/pinggy";
|
|
@@ -94,6 +94,114 @@ function maybeRotate(file, maxBytes = DEFAULT_MAX_BYTES, maxArchives = DEFAULT_M
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// src/types.ts
|
|
98
|
+
var TunnelStateType = /* @__PURE__ */ ((TunnelStateType2) => {
|
|
99
|
+
TunnelStateType2["New"] = "idle";
|
|
100
|
+
TunnelStateType2["Starting"] = "starting";
|
|
101
|
+
TunnelStateType2["Running"] = "running";
|
|
102
|
+
TunnelStateType2["Live"] = "live";
|
|
103
|
+
TunnelStateType2["Closed"] = "closed";
|
|
104
|
+
TunnelStateType2["Exited"] = "exited";
|
|
105
|
+
return TunnelStateType2;
|
|
106
|
+
})(TunnelStateType || {});
|
|
107
|
+
var TunnelErrorCodeType = /* @__PURE__ */ ((TunnelErrorCodeType2) => {
|
|
108
|
+
TunnelErrorCodeType2["NonResponsive"] = "non_responsive";
|
|
109
|
+
TunnelErrorCodeType2["FailedToConnect"] = "failed_to_connect";
|
|
110
|
+
TunnelErrorCodeType2["ErrorInAdditionalForwarding"] = "additional_forwarding_error";
|
|
111
|
+
TunnelErrorCodeType2["WebdebuggerError"] = "webdebugger_error";
|
|
112
|
+
TunnelErrorCodeType2["NoError"] = "";
|
|
113
|
+
return TunnelErrorCodeType2;
|
|
114
|
+
})(TunnelErrorCodeType || {});
|
|
115
|
+
var TunnelWarningCode = /* @__PURE__ */ ((TunnelWarningCode3) => {
|
|
116
|
+
TunnelWarningCode3["InvalidTunnelServePath"] = "INVALID_TUNNEL_SERVE_PATH";
|
|
117
|
+
TunnelWarningCode3["UnknownWarning"] = "UNKNOWN_WARNING";
|
|
118
|
+
return TunnelWarningCode3;
|
|
119
|
+
})(TunnelWarningCode || {});
|
|
120
|
+
var ErrorCode = {
|
|
121
|
+
InvalidRequestMethodError: "INVALID_REQUEST_METHOD",
|
|
122
|
+
InvalidRequestBodyError: "COULD_NOT_READ_BODY",
|
|
123
|
+
InternalServerError: "INTERNAL_SERVER_ERROR",
|
|
124
|
+
InvalidBodyFormatError: "INVALID_DATA_FORMAT",
|
|
125
|
+
ErrorStartingTunnel: "ERROR_STARTING_TUNNEL",
|
|
126
|
+
TunnelNotFound: "TUNNEL_WITH_ID_OR_CONFIG_ID_NOT_FOUND",
|
|
127
|
+
TunnelAlreadyRunningError: "TUNNEL_WITH_ID_OR_CONFIG_ID_ALREADY_RUNNING",
|
|
128
|
+
WebsocketUpgradeFailError: "WEBSOCKET_UPGRADE_FAILED",
|
|
129
|
+
RemoteManagementAlreadyRunning: "REMOTE_MANAGEMENT_ALREADY_RUNNING",
|
|
130
|
+
RemoteManagementNotRunning: "REMOTE_MANAGEMENT_NOT_RUNNING",
|
|
131
|
+
RemoteManagementDeserializationFailed: "REMOTE_MANAGEMENT_DESERIALIZATION_FAILED",
|
|
132
|
+
DeviceNotEnrolled: "DEVICE_NOT_ENROLLED"
|
|
133
|
+
};
|
|
134
|
+
function isErrorResponse(obj) {
|
|
135
|
+
return typeof obj === "object" && obj !== null && "code" in obj && "message" in obj && typeof obj.message === "string" && Object.values(ErrorCode).includes(obj.code);
|
|
136
|
+
}
|
|
137
|
+
function newErrorResponse(codeOrError, message) {
|
|
138
|
+
if (typeof codeOrError === "object") {
|
|
139
|
+
return codeOrError;
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
code: codeOrError,
|
|
143
|
+
message
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function NewResponseObject(data) {
|
|
147
|
+
const encoder = new TextEncoder();
|
|
148
|
+
const bytes = encoder.encode(JSON.stringify(data));
|
|
149
|
+
return {
|
|
150
|
+
response: bytes,
|
|
151
|
+
requestid: "",
|
|
152
|
+
command: "",
|
|
153
|
+
error: false,
|
|
154
|
+
errorresponse: {}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function NewErrorResponseObject(errorResponse) {
|
|
158
|
+
return {
|
|
159
|
+
response: new Uint8Array(),
|
|
160
|
+
requestid: "",
|
|
161
|
+
command: "",
|
|
162
|
+
error: true,
|
|
163
|
+
errorresponse: errorResponse
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function newStatus(tunnelState, errorCode, errorMsg) {
|
|
167
|
+
let assignedState = tunnelState;
|
|
168
|
+
if (tunnelState === "live" /* Live */) {
|
|
169
|
+
assignedState = "running" /* Running */;
|
|
170
|
+
} else if (tunnelState === "idle" /* New */) {
|
|
171
|
+
assignedState = "idle" /* New */;
|
|
172
|
+
} else if (tunnelState === "closed" /* Closed */) {
|
|
173
|
+
assignedState = "exited" /* Exited */;
|
|
174
|
+
}
|
|
175
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
176
|
+
return {
|
|
177
|
+
state: assignedState,
|
|
178
|
+
errorcode: errorCode,
|
|
179
|
+
errormsg: errorMsg,
|
|
180
|
+
createdtimestamp: now,
|
|
181
|
+
starttimestamp: now,
|
|
182
|
+
endtimestamp: now,
|
|
183
|
+
warnings: []
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function newStats() {
|
|
187
|
+
return {
|
|
188
|
+
numLiveConnections: 0,
|
|
189
|
+
numTotalConnections: 0,
|
|
190
|
+
numTotalReqBytes: 0,
|
|
191
|
+
numTotalResBytes: 0,
|
|
192
|
+
numTotalTxBytes: 0,
|
|
193
|
+
elapsedTime: 0
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
var RemoteManagementStatus = {
|
|
197
|
+
Connecting: "CONNECTING",
|
|
198
|
+
Disconnecting: "DISCONNECTING",
|
|
199
|
+
Reconnecting: "RECONNECTING",
|
|
200
|
+
Running: "RUNNING",
|
|
201
|
+
NotRunning: "NOT_RUNNING",
|
|
202
|
+
Error: "ERROR"
|
|
203
|
+
};
|
|
204
|
+
|
|
97
205
|
// src/tunnel_manager/TunnelManager.ts
|
|
98
206
|
import path2 from "path";
|
|
99
207
|
import { Worker } from "worker_threads";
|
|
@@ -268,6 +376,7 @@ function getLocalAddress(config) {
|
|
|
268
376
|
// src/tunnel_manager/TunnelManager.ts
|
|
269
377
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
270
378
|
var __dirname3 = path2.dirname(__filename3);
|
|
379
|
+
var START_FAILED_MESSAGE = "Failed to start tunnel";
|
|
271
380
|
function mapToSdkLogLevel(level) {
|
|
272
381
|
if (level === "debug") return SdkLogLevel.DEBUG;
|
|
273
382
|
if (level === "error") return SdkLogLevel.ERROR;
|
|
@@ -282,6 +391,13 @@ var TunnelAlreadyRunningError = class extends Error {
|
|
|
282
391
|
this.name = "TunnelAlreadyRunningError";
|
|
283
392
|
}
|
|
284
393
|
};
|
|
394
|
+
var TunnelRebuildInProgressError = class extends Error {
|
|
395
|
+
constructor(tunnelId) {
|
|
396
|
+
super(`Tunnel "${tunnelId}" is already restarting`);
|
|
397
|
+
this.tunnelId = tunnelId;
|
|
398
|
+
this.name = "TunnelRebuildInProgressError";
|
|
399
|
+
}
|
|
400
|
+
};
|
|
285
401
|
var TunnelManager = class _TunnelManager {
|
|
286
402
|
constructor() {
|
|
287
403
|
this.tunnelsByTunnelId = /* @__PURE__ */ new Map();
|
|
@@ -326,7 +442,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
326
442
|
throw new Error("configId is required and must be a non-empty string");
|
|
327
443
|
}
|
|
328
444
|
const existing = this.tunnelsByConfigId.get(configId);
|
|
329
|
-
if (existing && !existing.isStopped) {
|
|
445
|
+
if (existing && (!existing.isStopped || existing.rebuild)) {
|
|
330
446
|
throw new TunnelAlreadyRunningError(configId, existing.tunnelid);
|
|
331
447
|
}
|
|
332
448
|
return this._createTunnelWithProcessedConfig({
|
|
@@ -414,11 +530,12 @@ var TunnelManager = class _TunnelManager {
|
|
|
414
530
|
try {
|
|
415
531
|
urls = await managed.instance.start();
|
|
416
532
|
} catch (error) {
|
|
417
|
-
|
|
533
|
+
const reason = errorMessage(error) || START_FAILED_MESSAGE;
|
|
534
|
+
logger.warn("Failed to start tunnel", { tunnelId, error: reason });
|
|
418
535
|
managed.isStopped = true;
|
|
419
536
|
managed.stoppedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
420
537
|
managed.lastError = {
|
|
421
|
-
message:
|
|
538
|
+
message: reason,
|
|
422
539
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
423
540
|
isFatal: true
|
|
424
541
|
};
|
|
@@ -589,6 +706,9 @@ var TunnelManager = class _TunnelManager {
|
|
|
589
706
|
logger.error(`Tunnel "${tunnelId}" not found when fetching status`);
|
|
590
707
|
throw new Error(`Tunnel "${tunnelId}" not found`);
|
|
591
708
|
}
|
|
709
|
+
if (managed.rebuild?.startsAfter) {
|
|
710
|
+
return "starting" /* Starting */;
|
|
711
|
+
}
|
|
592
712
|
if (managed.isStopped) {
|
|
593
713
|
return "exited";
|
|
594
714
|
}
|
|
@@ -639,6 +759,10 @@ var TunnelManager = class _TunnelManager {
|
|
|
639
759
|
logger.warn("Attempted to remove tunnel that is not stopped", { tunnelId });
|
|
640
760
|
return false;
|
|
641
761
|
}
|
|
762
|
+
if (managed.rebuild) {
|
|
763
|
+
logger.warn("Attempted to remove tunnel that is restarting", { tunnelId });
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
642
766
|
this._cleanupTunnelRecords(managed);
|
|
643
767
|
logger.info("Removed stopped tunnel records", { tunnelId, configId: managed.configId });
|
|
644
768
|
return true;
|
|
@@ -718,17 +842,27 @@ var TunnelManager = class _TunnelManager {
|
|
|
718
842
|
if (!managed) {
|
|
719
843
|
return Promise.reject(new Error(`Tunnel with configId "${configId}" not found`));
|
|
720
844
|
}
|
|
721
|
-
return
|
|
845
|
+
return this.currentConfig(managed);
|
|
722
846
|
}
|
|
723
847
|
if (tunnelId) {
|
|
724
848
|
const managed = this.tunnelsByTunnelId.get(tunnelId);
|
|
725
849
|
if (!managed) {
|
|
726
850
|
return Promise.reject(new Error(`Tunnel with tunnelId "${tunnelId}" not found`));
|
|
727
851
|
}
|
|
728
|
-
return
|
|
852
|
+
return this.currentConfig(managed);
|
|
729
853
|
}
|
|
730
854
|
return Promise.reject(new Error(`Either configId or tunnelId must be provided`));
|
|
731
855
|
}
|
|
856
|
+
/**
|
|
857
|
+
* The instance's config, asked of its worker. A stopped instance has let its worker go, so a stopped
|
|
858
|
+
* tunnel, including one a restart is replacing, answers with the config it was built from.
|
|
859
|
+
*/
|
|
860
|
+
currentConfig(managed) {
|
|
861
|
+
if (managed.isStopped) {
|
|
862
|
+
return Promise.resolve(managed.tunnelConfig);
|
|
863
|
+
}
|
|
864
|
+
return Promise.resolve(managed.instance.getConfig());
|
|
865
|
+
}
|
|
732
866
|
/**
|
|
733
867
|
* Stop a tunnel instance and wait for the stop to settle, capped by a
|
|
734
868
|
* timeout. Failure is logged, not thrown: the caller replaces the
|
|
@@ -752,6 +886,46 @@ var TunnelManager = class _TunnelManager {
|
|
|
752
886
|
if (timer) clearTimeout(timer);
|
|
753
887
|
}
|
|
754
888
|
}
|
|
889
|
+
/**
|
|
890
|
+
* Marks the entry a restart or config update replaces. It stays in both maps until
|
|
891
|
+
* `_createTunnelWithProcessedConfig` sets the new entry under the same ids. When the new entry
|
|
892
|
+
* starts, the entry reports `starting`, and an error from an earlier run no longer describes it.
|
|
893
|
+
*/
|
|
894
|
+
beginRebuild(managed, startsAfter) {
|
|
895
|
+
if (managed.rebuild) {
|
|
896
|
+
throw new TunnelRebuildInProgressError(managed.tunnelid);
|
|
897
|
+
}
|
|
898
|
+
managed.rebuild = { startsAfter };
|
|
899
|
+
if (startsAfter) {
|
|
900
|
+
managed.lastError = {};
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
/** Stops the instance being replaced, and marks it stopped so no reader calls it again. */
|
|
904
|
+
async stopForRebuild(managed) {
|
|
905
|
+
if (managed.isStopped) {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
await this.stopInstanceGuarded(managed);
|
|
909
|
+
managed.isStopped = true;
|
|
910
|
+
managed.stoppedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Ends a failed rebuild. When the old entry is still in the maps, it stays listed as stopped with
|
|
914
|
+
* the reason. A new entry that failed to start already holds its own error.
|
|
915
|
+
*/
|
|
916
|
+
failRebuild(managed, error) {
|
|
917
|
+
managed.rebuild = void 0;
|
|
918
|
+
if (this.tunnelsByTunnelId.get(managed.tunnelid) !== managed) {
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
managed.isStopped = true;
|
|
922
|
+
managed.stoppedAt = managed.stoppedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
923
|
+
managed.lastError = {
|
|
924
|
+
message: errorMessage(error) || START_FAILED_MESSAGE,
|
|
925
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
926
|
+
isFatal: true
|
|
927
|
+
};
|
|
928
|
+
}
|
|
755
929
|
/**
|
|
756
930
|
* Restarts a tunnel with its current configuration.
|
|
757
931
|
* This function will stop the tunnel if it's running and start it again.
|
|
@@ -762,6 +936,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
762
936
|
if (!existingTunnel) {
|
|
763
937
|
throw new Error(`Tunnel "${tunnelid}" not found`);
|
|
764
938
|
}
|
|
939
|
+
this.beginRebuild(existingTunnel, true);
|
|
765
940
|
logger.info("Initiating tunnel restart", {
|
|
766
941
|
tunnelId: tunnelid,
|
|
767
942
|
configId: existingTunnel.configId
|
|
@@ -773,11 +948,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
773
948
|
const currentServe = existingTunnel.serve;
|
|
774
949
|
const autoReconnect = existingTunnel.autoReconnect || false;
|
|
775
950
|
const currentOrigin = existingTunnel.origin;
|
|
776
|
-
|
|
777
|
-
await this.stopInstanceGuarded(existingTunnel);
|
|
778
|
-
}
|
|
779
|
-
this.tunnelsByTunnelId.delete(tunnelid);
|
|
780
|
-
this.tunnelsByConfigId.delete(existingTunnel.configId);
|
|
951
|
+
await this.stopForRebuild(existingTunnel);
|
|
781
952
|
this.tunnelStats.delete(tunnelid);
|
|
782
953
|
this.tunnelStatsListeners.delete(tunnelid);
|
|
783
954
|
this.tunnelErrorListeners.delete(tunnelid);
|
|
@@ -803,6 +974,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
803
974
|
}
|
|
804
975
|
await this.startTunnel(newTunnel.tunnelid);
|
|
805
976
|
} catch (error) {
|
|
977
|
+
this.failRebuild(existingTunnel, error);
|
|
806
978
|
logger.error("Failed to restart tunnel", {
|
|
807
979
|
tunnelid,
|
|
808
980
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
@@ -860,12 +1032,9 @@ var TunnelManager = class _TunnelManager {
|
|
|
860
1032
|
const currentStartedAt = existingTunnel.startedAt;
|
|
861
1033
|
const currentStoppedAt = existingTunnel.stoppedAt;
|
|
862
1034
|
const requestedServe = this.resolveServePath(newConfig);
|
|
1035
|
+
this.beginRebuild(existingTunnel, !isStopped);
|
|
863
1036
|
try {
|
|
864
|
-
|
|
865
|
-
await this.stopInstanceGuarded(existingTunnel);
|
|
866
|
-
}
|
|
867
|
-
this.tunnelsByTunnelId.delete(currentTunnelId);
|
|
868
|
-
this.tunnelsByConfigId.delete(currentTunnelConfigId);
|
|
1037
|
+
await this.stopForRebuild(existingTunnel);
|
|
869
1038
|
const mergedBaseConfig = {
|
|
870
1039
|
...newConfig,
|
|
871
1040
|
configId,
|
|
@@ -926,6 +1095,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
926
1095
|
error: errorMessage(restoreError)
|
|
927
1096
|
});
|
|
928
1097
|
}
|
|
1098
|
+
this.failRebuild(existingTunnel, error);
|
|
929
1099
|
throw error;
|
|
930
1100
|
}
|
|
931
1101
|
}
|
|
@@ -1739,11 +1909,23 @@ export {
|
|
|
1739
1909
|
setTunnelLoggingEnabled,
|
|
1740
1910
|
isTunnelLoggingEnabled,
|
|
1741
1911
|
detachAllTunnelLoggers,
|
|
1912
|
+
TunnelStateType,
|
|
1913
|
+
TunnelErrorCodeType,
|
|
1914
|
+
TunnelWarningCode,
|
|
1915
|
+
ErrorCode,
|
|
1916
|
+
isErrorResponse,
|
|
1917
|
+
newErrorResponse,
|
|
1918
|
+
NewResponseObject,
|
|
1919
|
+
NewErrorResponseObject,
|
|
1920
|
+
newStatus,
|
|
1921
|
+
newStats,
|
|
1922
|
+
RemoteManagementStatus,
|
|
1742
1923
|
getRandomId,
|
|
1743
1924
|
isValidPort,
|
|
1744
1925
|
errorMessage,
|
|
1745
1926
|
getVersion,
|
|
1746
1927
|
getLocalAddress,
|
|
1747
1928
|
TunnelAlreadyRunningError,
|
|
1929
|
+
TunnelRebuildInProgressError,
|
|
1748
1930
|
TunnelManager
|
|
1749
1931
|
};
|