pinggy 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +519 -176
- package/dist/index.d.cts +162 -15
- package/dist/index.d.ts +162 -15
- package/dist/index.js +515 -175
- package/dist/workers/file_serve_worker.cjs +296 -0
- package/dist/workers/file_serve_worker.d.cts +2 -0
- package/dist/workers/file_serve_worker.d.ts +2 -0
- package/dist/workers/file_serve_worker.js +262 -0
- package/package.json +3 -2
- package/src/cli/buildConfig.ts +1 -0
- package/src/cli/defaults.ts +1 -0
- package/src/cli/options.ts +2 -0
- package/src/cli/starCli.tsx +119 -51
- package/src/index.ts +6 -2
- package/src/remote_management/handler.ts +90 -25
- package/src/remote_management/remoteManagement.ts +144 -83
- package/src/remote_management/remote_schema.ts +75 -34
- package/src/remote_management/websocket_handlers.ts +15 -1
- package/src/tunnel_manager/TunnelManager.ts +218 -39
- package/src/types.ts +33 -9
- package/tsup.config.ts +1 -1
package/dist/index.cjs
CHANGED
|
@@ -847,7 +847,10 @@ var index_exports = {};
|
|
|
847
847
|
__export(index_exports, {
|
|
848
848
|
TunnelManager: () => TunnelManager,
|
|
849
849
|
TunnelOperations: () => TunnelOperations,
|
|
850
|
-
|
|
850
|
+
closeRemoteManagement: () => closeRemoteManagement,
|
|
851
|
+
enablePackageLogging: () => enablePackageLogging,
|
|
852
|
+
getRemoteManagementState: () => getRemoteManagementState,
|
|
853
|
+
initiateRemoteManagement: () => initiateRemoteManagement
|
|
851
854
|
});
|
|
852
855
|
module.exports = __toCommonJS(index_exports);
|
|
853
856
|
init_cjs_shims();
|
|
@@ -971,6 +974,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
971
974
|
this.tunnelErrorListeners = /* @__PURE__ */ new Map();
|
|
972
975
|
this.tunnelDisconnectListeners = /* @__PURE__ */ new Map();
|
|
973
976
|
this.tunnelWorkerErrorListeners = /* @__PURE__ */ new Map();
|
|
977
|
+
this.tunnelStartListeners = /* @__PURE__ */ new Map();
|
|
974
978
|
}
|
|
975
979
|
static getInstance() {
|
|
976
980
|
if (!_TunnelManager.instance) {
|
|
@@ -1001,7 +1005,15 @@ var TunnelManager = class _TunnelManager {
|
|
|
1001
1005
|
throw new Error(`Tunnel with configId "${configid}" already exists`);
|
|
1002
1006
|
}
|
|
1003
1007
|
const tunnelid = config.tunnelid || await getUuid();
|
|
1004
|
-
|
|
1008
|
+
let instance;
|
|
1009
|
+
try {
|
|
1010
|
+
instance = await import_pinggy2.pinggy.createTunnel(config);
|
|
1011
|
+
} catch (e) {
|
|
1012
|
+
logger.error("Error creating tunnel instance:", e);
|
|
1013
|
+
throw e;
|
|
1014
|
+
}
|
|
1015
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1016
|
+
let autoReconnect = config.autoReconnect !== void 0 ? config.autoReconnect : false;
|
|
1005
1017
|
const managed = {
|
|
1006
1018
|
tunnelid,
|
|
1007
1019
|
configid,
|
|
@@ -1011,8 +1023,15 @@ var TunnelManager = class _TunnelManager {
|
|
|
1011
1023
|
additionalForwarding,
|
|
1012
1024
|
serve: config.serve,
|
|
1013
1025
|
warnings: [],
|
|
1014
|
-
isStopped: false
|
|
1026
|
+
isStopped: false,
|
|
1027
|
+
createdAt: now,
|
|
1028
|
+
startedAt: null,
|
|
1029
|
+
stoppedAt: null,
|
|
1030
|
+
autoReconnect
|
|
1015
1031
|
};
|
|
1032
|
+
instance.setPrimaryForwardingCallback(({}) => {
|
|
1033
|
+
managed.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1034
|
+
});
|
|
1016
1035
|
this.setupStatsCallback(tunnelid, managed);
|
|
1017
1036
|
this.setupErrorCallback(tunnelid, managed);
|
|
1018
1037
|
this.setupDisconnectCallback(tunnelid, managed);
|
|
@@ -1040,11 +1059,11 @@ var TunnelManager = class _TunnelManager {
|
|
|
1040
1059
|
if (Array.isArray(managed.additionalForwarding) && managed.additionalForwarding.length > 0) {
|
|
1041
1060
|
for (const f of managed.additionalForwarding) {
|
|
1042
1061
|
try {
|
|
1043
|
-
if (!f || typeof f.
|
|
1062
|
+
if (!f || typeof f.remoteDomain !== "string" || !f.localDomain || typeof f.localPort !== "number") {
|
|
1044
1063
|
logger.warn(`Skipping invalid additional forwarding rule: ${JSON.stringify(f)}`);
|
|
1045
1064
|
continue;
|
|
1046
1065
|
}
|
|
1047
|
-
const hostname = f.remoteDomain
|
|
1066
|
+
const hostname = f.remoteDomain;
|
|
1048
1067
|
const target = `${f.localDomain}:${f.localPort}`;
|
|
1049
1068
|
await managed.instance.tunnelRequestAdditionalForwarding(hostname, target);
|
|
1050
1069
|
logger.info("Applied additional forwarding", { tunnelId, hostname, target });
|
|
@@ -1056,6 +1075,20 @@ var TunnelManager = class _TunnelManager {
|
|
|
1056
1075
|
if (managed.serve) {
|
|
1057
1076
|
this.startStaticFileServer(managed);
|
|
1058
1077
|
}
|
|
1078
|
+
try {
|
|
1079
|
+
const startListeners = this.tunnelStartListeners.get(tunnelId);
|
|
1080
|
+
if (startListeners) {
|
|
1081
|
+
for (const [id, listener] of startListeners) {
|
|
1082
|
+
try {
|
|
1083
|
+
listener(tunnelId, urls);
|
|
1084
|
+
} catch (err) {
|
|
1085
|
+
logger.debug("Error in start-listener callback", { listenerId: id, tunnelId, err });
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
} catch (e) {
|
|
1090
|
+
logger.warn("Failed to notify start listeners", { tunnelId, e });
|
|
1091
|
+
}
|
|
1059
1092
|
return urls;
|
|
1060
1093
|
}
|
|
1061
1094
|
/**
|
|
@@ -1085,9 +1118,11 @@ var TunnelManager = class _TunnelManager {
|
|
|
1085
1118
|
this.tunnelErrorListeners.delete(tunnelId);
|
|
1086
1119
|
this.tunnelDisconnectListeners.delete(tunnelId);
|
|
1087
1120
|
this.tunnelWorkerErrorListeners.delete(tunnelId);
|
|
1121
|
+
this.tunnelStartListeners.delete(tunnelId);
|
|
1088
1122
|
managed.serveWorker = null;
|
|
1089
1123
|
managed.warnings = managed.warnings ?? [];
|
|
1090
1124
|
managed.isStopped = true;
|
|
1125
|
+
managed.stoppedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1091
1126
|
logger.info("Tunnel stopped", { tunnelId, configId: managed.configid });
|
|
1092
1127
|
return { configid: managed.configid, tunnelid: managed.tunnelid };
|
|
1093
1128
|
} catch (error) {
|
|
@@ -1125,7 +1160,9 @@ var TunnelManager = class _TunnelManager {
|
|
|
1125
1160
|
configid: tunnel.configid,
|
|
1126
1161
|
tunnelName: tunnel.tunnelName,
|
|
1127
1162
|
tunnelConfig: tunnel.tunnelConfig,
|
|
1128
|
-
remoteurls: !tunnel.isStopped ? await this.getTunnelUrls(tunnel.tunnelid) : []
|
|
1163
|
+
remoteurls: !tunnel.isStopped ? await this.getTunnelUrls(tunnel.tunnelid) : [],
|
|
1164
|
+
additionalForwarding: tunnel.additionalForwarding,
|
|
1165
|
+
serve: tunnel.serve
|
|
1129
1166
|
};
|
|
1130
1167
|
}));
|
|
1131
1168
|
return tunnelList;
|
|
@@ -1167,6 +1204,60 @@ var TunnelManager = class _TunnelManager {
|
|
|
1167
1204
|
this.tunnelStatsListeners.clear();
|
|
1168
1205
|
logger.info("All tunnels stopped and cleared");
|
|
1169
1206
|
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Remove a stopped tunnel's records so it will no longer be returned by list methods.
|
|
1209
|
+
*
|
|
1210
|
+
*
|
|
1211
|
+
* @param tunnelId - the tunnel id to remove
|
|
1212
|
+
* @returns true if the record was removed, false otherwise
|
|
1213
|
+
*/
|
|
1214
|
+
removeStoppedTunnelByTunnelId(tunnelId) {
|
|
1215
|
+
const managed = this.tunnelsByTunnelId.get(tunnelId);
|
|
1216
|
+
if (!managed) {
|
|
1217
|
+
logger.debug("Attempted to remove non-existent tunnel", { tunnelId });
|
|
1218
|
+
return false;
|
|
1219
|
+
}
|
|
1220
|
+
if (!managed.isStopped) {
|
|
1221
|
+
logger.warn("Attempted to remove tunnel that is not stopped", { tunnelId });
|
|
1222
|
+
return false;
|
|
1223
|
+
}
|
|
1224
|
+
this._cleanupTunnelRecords(managed);
|
|
1225
|
+
logger.info("Removed stopped tunnel records", { tunnelId, configId: managed.configid });
|
|
1226
|
+
return true;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Remove a stopped tunnel by its config id.
|
|
1230
|
+
* @param configId - the config id to remove
|
|
1231
|
+
* @returns true if the record was removed, false otherwise
|
|
1232
|
+
*/
|
|
1233
|
+
removeStoppedTunnelByConfigId(configId) {
|
|
1234
|
+
const managed = this.tunnelsByConfigId.get(configId);
|
|
1235
|
+
if (!managed) {
|
|
1236
|
+
logger.debug("Attempted to remove non-existent tunnel by configId", { configId });
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
return this.removeStoppedTunnelByTunnelId(managed.tunnelid);
|
|
1240
|
+
}
|
|
1241
|
+
_cleanupTunnelRecords(managed) {
|
|
1242
|
+
if (!managed.isStopped) {
|
|
1243
|
+
throw new Error(`Active tunnel "${managed.tunnelid}" cannot be removed`);
|
|
1244
|
+
}
|
|
1245
|
+
try {
|
|
1246
|
+
if (managed.serveWorker) {
|
|
1247
|
+
managed.serveWorker = null;
|
|
1248
|
+
}
|
|
1249
|
+
this.tunnelStats.delete(managed.tunnelid);
|
|
1250
|
+
this.tunnelStatsListeners.delete(managed.tunnelid);
|
|
1251
|
+
this.tunnelErrorListeners.delete(managed.tunnelid);
|
|
1252
|
+
this.tunnelDisconnectListeners.delete(managed.tunnelid);
|
|
1253
|
+
this.tunnelWorkerErrorListeners.delete(managed.tunnelid);
|
|
1254
|
+
this.tunnelStartListeners.delete(managed.tunnelid);
|
|
1255
|
+
this.tunnelsByTunnelId.delete(managed.tunnelid);
|
|
1256
|
+
this.tunnelsByConfigId.delete(managed.configid);
|
|
1257
|
+
} catch (e) {
|
|
1258
|
+
logger.warn("Failed cleaning up tunnel records", { tunnelId: managed.tunnelid, error: e });
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1170
1261
|
/**
|
|
1171
1262
|
* Get tunnel instance by either configId or tunnelId
|
|
1172
1263
|
* @param configId - The configuration ID of the tunnel
|
|
@@ -1241,6 +1332,9 @@ var TunnelManager = class _TunnelManager {
|
|
|
1241
1332
|
additionalForwarding,
|
|
1242
1333
|
tunnelName
|
|
1243
1334
|
});
|
|
1335
|
+
if (existingTunnel.createdAt) {
|
|
1336
|
+
newTunnel.createdAt = existingTunnel.createdAt;
|
|
1337
|
+
}
|
|
1244
1338
|
this.startTunnel(newTunnel.tunnelid);
|
|
1245
1339
|
} catch (error) {
|
|
1246
1340
|
logger.error("Failed to restart tunnel", {
|
|
@@ -1279,36 +1373,41 @@ var TunnelManager = class _TunnelManager {
|
|
|
1279
1373
|
if (!existingTunnel) {
|
|
1280
1374
|
throw new Error(`Tunnel with config id "${configid}" not found`);
|
|
1281
1375
|
}
|
|
1282
|
-
const
|
|
1376
|
+
const isStopped = existingTunnel.isStopped;
|
|
1283
1377
|
const currentTunnelConfig = existingTunnel.tunnelConfig;
|
|
1284
1378
|
const currentTunnelId = existingTunnel.tunnelid;
|
|
1285
1379
|
const currentTunnelConfigId = existingTunnel.configid;
|
|
1286
1380
|
const currentAdditionalForwarding = existingTunnel.additionalForwarding;
|
|
1287
1381
|
const currentTunnelName = existingTunnel.tunnelName;
|
|
1382
|
+
const currentServe = existingTunnel.serve;
|
|
1288
1383
|
try {
|
|
1289
|
-
if (
|
|
1384
|
+
if (!isStopped) {
|
|
1290
1385
|
existingTunnel.instance.stop();
|
|
1291
1386
|
}
|
|
1292
1387
|
this.tunnelsByTunnelId.delete(currentTunnelId);
|
|
1293
1388
|
this.tunnelsByConfigId.delete(currentTunnelConfigId);
|
|
1294
1389
|
const mergedConfig = {
|
|
1295
|
-
...currentTunnelConfig,
|
|
1296
1390
|
...newConfig,
|
|
1297
1391
|
configid,
|
|
1298
1392
|
tunnelName: newTunnelName !== void 0 ? newTunnelName : currentTunnelName,
|
|
1299
|
-
additionalForwarding: additionalForwarding !== void 0 ? additionalForwarding : currentAdditionalForwarding
|
|
1393
|
+
additionalForwarding: additionalForwarding !== void 0 ? additionalForwarding : currentAdditionalForwarding,
|
|
1394
|
+
serve: newConfig.serve !== void 0 ? newConfig.serve : currentServe
|
|
1300
1395
|
};
|
|
1301
1396
|
const newTunnel = await this.createTunnel(mergedConfig);
|
|
1302
|
-
if (
|
|
1397
|
+
if (!isStopped) {
|
|
1303
1398
|
this.startTunnel(newTunnel.tunnelid);
|
|
1304
1399
|
}
|
|
1305
1400
|
logger.info("Tunnel configuration updated", {
|
|
1306
1401
|
tunnelId: newTunnel.tunnelid,
|
|
1307
1402
|
configId: newTunnel.configid,
|
|
1308
|
-
|
|
1403
|
+
isStopped
|
|
1309
1404
|
});
|
|
1310
1405
|
return newTunnel;
|
|
1311
1406
|
} catch (error) {
|
|
1407
|
+
logger.error("Error updating tunnel configuration", {
|
|
1408
|
+
configId: configid,
|
|
1409
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1410
|
+
});
|
|
1312
1411
|
try {
|
|
1313
1412
|
const originalTunnel = await this.createTunnel({
|
|
1314
1413
|
...currentTunnelConfig,
|
|
@@ -1317,7 +1416,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
1317
1416
|
tunnelName: currentTunnelName,
|
|
1318
1417
|
additionalForwarding: currentAdditionalForwarding
|
|
1319
1418
|
});
|
|
1320
|
-
if (
|
|
1419
|
+
if (!isStopped) {
|
|
1321
1420
|
await this.startTunnel(originalTunnel.tunnelid);
|
|
1322
1421
|
}
|
|
1323
1422
|
logger.warn("Restored original tunnel configuration after update failure", {
|
|
@@ -1358,7 +1457,6 @@ var TunnelManager = class _TunnelManager {
|
|
|
1358
1457
|
}
|
|
1359
1458
|
try {
|
|
1360
1459
|
if (managed.isStopped) {
|
|
1361
|
-
logger.debug(`Tunnel "${tunnelId}" is stopped. No greet message available.`);
|
|
1362
1460
|
return null;
|
|
1363
1461
|
}
|
|
1364
1462
|
const messages = await managed.instance.getGreetMessage();
|
|
@@ -1381,13 +1479,26 @@ var TunnelManager = class _TunnelManager {
|
|
|
1381
1479
|
const stats = this.tunnelStats.get(tunnelId);
|
|
1382
1480
|
return stats || null;
|
|
1383
1481
|
}
|
|
1482
|
+
getLatestTunnelStats(tunnelId) {
|
|
1483
|
+
const managed = this.tunnelsByTunnelId.get(tunnelId);
|
|
1484
|
+
if (!managed) {
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
const stats = this.tunnelStats.get(tunnelId);
|
|
1488
|
+
if (stats && stats.length > 0) {
|
|
1489
|
+
return stats[stats.length - 1];
|
|
1490
|
+
}
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1384
1493
|
/**
|
|
1385
1494
|
* Registers a listener function to receive tunnel statistics updates.
|
|
1386
1495
|
* The listener will be called whenever any tunnel's stats are updated.
|
|
1387
1496
|
*
|
|
1388
1497
|
* @param tunnelId - The tunnel ID to listen to stats for
|
|
1389
1498
|
* @param listener - Function that receives tunnelId and stats when updates occur
|
|
1390
|
-
* @returns A unique listener ID that can be used to deregister the listener
|
|
1499
|
+
* @returns A unique listener ID that can be used to deregister the listener and tunnelId
|
|
1500
|
+
*
|
|
1501
|
+
* @throws {Error} When the specified tunnelId does not exist
|
|
1391
1502
|
*/
|
|
1392
1503
|
async registerStatsListener(tunnelId, listener) {
|
|
1393
1504
|
const managed = this.tunnelsByTunnelId.get(tunnelId);
|
|
@@ -1401,7 +1512,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
1401
1512
|
const tunnelListeners = this.tunnelStatsListeners.get(tunnelId);
|
|
1402
1513
|
tunnelListeners.set(listenerId, listener);
|
|
1403
1514
|
logger.info("Stats listener registered for tunnel", { tunnelId, listenerId });
|
|
1404
|
-
return listenerId;
|
|
1515
|
+
return [listenerId, tunnelId];
|
|
1405
1516
|
}
|
|
1406
1517
|
async registerErrorListener(tunnelId, listener) {
|
|
1407
1518
|
const managed = this.tunnelsByTunnelId.get(tunnelId);
|
|
@@ -1444,6 +1555,20 @@ var TunnelManager = class _TunnelManager {
|
|
|
1444
1555
|
tunnelWorkerErrorListner?.set(listenerId, listener);
|
|
1445
1556
|
logger.info("TunnelWorker error listener registered for tunnel", { tunnelId, listenerId });
|
|
1446
1557
|
}
|
|
1558
|
+
async registerStartListener(tunnelId, listener) {
|
|
1559
|
+
const managed = this.tunnelsByTunnelId.get(tunnelId);
|
|
1560
|
+
if (!managed) {
|
|
1561
|
+
throw new Error(`Tunnel "${tunnelId}" not found`);
|
|
1562
|
+
}
|
|
1563
|
+
if (!this.tunnelStartListeners.has(tunnelId)) {
|
|
1564
|
+
this.tunnelStartListeners.set(tunnelId, /* @__PURE__ */ new Map());
|
|
1565
|
+
}
|
|
1566
|
+
const listenerId = await getUuid();
|
|
1567
|
+
const listeners = this.tunnelStartListeners.get(tunnelId);
|
|
1568
|
+
listeners.set(listenerId, listener);
|
|
1569
|
+
logger.info("Start listener registered for tunnel", { tunnelId, listenerId });
|
|
1570
|
+
return listenerId;
|
|
1571
|
+
}
|
|
1447
1572
|
/**
|
|
1448
1573
|
* Removes a previously registered stats listener.
|
|
1449
1574
|
*
|
|
@@ -1506,7 +1631,6 @@ var TunnelManager = class _TunnelManager {
|
|
|
1506
1631
|
}
|
|
1507
1632
|
try {
|
|
1508
1633
|
if (managed.isStopped) {
|
|
1509
|
-
logger.debug(`Tunnel "${tunnelId}" is stopped. Cannot fetch local server TLS info`);
|
|
1510
1634
|
return false;
|
|
1511
1635
|
}
|
|
1512
1636
|
const tlsInfo = await managed.instance.getLocalServerTls();
|
|
@@ -1551,9 +1675,9 @@ var TunnelManager = class _TunnelManager {
|
|
|
1551
1675
|
}
|
|
1552
1676
|
setupErrorCallback(tunnelId, managed) {
|
|
1553
1677
|
try {
|
|
1554
|
-
const callback = (errorNo,
|
|
1678
|
+
const callback = ({ errorNo, error, recoverable }) => {
|
|
1555
1679
|
try {
|
|
1556
|
-
const msg = typeof
|
|
1680
|
+
const msg = typeof error === "string" ? error : String(error);
|
|
1557
1681
|
const isFatal = true;
|
|
1558
1682
|
logger.debug("Tunnel reported error", { tunnelId, errorNo, errorMsg: msg, recoverable });
|
|
1559
1683
|
this.notifyErrorListeners(tunnelId, msg, isFatal);
|
|
@@ -1569,9 +1693,25 @@ var TunnelManager = class _TunnelManager {
|
|
|
1569
1693
|
}
|
|
1570
1694
|
setupDisconnectCallback(tunnelId, managed) {
|
|
1571
1695
|
try {
|
|
1572
|
-
const callback = (error, messages) => {
|
|
1696
|
+
const callback = ({ error, messages }) => {
|
|
1573
1697
|
try {
|
|
1574
1698
|
logger.debug("Tunnel disconnected", { tunnelId, error, messages });
|
|
1699
|
+
const managedTunnel = this.tunnelsByTunnelId.get(tunnelId);
|
|
1700
|
+
if (managedTunnel) {
|
|
1701
|
+
managedTunnel.isStopped = true;
|
|
1702
|
+
managedTunnel.stoppedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1703
|
+
}
|
|
1704
|
+
if (managedTunnel && managedTunnel.autoReconnect) {
|
|
1705
|
+
logger.info("Auto-reconnecting tunnel", { tunnelId });
|
|
1706
|
+
setTimeout(async () => {
|
|
1707
|
+
try {
|
|
1708
|
+
await this.restartTunnel(tunnelId);
|
|
1709
|
+
logger.info("Tunnel auto-reconnected successfully", { tunnelId });
|
|
1710
|
+
} catch (e) {
|
|
1711
|
+
logger.error("Failed to auto-reconnect tunnel", { tunnelId, e });
|
|
1712
|
+
}
|
|
1713
|
+
}, 1e4);
|
|
1714
|
+
}
|
|
1575
1715
|
const listeners = this.tunnelDisconnectListeners.get(tunnelId);
|
|
1576
1716
|
if (!listeners) return;
|
|
1577
1717
|
for (const [id, listener] of listeners) {
|
|
@@ -1628,7 +1768,7 @@ var TunnelManager = class _TunnelManager {
|
|
|
1628
1768
|
if (tunnelListeners) {
|
|
1629
1769
|
for (const [listenerId, listener] of tunnelListeners) {
|
|
1630
1770
|
try {
|
|
1631
|
-
listener(tunnelId,
|
|
1771
|
+
listener(tunnelId, normalizedStats);
|
|
1632
1772
|
} catch (error) {
|
|
1633
1773
|
logger.warn("Error in stats listener callback", { listenerId, tunnelId, error });
|
|
1634
1774
|
}
|
|
@@ -1667,7 +1807,9 @@ var TunnelManager = class _TunnelManager {
|
|
|
1667
1807
|
}
|
|
1668
1808
|
startStaticFileServer(managed) {
|
|
1669
1809
|
try {
|
|
1670
|
-
const
|
|
1810
|
+
const __filename3 = (0, import_node_url.fileURLToPath)(importMetaUrl);
|
|
1811
|
+
const __dirname2 = import_node_path.default.dirname(__filename3);
|
|
1812
|
+
const fileServerWorkerPath = import_node_path.default.join(__dirname2, "workers", "file_serve_worker.js");
|
|
1671
1813
|
const staticServerWorker = new import_node_worker_threads.Worker(fileServerWorkerPath, {
|
|
1672
1814
|
workerData: {
|
|
1673
1815
|
dir: managed.serve,
|
|
@@ -1731,6 +1873,7 @@ var cliOptions = {
|
|
|
1731
1873
|
v: { type: "boolean", description: "Print logs to stdout for Cli. Overrides PINGGY_LOG_STDOUT environment variable" },
|
|
1732
1874
|
vv: { type: "boolean", description: "Enable detailed logging for the Node.js SDK and Libpinggy, including both info and debug level logs." },
|
|
1733
1875
|
vvv: { type: "boolean", description: "Enable all logs from Cli, SDK and internal components." },
|
|
1876
|
+
autoreconnect: { type: "boolean", short: "a", description: "Automatically reconnect tunnel on failure." },
|
|
1734
1877
|
// Save and load config
|
|
1735
1878
|
saveconf: { type: "string", description: "Create the configuration file based on the options provided here" },
|
|
1736
1879
|
conf: { type: "string", description: "Use the configuration file as base. Other options will be used to override this file" },
|
|
@@ -1805,7 +1948,8 @@ var defaultOptions = {
|
|
|
1805
1948
|
httpsOnly: false,
|
|
1806
1949
|
originalRequestUrl: false,
|
|
1807
1950
|
allowPreflight: false,
|
|
1808
|
-
reverseProxy: false
|
|
1951
|
+
reverseProxy: false,
|
|
1952
|
+
autoReconnect: false
|
|
1809
1953
|
};
|
|
1810
1954
|
|
|
1811
1955
|
// src/cli/extendedOptions.ts
|
|
@@ -2228,7 +2372,8 @@ async function buildFinalConfig(values, positionals) {
|
|
|
2228
2372
|
serverAddress: server || defaultOptions.serverAddress,
|
|
2229
2373
|
tunnelType: initialTunnel ? [initialTunnel] : defaultOptions.tunnelType,
|
|
2230
2374
|
NoTUI: values.notui || false,
|
|
2231
|
-
qrCode: qrCode || false
|
|
2375
|
+
qrCode: qrCode || false,
|
|
2376
|
+
autoReconnect: values.autoreconnect || false
|
|
2232
2377
|
};
|
|
2233
2378
|
parseType(finalConfig, values, type);
|
|
2234
2379
|
parseToken(finalConfig, token || values.token);
|
|
@@ -2312,17 +2457,18 @@ function newStatus(tunnelState, errorCode, errorMsg) {
|
|
|
2312
2457
|
if (tunnelState === "live" /* Live */) {
|
|
2313
2458
|
assignedState = "running" /* Running */;
|
|
2314
2459
|
} else if (tunnelState === "idle" /* New */) {
|
|
2315
|
-
assignedState = "
|
|
2460
|
+
assignedState = "idle" /* New */;
|
|
2316
2461
|
} else if (tunnelState === "closed" /* Closed */) {
|
|
2317
2462
|
assignedState = "exited" /* Exited */;
|
|
2318
2463
|
}
|
|
2464
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2319
2465
|
return {
|
|
2320
2466
|
state: assignedState,
|
|
2321
2467
|
errorcode: errorCode,
|
|
2322
2468
|
errormsg: errorMsg,
|
|
2323
|
-
createdtimestamp:
|
|
2324
|
-
starttimestamp:
|
|
2325
|
-
endtimestamp:
|
|
2469
|
+
createdtimestamp: now,
|
|
2470
|
+
starttimestamp: now,
|
|
2471
|
+
endtimestamp: now,
|
|
2326
2472
|
warnings: []
|
|
2327
2473
|
};
|
|
2328
2474
|
}
|
|
@@ -2336,6 +2482,14 @@ function newStats() {
|
|
|
2336
2482
|
elapsedTime: 0
|
|
2337
2483
|
};
|
|
2338
2484
|
}
|
|
2485
|
+
var RemoteManagementStatus = {
|
|
2486
|
+
Connecting: "CONNECTING",
|
|
2487
|
+
Disconnecting: "DISCONNECTING",
|
|
2488
|
+
Reconnecting: "RECONNECTING",
|
|
2489
|
+
Running: "RUNNING",
|
|
2490
|
+
NotRunning: "NOT_RUNNING",
|
|
2491
|
+
Error: "ERROR"
|
|
2492
|
+
};
|
|
2339
2493
|
|
|
2340
2494
|
// src/remote_management/handler.ts
|
|
2341
2495
|
init_cjs_shims();
|
|
@@ -2349,8 +2503,16 @@ var HeaderModificationSchema = import_zod.z.object({
|
|
|
2349
2503
|
value: import_zod.z.array(import_zod.z.string()).optional(),
|
|
2350
2504
|
type: import_zod.z.enum(["add", "remove", "update"])
|
|
2351
2505
|
});
|
|
2506
|
+
var AdditionalForwardingSchema = import_zod.z.object({
|
|
2507
|
+
remoteDomain: import_zod.z.string().optional(),
|
|
2508
|
+
localDomain: import_zod.z.string(),
|
|
2509
|
+
localPort: import_zod.z.number()
|
|
2510
|
+
});
|
|
2352
2511
|
var TunnelConfigSchema = import_zod.z.object({
|
|
2353
|
-
allowPreflight: import_zod.z.boolean(),
|
|
2512
|
+
allowPreflight: import_zod.z.boolean().optional(),
|
|
2513
|
+
// primary key
|
|
2514
|
+
allowpreflight: import_zod.z.boolean().optional(),
|
|
2515
|
+
// legacy key
|
|
2354
2516
|
autoreconnect: import_zod.z.boolean(),
|
|
2355
2517
|
basicauth: import_zod.z.array(import_zod.z.object({ username: import_zod.z.string(), password: import_zod.z.string() })).nullable(),
|
|
2356
2518
|
bearerauth: import_zod.z.string().nullable(),
|
|
@@ -2374,10 +2536,30 @@ var TunnelConfigSchema = import_zod.z.object({
|
|
|
2374
2536
|
statusCheckInterval: import_zod.z.number(),
|
|
2375
2537
|
token: import_zod.z.string(),
|
|
2376
2538
|
tunnelTimeout: import_zod.z.number(),
|
|
2377
|
-
type: import_zod.z.enum([
|
|
2539
|
+
type: import_zod.z.enum([
|
|
2540
|
+
import_pinggy5.TunnelType.Http,
|
|
2541
|
+
import_pinggy5.TunnelType.Tcp,
|
|
2542
|
+
import_pinggy5.TunnelType.Udp,
|
|
2543
|
+
import_pinggy5.TunnelType.Tls,
|
|
2544
|
+
import_pinggy5.TunnelType.TlsTcp
|
|
2545
|
+
]),
|
|
2378
2546
|
webdebuggerport: import_zod.z.number(),
|
|
2379
|
-
xff: import_zod.z.string()
|
|
2380
|
-
|
|
2547
|
+
xff: import_zod.z.string(),
|
|
2548
|
+
additionalForwarding: import_zod.z.array(AdditionalForwardingSchema).optional(),
|
|
2549
|
+
serve: import_zod.z.string().optional()
|
|
2550
|
+
}).superRefine((data, ctx) => {
|
|
2551
|
+
if (data.allowPreflight === void 0 && data.allowpreflight === void 0) {
|
|
2552
|
+
ctx.addIssue({
|
|
2553
|
+
code: "custom",
|
|
2554
|
+
message: "Either allowPreflight or allowpreflight is required",
|
|
2555
|
+
path: ["allowPreflight"]
|
|
2556
|
+
});
|
|
2557
|
+
}
|
|
2558
|
+
}).transform((data) => ({
|
|
2559
|
+
...data,
|
|
2560
|
+
allowPreflight: data.allowPreflight ?? data.allowpreflight,
|
|
2561
|
+
allowpreflight: data.allowPreflight ?? data.allowpreflight
|
|
2562
|
+
}));
|
|
2381
2563
|
var StartSchema = import_zod.z.object({
|
|
2382
2564
|
tunnelID: import_zod.z.string().uuid().nullable().optional(),
|
|
2383
2565
|
tunnelConfig: TunnelConfigSchema
|
|
@@ -2407,32 +2589,36 @@ function tunnelConfigToPinggyOptions(config) {
|
|
|
2407
2589
|
allowPreflight: config.allowPreflight,
|
|
2408
2590
|
reverseProxy: config.noReverseProxy,
|
|
2409
2591
|
force: config.force,
|
|
2592
|
+
autoReconnect: config.autoreconnect,
|
|
2410
2593
|
optional: {
|
|
2411
2594
|
sniServerName: config.localservertlssni || ""
|
|
2412
2595
|
}
|
|
2413
2596
|
};
|
|
2414
2597
|
}
|
|
2415
|
-
function pinggyOptionsToTunnelConfig(opts, configid, configName, localserverTls, greetMsg) {
|
|
2598
|
+
function pinggyOptionsToTunnelConfig(opts, configid, configName, localserverTls, greetMsg, additionalForwarding, serve) {
|
|
2416
2599
|
const forwarding = Array.isArray(opts.forwarding) ? String(opts.forwarding[0].address).replace("//", "").replace(/\/$/, "") : String(opts.forwarding).replace("//", "").replace(/\/$/, "");
|
|
2600
|
+
const parsedForwardedHost = forwarding.split(":").length == 3 ? forwarding.split(":")[1] : forwarding.split(":")[0];
|
|
2601
|
+
const parsedLocalPort = forwarding.split(":").length == 3 ? parseInt(forwarding.split(":")[2], 10) : parseInt(forwarding.split(":")[1], 10);
|
|
2417
2602
|
const tunnelType = Array.isArray(opts.tunnelType) ? opts.tunnelType[0] : opts.tunnelType ?? "http";
|
|
2418
2603
|
const parsedTokens = opts.bearerTokenAuth ? Array.isArray(opts.bearerTokenAuth) ? opts.bearerTokenAuth : JSON.parse(opts.bearerTokenAuth) : [];
|
|
2419
2604
|
return {
|
|
2420
2605
|
allowPreflight: opts.allowPreflight ?? false,
|
|
2421
|
-
|
|
2606
|
+
allowpreflight: opts.allowPreflight ?? false,
|
|
2607
|
+
autoreconnect: opts.autoReconnect ?? false,
|
|
2422
2608
|
basicauth: opts.basicAuth && Object.keys(opts.basicAuth).length ? opts.basicAuth : null,
|
|
2423
2609
|
bearerauth: parsedTokens.length ? parsedTokens.join(",") : null,
|
|
2424
2610
|
configid,
|
|
2425
2611
|
configname: configName,
|
|
2426
2612
|
greetmsg: greetMsg || "",
|
|
2427
2613
|
force: opts.force ?? false,
|
|
2428
|
-
forwardedhost:
|
|
2614
|
+
forwardedhost: parsedForwardedHost || "localhost",
|
|
2429
2615
|
fullRequestUrl: opts.originalRequestUrl ?? false,
|
|
2430
2616
|
headermodification: opts.headerModification || [],
|
|
2431
2617
|
//structured list
|
|
2432
2618
|
httpsOnly: opts.httpsOnly ?? false,
|
|
2433
2619
|
internalwebdebuggerport: 0,
|
|
2434
2620
|
ipwhitelist: opts.ipWhitelist ? Array.isArray(opts.ipWhitelist) ? opts.ipWhitelist : JSON.parse(opts.ipWhitelist) : null,
|
|
2435
|
-
localport:
|
|
2621
|
+
localport: parsedLocalPort || 0,
|
|
2436
2622
|
localservertlssni: null,
|
|
2437
2623
|
regioncode: "",
|
|
2438
2624
|
noReverseProxy: opts.reverseProxy ?? false,
|
|
@@ -2444,7 +2630,9 @@ function pinggyOptionsToTunnelConfig(opts, configid, configName, localserverTls,
|
|
|
2444
2630
|
type: tunnelType,
|
|
2445
2631
|
webdebuggerport: Number(opts.webDebugger?.split(":")[0]) || 0,
|
|
2446
2632
|
xff: opts.xForwardedFor ? "1" : "",
|
|
2447
|
-
localsservertls: localserverTls || false
|
|
2633
|
+
localsservertls: localserverTls || false,
|
|
2634
|
+
additionalForwarding: additionalForwarding || [],
|
|
2635
|
+
serve: serve || ""
|
|
2448
2636
|
};
|
|
2449
2637
|
}
|
|
2450
2638
|
|
|
@@ -2453,11 +2641,24 @@ var TunnelOperations = class {
|
|
|
2453
2641
|
constructor() {
|
|
2454
2642
|
this.tunnelManager = TunnelManager.getInstance();
|
|
2455
2643
|
}
|
|
2644
|
+
buildStatus(tunnelId, state, errorCode) {
|
|
2645
|
+
const status = newStatus(state, errorCode, "");
|
|
2646
|
+
try {
|
|
2647
|
+
const managed = this.tunnelManager.getManagedTunnel("", tunnelId);
|
|
2648
|
+
if (managed) {
|
|
2649
|
+
status.createdtimestamp = managed.createdAt || "";
|
|
2650
|
+
status.starttimestamp = managed.startedAt || "";
|
|
2651
|
+
status.endtimestamp = managed.stoppedAt || "";
|
|
2652
|
+
}
|
|
2653
|
+
} catch (e) {
|
|
2654
|
+
}
|
|
2655
|
+
return status;
|
|
2656
|
+
}
|
|
2456
2657
|
// --- Helper to construct TunnelResponse ---
|
|
2457
|
-
async buildTunnelResponse(tunnelid, tunnelConfig, configid, tunnelName) {
|
|
2658
|
+
async buildTunnelResponse(tunnelid, tunnelConfig, configid, tunnelName, additionalForwarding, serve) {
|
|
2458
2659
|
const [status, stats, tlsInfo, greetMsg, remoteurls] = await Promise.all([
|
|
2459
2660
|
this.tunnelManager.getTunnelStatus(tunnelid),
|
|
2460
|
-
this.tunnelManager.
|
|
2661
|
+
this.tunnelManager.getLatestTunnelStats(tunnelid) || newStats(),
|
|
2461
2662
|
this.tunnelManager.getLocalserverTlsInfo(tunnelid),
|
|
2462
2663
|
this.tunnelManager.getTunnelGreetMessage(tunnelid),
|
|
2463
2664
|
this.tunnelManager.getTunnelUrls(tunnelid)
|
|
@@ -2465,8 +2666,8 @@ var TunnelOperations = class {
|
|
|
2465
2666
|
return {
|
|
2466
2667
|
tunnelid,
|
|
2467
2668
|
remoteurls,
|
|
2468
|
-
tunnelconfig: pinggyOptionsToTunnelConfig(tunnelConfig, configid, tunnelName, tlsInfo, greetMsg),
|
|
2469
|
-
status:
|
|
2669
|
+
tunnelconfig: pinggyOptionsToTunnelConfig(tunnelConfig, configid, tunnelName, tlsInfo, greetMsg, additionalForwarding),
|
|
2670
|
+
status: this.buildStatus(tunnelid, status, "" /* NoError */),
|
|
2470
2671
|
stats
|
|
2471
2672
|
};
|
|
2472
2673
|
}
|
|
@@ -2480,14 +2681,17 @@ var TunnelOperations = class {
|
|
|
2480
2681
|
async handleStart(config) {
|
|
2481
2682
|
try {
|
|
2482
2683
|
const opts = tunnelConfigToPinggyOptions(config);
|
|
2483
|
-
const
|
|
2684
|
+
const additionalForwardingParsed = config.additionalForwarding || [];
|
|
2685
|
+
const { tunnelid, instance, tunnelName, additionalForwarding, serve } = await this.tunnelManager.createTunnel({
|
|
2484
2686
|
...opts,
|
|
2485
2687
|
configid: config.configid,
|
|
2486
|
-
tunnelName: config.configname
|
|
2688
|
+
tunnelName: config.configname,
|
|
2689
|
+
additionalForwarding: additionalForwardingParsed,
|
|
2690
|
+
serve: config.serve
|
|
2487
2691
|
});
|
|
2488
2692
|
this.tunnelManager.startTunnel(tunnelid);
|
|
2489
2693
|
const tunnelPconfig = await this.tunnelManager.getTunnelConfig("", tunnelid);
|
|
2490
|
-
const resp = this.buildTunnelResponse(tunnelid, tunnelPconfig, config.configid, tunnelName);
|
|
2694
|
+
const resp = this.buildTunnelResponse(tunnelid, tunnelPconfig, config.configid, tunnelName, additionalForwarding, serve);
|
|
2491
2695
|
return resp;
|
|
2492
2696
|
} catch (err) {
|
|
2493
2697
|
return this.error(ErrorCode.ErrorStartingTunnel, err, "Unknown error occurred while starting tunnel");
|
|
@@ -2499,11 +2703,13 @@ var TunnelOperations = class {
|
|
|
2499
2703
|
const tunnel = await this.tunnelManager.updateConfig({
|
|
2500
2704
|
...opts,
|
|
2501
2705
|
configid: config.configid,
|
|
2502
|
-
tunnelName: config.configname
|
|
2706
|
+
tunnelName: config.configname,
|
|
2707
|
+
additionalForwarding: config.additionalForwarding || [],
|
|
2708
|
+
serve: config.serve
|
|
2503
2709
|
});
|
|
2504
2710
|
if (!tunnel.instance || !tunnel.tunnelConfig)
|
|
2505
2711
|
throw new Error("Invalid tunnel state after configuration update");
|
|
2506
|
-
return this.buildTunnelResponse(tunnel.tunnelid, tunnel.tunnelConfig, config.configid, tunnel.tunnelName);
|
|
2712
|
+
return this.buildTunnelResponse(tunnel.tunnelid, tunnel.tunnelConfig, config.configid, tunnel.tunnelName, tunnel.additionalForwarding, tunnel.serve);
|
|
2507
2713
|
} catch (err) {
|
|
2508
2714
|
return this.error(ErrorCode.InternalServerError, err, "Failed to update tunnel configuration");
|
|
2509
2715
|
}
|
|
@@ -2516,19 +2722,19 @@ var TunnelOperations = class {
|
|
|
2516
2722
|
}
|
|
2517
2723
|
return Promise.all(
|
|
2518
2724
|
tunnels.map(async (t) => {
|
|
2519
|
-
const rawStats = this.tunnelManager.
|
|
2520
|
-
const stats = rawStats ?? newStats();
|
|
2725
|
+
const rawStats = this.tunnelManager.getLatestTunnelStats(t.tunnelid) || newStats();
|
|
2521
2726
|
const [status, tlsInfo, greetMsg] = await Promise.all([
|
|
2522
2727
|
this.tunnelManager.getTunnelStatus(t.tunnelid),
|
|
2523
2728
|
this.tunnelManager.getLocalserverTlsInfo(t.tunnelid),
|
|
2524
2729
|
this.tunnelManager.getTunnelGreetMessage(t.tunnelid)
|
|
2525
2730
|
]);
|
|
2526
|
-
const
|
|
2731
|
+
const pinggyOptions = status !== "closed" /* Closed */ && status !== "exited" /* Exited */ ? await this.tunnelManager.getTunnelConfig("", t.tunnelid) : t.tunnelConfig;
|
|
2732
|
+
const tunnelConfig = pinggyOptionsToTunnelConfig(pinggyOptions, t.configid, t.tunnelName, tlsInfo, greetMsg, t.additionalForwarding, t.serve);
|
|
2527
2733
|
return {
|
|
2528
2734
|
tunnelid: t.tunnelid,
|
|
2529
2735
|
remoteurls: t.remoteurls,
|
|
2530
|
-
status:
|
|
2531
|
-
stats,
|
|
2736
|
+
status: this.buildStatus(t.tunnelid, status, "" /* NoError */),
|
|
2737
|
+
stats: rawStats,
|
|
2532
2738
|
tunnelconfig: tunnelConfig
|
|
2533
2739
|
};
|
|
2534
2740
|
})
|
|
@@ -2542,7 +2748,7 @@ var TunnelOperations = class {
|
|
|
2542
2748
|
const { configid } = this.tunnelManager.stopTunnel(tunnelid);
|
|
2543
2749
|
const managed = this.tunnelManager.getManagedTunnel("", tunnelid);
|
|
2544
2750
|
if (!managed?.tunnelConfig) throw new Error(`Tunnel config for ID "${tunnelid}" not found`);
|
|
2545
|
-
return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, configid, managed.tunnelName);
|
|
2751
|
+
return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, configid, managed.tunnelName, managed.additionalForwarding, managed.serve);
|
|
2546
2752
|
} catch (err) {
|
|
2547
2753
|
return this.error(ErrorCode.TunnelNotFound, err, "Failed to stop tunnel");
|
|
2548
2754
|
}
|
|
@@ -2551,7 +2757,7 @@ var TunnelOperations = class {
|
|
|
2551
2757
|
try {
|
|
2552
2758
|
const managed = this.tunnelManager.getManagedTunnel("", tunnelid);
|
|
2553
2759
|
if (!managed?.tunnelConfig) throw new Error(`Tunnel config for ID "${tunnelid}" not found`);
|
|
2554
|
-
return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, managed.configid, managed.tunnelName);
|
|
2760
|
+
return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, managed.configid, managed.tunnelName, managed.additionalForwarding, managed.serve);
|
|
2555
2761
|
} catch (err) {
|
|
2556
2762
|
return this.error(ErrorCode.TunnelNotFound, err, "Failed to get tunnel information");
|
|
2557
2763
|
}
|
|
@@ -2561,7 +2767,7 @@ var TunnelOperations = class {
|
|
|
2561
2767
|
await this.tunnelManager.restartTunnel(tunnelid);
|
|
2562
2768
|
const managed = this.tunnelManager.getManagedTunnel("", tunnelid);
|
|
2563
2769
|
if (!managed?.tunnelConfig) throw new Error(`Tunnel config for ID "${tunnelid}" not found`);
|
|
2564
|
-
return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, managed.configid, managed.tunnelName);
|
|
2770
|
+
return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, managed.configid, managed.tunnelName, managed.additionalForwarding, managed.serve);
|
|
2565
2771
|
} catch (err) {
|
|
2566
2772
|
return this.error(ErrorCode.TunnelNotFound, err, "Failed to restart tunnel");
|
|
2567
2773
|
}
|
|
@@ -2572,6 +2778,34 @@ var TunnelOperations = class {
|
|
|
2572
2778
|
handleUnregisterStatsListener(tunnelid, listnerId) {
|
|
2573
2779
|
this.tunnelManager.deregisterStatsListener(tunnelid, listnerId);
|
|
2574
2780
|
}
|
|
2781
|
+
handleGetTunnelStats(tunnelid) {
|
|
2782
|
+
try {
|
|
2783
|
+
const stats = this.tunnelManager.getTunnelStats(tunnelid);
|
|
2784
|
+
if (!stats) {
|
|
2785
|
+
return [newStats()];
|
|
2786
|
+
}
|
|
2787
|
+
return stats;
|
|
2788
|
+
} catch (err) {
|
|
2789
|
+
return this.error(ErrorCode.TunnelNotFound, err, "Failed to get tunnel stats");
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
handleRegisterDisconnectListener(tunnelid, listener) {
|
|
2793
|
+
this.tunnelManager.registerDisconnectListener(tunnelid, listener);
|
|
2794
|
+
}
|
|
2795
|
+
handleRemoveStoppedTunnelByConfigId(configId) {
|
|
2796
|
+
try {
|
|
2797
|
+
return this.tunnelManager.removeStoppedTunnelByConfigId(configId);
|
|
2798
|
+
} catch (err) {
|
|
2799
|
+
return this.error(ErrorCode.InternalServerError, err, "Failed to remove stopped tunnel by configId");
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
handleRemoveStoppedTunnelByTunnelId(tunnelId) {
|
|
2803
|
+
try {
|
|
2804
|
+
return this.tunnelManager.removeStoppedTunnelByTunnelId(tunnelId);
|
|
2805
|
+
} catch (err) {
|
|
2806
|
+
return this.error(ErrorCode.InternalServerError, err, "Failed to remove stopped tunnel by tunnelId");
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2575
2809
|
};
|
|
2576
2810
|
|
|
2577
2811
|
// src/remote_management/websocket_handlers.ts
|
|
@@ -2604,11 +2838,13 @@ var WebSocketCommandHandler = class {
|
|
|
2604
2838
|
}
|
|
2605
2839
|
async handleStartReq(req, raw) {
|
|
2606
2840
|
const dc = StartSchema.parse(raw);
|
|
2841
|
+
printer_default.info("Starting tunnel with config name: " + dc.tunnelConfig.configname);
|
|
2607
2842
|
const result = await this.tunnelHandler.handleStart(dc.tunnelConfig);
|
|
2608
2843
|
return this.wrapResponse(result, req);
|
|
2609
2844
|
}
|
|
2610
2845
|
async handleStopReq(req, raw) {
|
|
2611
2846
|
const dc = StopSchema.parse(raw);
|
|
2847
|
+
printer_default.info("Stopping tunnel with ID: " + dc.tunnelID);
|
|
2612
2848
|
const result = await this.tunnelHandler.handleStop(dc.tunnelID);
|
|
2613
2849
|
return this.wrapResponse(result, req);
|
|
2614
2850
|
}
|
|
@@ -2638,7 +2874,17 @@ var WebSocketCommandHandler = class {
|
|
|
2638
2874
|
errResp.requestid = req.requestid;
|
|
2639
2875
|
return errResp;
|
|
2640
2876
|
}
|
|
2641
|
-
const
|
|
2877
|
+
const finalResult = JSON.parse(JSON.stringify(result));
|
|
2878
|
+
if (Array.isArray(finalResult)) {
|
|
2879
|
+
finalResult.forEach((item) => {
|
|
2880
|
+
if (item?.tunnelconfig) {
|
|
2881
|
+
delete item.tunnelconfig.allowPreflight;
|
|
2882
|
+
}
|
|
2883
|
+
});
|
|
2884
|
+
} else if (finalResult?.tunnelconfig) {
|
|
2885
|
+
delete finalResult.tunnelconfig.allowPreflight;
|
|
2886
|
+
}
|
|
2887
|
+
const respObj = NewResponseObject(finalResult);
|
|
2642
2888
|
respObj.command = req.command;
|
|
2643
2889
|
respObj.requestid = req.requestid;
|
|
2644
2890
|
return respObj;
|
|
@@ -2711,6 +2957,12 @@ function handleConnectionStatusMessage(firstMessage) {
|
|
|
2711
2957
|
// src/remote_management/remoteManagement.ts
|
|
2712
2958
|
var RECONNECT_SLEEP_MS = 5e3;
|
|
2713
2959
|
var PING_INTERVAL_MS = 3e4;
|
|
2960
|
+
var _remoteManagementState = {
|
|
2961
|
+
status: "NOT_RUNNING",
|
|
2962
|
+
errorMessage: ""
|
|
2963
|
+
};
|
|
2964
|
+
var _stopRequested = false;
|
|
2965
|
+
var currentWs = null;
|
|
2714
2966
|
function buildRemoteManagementWsUrl(manage) {
|
|
2715
2967
|
let baseUrl = (manage || "dashboard.pinggy.io").trim();
|
|
2716
2968
|
if (!(baseUrl.startsWith("ws://") || baseUrl.startsWith("wss://"))) {
|
|
@@ -2744,95 +2996,133 @@ async function parseRemoteManagement(values) {
|
|
|
2744
2996
|
}
|
|
2745
2997
|
}
|
|
2746
2998
|
async function initiateRemoteManagement(token, manage) {
|
|
2999
|
+
await printer_default.ensureDeps();
|
|
3000
|
+
await loadChalk();
|
|
2747
3001
|
if (!token || token.trim().length === 0) {
|
|
2748
3002
|
throw new Error("Remote management token is required (use --remote-management <TOKEN>)");
|
|
2749
3003
|
}
|
|
2750
3004
|
const wsUrl = buildRemoteManagementWsUrl(manage);
|
|
2751
3005
|
const wsHost = extractHostname(wsUrl);
|
|
2752
3006
|
logger.info("Remote management mode enabled.");
|
|
2753
|
-
|
|
3007
|
+
_stopRequested = false;
|
|
2754
3008
|
const sigintHandler = () => {
|
|
2755
|
-
|
|
3009
|
+
_stopRequested = true;
|
|
2756
3010
|
};
|
|
2757
3011
|
process.once("SIGINT", sigintHandler);
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
printer_default.print(`Connecting to ${wsHost}`);
|
|
2770
|
-
logger.info("Connecting to remote management", { wsUrl });
|
|
3012
|
+
const logConnecting = () => {
|
|
3013
|
+
printer_default.print(`Connecting to ${wsHost}`);
|
|
3014
|
+
logger.info("Connecting to remote management", { wsUrl });
|
|
3015
|
+
};
|
|
3016
|
+
while (!_stopRequested) {
|
|
3017
|
+
logConnecting();
|
|
3018
|
+
setRemoteManagementState({ status: RemoteManagementStatus.Connecting, errorMessage: "" });
|
|
3019
|
+
try {
|
|
3020
|
+
await handleWebSocketConnection(wsUrl, wsHost, token);
|
|
3021
|
+
} catch (error) {
|
|
3022
|
+
logger.warn("Remote management connection error", { error: String(error) });
|
|
2771
3023
|
}
|
|
3024
|
+
if (_stopRequested) break;
|
|
3025
|
+
printer_default.warn(`Remote management disconnected. Reconnecting in ${RECONNECT_SLEEP_MS / 1e3} seconds...`);
|
|
3026
|
+
logger.info("Reconnecting to remote management after disconnect");
|
|
3027
|
+
await sleep(RECONNECT_SLEEP_MS);
|
|
3028
|
+
}
|
|
3029
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
3030
|
+
logger.info("Remote management stopped.");
|
|
3031
|
+
return getRemoteManagementState();
|
|
3032
|
+
}
|
|
3033
|
+
async function handleWebSocketConnection(wsUrl, wsHost, token) {
|
|
3034
|
+
return new Promise((resolve) => {
|
|
2772
3035
|
const ws = new import_ws.default(wsUrl, {
|
|
2773
3036
|
headers: { Authorization: `Bearer ${token}` }
|
|
2774
3037
|
});
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
return;
|
|
2798
|
-
}
|
|
2799
|
-
const text = data.toString("utf8");
|
|
2800
|
-
const req = JSON.parse(text);
|
|
2801
|
-
const webSocketHandler = new WebSocketCommandHandler();
|
|
2802
|
-
await webSocketHandler.handle(ws, req);
|
|
2803
|
-
} catch (e) {
|
|
2804
|
-
logger.warn("Failed handling websocket message", { error: String(e) });
|
|
2805
|
-
}
|
|
2806
|
-
});
|
|
2807
|
-
ws.on("unexpected-response", (_req, res) => {
|
|
2808
|
-
if (res.statusCode === 401) {
|
|
2809
|
-
printer_default.error("Unauthorized. Please enter a valid token.");
|
|
2810
|
-
logger.error("Unauthorized (401) on remote management connect");
|
|
2811
|
-
stopRequested = true;
|
|
2812
|
-
ws.close();
|
|
2813
|
-
} else {
|
|
2814
|
-
printer_default.warn(`Unexpected HTTP response ${res.statusCode} from server. Retrying...`);
|
|
2815
|
-
logger.warn("Unexpected HTTP response on WebSocket connect", { statusCode: res.statusCode });
|
|
2816
|
-
ws.close();
|
|
3038
|
+
currentWs = ws;
|
|
3039
|
+
let heartbeat = null;
|
|
3040
|
+
let firstMessage = true;
|
|
3041
|
+
const cleanup = () => {
|
|
3042
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
3043
|
+
currentWs = null;
|
|
3044
|
+
resolve();
|
|
3045
|
+
};
|
|
3046
|
+
ws.once("open", () => {
|
|
3047
|
+
printer_default.success(`Connected to ${wsHost}`);
|
|
3048
|
+
heartbeat = setInterval(() => {
|
|
3049
|
+
if (ws.readyState === import_ws.default.OPEN) ws.ping();
|
|
3050
|
+
}, PING_INTERVAL_MS);
|
|
3051
|
+
});
|
|
3052
|
+
ws.on("ping", () => ws.pong());
|
|
3053
|
+
ws.on("message", async (data) => {
|
|
3054
|
+
try {
|
|
3055
|
+
if (firstMessage) {
|
|
3056
|
+
firstMessage = false;
|
|
3057
|
+
const ok = handleConnectionStatusMessage(data);
|
|
3058
|
+
if (!ok) ws.close();
|
|
3059
|
+
return;
|
|
2817
3060
|
}
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
}
|
|
3061
|
+
setRemoteManagementState({ status: RemoteManagementStatus.Running, errorMessage: "" });
|
|
3062
|
+
const req = JSON.parse(data.toString("utf8"));
|
|
3063
|
+
await new WebSocketCommandHandler().handle(ws, req);
|
|
3064
|
+
} catch (e) {
|
|
3065
|
+
logger.warn("Failed handling websocket message", { error: String(e) });
|
|
3066
|
+
}
|
|
3067
|
+
});
|
|
3068
|
+
ws.on("unexpected-response", (_, res) => {
|
|
3069
|
+
setRemoteManagementState({ status: RemoteManagementStatus.NotRunning, errorMessage: `HTTP ${res.statusCode}` });
|
|
3070
|
+
if (res.statusCode === 401) {
|
|
3071
|
+
printer_default.error("Unauthorized. Please enter a valid token.");
|
|
3072
|
+
logger.error("Unauthorized (401) on remote management connect");
|
|
3073
|
+
} else {
|
|
3074
|
+
printer_default.warn(`Unexpected HTTP ${res.statusCode}. Retrying...`);
|
|
3075
|
+
logger.warn("Unexpected HTTP response", { statusCode: res.statusCode });
|
|
3076
|
+
}
|
|
3077
|
+
ws.close();
|
|
3078
|
+
});
|
|
3079
|
+
ws.on("close", (code, reason) => {
|
|
3080
|
+
setRemoteManagementState({ status: RemoteManagementStatus.NotRunning, errorMessage: "" });
|
|
3081
|
+
logger.info("WebSocket closed", { code, reason: reason.toString() });
|
|
3082
|
+
printer_default.warn(`Disconnected (code: ${code}). Retrying...`);
|
|
3083
|
+
cleanup();
|
|
3084
|
+
});
|
|
3085
|
+
ws.on("error", (err) => {
|
|
3086
|
+
setRemoteManagementState({ status: RemoteManagementStatus.Error, errorMessage: err.message });
|
|
3087
|
+
logger.warn("WebSocket error", { error: err.message });
|
|
3088
|
+
printer_default.error(err);
|
|
3089
|
+
cleanup();
|
|
2831
3090
|
});
|
|
2832
|
-
|
|
3091
|
+
});
|
|
3092
|
+
}
|
|
3093
|
+
async function closeRemoteManagement(timeoutMs = 1e4) {
|
|
3094
|
+
_stopRequested = true;
|
|
3095
|
+
try {
|
|
3096
|
+
if (currentWs) {
|
|
3097
|
+
try {
|
|
3098
|
+
setRemoteManagementState({ status: RemoteManagementStatus.Disconnecting, errorMessage: "" });
|
|
3099
|
+
currentWs.close();
|
|
3100
|
+
} catch (e) {
|
|
3101
|
+
logger.warn("Error while closing current remote management websocket", { error: String(e) });
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
const start = Date.now();
|
|
3105
|
+
while (_remoteManagementState.status === "RUNNING") {
|
|
3106
|
+
if (Date.now() - start > timeoutMs) {
|
|
3107
|
+
logger.warn("Timed out waiting for remote management to stop");
|
|
3108
|
+
break;
|
|
3109
|
+
}
|
|
3110
|
+
await sleep(200);
|
|
3111
|
+
}
|
|
3112
|
+
} finally {
|
|
3113
|
+
currentWs = null;
|
|
3114
|
+
setRemoteManagementState({ status: RemoteManagementStatus.NotRunning, errorMessage: "" });
|
|
3115
|
+
return getRemoteManagementState();
|
|
2833
3116
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
3117
|
+
}
|
|
3118
|
+
function getRemoteManagementState() {
|
|
3119
|
+
return _remoteManagementState;
|
|
3120
|
+
}
|
|
3121
|
+
function setRemoteManagementState(state, errorMessage) {
|
|
3122
|
+
_remoteManagementState = {
|
|
3123
|
+
status: state.status,
|
|
3124
|
+
errorMessage: errorMessage || ""
|
|
3125
|
+
};
|
|
2836
3126
|
}
|
|
2837
3127
|
|
|
2838
3128
|
// src/utils/parseArgs.ts
|
|
@@ -2926,6 +3216,57 @@ var TunnelData = {
|
|
|
2926
3216
|
var activeTui = null;
|
|
2927
3217
|
var disconnectState = null;
|
|
2928
3218
|
var updateDisconnectState = null;
|
|
3219
|
+
async function launchTui(finalConfig, urls, greet) {
|
|
3220
|
+
try {
|
|
3221
|
+
const { withFullScreen } = await import("fullscreen-ink");
|
|
3222
|
+
const { default: TunnelTui2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
|
|
3223
|
+
const React3 = await import("react");
|
|
3224
|
+
const isTTYEnabled = process.stdin.isTTY;
|
|
3225
|
+
const TunnelTuiWrapper = ({ finalConfig: finalConfig2, urls: urls2, greet: greet2 }) => {
|
|
3226
|
+
const [disconnectInfo, setDisconnectInfo] = React3.useState(null);
|
|
3227
|
+
React3.useEffect(() => {
|
|
3228
|
+
updateDisconnectState = setDisconnectInfo;
|
|
3229
|
+
return () => {
|
|
3230
|
+
updateDisconnectState = null;
|
|
3231
|
+
};
|
|
3232
|
+
}, []);
|
|
3233
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3234
|
+
TunnelTui2,
|
|
3235
|
+
{
|
|
3236
|
+
urls: urls2 ?? [],
|
|
3237
|
+
greet: greet2 ?? "",
|
|
3238
|
+
tunnelConfig: finalConfig2,
|
|
3239
|
+
disconnectInfo
|
|
3240
|
+
}
|
|
3241
|
+
);
|
|
3242
|
+
};
|
|
3243
|
+
const tui = withFullScreen(
|
|
3244
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3245
|
+
TunnelTuiWrapper,
|
|
3246
|
+
{
|
|
3247
|
+
finalConfig,
|
|
3248
|
+
urls,
|
|
3249
|
+
greet
|
|
3250
|
+
}
|
|
3251
|
+
)
|
|
3252
|
+
);
|
|
3253
|
+
activeTui = tui;
|
|
3254
|
+
if (isTTYEnabled) {
|
|
3255
|
+
try {
|
|
3256
|
+
await tui.start();
|
|
3257
|
+
await tui.waitUntilExit();
|
|
3258
|
+
} catch (e) {
|
|
3259
|
+
logger.warn("TUI error", e);
|
|
3260
|
+
} finally {
|
|
3261
|
+
activeTui = null;
|
|
3262
|
+
}
|
|
3263
|
+
} else {
|
|
3264
|
+
printer_default.warn("Unable to initiate the TUI: your terminal does not support the required input mode.");
|
|
3265
|
+
}
|
|
3266
|
+
} catch (e) {
|
|
3267
|
+
logger.warn("Failed to (re-)initiate TUI", e);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
2929
3270
|
async function startCli(finalConfig, manager) {
|
|
2930
3271
|
await printer_default.ensureDeps();
|
|
2931
3272
|
const chalk = await loadChalk();
|
|
@@ -2981,64 +3322,61 @@ async function startCli(finalConfig, manager) {
|
|
|
2981
3322
|
logger.warn("Failed to wait for TUI exit", e);
|
|
2982
3323
|
} finally {
|
|
2983
3324
|
activeTui = null;
|
|
2984
|
-
|
|
3325
|
+
printer_default.warn(`Error in tunnel:`);
|
|
3326
|
+
messages?.forEach(function(m) {
|
|
2985
3327
|
printer_default.warn(m);
|
|
2986
3328
|
});
|
|
2987
|
-
|
|
3329
|
+
if (!finalConfig.autoReconnect) {
|
|
3330
|
+
process.exit(0);
|
|
3331
|
+
}
|
|
2988
3332
|
}
|
|
2989
3333
|
} else {
|
|
2990
|
-
messages
|
|
3334
|
+
messages?.forEach(function(m) {
|
|
2991
3335
|
printer_default.warn(m);
|
|
2992
3336
|
});
|
|
2993
|
-
|
|
3337
|
+
if (!finalConfig.autoReconnect) {
|
|
3338
|
+
process.exit(0);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
if (finalConfig.autoReconnect) {
|
|
3342
|
+
printer_default.startSpinner("Reconnecting to Pinggy");
|
|
2994
3343
|
}
|
|
2995
3344
|
});
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
const { default: TunnelTui2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
|
|
2999
|
-
const React3 = await import("react");
|
|
3000
|
-
const isTTYEnabled = process.stdin.isTTY;
|
|
3001
|
-
const TunnelTuiWrapper = ({ finalConfig: finalConfig2, urls, greet }) => {
|
|
3002
|
-
const [disconnectInfo, setDisconnectInfo] = React3.useState(null);
|
|
3003
|
-
React3.useEffect(() => {
|
|
3004
|
-
updateDisconnectState = setDisconnectInfo;
|
|
3005
|
-
return () => {
|
|
3006
|
-
updateDisconnectState = null;
|
|
3007
|
-
};
|
|
3008
|
-
}, []);
|
|
3009
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3010
|
-
TunnelTui2,
|
|
3011
|
-
{
|
|
3012
|
-
urls: urls ?? [],
|
|
3013
|
-
greet: greet ?? "",
|
|
3014
|
-
tunnelConfig: finalConfig2,
|
|
3015
|
-
disconnectInfo
|
|
3016
|
-
}
|
|
3017
|
-
);
|
|
3018
|
-
};
|
|
3019
|
-
const tui = withFullScreen(
|
|
3020
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3021
|
-
TunnelTuiWrapper,
|
|
3022
|
-
{
|
|
3023
|
-
finalConfig,
|
|
3024
|
-
urls: TunnelData.urls,
|
|
3025
|
-
greet: TunnelData.greet
|
|
3026
|
-
}
|
|
3027
|
-
)
|
|
3028
|
-
);
|
|
3029
|
-
activeTui = tui;
|
|
3030
|
-
if (isTTYEnabled) {
|
|
3345
|
+
try {
|
|
3346
|
+
await manager2.registerStartListener(tunnel.tunnelid, async (tunnelId, urls) => {
|
|
3031
3347
|
try {
|
|
3032
|
-
|
|
3033
|
-
await tui.waitUntilExit();
|
|
3348
|
+
printer_default.stopSpinnerSuccess("Reconnected to Pinggy");
|
|
3034
3349
|
} catch (e) {
|
|
3035
|
-
logger.warn("TUI error", e);
|
|
3036
|
-
} finally {
|
|
3037
|
-
activeTui = null;
|
|
3038
3350
|
}
|
|
3039
|
-
|
|
3040
|
-
printer_default.
|
|
3041
|
-
|
|
3351
|
+
printer_default.success(chalk.bold("Tunnel re-established!"));
|
|
3352
|
+
printer_default.print(chalk.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3353
|
+
TunnelData.urls = urls;
|
|
3354
|
+
TunnelData.greet = await manager2.getTunnelGreetMessage(tunnel.tunnelid);
|
|
3355
|
+
printer_default.info(chalk.cyanBright("Remote URLs:"));
|
|
3356
|
+
(TunnelData.urls ?? []).forEach(
|
|
3357
|
+
(url) => printer_default.print(" " + chalk.magentaBright(url))
|
|
3358
|
+
);
|
|
3359
|
+
printer_default.print(chalk.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3360
|
+
if (TunnelData.greet?.includes("not authenticated")) {
|
|
3361
|
+
printer_default.warn(chalk.yellowBright(TunnelData.greet));
|
|
3362
|
+
} else if (TunnelData.greet?.includes("authenticated as")) {
|
|
3363
|
+
const emailMatch = /authenticated as (.+)/.exec(TunnelData.greet);
|
|
3364
|
+
if (emailMatch) {
|
|
3365
|
+
const email = emailMatch[1];
|
|
3366
|
+
printer_default.info(chalk.cyanBright("Authenticated as: " + email));
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
printer_default.print(chalk.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3370
|
+
printer_default.print(chalk.gray("\nPress Ctrl+C to stop the tunnel.\n"));
|
|
3371
|
+
if (!finalConfig.NoTUI) {
|
|
3372
|
+
await launchTui(finalConfig, TunnelData.urls, TunnelData.greet);
|
|
3373
|
+
}
|
|
3374
|
+
});
|
|
3375
|
+
} catch (e) {
|
|
3376
|
+
logger.debug("Failed to register start listener", e);
|
|
3377
|
+
}
|
|
3378
|
+
if (!finalConfig.NoTUI) {
|
|
3379
|
+
await launchTui(finalConfig, TunnelData.urls, TunnelData.greet);
|
|
3042
3380
|
}
|
|
3043
3381
|
} catch (err) {
|
|
3044
3382
|
printer_default.stopSpinnerFail("Failed to connect");
|
|
@@ -3054,6 +3392,8 @@ var import_fs3 = require("fs");
|
|
|
3054
3392
|
init_logger();
|
|
3055
3393
|
async function main() {
|
|
3056
3394
|
try {
|
|
3395
|
+
await printer_default.ensureDeps();
|
|
3396
|
+
await loadChalk();
|
|
3057
3397
|
const { values, positionals, hasAnyArgs } = parseCliArgs(cliOptions);
|
|
3058
3398
|
configureLogger(values);
|
|
3059
3399
|
const manager = TunnelManager.getInstance();
|
|
@@ -3101,5 +3441,8 @@ if (entryFile && entryFile === currentFile) {
|
|
|
3101
3441
|
0 && (module.exports = {
|
|
3102
3442
|
TunnelManager,
|
|
3103
3443
|
TunnelOperations,
|
|
3104
|
-
|
|
3444
|
+
closeRemoteManagement,
|
|
3445
|
+
enablePackageLogging,
|
|
3446
|
+
getRemoteManagementState,
|
|
3447
|
+
initiateRemoteManagement
|
|
3105
3448
|
});
|