pinggy 0.3.8 → 0.3.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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { PinggyOptions, TunnelUsageType, TunnelInstance, TunnelType } from '@pinggy/pinggy';
2
+ import { TunnelConfigurationV1, TunnelUsageType, TunnelInstance, TunnelType, RemoteManagementConfig } from '@pinggy/pinggy';
3
3
  import { Worker } from 'node:worker_threads';
4
4
  import { z } from 'zod';
5
5
  import winston from 'winston';
@@ -14,7 +14,7 @@ interface AdditionalForwarding {
14
14
  interface TunnelStatus {
15
15
  tunnelid: string;
16
16
  remoteurls: string[];
17
- tunnelconfig: PinggyOptions;
17
+ tunnelconfig: TunnelConfigurationV1;
18
18
  status: Status;
19
19
  stats: TunnelUsageType;
20
20
  }
@@ -49,20 +49,11 @@ interface Status {
49
49
  starttimestamp: string;
50
50
  endtimestamp: string;
51
51
  warnings: Warning[];
52
+ lastError?: lastError;
52
53
  }
53
- type FinalConfig = (PinggyOptions & {
54
- configid: string;
55
- }) & {
56
- tunnelType: string[];
54
+ type FinalConfig = (TunnelConfigurationV1) & {
57
55
  conf?: string;
58
56
  saveconf?: string;
59
- serve?: string;
60
- remoteManagement?: string;
61
- additionalForwarding?: AdditionalForwarding[];
62
- manage?: string;
63
- version?: boolean;
64
- NoTUI?: boolean;
65
- qrCode?: boolean;
66
57
  };
67
58
  type ErrorCodeType = "INVALID_REQUEST_METHOD" | "COULD_NOT_READ_BODY" | "INTERNAL_SERVER_ERROR" | "INVALID_DATA_FORMAT" | "ERROR_STARTING_TUNNEL" | "TUNNEL_WITH_ID_OR_CONFIG_ID_NOT_FOUND" | "TUNNEL_WITH_ID_OR_CONFIG_ID_ALREADY_RUNNING" | "WEBSOCKET_UPGRADE_FAILED" | "REMOTE_MANAGEMENT_ALREADY_RUNNING" | "REMOTE_MANAGEMENT_NOT_RUNNING" | "REMOTE_MANAGEMENT_DESERIALIZATION_FAILED";
68
59
  interface ErrorResponse {
@@ -93,12 +84,10 @@ interface RemoteManagementState {
93
84
 
94
85
  interface ManagedTunnel {
95
86
  tunnelid: string;
96
- configid: string;
87
+ configId: string;
97
88
  tunnelName?: string;
98
89
  instance: TunnelInstance;
99
- tunnelConfig?: PinggyOptions;
100
- configWithForwarding?: PinggyOptions;
101
- additionalForwarding?: AdditionalForwarding[];
90
+ tunnelConfig?: TunnelConfigurationV1;
102
91
  serveWorker?: Worker | null;
103
92
  warnings?: Warning[];
104
93
  serve?: string;
@@ -107,18 +96,33 @@ interface ManagedTunnel {
107
96
  startedAt?: string | null;
108
97
  stoppedAt?: string | null;
109
98
  autoReconnect?: boolean;
99
+ lastError: lastError;
100
+ }
101
+ interface lastError {
102
+ message: string;
103
+ timestamp: string;
104
+ isFatal: boolean;
110
105
  }
111
106
  interface TunnelList {
112
107
  tunnelid: string;
113
- configid: string;
108
+ configId: string;
114
109
  tunnelName?: string;
115
- tunnelConfig: PinggyOptions;
110
+ tunnelConfig: TunnelConfigurationV1;
116
111
  remoteurls: string[];
117
- additionalForwarding?: AdditionalForwarding[];
112
+ serve?: string;
113
+ }
114
+ interface TunnelCreationConfig extends TunnelConfigurationV1 {
115
+ tunnelid?: string;
116
+ tunnelName?: string;
117
+ serve?: string;
118
+ }
119
+ interface TunnelUpdateConfig extends TunnelConfigurationV1 {
120
+ tunnelName?: string;
118
121
  serve?: string;
119
122
  }
120
123
  type StatsListener = (tunnelId: string, stats: TunnelUsageType) => void;
121
124
  type ErrorListener = (tunnelId: string, errorMsg: string, isFatal: boolean) => void;
125
+ type PollingErrorListener = (tunnelId: string, errorMsg: string) => void;
122
126
  type DisconnectListener = (tunnelId: string, error: string, messages: string[]) => void;
123
127
  type TunnelWorkerErrorListner = (tunnelid: string, error: Error) => void;
124
128
  type StartListener = (tunnelId: string, urls: string[]) => void;
@@ -127,16 +131,10 @@ type ReconnectingListener = (tunnelId: string, retryCnt: number) => void;
127
131
  type ReconnectionCompletedListener = (tunnelId: string, urls: string[]) => void;
128
132
  type ReconnectionFailedListener = (tunnelId: string, retryCnt: number) => void;
129
133
  interface ITunnelManager {
130
- createTunnel(config: (PinggyOptions & {
131
- configid: string;
132
- tunnelid?: string;
133
- tunnelName?: string;
134
- }) & {
135
- additionalForwarding?: AdditionalForwarding[];
136
- }): Promise<ManagedTunnel>;
134
+ createTunnel(config: TunnelCreationConfig, buildConfig?: boolean): Promise<ManagedTunnel>;
137
135
  startTunnel(tunnelId: string): Promise<string[]>;
138
136
  stopTunnel(tunnelId: string): {
139
- configid: string;
137
+ configId: string;
140
138
  tunnelid: string;
141
139
  };
142
140
  stopAllTunnels(): void;
@@ -144,22 +142,20 @@ interface ITunnelManager {
144
142
  getAllTunnels(): Promise<TunnelList[]>;
145
143
  getTunnelStatus(tunnelId: string): Promise<string>;
146
144
  getTunnelInstance(configId?: string, tunnelId?: string): TunnelInstance;
147
- getTunnelConfig(configId?: string, tunnelId?: string): Promise<PinggyOptions>;
145
+ getTunnelConfig(configId?: string, tunnelId?: string): Promise<TunnelConfigurationV1>;
148
146
  restartTunnel(tunnelId: string): Promise<void>;
149
- updateConfig(newConfig: PinggyOptions & {
150
- configid: string;
151
- additionalForwarding?: AdditionalForwarding[];
152
- tunnelName?: string;
153
- }): Promise<ManagedTunnel>;
147
+ updateConfig(newConfig: TunnelUpdateConfig, buildConfig: boolean): Promise<ManagedTunnel>;
154
148
  getManagedTunnel(configId?: string, tunnelId?: string): ManagedTunnel;
155
149
  getTunnelGreetMessage(tunnelId: string): Promise<string | null>;
156
150
  getTunnelStats(tunnelId: string): TunnelUsageType[] | null;
157
151
  getLatestTunnelStats(tunnelId: string): TunnelUsageType | null;
158
152
  registerStatsListener(tunnelId: string, listener: StatsListener): Promise<[string, string]>;
159
153
  registerErrorListener(tunnelId: string, listener: ErrorListener): Promise<string>;
154
+ registerPollingErrorListener(tunnelId: string, listener: PollingErrorListener): Promise<string>;
160
155
  registerWorkerErrorListner(tunnelId: string, listener: TunnelWorkerErrorListner): void;
161
156
  registerStartListener(tunnelId: string, listener: StartListener): Promise<string>;
162
157
  deregisterErrorListener(tunnelId: string, listenerId: string): void;
158
+ deregisterPollingErrorListener(tunnelId: string, listenerId: string): void;
163
159
  registerDisconnectListener(tunnelId: string, listener: DisconnectListener): Promise<string>;
164
160
  deregisterDisconnectListener(tunnelId: string, listenerId: string): void;
165
161
  deregisterStatsListener(tunnelId: string, listenerId: string): void;
@@ -182,6 +178,7 @@ declare class TunnelManager implements ITunnelManager {
182
178
  private tunnelStats;
183
179
  private tunnelStatsListeners;
184
180
  private tunnelErrorListeners;
181
+ private tunnelPollingErrorListeners;
185
182
  private tunnelDisconnectListeners;
186
183
  private tunnelWorkerErrorListeners;
187
184
  private tunnelStartListeners;
@@ -193,12 +190,9 @@ declare class TunnelManager implements ITunnelManager {
193
190
  static getInstance(): TunnelManager;
194
191
  /**
195
192
  * Creates a new managed tunnel instance with the given configuration.
196
- * Builds the config with forwarding rules and creates the tunnel instance.
193
+ * Optionally builds the config with forwarding rules based on buildConfig flag.
197
194
  *
198
195
  * @param config - The tunnel configuration options
199
- * @param config.configid - Unique identifier for the tunnel configuration
200
- * @param config.tunnelid - Optional custom tunnel identifier. If not provided, a random UUID will be generated
201
- * @param config.additionalForwarding - Optional array of additional forwarding configurations
202
196
  *
203
197
  * @throws {Error} When configId is invalid or empty
204
198
  * @throws {Error} When a tunnel with the given configId already exists
@@ -206,17 +200,7 @@ declare class TunnelManager implements ITunnelManager {
206
200
  * @returns {ManagedTunnel} A new managed tunnel instance containing the tunnel details,
207
201
  * status information, and statistics
208
202
  */
209
- createTunnel(config: (PinggyOptions & {
210
- tunnelType: string[] | undefined;
211
- } & {
212
- configid: string;
213
- tunnelid?: string;
214
- tunnelName?: string;
215
- }) & {
216
- additionalForwarding?: AdditionalForwarding[];
217
- } & {
218
- serve?: string;
219
- }): Promise<ManagedTunnel>;
203
+ createTunnel(config: TunnelCreationConfig): Promise<ManagedTunnel>;
220
204
  /**
221
205
  * Internal method to create a tunnel with an already-processed configuration.
222
206
  * This is used by createTunnel, restartTunnel, and updateConfig to avoid config processing.
@@ -226,15 +210,6 @@ declare class TunnelManager implements ITunnelManager {
226
210
  * @private
227
211
  */
228
212
  private _createTunnelWithProcessedConfig;
229
- /**
230
- * Builds the Pinggy configuration by merging the default forwarding rule
231
- * with additional forwarding rules from additionalForwarding array.
232
- *
233
- * @param config - The base Pinggy configuration
234
- * @param additionalForwarding - Optional array of additional forwarding rules
235
- * @returns Modified PinggyOptions
236
- */
237
- private buildPinggyConfig;
238
213
  /**
239
214
  * Start a tunnel that was created but not yet started
240
215
  */
@@ -250,7 +225,7 @@ declare class TunnelManager implements ITunnelManager {
250
225
  * - Logs the stop operation with tunnelId and configId
251
226
  */
252
227
  stopTunnel(tunnelId: string): {
253
- configid: string;
228
+ configId: string;
254
229
  tunnelid: string;
255
230
  };
256
231
  /**
@@ -300,7 +275,7 @@ declare class TunnelManager implements ITunnelManager {
300
275
  * @returns The tunnel config
301
276
  * @throws Error if neither configId nor tunnelId is provided, or if tunnel is not found
302
277
  */
303
- getTunnelConfig(configId?: string, tunnelId?: string): Promise<PinggyOptions>;
278
+ getTunnelConfig(configId?: string, tunnelId?: string): Promise<TunnelConfigurationV1>;
304
279
  /**
305
280
  * Restarts a tunnel with its current configuration.
306
281
  * This function will stop the tunnel if it's running and start it again.
@@ -319,14 +294,7 @@ declare class TunnelManager implements ITunnelManager {
319
294
  * @returns Promise resolving to the updated ManagedTunnel
320
295
  * @throws Error if the tunnel is not found or if the update process fails
321
296
  */
322
- updateConfig(newConfig: PinggyOptions & {
323
- tunnelType: string[] | undefined;
324
- } & {
325
- configid: string;
326
- additionalForwarding?: AdditionalForwarding[];
327
- tunnelName?: string;
328
- serve?: string;
329
- }): Promise<ManagedTunnel>;
297
+ updateConfig(newConfig: TunnelUpdateConfig): Promise<ManagedTunnel>;
330
298
  /**
331
299
  * Retrieve the ManagedTunnel object by either configId or tunnelId.
332
300
  * Throws an error if neither id is provided or the tunnel is not found.
@@ -347,6 +315,7 @@ declare class TunnelManager implements ITunnelManager {
347
315
  */
348
316
  registerStatsListener(tunnelId: string, listener: StatsListener): Promise<[string, string]>;
349
317
  registerErrorListener(tunnelId: string, listener: ErrorListener): Promise<string>;
318
+ registerPollingErrorListener(tunnelId: string, listener: PollingErrorListener): Promise<string>;
350
319
  registerDisconnectListener(tunnelId: string, listener: DisconnectListener): Promise<string>;
351
320
  registerWorkerErrorListner(tunnelId: string, listener: TunnelWorkerErrorListner): Promise<void>;
352
321
  registerStartListener(tunnelId: string, listener: StartListener): Promise<string>;
@@ -362,6 +331,7 @@ declare class TunnelManager implements ITunnelManager {
362
331
  */
363
332
  deregisterStatsListener(tunnelId: string, listenerId: string): void;
364
333
  deregisterErrorListener(tunnelId: string, listenerId: string): void;
334
+ deregisterPollingErrorListener(tunnelId: string, listenerId: string): void;
365
335
  deregisterDisconnectListener(tunnelId: string, listenerId: string): void;
366
336
  deregisterWillReconnectListener(tunnelId: string, listenerId: string): void;
367
337
  deregisterReconnectingListener(tunnelId: string, listenerId: string): void;
@@ -373,6 +343,8 @@ declare class TunnelManager implements ITunnelManager {
373
343
  * This callback will update stored stats and notify all registered listeners.
374
344
  */
375
345
  private setupStatsCallback;
346
+ private setupTunnelPollingErrorCallback;
347
+ private notifyPollingErrorListeners;
376
348
  private notifyErrorListeners;
377
349
  private setupErrorCallback;
378
350
  private setupDisconnectCallback;
@@ -417,7 +389,7 @@ declare const TunnelConfigSchema: z.ZodPipe<z.ZodObject<{
417
389
  username: z.ZodString;
418
390
  password: z.ZodString;
419
391
  }, z.core.$strip>>>;
420
- bearerauth: z.ZodNullable<z.ZodString>;
392
+ bearerauth: z.ZodNullable<z.ZodArray<z.ZodString>>;
421
393
  configid: z.ZodString;
422
394
  configname: z.ZodString;
423
395
  greetmsg: z.ZodOptional<z.ZodString>;
@@ -426,7 +398,7 @@ declare const TunnelConfigSchema: z.ZodPipe<z.ZodObject<{
426
398
  fullRequestUrl: z.ZodBoolean;
427
399
  headermodification: z.ZodArray<z.ZodObject<{
428
400
  key: z.ZodString;
429
- value: z.ZodOptional<z.ZodArray<z.ZodString>>;
401
+ value: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
430
402
  type: z.ZodEnum<{
431
403
  add: "add";
432
404
  remove: "remove";
@@ -470,7 +442,7 @@ declare const TunnelConfigSchema: z.ZodPipe<z.ZodObject<{
470
442
  username: string;
471
443
  password: string;
472
444
  }[] | null;
473
- bearerauth: string | null;
445
+ bearerauth: string[] | null;
474
446
  configid: string;
475
447
  configname: string;
476
448
  force: boolean;
@@ -479,7 +451,7 @@ declare const TunnelConfigSchema: z.ZodPipe<z.ZodObject<{
479
451
  headermodification: {
480
452
  key: string;
481
453
  type: "add" | "remove" | "update";
482
- value?: string[] | undefined;
454
+ value?: string[] | null | undefined;
483
455
  }[];
484
456
  httpsOnly: boolean;
485
457
  internalwebdebuggerport: number;
@@ -511,7 +483,7 @@ declare const TunnelConfigSchema: z.ZodPipe<z.ZodObject<{
511
483
  username: string;
512
484
  password: string;
513
485
  }[] | null;
514
- bearerauth: string | null;
486
+ bearerauth: string[] | null;
515
487
  configid: string;
516
488
  configname: string;
517
489
  force: boolean;
@@ -520,7 +492,7 @@ declare const TunnelConfigSchema: z.ZodPipe<z.ZodObject<{
520
492
  headermodification: {
521
493
  key: string;
522
494
  type: "add" | "remove" | "update";
523
- value?: string[] | undefined;
495
+ value?: string[] | null | undefined;
524
496
  }[];
525
497
  httpsOnly: boolean;
526
498
  internalwebdebuggerport: number;
@@ -550,6 +522,56 @@ declare const TunnelConfigSchema: z.ZodPipe<z.ZodObject<{
550
522
  serve?: string | undefined;
551
523
  }>>;
552
524
  type TunnelConfig = z.infer<typeof TunnelConfigSchema>;
525
+ /**
526
+ * V1 Tunnel Config Schema
527
+ */
528
+ declare const TunnelConfigV1Schema: z.ZodObject<{
529
+ version: z.ZodString;
530
+ name: z.ZodString;
531
+ configId: z.ZodString;
532
+ serverAddress: z.ZodOptional<z.ZodString>;
533
+ token: z.ZodOptional<z.ZodString>;
534
+ autoReconnect: z.ZodOptional<z.ZodBoolean>;
535
+ reconnectInterval: z.ZodOptional<z.ZodNumber>;
536
+ maxReconnectAttempts: z.ZodOptional<z.ZodNumber>;
537
+ force: z.ZodBoolean;
538
+ keepAliveInterval: z.ZodOptional<z.ZodNumber>;
539
+ webDebugger: z.ZodString;
540
+ forwarding: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodObject<{
541
+ listenAddress: z.ZodOptional<z.ZodString>;
542
+ address: z.ZodString;
543
+ type: z.ZodOptional<z.ZodEnum<{
544
+ http: TunnelType.Http;
545
+ tcp: TunnelType.Tcp;
546
+ tls: TunnelType.Tls;
547
+ udp: TunnelType.Udp;
548
+ tlstcp: TunnelType.TlsTcp;
549
+ }>>;
550
+ }, z.core.$strip>>]>;
551
+ ipWhitelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
552
+ basicAuth: z.ZodOptional<z.ZodArray<z.ZodObject<{
553
+ username: z.ZodString;
554
+ password: z.ZodString;
555
+ }, z.core.$strip>>>;
556
+ bearerTokenAuth: z.ZodOptional<z.ZodArray<z.ZodString>>;
557
+ headerModification: z.ZodOptional<z.ZodArray<z.ZodObject<{
558
+ key: z.ZodString;
559
+ value: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
560
+ type: z.ZodEnum<{
561
+ add: "add";
562
+ remove: "remove";
563
+ update: "update";
564
+ }>;
565
+ }, z.core.$strip>>>;
566
+ reverseProxy: z.ZodOptional<z.ZodBoolean>;
567
+ xForwardedFor: z.ZodOptional<z.ZodBoolean>;
568
+ httpsOnly: z.ZodOptional<z.ZodBoolean>;
569
+ originalRequestUrl: z.ZodOptional<z.ZodBoolean>;
570
+ allowPreflight: z.ZodOptional<z.ZodBoolean>;
571
+ serve: z.ZodOptional<z.ZodString>;
572
+ optional: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
573
+ }, z.core.$strip>;
574
+ type TunnelConfigV1 = z.infer<typeof TunnelConfigV1Schema>;
553
575
 
554
576
  interface TunnelResponse {
555
577
  tunnelid: string;
@@ -558,10 +580,21 @@ interface TunnelResponse {
558
580
  status: Status;
559
581
  stats: TunnelUsageType;
560
582
  }
583
+ interface TunnelResponseV2 {
584
+ tunnelid: string;
585
+ remoteurls: string[];
586
+ tunnelconfig: TunnelConfigV1;
587
+ status: Status;
588
+ stats: TunnelUsageType;
589
+ greetmsg?: string;
590
+ }
561
591
  interface TunnelHandler {
562
592
  handleStart(config: TunnelConfig): Promise<TunnelResponse | ErrorResponse>;
593
+ handleStartV2(config: TunnelConfigV1): Promise<TunnelResponseV2 | ErrorResponse>;
563
594
  handleUpdateConfig(config: TunnelConfig): Promise<TunnelResponse | ErrorResponse>;
595
+ handleUpdateConfigV2(config: TunnelConfigV1): Promise<TunnelResponseV2 | ErrorResponse>;
564
596
  handleList(): Promise<TunnelResponse[] | ErrorResponse>;
597
+ handleListV2(): Promise<TunnelResponseV2[] | ErrorResponse>;
565
598
  handleStop(tunnelid: string): Promise<TunnelResponse | ErrorResponse>;
566
599
  handleGet(tunnelid: string): Promise<TunnelResponse | ErrorResponse>;
567
600
  handleRestart(tunnelid: string): Promise<TunnelResponse | ErrorResponse>;
@@ -577,9 +610,13 @@ declare class TunnelOperations implements TunnelHandler {
577
610
  constructor();
578
611
  private buildStatus;
579
612
  private buildTunnelResponse;
613
+ private buildTunnelResponseV2;
580
614
  private error;
581
615
  handleStart(config: TunnelConfig): Promise<TunnelResponse | ErrorResponse>;
616
+ handleStartV2(config: TunnelConfigV1): Promise<TunnelResponseV2 | ErrorResponse>;
582
617
  handleUpdateConfig(config: TunnelConfig): Promise<TunnelResponse | ErrorResponse>;
618
+ handleUpdateConfigV2(config: TunnelConfigV1): Promise<TunnelResponseV2 | ErrorResponse>;
619
+ handleListV2(): Promise<TunnelResponseV2[] | ErrorResponse>;
583
620
  handleList(): Promise<TunnelResponse[] | ErrorResponse>;
584
621
  handleStop(tunnelid: string): Promise<TunnelResponse | ErrorResponse>;
585
622
  handleGet(tunnelid: string): Promise<TunnelResponse | ErrorResponse>;
@@ -610,7 +647,7 @@ declare function enablePackageLogging(opts?: BaseLogConfigType): winston.Logger;
610
647
  * - On other failures: retry every 15 seconds
611
648
  * - Keep running until closed or SIGINT
612
649
  */
613
- declare function initiateRemoteManagement(token: string, manage?: string): Promise<RemoteManagementState>;
650
+ declare function initiateRemoteManagement(remoteManagementConfig: RemoteManagementConfig): Promise<RemoteManagementState>;
614
651
  declare function closeRemoteManagement(timeoutMs?: number): Promise<RemoteManagementState>;
615
652
  declare function getRemoteManagementState(): RemoteManagementState;
616
653
 
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  getRemoteManagementState,
10
10
  initiateRemoteManagement,
11
11
  printer_default
12
- } from "./chunk-65R2GMKQ.js";
12
+ } from "./chunk-MBN3YBO4.js";
13
13
  import {
14
14
  enablePackageLogging
15
15
  } from "./chunk-HUN2MRZO.js";
@@ -104,7 +104,7 @@ async function verifyAndLoad() {
104
104
  process.exit(1);
105
105
  }
106
106
  }
107
- await import("./main-2QDG7PWL.js");
107
+ await import("./main-VCUAV22W.js");
108
108
  }
109
109
  verifyAndLoad().catch((err) => {
110
110
  printer_default.error(`Failed to start CLI:, ${err}`);
@@ -10,7 +10,7 @@ import {
10
10
  isValidPort,
11
11
  parseRemoteManagement,
12
12
  printer_default
13
- } from "./chunk-65R2GMKQ.js";
13
+ } from "./chunk-MBN3YBO4.js";
14
14
  import {
15
15
  configureLogger,
16
16
  enablePackageLogging,
@@ -36,6 +36,7 @@ var cliOptions = {
36
36
  localport: { type: "string", short: "l", description: "Takes input as [protocol:][host:]port. Eg. --localport https://localhost:8000 OR -l 3000" },
37
37
  debugger: { type: "string", short: "d", description: "Port for web debugger. Eg. --debugger 4300 OR -d 4300" },
38
38
  token: { type: "string", description: "Token for authentication. Eg. --token TOKEN_VALUE" },
39
+ force: { type: "boolean", short: "f", description: "Forcefully close existing tunnels and establish a new tunnel" },
39
40
  // Logging options (CLI overrides env)
40
41
  loglevel: { type: "string", description: "Logging level: ERROR, INFO, DEBUG. Overrides PINGGY_LOG_LEVEL environment variable" },
41
42
  logfile: { type: "string", description: "Path to log file. Overrides PINGGY_LOG_FILE environment variable" },
@@ -51,7 +52,8 @@ var cliOptions = {
51
52
  // Remote Control
52
53
  "remote-management": { type: "string", description: "Enable remote management of tunnels with token. Eg. --remote-management API_KEY" },
53
54
  manage: { type: "string", description: "Provide a server address to manage tunnels. Eg --manage dashboard.pinggy.io" },
54
- notui: { type: "boolean", description: "Disable TUI in remote management mode" },
55
+ noTui: { type: "boolean", description: "Disable TUI in remote management mode" },
56
+ notui: { type: "boolean", description: "hidden", hidden: true },
55
57
  // Misc
56
58
  version: { type: "boolean", description: "Print version" },
57
59
  // Help
@@ -117,8 +119,8 @@ var defaultOptions = {
117
119
 
118
120
  // src/cli/extendedOptions.ts
119
121
  import { isIP } from "net";
120
- function parseExtendedOptions(options, config) {
121
- if (!options) return;
122
+ function parseExtendedOptions(options, config, localServerTls) {
123
+ if (!options) return localServerTls;
122
124
  for (const opt of options) {
123
125
  const [key, value] = opt.replace(/^"|"$/g, "").split(/:(.+)/).filter(Boolean);
124
126
  switch (key) {
@@ -142,10 +144,16 @@ function parseExtendedOptions(options, config) {
142
144
  case "fullrequesturl":
143
145
  config.originalRequestUrl = true;
144
146
  break;
145
- default:
146
- printer_default.warn(`Unknown extended option "${key}"`);
147
- logger.warn(`Warning: Unknown extended option "${key}"`);
147
+ default: {
148
+ if (value && (value.startsWith("localServerTls") || value.startsWith("localservertls"))) {
149
+ const parts = value.split(/:(.+)/);
150
+ localServerTls = parts[1] ? parts[1] : "";
151
+ } else {
152
+ printer_default.warn(`Unknown extended option "${value}"`);
153
+ logger.warn(`Warning: Unknown extended option "${value}"`);
154
+ }
148
155
  break;
156
+ }
149
157
  }
150
158
  break;
151
159
  case "w":
@@ -216,6 +224,7 @@ function parseExtendedOptions(options, config) {
216
224
  break;
217
225
  }
218
226
  }
227
+ return localServerTls;
219
228
  }
220
229
  function isValidIpV4Cidr(input) {
221
230
  if (input.includes("/")) {
@@ -338,9 +347,9 @@ function parseUsers(positionalArgs, explicitToken) {
338
347
  return { token, server, type, forceFlag, qrCode, remaining };
339
348
  }
340
349
  function parseType(finalConfig, values, inferredType) {
341
- const t = inferredType || values.type || finalConfig.tunnelType;
350
+ const t = inferredType || values.type;
342
351
  if (t === TunnelType.Http || t === TunnelType.Tcp || t === TunnelType.Tls || t === TunnelType.Udp || t === TunnelType.TlsTcp) {
343
- finalConfig.tunnelType = [t];
352
+ return t;
344
353
  }
345
354
  }
346
355
  function parseLocalPort(finalConfig, values) {
@@ -475,15 +484,14 @@ function parseAdditionalForwarding(forwarding) {
475
484
  return new Error("forwarding address incorrect: invalid local port");
476
485
  }
477
486
  return {
478
- protocol,
479
- remoteDomain: remoteDomainRaw,
480
- remotePort,
481
- localDomain,
482
- localPort
487
+ type: protocol,
488
+ listenAddress: `${remoteDomainRaw}:${remotePort}`,
489
+ address: `${localDomain}:${localPort}`
483
490
  };
484
491
  }
485
- function parseReverseTunnelAddr(finalConfig, values) {
492
+ function parseReverseTunnelAddr(finalConfig, values, primaryType) {
486
493
  const reverseTunnel = values.R;
494
+ let forwardingData = [];
487
495
  if ((!Array.isArray(reverseTunnel) || reverseTunnel.length === 0) && !values.localport && !finalConfig.forwarding) {
488
496
  return new Error("local port not specified. Please use '-h' option for help.");
489
497
  }
@@ -495,18 +503,21 @@ function parseReverseTunnelAddr(finalConfig, values) {
495
503
  if (slicedForwarding.length === 3) {
496
504
  const parsed = parseDefaultForwarding(forwarding);
497
505
  if (parsed instanceof Error) return parsed;
498
- finalConfig.forwarding = `${parsed.localDomain}:${parsed.localPort}`;
506
+ forwardingData.push({
507
+ address: `${parsed.localDomain}:${parsed.localPort}`,
508
+ type: primaryType || TunnelType.Http
509
+ });
499
510
  } else if (slicedForwarding.length === 4) {
500
- finalConfig.additionalForwarding ?? (finalConfig.additionalForwarding = []);
501
511
  const parsed = parseAdditionalForwarding(forwarding);
502
512
  if (parsed instanceof Error) return parsed;
503
- finalConfig.additionalForwarding.push(parsed);
513
+ forwardingData.push(parsed);
504
514
  } else {
505
515
  return new Error(
506
516
  "Incorrect command line arguments: reverse tunnel address incorrect. Please use '-h' option for help."
507
517
  );
508
518
  }
509
519
  }
520
+ finalConfig.forwarding = forwardingData;
510
521
  return null;
511
522
  }
512
523
  function parseLocalTunnelAddr(finalConfig, values) {
@@ -542,7 +553,13 @@ function parseToken(finalConfig, explicitToken) {
542
553
  }
543
554
  }
544
555
  function parseArgs(finalConfig, remainingPositionals) {
545
- parseExtendedOptions(remainingPositionals, finalConfig);
556
+ let localserverTls = "";
557
+ localserverTls = parseExtendedOptions(remainingPositionals, finalConfig, localserverTls);
558
+ if (localserverTls.length > 0 && finalConfig.forwarding) {
559
+ if (typeof finalConfig.forwarding[0] === "object" && "address" in finalConfig.forwarding[0]) {
560
+ finalConfig.forwarding[0].address = `https://${finalConfig.forwarding[0].address}`;
561
+ }
562
+ }
546
563
  }
547
564
  function storeJson(config, saveconf) {
548
565
  if (saveconf) {
@@ -580,7 +597,7 @@ function isSaveConfOption(values) {
580
597
  function parseServe(finalConfig, values) {
581
598
  const sv = values.serve;
582
599
  if (typeof sv !== "string" || sv.trim().length === 0) return null;
583
- finalConfig.serve = sv;
600
+ finalConfig.optional.serve = sv;
584
601
  return null;
585
602
  }
586
603
  function parseAutoReconnect(finalConfig, values) {
@@ -618,21 +635,23 @@ async function buildFinalConfig(values, positionals) {
618
635
  ...defaultOptions,
619
636
  ...configFromFile || {},
620
637
  // Apply loaded config on top of defaults
621
- configid: getRandomId(),
638
+ configId: getRandomId(),
622
639
  token: token || (configFromFile?.token || (typeof values.token === "string" ? values.token : "")),
623
640
  serverAddress: server || (configFromFile?.serverAddress || defaultOptions.serverAddress),
624
- tunnelType: initialTunnel ? [initialTunnel] : configFromFile?.tunnelType || [TunnelType.Http],
625
- NoTUI: values.notui || (configFromFile?.NoTUI || false),
626
- qrCode: qrCode || (configFromFile?.qrCode || false),
627
- autoReconnect: configFromFile?.autoReconnect ? configFromFile.autoReconnect : defaultOptions.autoReconnect
641
+ isQRCode: qrCode || (configFromFile?.isQRCode || false),
642
+ autoReconnect: configFromFile?.autoReconnect ? configFromFile.autoReconnect : defaultOptions.autoReconnect,
643
+ optional: {
644
+ serve: configFromFile?.optional?.serve || void 0,
645
+ noTui: values.noTui || values.notui || (configFromFile?.optional?.noTui || false)
646
+ }
628
647
  };
629
- parseType(finalConfig, values, type);
648
+ type = parseType(finalConfig, values, type);
630
649
  parseToken(finalConfig, token || values.token);
631
650
  const dbgErr = parseDebugger(finalConfig, values);
632
651
  if (dbgErr instanceof Error) throw dbgErr;
633
652
  const lpErr = parseLocalPort(finalConfig, values);
634
653
  if (lpErr instanceof Error) throw lpErr;
635
- const rErr = parseReverseTunnelAddr(finalConfig, values);
654
+ const rErr = parseReverseTunnelAddr(finalConfig, values, type);
636
655
  if (rErr instanceof Error) throw rErr;
637
656
  const lErr = parseLocalTunnelAddr(finalConfig, values);
638
657
  if (lErr instanceof Error) throw lErr;
@@ -640,7 +659,7 @@ async function buildFinalConfig(values, positionals) {
640
659
  if (serveErr instanceof Error) throw serveErr;
641
660
  const autoReconnectErr = parseAutoReconnect(finalConfig, values);
642
661
  if (autoReconnectErr instanceof Error) throw autoReconnectErr;
643
- if (forceFlag) finalConfig.force = true;
662
+ if (forceFlag || values.force) finalConfig.force = true;
644
663
  parseArgs(finalConfig, remainingPositionals);
645
664
  storeJson(finalConfig, saveconf);
646
665
  return finalConfig;
@@ -2026,7 +2045,7 @@ async function launchTui(finalConfig, urls, greet, tunnel) {
2026
2045
  }
2027
2046
  }
2028
2047
  async function startCli(finalConfig, manager) {
2029
- if (!finalConfig.NoTUI && finalConfig.webDebugger === "") {
2048
+ if (!finalConfig.optional?.noTui && finalConfig.webDebugger === "") {
2030
2049
  const freePort = await getFreePort(finalConfig.webDebugger || "");
2031
2050
  finalConfig.webDebugger = `localhost:${freePort}`;
2032
2051
  }
@@ -2034,7 +2053,7 @@ async function startCli(finalConfig, manager) {
2034
2053
  const manager2 = TunnelManager.getInstance();
2035
2054
  const tunnel = await manager2.createTunnel(finalConfig);
2036
2055
  printer_default.startSpinner("Connecting to Pinggy...");
2037
- if (!finalConfig.NoTUI) {
2056
+ if (!finalConfig.optional?.noTui) {
2038
2057
  manager2.registerStatsListener(tunnel.tunnelid, (tunnelId, stats) => {
2039
2058
  globalThis.__PINGGY_TUNNEL_STATS__?.(stats);
2040
2059
  });
@@ -2153,14 +2172,14 @@ async function startCli(finalConfig, manager) {
2153
2172
  }
2154
2173
  printer_default.print(pico.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"));
2155
2174
  printer_default.print(pico.gray("\nPress Ctrl+C to stop the tunnel.\n"));
2156
- if (!finalConfig.NoTUI) {
2175
+ if (!finalConfig.optional?.noTui) {
2157
2176
  await launchTui(finalConfig, TunnelData.urls, TunnelData.greet, tunnel);
2158
2177
  }
2159
2178
  });
2160
2179
  } catch (e) {
2161
2180
  logger.debug("Failed to register start listener", e);
2162
2181
  }
2163
- if (!finalConfig.NoTUI) {
2182
+ if (!finalConfig.optional?.noTui) {
2164
2183
  await launchTui(finalConfig, TunnelData.urls, TunnelData.greet, tunnel);
2165
2184
  }
2166
2185
  } catch (err) {
@@ -0,0 +1,13 @@
1
+ const { createDefaultEsmPreset } = require('ts-jest');
2
+
3
+ const presetConfig = createDefaultEsmPreset();
4
+
5
+ /** @type {import("jest").Config} **/
6
+ module.exports = {
7
+ ...presetConfig,
8
+ testEnvironment: "node",
9
+ moduleNameMapper: {
10
+ // This handles the .js extension in your TS imports
11
+ '^(\\.{1,2}/.*)\\.js$': '$1',
12
+ },
13
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinggy",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "description": "Create secure, shareable tunnels to your localhost and manage them from the command line. ",
@@ -14,7 +14,7 @@
14
14
  "build:tsc": "tsc",
15
15
  "build": "tsup",
16
16
  "start": "node dist/index.js",
17
- "test": "jest --config jest.config.js",
17
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
18
18
  "bump": "node scripts/bumpVersion.js --bump && npm install",
19
19
  "bump:minor": "node scripts/bumpVersion.js --bump --minor && npm install",
20
20
  "bump:major": "node scripts/bumpVersion.js --bump --major && npm install",
@@ -51,7 +51,7 @@
51
51
  ]
52
52
  },
53
53
  "dependencies": {
54
- "@pinggy/pinggy": "^0.3.5",
54
+ "@pinggy/pinggy": "^0.3.8",
55
55
  "blessed": "^0.1.81",
56
56
  "clipboardy": "^5.0.0",
57
57
  "mime": "^4.1.0",