pinggy 0.4.6 → 0.4.8
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 +92 -4
- package/dist/{chunk-STEISST3.js → chunk-YFTL44B3.js} +25 -5
- package/dist/index.cjs +735 -113
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -2
- package/dist/{main-XKFFUSKJ.js → main-4WTJG54V.js} +667 -100
- package/dist/workers/file_serve_worker.cjs +14 -2
- package/dist/workers/file_serve_worker.js +14 -2
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -367,9 +367,10 @@ var init_TunnelManager = __esm({
|
|
|
367
367
|
* status information, and statistics
|
|
368
368
|
*/
|
|
369
369
|
async createTunnel(config) {
|
|
370
|
-
const { configId, tunnelid: requestedTunnelId, tunnelName, name
|
|
370
|
+
const { configId, tunnelid: requestedTunnelId, tunnelName, name } = config;
|
|
371
371
|
const tunnelid = requestedTunnelId || getRandomId();
|
|
372
372
|
const autoReconnect = config.autoReconnect || false;
|
|
373
|
+
const serve = this.resolveServePath(config);
|
|
373
374
|
if (!configId || typeof configId !== "string" || configId.trim() === "") {
|
|
374
375
|
throw new Error("configId is required and must be a non-empty string");
|
|
375
376
|
}
|
|
@@ -456,8 +457,11 @@ var init_TunnelManager = __esm({
|
|
|
456
457
|
throw error;
|
|
457
458
|
}
|
|
458
459
|
logger.info("Tunnel started", { tunnelId, urls });
|
|
460
|
+
logger.info("Checking serve config for tunnel", { tunnelId, serve: managed.serve });
|
|
459
461
|
if (managed.serve) {
|
|
460
462
|
this.startStaticFileServer(managed);
|
|
463
|
+
} else {
|
|
464
|
+
logger.debug("No serve path configured, skipping static file server", { tunnelId });
|
|
461
465
|
}
|
|
462
466
|
try {
|
|
463
467
|
const startListeners = this.tunnelStartListeners.get(tunnelId);
|
|
@@ -789,6 +793,7 @@ var init_TunnelManager = __esm({
|
|
|
789
793
|
const currentTunnelName = existingTunnel.tunnelName;
|
|
790
794
|
const currentServe = existingTunnel.serve;
|
|
791
795
|
const currentAutoReconnect = existingTunnel.autoReconnect || false;
|
|
796
|
+
const requestedServe = this.resolveServePath(newConfig);
|
|
792
797
|
try {
|
|
793
798
|
if (!isStopped) {
|
|
794
799
|
existingTunnel.instance.stop();
|
|
@@ -799,9 +804,9 @@ var init_TunnelManager = __esm({
|
|
|
799
804
|
...newConfig,
|
|
800
805
|
configId,
|
|
801
806
|
tunnelName: newTunnelName !== void 0 ? newTunnelName : currentTunnelName,
|
|
802
|
-
serve:
|
|
807
|
+
serve: requestedServe !== void 0 ? requestedServe : currentServe
|
|
803
808
|
};
|
|
804
|
-
const effectiveServe =
|
|
809
|
+
const effectiveServe = requestedServe !== void 0 ? requestedServe : currentServe;
|
|
805
810
|
const effectiveTunnelName = newTunnelName !== void 0 ? newTunnelName : currentTunnelName;
|
|
806
811
|
let configWithForwarding;
|
|
807
812
|
const newTunnel = await this._createTunnelWithProcessedConfig({
|
|
@@ -1560,21 +1565,35 @@ var init_TunnelManager = __esm({
|
|
|
1560
1565
|
const parsed = typeof value === "number" ? value : parseInt(String(value), 10);
|
|
1561
1566
|
return isNaN(parsed) ? 0 : parsed;
|
|
1562
1567
|
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Read serve path only from config.optional.serve.
|
|
1570
|
+
*/
|
|
1571
|
+
resolveServePath(config) {
|
|
1572
|
+
const optional = config.optional;
|
|
1573
|
+
const servePath = optional?.serve;
|
|
1574
|
+
logger.debug("resolveServePath", { servePath, hasOptional: !!optional, optionalKeys: optional ? Object.keys(optional) : [] });
|
|
1575
|
+
return servePath;
|
|
1576
|
+
}
|
|
1563
1577
|
startStaticFileServer(managed) {
|
|
1564
1578
|
try {
|
|
1565
1579
|
const __filename4 = (0, import_node_url.fileURLToPath)(importMetaUrl);
|
|
1566
1580
|
const __dirname3 = import_node_path.default.dirname(__filename4);
|
|
1567
1581
|
const fileServerWorkerPath = import_node_path.default.join(__dirname3, "workers", "file_serve_worker.cjs");
|
|
1582
|
+
logger.info("Starting static file server worker", {
|
|
1583
|
+
dir: managed.serve,
|
|
1584
|
+
forwarding: JSON.stringify(managed.tunnelConfig?.forwarding),
|
|
1585
|
+
workerPath: fileServerWorkerPath
|
|
1586
|
+
});
|
|
1568
1587
|
const staticServerWorker = new import_node_worker_threads.Worker(fileServerWorkerPath, {
|
|
1569
1588
|
workerData: {
|
|
1570
1589
|
dir: managed.serve,
|
|
1571
|
-
|
|
1590
|
+
forwarding: managed.tunnelConfig?.forwarding
|
|
1572
1591
|
}
|
|
1573
1592
|
});
|
|
1574
1593
|
staticServerWorker.on("message", (msg) => {
|
|
1575
1594
|
switch (msg.type) {
|
|
1576
1595
|
case "started":
|
|
1577
|
-
logger.info("Static file server started", { dir: managed.serve });
|
|
1596
|
+
logger.info("Static file server started", { dir: managed.serve, port: msg.portNum });
|
|
1578
1597
|
break;
|
|
1579
1598
|
case "warning":
|
|
1580
1599
|
if (msg.code === "INVALID_TUNNEL_SERVE_PATH") {
|
|
@@ -3084,10 +3103,14 @@ var init_options = __esm({
|
|
|
3084
3103
|
v: { type: "boolean", description: "Print logs to stdout for Cli. Overrides PINGGY_LOG_STDOUT environment variable" },
|
|
3085
3104
|
vv: { type: "boolean", description: "Enable detailed logging for the Node.js SDK and Libpinggy, including both info and debug level logs." },
|
|
3086
3105
|
vvv: { type: "boolean", description: "Enable all logs from Cli, SDK and internal components." },
|
|
3087
|
-
autoreconnect: { type: "
|
|
3088
|
-
// Save and load config
|
|
3106
|
+
"no-autoreconnect": { type: "boolean", short: "a", description: "Disable auto reconnection on failure (enabled by default)." },
|
|
3107
|
+
// Save and load config (legacy file-based)
|
|
3089
3108
|
saveconf: { type: "string", description: "Create the configuration file based on the options provided here" },
|
|
3090
3109
|
conf: { type: "string", description: "Use the configuration file as base. Other options will be used to override this file" },
|
|
3110
|
+
// Used by `pinggy config save` and `buildAndStartTunnel` save flow
|
|
3111
|
+
save: { type: "boolean", short: "s", description: "Save the tunnel config (use with config save or -l)", hidden: true },
|
|
3112
|
+
name: { type: "string", description: "Name for the tunnel config", hidden: true },
|
|
3113
|
+
auto: { type: "boolean", description: "Mark tunnel config for auto-start", hidden: true },
|
|
3091
3114
|
// File server
|
|
3092
3115
|
serve: { type: "string", description: "Start a webserver to serve files from the specified path. Eg --serve /path/to/files" },
|
|
3093
3116
|
// Remote Control
|
|
@@ -3137,7 +3160,21 @@ function printHelpMessage() {
|
|
|
3137
3160
|
console.log(" pinggy -R0:localhost:3000 # Basic HTTP tunnel");
|
|
3138
3161
|
console.log(" pinggy --type tcp -R0:localhost:22 # TCP tunnel for SSH");
|
|
3139
3162
|
console.log(" pinggy -R0:localhost:8080 -L4300:localhost:4300 # HTTP tunnel with debugger");
|
|
3140
|
-
console.log(" pinggy tcp@ap.example.com -R0:localhost:22 # TCP tunnel to region
|
|
3163
|
+
console.log(" pinggy tcp@ap.example.com -R0:localhost:22 # TCP tunnel to region");
|
|
3164
|
+
console.log("\nConfig Management:");
|
|
3165
|
+
console.log(" pinggy config list # List saved configs");
|
|
3166
|
+
console.log(" pinggy config show my-tunnel # Show config details");
|
|
3167
|
+
console.log(" pinggy config save my-tunnel -l 3000 token@pro.pinggy.io # Save config");
|
|
3168
|
+
console.log(" pinggy config save my-tunnel --auto -l 3000 # Save with auto-start");
|
|
3169
|
+
console.log(" pinggy config update my-tunnel -l 4000 # Update saved config");
|
|
3170
|
+
console.log(" pinggy config delete my-tunnel # Delete saved config");
|
|
3171
|
+
console.log(" pinggy config auto my-tunnel # Enable auto-start");
|
|
3172
|
+
console.log(" pinggy config noauto my-tunnel # Disable auto-start");
|
|
3173
|
+
console.log("\nStart Saved Tunnels:");
|
|
3174
|
+
console.log(" pinggy start my-tunnel # Start saved tunnel");
|
|
3175
|
+
console.log(" pinggy start my-tunnel -l 4000 # Start with runtime overrides");
|
|
3176
|
+
console.log(" pinggy start tunnela tunnelb # Start multiple tunnels");
|
|
3177
|
+
console.log(" pinggy start --all # Start all auto-start tunnels\n");
|
|
3141
3178
|
}
|
|
3142
3179
|
var init_help = __esm({
|
|
3143
3180
|
"src/cli/help.ts"() {
|
|
@@ -3147,6 +3184,74 @@ var init_help = __esm({
|
|
|
3147
3184
|
}
|
|
3148
3185
|
});
|
|
3149
3186
|
|
|
3187
|
+
// src/utils/parseArgs.ts
|
|
3188
|
+
function isAttachedReverseOrLocalFlag(arg) {
|
|
3189
|
+
return /^-[RL].+/.test(arg);
|
|
3190
|
+
}
|
|
3191
|
+
function shouldMergeReverseOrLocalFragment(current, next) {
|
|
3192
|
+
if (next.startsWith("-")) {
|
|
3193
|
+
return false;
|
|
3194
|
+
}
|
|
3195
|
+
if (next.startsWith(".")) {
|
|
3196
|
+
return true;
|
|
3197
|
+
}
|
|
3198
|
+
const body = current.slice(2);
|
|
3199
|
+
if (body.endsWith(":")) {
|
|
3200
|
+
return true;
|
|
3201
|
+
}
|
|
3202
|
+
if (body.includes("//") && !body.includes(":")) {
|
|
3203
|
+
return true;
|
|
3204
|
+
}
|
|
3205
|
+
return false;
|
|
3206
|
+
}
|
|
3207
|
+
function preprocessWindowsArgs(args) {
|
|
3208
|
+
if (os2.platform() !== "win32") {
|
|
3209
|
+
return args;
|
|
3210
|
+
}
|
|
3211
|
+
;
|
|
3212
|
+
const out = [];
|
|
3213
|
+
let i = 0;
|
|
3214
|
+
while (i < args.length) {
|
|
3215
|
+
const arg = args[i];
|
|
3216
|
+
if (isAttachedReverseOrLocalFlag(arg)) {
|
|
3217
|
+
let merged = arg;
|
|
3218
|
+
while (i + 1 < args.length && shouldMergeReverseOrLocalFragment(merged, args[i + 1])) {
|
|
3219
|
+
merged += args[i + 1];
|
|
3220
|
+
i++;
|
|
3221
|
+
}
|
|
3222
|
+
out.push(merged);
|
|
3223
|
+
i++;
|
|
3224
|
+
continue;
|
|
3225
|
+
}
|
|
3226
|
+
out.push(arg);
|
|
3227
|
+
i++;
|
|
3228
|
+
}
|
|
3229
|
+
return out;
|
|
3230
|
+
}
|
|
3231
|
+
function parseCliArgs(options, overrideArgs) {
|
|
3232
|
+
const rawArgs = overrideArgs ?? process.argv.slice(2);
|
|
3233
|
+
const processedArgs = preprocessWindowsArgs(rawArgs);
|
|
3234
|
+
const parsed = (0, import_util5.parseArgs)({
|
|
3235
|
+
args: processedArgs,
|
|
3236
|
+
options,
|
|
3237
|
+
allowPositionals: true
|
|
3238
|
+
});
|
|
3239
|
+
const hasAnyArgs = parsed.positionals.length > 0 || Object.values(parsed.values).some((v) => v !== void 0 && v !== false);
|
|
3240
|
+
return {
|
|
3241
|
+
...parsed,
|
|
3242
|
+
hasAnyArgs
|
|
3243
|
+
};
|
|
3244
|
+
}
|
|
3245
|
+
var import_util5, os2;
|
|
3246
|
+
var init_parseArgs = __esm({
|
|
3247
|
+
"src/utils/parseArgs.ts"() {
|
|
3248
|
+
"use strict";
|
|
3249
|
+
init_cjs_shims();
|
|
3250
|
+
import_util5 = require("util");
|
|
3251
|
+
os2 = __toESM(require("os"), 1);
|
|
3252
|
+
}
|
|
3253
|
+
});
|
|
3254
|
+
|
|
3150
3255
|
// src/cli/defaults.ts
|
|
3151
3256
|
var defaultOptions;
|
|
3152
3257
|
var init_defaults = __esm({
|
|
@@ -3154,6 +3259,7 @@ var init_defaults = __esm({
|
|
|
3154
3259
|
"use strict";
|
|
3155
3260
|
init_cjs_shims();
|
|
3156
3261
|
defaultOptions = {
|
|
3262
|
+
version: "1.0",
|
|
3157
3263
|
token: void 0,
|
|
3158
3264
|
// No default token
|
|
3159
3265
|
serverAddress: "a.pinggy.io",
|
|
@@ -3656,7 +3762,7 @@ function parseToken(finalConfig, explicitToken) {
|
|
|
3656
3762
|
finalConfig.token = explicitToken;
|
|
3657
3763
|
}
|
|
3658
3764
|
}
|
|
3659
|
-
function
|
|
3765
|
+
function parseArgs2(finalConfig, remainingPositionals) {
|
|
3660
3766
|
let localserverTls = "";
|
|
3661
3767
|
localserverTls = parseExtendedOptions(remainingPositionals, finalConfig, localserverTls);
|
|
3662
3768
|
if (localserverTls.length > 0 && finalConfig.forwarding) {
|
|
@@ -3667,10 +3773,10 @@ function parseArgs(finalConfig, remainingPositionals) {
|
|
|
3667
3773
|
}
|
|
3668
3774
|
function storeJson(config, saveconf) {
|
|
3669
3775
|
if (saveconf) {
|
|
3670
|
-
const
|
|
3776
|
+
const path7 = saveconf;
|
|
3671
3777
|
try {
|
|
3672
|
-
import_fs4.default.writeFileSync(
|
|
3673
|
-
logger.info(`Configuration saved to ${
|
|
3778
|
+
import_fs4.default.writeFileSync(path7, JSON.stringify(config, null, 2), { encoding: "utf-8", flag: "w" });
|
|
3779
|
+
logger.info(`Configuration saved to ${path7}`);
|
|
3674
3780
|
} catch (err) {
|
|
3675
3781
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3676
3782
|
logger.error("Error loading configuration:", msg);
|
|
@@ -3707,20 +3813,12 @@ function parseServe(finalConfig, values) {
|
|
|
3707
3813
|
return null;
|
|
3708
3814
|
}
|
|
3709
3815
|
function parseAutoReconnect(finalConfig, values) {
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
const trimmed = autoReconnectValue.trim().toLowerCase();
|
|
3713
|
-
if (trimmed === "true" || trimmed === "") {
|
|
3714
|
-
finalConfig.autoReconnect = true;
|
|
3715
|
-
} else if (trimmed === "false") {
|
|
3716
|
-
finalConfig.autoReconnect = false;
|
|
3717
|
-
} else {
|
|
3718
|
-
return new Error(`Invalid autoreconnect value: ${autoReconnectValue}. Use true or false.`);
|
|
3719
|
-
}
|
|
3816
|
+
if (values["no-autoreconnect"]) {
|
|
3817
|
+
finalConfig.autoReconnect = false;
|
|
3720
3818
|
}
|
|
3721
3819
|
return null;
|
|
3722
3820
|
}
|
|
3723
|
-
async function buildFinalConfig(values, positionals) {
|
|
3821
|
+
async function buildFinalConfig(values, positionals, baseConfig) {
|
|
3724
3822
|
let token;
|
|
3725
3823
|
let server;
|
|
3726
3824
|
let type;
|
|
@@ -3728,7 +3826,7 @@ async function buildFinalConfig(values, positionals) {
|
|
|
3728
3826
|
let qrCode = false;
|
|
3729
3827
|
let finalConfig = new Object();
|
|
3730
3828
|
let saveconf = isSaveConfOption(values);
|
|
3731
|
-
const configFromFile = loadJsonConfig(values);
|
|
3829
|
+
const configFromFile = baseConfig || loadJsonConfig(values);
|
|
3732
3830
|
const userParse = parseUsers(positionals, values.token);
|
|
3733
3831
|
token = userParse.token;
|
|
3734
3832
|
server = userParse.server;
|
|
@@ -3780,7 +3878,7 @@ async function buildFinalConfig(values, positionals) {
|
|
|
3780
3878
|
if (forceFlag || values.force) {
|
|
3781
3879
|
finalConfig.force = true;
|
|
3782
3880
|
}
|
|
3783
|
-
|
|
3881
|
+
parseArgs2(finalConfig, remainingPositionals);
|
|
3784
3882
|
storeJson(finalConfig, saveconf);
|
|
3785
3883
|
return finalConfig;
|
|
3786
3884
|
}
|
|
@@ -3811,74 +3909,6 @@ var init_buildConfig = __esm({
|
|
|
3811
3909
|
}
|
|
3812
3910
|
});
|
|
3813
3911
|
|
|
3814
|
-
// src/utils/parseArgs.ts
|
|
3815
|
-
function isAttachedReverseOrLocalFlag(arg) {
|
|
3816
|
-
return /^-[RL].+/.test(arg);
|
|
3817
|
-
}
|
|
3818
|
-
function shouldMergeReverseOrLocalFragment(current, next) {
|
|
3819
|
-
if (next.startsWith("-")) {
|
|
3820
|
-
return false;
|
|
3821
|
-
}
|
|
3822
|
-
if (next.startsWith(".")) {
|
|
3823
|
-
return true;
|
|
3824
|
-
}
|
|
3825
|
-
const body = current.slice(2);
|
|
3826
|
-
if (body.endsWith(":")) {
|
|
3827
|
-
return true;
|
|
3828
|
-
}
|
|
3829
|
-
if (body.includes("//") && !body.includes(":")) {
|
|
3830
|
-
return true;
|
|
3831
|
-
}
|
|
3832
|
-
return false;
|
|
3833
|
-
}
|
|
3834
|
-
function preprocessWindowsArgs(args) {
|
|
3835
|
-
if (os2.platform() !== "win32") {
|
|
3836
|
-
return args;
|
|
3837
|
-
}
|
|
3838
|
-
;
|
|
3839
|
-
const out = [];
|
|
3840
|
-
let i = 0;
|
|
3841
|
-
while (i < args.length) {
|
|
3842
|
-
const arg = args[i];
|
|
3843
|
-
if (isAttachedReverseOrLocalFlag(arg)) {
|
|
3844
|
-
let merged = arg;
|
|
3845
|
-
while (i + 1 < args.length && shouldMergeReverseOrLocalFragment(merged, args[i + 1])) {
|
|
3846
|
-
merged += args[i + 1];
|
|
3847
|
-
i++;
|
|
3848
|
-
}
|
|
3849
|
-
out.push(merged);
|
|
3850
|
-
i++;
|
|
3851
|
-
continue;
|
|
3852
|
-
}
|
|
3853
|
-
out.push(arg);
|
|
3854
|
-
i++;
|
|
3855
|
-
}
|
|
3856
|
-
return out;
|
|
3857
|
-
}
|
|
3858
|
-
function parseCliArgs(options) {
|
|
3859
|
-
const rawArgs = process.argv.slice(2);
|
|
3860
|
-
const processedArgs = preprocessWindowsArgs(rawArgs);
|
|
3861
|
-
const parsed = (0, import_util6.parseArgs)({
|
|
3862
|
-
args: processedArgs,
|
|
3863
|
-
options,
|
|
3864
|
-
allowPositionals: true
|
|
3865
|
-
});
|
|
3866
|
-
const hasAnyArgs = parsed.positionals.length > 0 || Object.values(parsed.values).some((v) => v !== void 0 && v !== false);
|
|
3867
|
-
return {
|
|
3868
|
-
...parsed,
|
|
3869
|
-
hasAnyArgs
|
|
3870
|
-
};
|
|
3871
|
-
}
|
|
3872
|
-
var import_util6, os2;
|
|
3873
|
-
var init_parseArgs = __esm({
|
|
3874
|
-
"src/utils/parseArgs.ts"() {
|
|
3875
|
-
"use strict";
|
|
3876
|
-
init_cjs_shims();
|
|
3877
|
-
import_util6 = require("util");
|
|
3878
|
-
os2 = __toESM(require("os"), 1);
|
|
3879
|
-
}
|
|
3880
|
-
});
|
|
3881
|
-
|
|
3882
3912
|
// src/utils/getFreePort.ts
|
|
3883
3913
|
function getFreePort(webDebugger) {
|
|
3884
3914
|
return new Promise((resolve, reject) => {
|
|
@@ -5492,6 +5522,600 @@ var init_starCli = __esm({
|
|
|
5492
5522
|
}
|
|
5493
5523
|
});
|
|
5494
5524
|
|
|
5525
|
+
// src/utils/configDir.ts
|
|
5526
|
+
function getPinggyConfigDir() {
|
|
5527
|
+
const platform2 = import_os2.default.platform();
|
|
5528
|
+
let baseDir;
|
|
5529
|
+
if (platform2 === "win32") {
|
|
5530
|
+
baseDir = process.env.APPDATA || import_path5.default.join(import_os2.default.homedir(), "AppData", "Roaming");
|
|
5531
|
+
} else {
|
|
5532
|
+
baseDir = process.env.XDG_CONFIG_HOME || import_path5.default.join(import_os2.default.homedir(), ".config");
|
|
5533
|
+
}
|
|
5534
|
+
return import_path5.default.join(baseDir, "pinggy");
|
|
5535
|
+
}
|
|
5536
|
+
function getTunnelConfigDir() {
|
|
5537
|
+
return import_path5.default.join(getPinggyConfigDir(), "tunnels");
|
|
5538
|
+
}
|
|
5539
|
+
function ensureTunnelConfigDir() {
|
|
5540
|
+
const dir = getTunnelConfigDir();
|
|
5541
|
+
import_fs5.default.mkdirSync(dir, { recursive: true });
|
|
5542
|
+
return dir;
|
|
5543
|
+
}
|
|
5544
|
+
var import_os2, import_path5, import_fs5;
|
|
5545
|
+
var init_configDir = __esm({
|
|
5546
|
+
"src/utils/configDir.ts"() {
|
|
5547
|
+
"use strict";
|
|
5548
|
+
init_cjs_shims();
|
|
5549
|
+
import_os2 = __toESM(require("os"), 1);
|
|
5550
|
+
import_path5 = __toESM(require("path"), 1);
|
|
5551
|
+
import_fs5 = __toESM(require("fs"), 1);
|
|
5552
|
+
}
|
|
5553
|
+
});
|
|
5554
|
+
|
|
5555
|
+
// src/cli/configStore.ts
|
|
5556
|
+
function buildFilename(name, configId) {
|
|
5557
|
+
return `${name}_${configId}.json`;
|
|
5558
|
+
}
|
|
5559
|
+
function sanitizeName(name) {
|
|
5560
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5561
|
+
}
|
|
5562
|
+
function validateName(name) {
|
|
5563
|
+
if (!name || name.trim().length === 0) {
|
|
5564
|
+
return new Error("Tunnel name cannot be empty.");
|
|
5565
|
+
}
|
|
5566
|
+
if (name.length > 128) {
|
|
5567
|
+
return new Error("Tunnel name cannot exceed 128 characters.");
|
|
5568
|
+
}
|
|
5569
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
5570
|
+
return new Error("Tunnel name can only contain alphanumeric characters, hyphens, and underscores.");
|
|
5571
|
+
}
|
|
5572
|
+
return null;
|
|
5573
|
+
}
|
|
5574
|
+
function readConfigFile(filePath) {
|
|
5575
|
+
try {
|
|
5576
|
+
const data = import_fs6.default.readFileSync(filePath, { encoding: "utf-8" });
|
|
5577
|
+
return JSON.parse(data);
|
|
5578
|
+
} catch (err) {
|
|
5579
|
+
logger.warn(`Failed to read config file ${filePath}:`, err);
|
|
5580
|
+
return null;
|
|
5581
|
+
}
|
|
5582
|
+
}
|
|
5583
|
+
function writeConfigFile(filePath, config) {
|
|
5584
|
+
import_fs6.default.writeFileSync(filePath, JSON.stringify(config, null, 2), { encoding: "utf-8" });
|
|
5585
|
+
}
|
|
5586
|
+
function listSavedConfigs() {
|
|
5587
|
+
const dir = getTunnelConfigDir();
|
|
5588
|
+
if (!import_fs6.default.existsSync(dir)) {
|
|
5589
|
+
return [];
|
|
5590
|
+
}
|
|
5591
|
+
const files = import_fs6.default.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
5592
|
+
const configs = [];
|
|
5593
|
+
for (const file of files) {
|
|
5594
|
+
const config = readConfigFile(import_path6.default.join(dir, file));
|
|
5595
|
+
if (config && config.name && config.configId) {
|
|
5596
|
+
configs.push(config);
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
return configs;
|
|
5600
|
+
}
|
|
5601
|
+
function findConfigFile(nameOrId) {
|
|
5602
|
+
const dir = getTunnelConfigDir();
|
|
5603
|
+
if (!import_fs6.default.existsSync(dir)) return null;
|
|
5604
|
+
const files = import_fs6.default.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
5605
|
+
const sanitized = sanitizeName(nameOrId);
|
|
5606
|
+
const nameMatch = files.find((f) => f.startsWith(sanitized + "_"));
|
|
5607
|
+
if (nameMatch) {
|
|
5608
|
+
const filePath = import_path6.default.join(dir, nameMatch);
|
|
5609
|
+
const config = readConfigFile(filePath);
|
|
5610
|
+
if (config && config.name === nameOrId) return { filePath, config };
|
|
5611
|
+
}
|
|
5612
|
+
const idCandidates = files.filter((f) => {
|
|
5613
|
+
const withoutExt = f.replace(/\.json$/, "");
|
|
5614
|
+
const lastUnderscore = withoutExt.indexOf("_");
|
|
5615
|
+
if (lastUnderscore === -1) return false;
|
|
5616
|
+
const idPart = withoutExt.slice(lastUnderscore + 1);
|
|
5617
|
+
return idPart.startsWith(nameOrId);
|
|
5618
|
+
});
|
|
5619
|
+
if (idCandidates.length === 1) {
|
|
5620
|
+
const filePath = import_path6.default.join(dir, idCandidates[0]);
|
|
5621
|
+
const config = readConfigFile(filePath);
|
|
5622
|
+
if (config) return { filePath, config };
|
|
5623
|
+
}
|
|
5624
|
+
return null;
|
|
5625
|
+
}
|
|
5626
|
+
function findConfigByName(name) {
|
|
5627
|
+
const resolved = findConfigFile(name);
|
|
5628
|
+
return resolved?.config.name === name ? resolved.config : null;
|
|
5629
|
+
}
|
|
5630
|
+
function findConfig(nameOrId) {
|
|
5631
|
+
return findConfigFile(nameOrId)?.config ?? null;
|
|
5632
|
+
}
|
|
5633
|
+
function saveConfig(name, configId, tunnelConfig, autoStart = false) {
|
|
5634
|
+
const nameErr = validateName(name);
|
|
5635
|
+
if (nameErr) {
|
|
5636
|
+
throw nameErr;
|
|
5637
|
+
}
|
|
5638
|
+
const existing = findConfigByName(name);
|
|
5639
|
+
if (existing) {
|
|
5640
|
+
throw new Error(
|
|
5641
|
+
`A tunnel config with the name "${name}" already exists (configId: ${existing.configId}). Please use a different name.`
|
|
5642
|
+
);
|
|
5643
|
+
}
|
|
5644
|
+
const dir = ensureTunnelConfigDir();
|
|
5645
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5646
|
+
const saved = {
|
|
5647
|
+
name,
|
|
5648
|
+
configId,
|
|
5649
|
+
autoStart,
|
|
5650
|
+
createdAt: now,
|
|
5651
|
+
updatedAt: now,
|
|
5652
|
+
tunnelConfig
|
|
5653
|
+
};
|
|
5654
|
+
const filename = buildFilename(sanitizeName(name), configId);
|
|
5655
|
+
const filePath = import_path6.default.join(dir, filename);
|
|
5656
|
+
import_fs6.default.writeFileSync(filePath, JSON.stringify(saved, null, 2), { encoding: "utf-8" });
|
|
5657
|
+
logger.info(`Config "${name}" saved to ${filePath}`);
|
|
5658
|
+
return saved;
|
|
5659
|
+
}
|
|
5660
|
+
function deleteConfig(nameOrId) {
|
|
5661
|
+
const resolved = findConfigFile(nameOrId);
|
|
5662
|
+
if (!resolved) return null;
|
|
5663
|
+
import_fs6.default.unlinkSync(resolved.filePath);
|
|
5664
|
+
logger.info(`Config "${resolved.config.name}" deleted.`);
|
|
5665
|
+
return resolved.config.name;
|
|
5666
|
+
}
|
|
5667
|
+
function updateConfigAutoStart(nameOrId, autoStart) {
|
|
5668
|
+
const resolved = findConfigFile(nameOrId);
|
|
5669
|
+
if (!resolved) return null;
|
|
5670
|
+
resolved.config.autoStart = autoStart;
|
|
5671
|
+
resolved.config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5672
|
+
writeConfigFile(resolved.filePath, resolved.config);
|
|
5673
|
+
logger.info(`Config "${resolved.config.name}" auto-start set to ${autoStart}`);
|
|
5674
|
+
return resolved.config;
|
|
5675
|
+
}
|
|
5676
|
+
function updateTunnelConfig(nameOrId, tunnelConfig) {
|
|
5677
|
+
const resolved = findConfigFile(nameOrId);
|
|
5678
|
+
if (!resolved) return null;
|
|
5679
|
+
resolved.config.tunnelConfig = tunnelConfig;
|
|
5680
|
+
resolved.config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5681
|
+
writeConfigFile(resolved.filePath, resolved.config);
|
|
5682
|
+
logger.info(`Config "${resolved.config.name}" tunnel configuration updated`);
|
|
5683
|
+
return resolved.config;
|
|
5684
|
+
}
|
|
5685
|
+
function getAutoStartConfigs() {
|
|
5686
|
+
return listSavedConfigs().filter((c) => c.autoStart);
|
|
5687
|
+
}
|
|
5688
|
+
function printConfigList() {
|
|
5689
|
+
const configs = listSavedConfigs();
|
|
5690
|
+
if (configs.length === 0) {
|
|
5691
|
+
console.log(import_picocolors5.default.yellow("No saved tunnel configs found."));
|
|
5692
|
+
console.log(import_picocolors5.default.gray(`Config directory: ${getTunnelConfigDir()}`));
|
|
5693
|
+
return;
|
|
5694
|
+
}
|
|
5695
|
+
const nameW = 20;
|
|
5696
|
+
const idW = 12;
|
|
5697
|
+
const typeW = 8;
|
|
5698
|
+
const fwdW = 25;
|
|
5699
|
+
const serverW = 22;
|
|
5700
|
+
const autoW = 10;
|
|
5701
|
+
const header = import_picocolors5.default.bold("Name".padEnd(nameW)) + import_picocolors5.default.bold("Config ID".padEnd(idW)) + import_picocolors5.default.bold("Type".padEnd(typeW)) + import_picocolors5.default.bold("Forwarding".padEnd(fwdW)) + import_picocolors5.default.bold("Server".padEnd(serverW)) + import_picocolors5.default.bold("Auto-start".padEnd(autoW));
|
|
5702
|
+
console.log("\n" + header);
|
|
5703
|
+
console.log(import_picocolors5.default.gray("\u2500".repeat(nameW + idW + typeW + fwdW + serverW + autoW)));
|
|
5704
|
+
for (const c of configs) {
|
|
5705
|
+
const tc = c.tunnelConfig;
|
|
5706
|
+
const forwarding = Array.isArray(tc.forwarding) ? tc.forwarding[0]?.address : String(tc.forwarding || "");
|
|
5707
|
+
const type = (Array.isArray(tc.forwarding) ? tc.forwarding[0]?.type : void 0) || "http";
|
|
5708
|
+
const server = tc.serverAddress || "a.pinggy.io";
|
|
5709
|
+
const line = import_picocolors5.default.cyanBright(c.name.padEnd(nameW)) + import_picocolors5.default.gray(c.configId.slice(0, 8).padEnd(idW)) + type.padEnd(typeW) + forwarding.slice(0, fwdW - 2).padEnd(fwdW) + server.slice(0, serverW - 2).padEnd(serverW) + (c.autoStart ? import_picocolors5.default.green("yes") : import_picocolors5.default.gray("no")).padEnd(autoW);
|
|
5710
|
+
console.log(line);
|
|
5711
|
+
}
|
|
5712
|
+
console.log();
|
|
5713
|
+
}
|
|
5714
|
+
function printConfigDetail(config) {
|
|
5715
|
+
console.log(import_picocolors5.default.bold(`
|
|
5716
|
+
Tunnel Config: ${import_picocolors5.default.cyanBright(config.name)}`));
|
|
5717
|
+
console.log(import_picocolors5.default.gray("\u2500".repeat(40)));
|
|
5718
|
+
console.log(` Config ID: ${config.configId}`);
|
|
5719
|
+
console.log(` Auto-start: ${config.autoStart ? import_picocolors5.default.green("yes") : import_picocolors5.default.gray("no")}`);
|
|
5720
|
+
console.log(` Created: ${config.createdAt}`);
|
|
5721
|
+
console.log(` Updated: ${config.updatedAt}`);
|
|
5722
|
+
console.log(import_picocolors5.default.gray("\u2500".repeat(40)));
|
|
5723
|
+
console.log(` Server: ${config.tunnelConfig.serverAddress || "a.pinggy.io"}`);
|
|
5724
|
+
console.log(` Token: ${config.tunnelConfig.token ? "***" + config.tunnelConfig.token.slice(-4) : "(none)"}`);
|
|
5725
|
+
const fwd = config.tunnelConfig.forwarding;
|
|
5726
|
+
if (Array.isArray(fwd)) {
|
|
5727
|
+
const defaultFwds = [];
|
|
5728
|
+
const customFwds = [];
|
|
5729
|
+
for (const f of fwd) {
|
|
5730
|
+
if (typeof f === "string") {
|
|
5731
|
+
defaultFwds.push(f);
|
|
5732
|
+
} else if (f.listenAddress) {
|
|
5733
|
+
customFwds.push(f);
|
|
5734
|
+
} else {
|
|
5735
|
+
defaultFwds.push(f);
|
|
5736
|
+
}
|
|
5737
|
+
}
|
|
5738
|
+
for (const f of defaultFwds) {
|
|
5739
|
+
const addr = typeof f === "string" ? f : `${f.address} (${f.type || "http"})`;
|
|
5740
|
+
console.log(` Forwarding: ${addr}`);
|
|
5741
|
+
if (config.tunnelConfig.webDebugger) {
|
|
5742
|
+
console.log(` Debugger: ${config.tunnelConfig.webDebugger}`);
|
|
5743
|
+
}
|
|
5744
|
+
}
|
|
5745
|
+
if (customFwds.length > 0) {
|
|
5746
|
+
console.log(import_picocolors5.default.gray("\u2500".repeat(40)));
|
|
5747
|
+
console.log(import_picocolors5.default.bold(" Domain Mappings:"));
|
|
5748
|
+
for (const f of customFwds) {
|
|
5749
|
+
if (typeof f === "string") continue;
|
|
5750
|
+
const domain = f.listenAddress;
|
|
5751
|
+
const target = f.address;
|
|
5752
|
+
const type = f.type || "http";
|
|
5753
|
+
console.log(` ${import_picocolors5.default.cyanBright(domain)} \u2192 ${target} (${type})`);
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
} else if (fwd) {
|
|
5757
|
+
console.log(` Forwarding: ${fwd}`);
|
|
5758
|
+
}
|
|
5759
|
+
console.log();
|
|
5760
|
+
}
|
|
5761
|
+
var import_fs6, import_path6, import_picocolors5;
|
|
5762
|
+
var init_configStore = __esm({
|
|
5763
|
+
"src/cli/configStore.ts"() {
|
|
5764
|
+
"use strict";
|
|
5765
|
+
init_cjs_shims();
|
|
5766
|
+
import_fs6 = __toESM(require("fs"), 1);
|
|
5767
|
+
import_path6 = __toESM(require("path"), 1);
|
|
5768
|
+
init_configDir();
|
|
5769
|
+
init_logger();
|
|
5770
|
+
import_picocolors5 = __toESM(require("picocolors"), 1);
|
|
5771
|
+
}
|
|
5772
|
+
});
|
|
5773
|
+
|
|
5774
|
+
// src/cli/buildAndStartTunnel.ts
|
|
5775
|
+
async function buildAndStartTunnel(values, positionals, manager) {
|
|
5776
|
+
await initRemoteManagement(values);
|
|
5777
|
+
logger.debug("Building final config from CLI values and positionals", { values, positionals });
|
|
5778
|
+
const finalConfig = await buildFinalConfig(values, positionals);
|
|
5779
|
+
logger.debug("Final configuration built", finalConfig);
|
|
5780
|
+
if (values.save) {
|
|
5781
|
+
const name = values.name;
|
|
5782
|
+
if (!name) {
|
|
5783
|
+
printer_default.error("--save requires --name to specify a name for the tunnel config.");
|
|
5784
|
+
process.exit(1);
|
|
5785
|
+
}
|
|
5786
|
+
const nameErr = validateName(name);
|
|
5787
|
+
if (nameErr) {
|
|
5788
|
+
printer_default.error(nameErr.message);
|
|
5789
|
+
process.exit(1);
|
|
5790
|
+
}
|
|
5791
|
+
const autoStart = !!values.auto;
|
|
5792
|
+
saveConfig(name, finalConfig.configId, finalConfig, autoStart);
|
|
5793
|
+
printer_default.success(`Config "${name}" saved.`);
|
|
5794
|
+
}
|
|
5795
|
+
await startCli(finalConfig, manager);
|
|
5796
|
+
}
|
|
5797
|
+
async function initRemoteManagement(values) {
|
|
5798
|
+
const parseResult = await parseRemoteManagement(values);
|
|
5799
|
+
if (parseResult?.ok === false) {
|
|
5800
|
+
logger.error("Failed to initiate remote management:", parseResult.error);
|
|
5801
|
+
printer_default.fatal(parseResult.error);
|
|
5802
|
+
}
|
|
5803
|
+
}
|
|
5804
|
+
var init_buildAndStartTunnel = __esm({
|
|
5805
|
+
"src/cli/buildAndStartTunnel.ts"() {
|
|
5806
|
+
"use strict";
|
|
5807
|
+
init_cjs_shims();
|
|
5808
|
+
init_logger();
|
|
5809
|
+
init_remoteManagement();
|
|
5810
|
+
init_buildConfig();
|
|
5811
|
+
init_starCli();
|
|
5812
|
+
init_printer();
|
|
5813
|
+
init_configStore();
|
|
5814
|
+
}
|
|
5815
|
+
});
|
|
5816
|
+
|
|
5817
|
+
// src/cli/subcommands.ts
|
|
5818
|
+
function isSubcommand(rawArgs) {
|
|
5819
|
+
return rawArgs.length > 0 && SUBCOMMANDS.has(rawArgs[0]);
|
|
5820
|
+
}
|
|
5821
|
+
async function handleSubcommand(rawArgs, manager) {
|
|
5822
|
+
const sub = rawArgs[0];
|
|
5823
|
+
const rest = rawArgs.slice(1);
|
|
5824
|
+
switch (sub) {
|
|
5825
|
+
case "config":
|
|
5826
|
+
await handleConfig(rest);
|
|
5827
|
+
return;
|
|
5828
|
+
case "start":
|
|
5829
|
+
await handleStart(rest, manager);
|
|
5830
|
+
return;
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5833
|
+
async function handleConfig(args) {
|
|
5834
|
+
if (args.length === 0) {
|
|
5835
|
+
printConfigHelp();
|
|
5836
|
+
return;
|
|
5837
|
+
}
|
|
5838
|
+
const verb = args[0];
|
|
5839
|
+
const rest = args.slice(1);
|
|
5840
|
+
switch (verb) {
|
|
5841
|
+
case "list":
|
|
5842
|
+
case "ls":
|
|
5843
|
+
printConfigList();
|
|
5844
|
+
return;
|
|
5845
|
+
case "show": {
|
|
5846
|
+
const names = requireNames(rest, "config show");
|
|
5847
|
+
for (const name of names) {
|
|
5848
|
+
const saved2 = resolveConfig(name);
|
|
5849
|
+
if (saved2) printConfigDetail(saved2);
|
|
5850
|
+
}
|
|
5851
|
+
return;
|
|
5852
|
+
}
|
|
5853
|
+
case "save": {
|
|
5854
|
+
const name = requireName(rest, "config save");
|
|
5855
|
+
await handleConfigSave(name, rest.slice(1));
|
|
5856
|
+
return;
|
|
5857
|
+
}
|
|
5858
|
+
case "delete": {
|
|
5859
|
+
const names = requireNames(rest, "config delete");
|
|
5860
|
+
for (const name of names) {
|
|
5861
|
+
const deletedName = deleteConfig(name);
|
|
5862
|
+
if (deletedName) {
|
|
5863
|
+
printer_default.success(`Config "${deletedName}" deleted.`);
|
|
5864
|
+
} else {
|
|
5865
|
+
printer_default.error(`No config found matching "${name}". Use: pinggy config list`);
|
|
5866
|
+
}
|
|
5867
|
+
}
|
|
5868
|
+
return;
|
|
5869
|
+
}
|
|
5870
|
+
case "update": {
|
|
5871
|
+
const name = requireName(rest, "config update");
|
|
5872
|
+
await handleConfigUpdate(name, rest.slice(1));
|
|
5873
|
+
return;
|
|
5874
|
+
}
|
|
5875
|
+
case "auto": {
|
|
5876
|
+
const names = requireNames(rest, "config auto");
|
|
5877
|
+
for (const name of names) {
|
|
5878
|
+
const updated = updateConfigAutoStart(name, true);
|
|
5879
|
+
if (updated) {
|
|
5880
|
+
printer_default.success(`Config "${updated.name}" auto-start set to on.`);
|
|
5881
|
+
} else {
|
|
5882
|
+
printer_default.error(`No config found matching "${name}". Use: pinggy config list`);
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
return;
|
|
5886
|
+
}
|
|
5887
|
+
case "noauto": {
|
|
5888
|
+
const names = requireNames(rest, "config noauto");
|
|
5889
|
+
for (const name of names) {
|
|
5890
|
+
const updated = updateConfigAutoStart(name, false);
|
|
5891
|
+
if (updated) {
|
|
5892
|
+
printer_default.success(`Config "${updated.name}" auto-start set to off.`);
|
|
5893
|
+
} else {
|
|
5894
|
+
printer_default.error(`No config found matching "${name}". Use: pinggy config list`);
|
|
5895
|
+
}
|
|
5896
|
+
}
|
|
5897
|
+
return;
|
|
5898
|
+
}
|
|
5899
|
+
default:
|
|
5900
|
+
const saved = resolveConfig(verb);
|
|
5901
|
+
if (saved) printConfigDetail(saved);
|
|
5902
|
+
return;
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
async function handleConfigSave(name, remainingArgs) {
|
|
5906
|
+
const nameErr = validateName(name);
|
|
5907
|
+
if (nameErr) {
|
|
5908
|
+
printer_default.error(nameErr.message);
|
|
5909
|
+
process.exit(1);
|
|
5910
|
+
}
|
|
5911
|
+
const { values, positionals } = parseCliArgs(cliOptions, remainingArgs);
|
|
5912
|
+
const autoStart = !!values.auto;
|
|
5913
|
+
logger.debug("Building config for save", { name, values, positionals });
|
|
5914
|
+
const finalConfig = await buildFinalConfig(values, positionals);
|
|
5915
|
+
saveConfig(name, finalConfig.configId, finalConfig, autoStart);
|
|
5916
|
+
printer_default.success(`Config "${name}" saved.`);
|
|
5917
|
+
}
|
|
5918
|
+
async function handleConfigUpdate(nameOrId, remainingArgs) {
|
|
5919
|
+
const saved = resolveConfig(nameOrId);
|
|
5920
|
+
if (!saved) return;
|
|
5921
|
+
const { values, positionals } = parseCliArgs(cliOptions, remainingArgs);
|
|
5922
|
+
logger.debug("Building updated config", { nameOrId, values, positionals });
|
|
5923
|
+
const updatedConfig = await buildFinalConfig(values, positionals, saved.tunnelConfig);
|
|
5924
|
+
const result = updateTunnelConfig(nameOrId, updatedConfig);
|
|
5925
|
+
if (result) {
|
|
5926
|
+
printer_default.success(`Config "${result.name}" updated.`);
|
|
5927
|
+
printConfigDetail(result);
|
|
5928
|
+
} else {
|
|
5929
|
+
printer_default.error(`Failed to update config "${nameOrId}".`);
|
|
5930
|
+
}
|
|
5931
|
+
}
|
|
5932
|
+
async function handleStart(args, manager) {
|
|
5933
|
+
const startAll = args.includes("--all");
|
|
5934
|
+
const argsWithoutAll = args.filter((a) => a !== "--all");
|
|
5935
|
+
const names = [];
|
|
5936
|
+
let i = 0;
|
|
5937
|
+
while (i < argsWithoutAll.length && !argsWithoutAll[i].startsWith("-")) {
|
|
5938
|
+
names.push(argsWithoutAll[i]);
|
|
5939
|
+
i++;
|
|
5940
|
+
}
|
|
5941
|
+
const flagArgs = argsWithoutAll.slice(i);
|
|
5942
|
+
const { values, positionals } = parseCliArgs(cliOptions, flagArgs);
|
|
5943
|
+
configureLogger(values);
|
|
5944
|
+
if (startAll) {
|
|
5945
|
+
await initRemoteManagementBackground(values);
|
|
5946
|
+
await startAutoStartTunnels(manager);
|
|
5947
|
+
return;
|
|
5948
|
+
}
|
|
5949
|
+
if (names.length === 0) {
|
|
5950
|
+
printStartHelp();
|
|
5951
|
+
return;
|
|
5952
|
+
}
|
|
5953
|
+
const resolved = [];
|
|
5954
|
+
for (const name of names) {
|
|
5955
|
+
const saved = resolveConfig(name);
|
|
5956
|
+
if (!saved) return;
|
|
5957
|
+
resolved.push(saved);
|
|
5958
|
+
}
|
|
5959
|
+
if (resolved.length > 1 && flagArgs.length > 0) {
|
|
5960
|
+
printer_default.error("Runtime overrides (-l, --type, etc.) can only be used when starting a single tunnel.");
|
|
5961
|
+
printer_default.print(" Start one tunnel: pinggy start my-tunnel -l 4000");
|
|
5962
|
+
printer_default.print(" Or update first: pinggy config update my-tunnel -l 4000");
|
|
5963
|
+
return;
|
|
5964
|
+
}
|
|
5965
|
+
await initRemoteManagementBackground(values);
|
|
5966
|
+
if (resolved.length === 1) {
|
|
5967
|
+
const saved = resolved[0];
|
|
5968
|
+
logger.debug("Building config with overrides", { name: saved.name });
|
|
5969
|
+
const finalConfig = await buildFinalConfig(values, positionals, saved.tunnelConfig);
|
|
5970
|
+
finalConfig.configId = saved.configId;
|
|
5971
|
+
await startCli(finalConfig, manager);
|
|
5972
|
+
} else {
|
|
5973
|
+
await startNamedTunnels(resolved, manager);
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
async function startAutoStartTunnels(manager) {
|
|
5977
|
+
const configs = getAutoStartConfigs();
|
|
5978
|
+
if (configs.length === 0) {
|
|
5979
|
+
printer_default.warn("No configs marked for auto-start. Use: pinggy config auto <name>");
|
|
5980
|
+
return;
|
|
5981
|
+
}
|
|
5982
|
+
printer_default.print(import_picocolors6.default.cyanBright(`Starting ${configs.length} auto-start tunnel(s)...`));
|
|
5983
|
+
for (const saved of configs) {
|
|
5984
|
+
await startSavedTunnel(saved, manager);
|
|
5985
|
+
}
|
|
5986
|
+
printer_default.print(import_picocolors6.default.gray("\nAll auto-start tunnels launched. Press Ctrl+C to stop.\n"));
|
|
5987
|
+
await new Promise(() => {
|
|
5988
|
+
});
|
|
5989
|
+
}
|
|
5990
|
+
async function startNamedTunnels(configs, manager) {
|
|
5991
|
+
printer_default.print(import_picocolors6.default.cyanBright(`Starting ${configs.length} tunnel(s)...`));
|
|
5992
|
+
for (const saved of configs) {
|
|
5993
|
+
await startSavedTunnel(saved, manager);
|
|
5994
|
+
}
|
|
5995
|
+
printer_default.print(import_picocolors6.default.gray("\nAll tunnels launched. Press Ctrl+C to stop.\n"));
|
|
5996
|
+
await new Promise(() => {
|
|
5997
|
+
});
|
|
5998
|
+
}
|
|
5999
|
+
async function startSavedTunnel(saved, manager) {
|
|
6000
|
+
const config = {
|
|
6001
|
+
...saved.tunnelConfig,
|
|
6002
|
+
configId: saved.configId,
|
|
6003
|
+
name: saved.name,
|
|
6004
|
+
optional: {
|
|
6005
|
+
...saved.tunnelConfig.optional,
|
|
6006
|
+
noTui: true
|
|
6007
|
+
}
|
|
6008
|
+
};
|
|
6009
|
+
try {
|
|
6010
|
+
const tunnel = await manager.createTunnel(config);
|
|
6011
|
+
await manager.startTunnel(tunnel.tunnelid);
|
|
6012
|
+
const urls = await manager.getTunnelUrls(tunnel.tunnelid);
|
|
6013
|
+
printer_default.success(`"${saved.name}" started`);
|
|
6014
|
+
(urls ?? []).forEach(
|
|
6015
|
+
(url) => printer_default.print(" " + import_picocolors6.default.magentaBright(url))
|
|
6016
|
+
);
|
|
6017
|
+
manager.registerWorkerErrorListner(tunnel.tunnelid, (_id, error) => {
|
|
6018
|
+
printer_default.error(`[${saved.name}] Fatal: ${error.message}`);
|
|
6019
|
+
});
|
|
6020
|
+
manager.registerDisconnectListener(tunnel.tunnelid, async (_id, error, messages) => {
|
|
6021
|
+
if (error) printer_default.warn(`[${saved.name}] Disconnected: ${error}`);
|
|
6022
|
+
messages?.forEach((m) => printer_default.warn(`[${saved.name}] ${m}`));
|
|
6023
|
+
});
|
|
6024
|
+
manager.registerReconnectingListener(tunnel.tunnelid, (_id, retryCnt) => {
|
|
6025
|
+
printer_default.print(import_picocolors6.default.gray(`[${saved.name}] Reconnecting (attempt #${retryCnt})...`));
|
|
6026
|
+
});
|
|
6027
|
+
manager.registerReconnectionCompletedListener(tunnel.tunnelid, async (_id, urls2) => {
|
|
6028
|
+
printer_default.success(`[${saved.name}] Reconnected`);
|
|
6029
|
+
(urls2 ?? []).forEach(
|
|
6030
|
+
(url) => printer_default.print(" " + import_picocolors6.default.magentaBright(url))
|
|
6031
|
+
);
|
|
6032
|
+
});
|
|
6033
|
+
manager.registerReconnectionFailedListener(tunnel.tunnelid, (_id, retryCnt) => {
|
|
6034
|
+
printer_default.error(`[${saved.name}] Reconnection failed after ${retryCnt} attempts`);
|
|
6035
|
+
});
|
|
6036
|
+
} catch (err) {
|
|
6037
|
+
printer_default.error(`[${saved.name}] Failed to start: ${err.message || err}`);
|
|
6038
|
+
}
|
|
6039
|
+
}
|
|
6040
|
+
function resolveConfig(nameOrId) {
|
|
6041
|
+
const saved = findConfig(nameOrId);
|
|
6042
|
+
if (!saved) {
|
|
6043
|
+
printer_default.error(`No config found matching "${nameOrId}". Use: pinggy config list`);
|
|
6044
|
+
return null;
|
|
6045
|
+
}
|
|
6046
|
+
return saved;
|
|
6047
|
+
}
|
|
6048
|
+
function requireName(args, command) {
|
|
6049
|
+
if (args.length === 0 || args[0].startsWith("-")) {
|
|
6050
|
+
printer_default.error(`Tunnel name is required. Usage: pinggy ${command} <name>`);
|
|
6051
|
+
process.exit(1);
|
|
6052
|
+
}
|
|
6053
|
+
return args[0];
|
|
6054
|
+
}
|
|
6055
|
+
function requireNames(args, command) {
|
|
6056
|
+
const names = [];
|
|
6057
|
+
for (const arg of args) {
|
|
6058
|
+
if (arg.startsWith("-")) break;
|
|
6059
|
+
names.push(arg);
|
|
6060
|
+
}
|
|
6061
|
+
if (names.length === 0) {
|
|
6062
|
+
printer_default.error(`At least one tunnel name is required. Usage: pinggy ${command} <name> [name2 ...]`);
|
|
6063
|
+
process.exit(1);
|
|
6064
|
+
}
|
|
6065
|
+
return names;
|
|
6066
|
+
}
|
|
6067
|
+
async function initRemoteManagementBackground(values) {
|
|
6068
|
+
const rmToken = values["remote-management"];
|
|
6069
|
+
if (typeof rmToken === "string" && rmToken.trim().length > 0) {
|
|
6070
|
+
const manageHost = values["manage"];
|
|
6071
|
+
try {
|
|
6072
|
+
await startRemoteManagement({
|
|
6073
|
+
apiKey: rmToken,
|
|
6074
|
+
serverUrl: buildRemoteManagementWsUrl(manageHost)
|
|
6075
|
+
});
|
|
6076
|
+
} catch (e) {
|
|
6077
|
+
logger.error("Failed to initiate remote management:", e);
|
|
6078
|
+
printer_default.fatal(e);
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
function printConfigHelp() {
|
|
6083
|
+
console.log("\nUsage: pinggy config <command> [name] [options]\n");
|
|
6084
|
+
console.log("Commands:");
|
|
6085
|
+
console.log(" list List all saved configs");
|
|
6086
|
+
console.log(" show <name> Show config details");
|
|
6087
|
+
console.log(" save <name> [tunnel flags] Save a tunnel config");
|
|
6088
|
+
console.log(" update <name> [tunnel flags] Update a saved config");
|
|
6089
|
+
console.log(" delete <name> Delete a saved config");
|
|
6090
|
+
console.log(" auto <name> Enable auto-start");
|
|
6091
|
+
console.log(" noauto <name> Disable auto-start\n");
|
|
6092
|
+
}
|
|
6093
|
+
function printStartHelp() {
|
|
6094
|
+
console.log("\nUsage: pinggy start <name> [options]\n");
|
|
6095
|
+
console.log("Examples:");
|
|
6096
|
+
console.log(" pinggy start my-tunnel Start a saved tunnel");
|
|
6097
|
+
console.log(" pinggy start my-tunnel -l 4000 Start with override");
|
|
6098
|
+
console.log(" pinggy start tunnela tunnelb Start multiple tunnels");
|
|
6099
|
+
console.log(" pinggy start --all Start all auto-start tunnels\n");
|
|
6100
|
+
}
|
|
6101
|
+
var import_picocolors6, SUBCOMMANDS;
|
|
6102
|
+
var init_subcommands = __esm({
|
|
6103
|
+
"src/cli/subcommands.ts"() {
|
|
6104
|
+
"use strict";
|
|
6105
|
+
init_cjs_shims();
|
|
6106
|
+
init_options();
|
|
6107
|
+
init_parseArgs();
|
|
6108
|
+
init_buildConfig();
|
|
6109
|
+
init_starCli();
|
|
6110
|
+
init_printer();
|
|
6111
|
+
init_logger();
|
|
6112
|
+
import_picocolors6 = __toESM(require("picocolors"), 1);
|
|
6113
|
+
init_configStore();
|
|
6114
|
+
init_remoteManagement();
|
|
6115
|
+
SUBCOMMANDS = /* @__PURE__ */ new Set(["config", "start"]);
|
|
6116
|
+
}
|
|
6117
|
+
});
|
|
6118
|
+
|
|
5495
6119
|
// src/main.ts
|
|
5496
6120
|
var main_exports = {};
|
|
5497
6121
|
__export(main_exports, {
|
|
@@ -5505,16 +6129,23 @@ __export(main_exports, {
|
|
|
5505
6129
|
});
|
|
5506
6130
|
async function main() {
|
|
5507
6131
|
try {
|
|
5508
|
-
const
|
|
5509
|
-
configureLogger(values);
|
|
6132
|
+
const rawArgs = process.argv.slice(2);
|
|
5510
6133
|
const manager = TunnelManager.getInstance();
|
|
5511
|
-
|
|
5512
|
-
logger.info(
|
|
6134
|
+
const gracefulShutdown = (signal) => {
|
|
6135
|
+
logger.info(`${signal} received: stopping tunnels and exiting`);
|
|
5513
6136
|
console.log("\nStopping all tunnels...");
|
|
5514
6137
|
manager.stopAllTunnels();
|
|
5515
6138
|
console.log("Tunnels stopped. Exiting.");
|
|
5516
6139
|
process.exit(0);
|
|
5517
|
-
}
|
|
6140
|
+
};
|
|
6141
|
+
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
6142
|
+
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
|
|
6143
|
+
if (isSubcommand(rawArgs)) {
|
|
6144
|
+
await handleSubcommand(rawArgs, manager);
|
|
6145
|
+
return;
|
|
6146
|
+
}
|
|
6147
|
+
const { values, positionals, hasAnyArgs } = parseCliArgs(cliOptions);
|
|
6148
|
+
configureLogger(values);
|
|
5518
6149
|
if (!hasAnyArgs || values.help) {
|
|
5519
6150
|
printHelpMessage();
|
|
5520
6151
|
return;
|
|
@@ -5523,21 +6154,13 @@ async function main() {
|
|
|
5523
6154
|
printer_default.print(`Pinggy CLI version: ${getVersion()}`);
|
|
5524
6155
|
return;
|
|
5525
6156
|
}
|
|
5526
|
-
|
|
5527
|
-
if (parseResult?.ok === false) {
|
|
5528
|
-
logger.error("Failed to initiate remote management:", parseResult.error);
|
|
5529
|
-
printer_default.fatal(parseResult.error);
|
|
5530
|
-
}
|
|
5531
|
-
logger.debug("Building final config from CLI values and positionals", { values, positionals });
|
|
5532
|
-
const finalConfig = await buildFinalConfig(values, positionals);
|
|
5533
|
-
logger.debug("Final configuration built", finalConfig);
|
|
5534
|
-
await startCli(finalConfig, manager);
|
|
6157
|
+
await buildAndStartTunnel(values, positionals, manager);
|
|
5535
6158
|
} catch (error) {
|
|
5536
6159
|
logger.error("Unhandled error in CLI:", error);
|
|
5537
6160
|
printer_default.fatal(error);
|
|
5538
6161
|
}
|
|
5539
6162
|
}
|
|
5540
|
-
var import_url2, import_process,
|
|
6163
|
+
var import_url2, import_process, import_fs7, currentFile, entryFile;
|
|
5541
6164
|
var init_main = __esm({
|
|
5542
6165
|
"src/main.ts"() {
|
|
5543
6166
|
"use strict";
|
|
@@ -5545,23 +6168,22 @@ var init_main = __esm({
|
|
|
5545
6168
|
init_TunnelManager();
|
|
5546
6169
|
init_help();
|
|
5547
6170
|
init_options();
|
|
5548
|
-
init_buildConfig();
|
|
5549
6171
|
init_logger();
|
|
5550
|
-
init_remoteManagement();
|
|
5551
6172
|
init_parseArgs();
|
|
5552
6173
|
init_printer();
|
|
5553
|
-
init_starCli();
|
|
5554
6174
|
init_util();
|
|
5555
6175
|
init_handler();
|
|
5556
6176
|
import_url2 = require("url");
|
|
5557
6177
|
import_process = require("process");
|
|
5558
|
-
|
|
6178
|
+
import_fs7 = require("fs");
|
|
5559
6179
|
init_logger();
|
|
5560
6180
|
init_remoteManagement();
|
|
6181
|
+
init_buildAndStartTunnel();
|
|
6182
|
+
init_subcommands();
|
|
5561
6183
|
currentFile = (0, import_url2.fileURLToPath)(importMetaUrl);
|
|
5562
6184
|
entryFile = null;
|
|
5563
6185
|
try {
|
|
5564
|
-
entryFile = import_process.argv[1] ? (0,
|
|
6186
|
+
entryFile = import_process.argv[1] ? (0, import_fs7.realpathSync)(import_process.argv[1]) : null;
|
|
5565
6187
|
} catch (e) {
|
|
5566
6188
|
entryFile = null;
|
|
5567
6189
|
}
|