pinggy 0.4.9 → 0.5.1

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.cts CHANGED
@@ -82,16 +82,19 @@ interface RemoteManagementState {
82
82
  * @singleton
83
83
  */
84
84
 
85
+ type TunnelOrigin = "app" | "cli" | "remote";
85
86
  interface ManagedTunnel {
86
87
  tunnelid: string;
87
88
  configId: string;
88
89
  tunnelName?: string;
90
+ origin: TunnelOrigin;
89
91
  instance: TunnelInstance;
90
92
  tunnelConfig?: TunnelConfigurationV1;
91
93
  serveWorker?: Worker | null;
92
94
  warnings?: Warning[];
93
95
  serve?: string;
94
96
  isStopped?: boolean;
97
+ isStopping?: boolean;
95
98
  createdAt?: string;
96
99
  startedAt?: string | null;
97
100
  stoppedAt?: string | null;
@@ -121,6 +124,7 @@ interface TunnelUpdateConfig extends TunnelConfigurationV1 {
121
124
  serve?: string;
122
125
  }
123
126
  type StatsListener = (tunnelId: string, stats: TunnelUsageType) => void;
127
+ type StoppedListener = (tunnelId: string) => void;
124
128
  type ErrorListener = (tunnelId: string, errorMsg: string, isFatal: boolean) => void;
125
129
  type PollingErrorListener = (tunnelId: string, errorMsg: string) => void;
126
130
  type DisconnectListener = (tunnelId: string, error: string, messages: string[]) => void;
@@ -131,7 +135,7 @@ type ReconnectingListener = (tunnelId: string, retryCnt: number) => void;
131
135
  type ReconnectionCompletedListener = (tunnelId: string, urls: string[]) => void;
132
136
  type ReconnectionFailedListener = (tunnelId: string, retryCnt: number) => void;
133
137
  interface ITunnelManager {
134
- createTunnel(config: TunnelCreationConfig, buildConfig?: boolean): Promise<ManagedTunnel>;
138
+ createTunnel(config: TunnelCreationConfig, origin?: TunnelOrigin): Promise<ManagedTunnel>;
135
139
  startTunnel(tunnelId: string): Promise<string[]>;
136
140
  stopTunnel(tunnelId: string): {
137
141
  configId: string;
@@ -152,12 +156,15 @@ interface ITunnelManager {
152
156
  registerStatsListener(tunnelId: string, listener: StatsListener): Promise<[string, string]>;
153
157
  registerErrorListener(tunnelId: string, listener: ErrorListener): Promise<string>;
154
158
  registerPollingErrorListener(tunnelId: string, listener: PollingErrorListener): Promise<string>;
155
- registerWorkerErrorListner(tunnelId: string, listener: TunnelWorkerErrorListner): void;
159
+ registerWorkerErrorListner(tunnelId: string, listener: TunnelWorkerErrorListner): Promise<string>;
160
+ deregisterWorkerErrorListener(tunnelId: string, listenerId: string): void;
156
161
  registerStartListener(tunnelId: string, listener: StartListener): Promise<string>;
157
162
  deregisterErrorListener(tunnelId: string, listenerId: string): void;
158
163
  deregisterPollingErrorListener(tunnelId: string, listenerId: string): void;
159
164
  registerDisconnectListener(tunnelId: string, listener: DisconnectListener): Promise<string>;
160
165
  deregisterDisconnectListener(tunnelId: string, listenerId: string): void;
166
+ registerStoppedListener(tunnelId: string, listener: StoppedListener): Promise<string>;
167
+ deregisterStoppedListener(tunnelId: string, listenerId: string): void;
161
168
  deregisterStatsListener(tunnelId: string, listenerId: string): void;
162
169
  getLocalserverTlsInfo(tunnelId: string): Promise<string | boolean>;
163
170
  removeStoppedTunnelByTunnelId(tunnelId: string): boolean;
@@ -180,6 +187,7 @@ declare class TunnelManager implements ITunnelManager {
180
187
  private tunnelErrorListeners;
181
188
  private tunnelPollingErrorListeners;
182
189
  private tunnelDisconnectListeners;
190
+ private tunnelStoppedListeners;
183
191
  private tunnelWorkerErrorListeners;
184
192
  private tunnelStartListeners;
185
193
  private tunnelWillReconnectListeners;
@@ -200,7 +208,7 @@ declare class TunnelManager implements ITunnelManager {
200
208
  * @returns {ManagedTunnel} A new managed tunnel instance containing the tunnel details,
201
209
  * status information, and statistics
202
210
  */
203
- createTunnel(config: TunnelCreationConfig): Promise<ManagedTunnel>;
211
+ createTunnel(config: TunnelCreationConfig, origin?: TunnelOrigin): Promise<ManagedTunnel>;
204
212
  /**
205
213
  * Internal method to create a tunnel with an already-processed configuration.
206
214
  * This is used by createTunnel, restartTunnel, and updateConfig to avoid config processing.
@@ -228,6 +236,7 @@ declare class TunnelManager implements ITunnelManager {
228
236
  configId: string;
229
237
  tunnelid: string;
230
238
  };
239
+ private notifyStoppedListeners;
231
240
  /**
232
241
  * Get all public URLs for a tunnel
233
242
  */
@@ -237,6 +246,17 @@ declare class TunnelManager implements ITunnelManager {
237
246
  * @returns An array of all TunnelStatus objects
238
247
  */
239
248
  getAllTunnels(): Promise<TunnelList[]>;
249
+ /**
250
+ * Re-apply the given log level to every live tunnel's worker JS logger.
251
+ * Native libpinggy level is fixed at worker init and cannot be re-pathed
252
+ * or re-levelled at runtime without restarting the tunnel.
253
+ */
254
+ applyLogLevelToActiveTunnels(level: string): void;
255
+ /**
256
+ * Tunnel IDs whose ManagedTunnel is currently alive (not stopped, no fatal error).
257
+ * Cheap, synchronous, suitable for hot paths like /logs/paths.
258
+ */
259
+ getActiveTunnelIds(): Set<string>;
240
260
  /**
241
261
  * Get status of a tunnel
242
262
  */
@@ -317,7 +337,8 @@ declare class TunnelManager implements ITunnelManager {
317
337
  registerErrorListener(tunnelId: string, listener: ErrorListener): Promise<string>;
318
338
  registerPollingErrorListener(tunnelId: string, listener: PollingErrorListener): Promise<string>;
319
339
  registerDisconnectListener(tunnelId: string, listener: DisconnectListener): Promise<string>;
320
- registerWorkerErrorListner(tunnelId: string, listener: TunnelWorkerErrorListner): Promise<void>;
340
+ registerWorkerErrorListner(tunnelId: string, listener: TunnelWorkerErrorListner): Promise<string>;
341
+ deregisterWorkerErrorListener(tunnelId: string, listenerId: string): void;
321
342
  registerStartListener(tunnelId: string, listener: StartListener): Promise<string>;
322
343
  registerWillReconnectListener(tunnelId: string, listener: WillReconnectListener): Promise<string>;
323
344
  registerReconnectingListener(tunnelId: string, listener: ReconnectingListener): Promise<string>;
@@ -333,6 +354,8 @@ declare class TunnelManager implements ITunnelManager {
333
354
  deregisterErrorListener(tunnelId: string, listenerId: string): void;
334
355
  deregisterPollingErrorListener(tunnelId: string, listenerId: string): void;
335
356
  deregisterDisconnectListener(tunnelId: string, listenerId: string): void;
357
+ registerStoppedListener(tunnelId: string, listener: StoppedListener): Promise<string>;
358
+ deregisterStoppedListener(tunnelId: string, listenerId: string): void;
336
359
  deregisterWillReconnectListener(tunnelId: string, listenerId: string): void;
337
360
  deregisterReconnectingListener(tunnelId: string, listenerId: string): void;
338
361
  deregisterReconnectionCompletedListener(tunnelId: string, listenerId: string): void;
@@ -544,13 +567,13 @@ declare const TunnelConfigV1Schema: z.ZodObject<{
544
567
  forwarding: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodObject<{
545
568
  listenAddress: z.ZodOptional<z.ZodString>;
546
569
  address: z.ZodString;
547
- type: z.ZodOptional<z.ZodEnum<{
570
+ type: z.ZodEnum<{
548
571
  http: TunnelType.Http;
549
572
  tcp: TunnelType.Tcp;
550
573
  tls: TunnelType.Tls;
551
574
  udp: TunnelType.Udp;
552
575
  tlstcp: TunnelType.TlsTcp;
553
- }>>;
576
+ }>;
554
577
  }, z.core.$strip>>]>;
555
578
  ipWhitelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
556
579
  basicAuth: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -577,6 +600,219 @@ declare const TunnelConfigV1Schema: z.ZodObject<{
577
600
  }, z.core.$strip>;
578
601
  type TunnelConfigV1 = z.infer<typeof TunnelConfigV1Schema>;
579
602
 
603
+ /**
604
+ * Typed IPC route contract shared by IPCClient (caller) and IPCServer (handler).
605
+ * Each `Route` constant binds a `METHOD PATH` string used over the wire; the
606
+ * `IPCRoutes` map binds that same key to its request and response shapes.
607
+ *
608
+ * Adding a route: add an entry to `Route`, then a matching key in `IPCRoutes`.
609
+ * Both sides will fail to compile until the new handler is wired up.
610
+ */
611
+
612
+ declare const Route: {
613
+ readonly Ping: "GET /ping";
614
+ readonly ListTunnels: "GET /tunnels";
615
+ readonly ListTunnelsV1: "GET /tunnels-v1";
616
+ readonly StartTunnel: "POST /tunnels/start";
617
+ readonly StartTunnelConfig: "POST /tunnels/start-config";
618
+ readonly StartTunnelV1: "POST /tunnels/start-v1";
619
+ readonly StopTunnel: "POST /tunnels/stop";
620
+ readonly RestartTunnel: "POST /tunnels/restart";
621
+ readonly UpdateConfig: "POST /tunnels/update-config";
622
+ readonly UpdateConfigV2: "POST /tunnels/update-config-v2";
623
+ readonly RemoveStopped: "POST /tunnels/remove-stopped";
624
+ readonly Shutdown: "POST /shutdown";
625
+ readonly GetLogLevel: "GET /loglevel";
626
+ readonly SetLogLevel: "POST /loglevel";
627
+ readonly GetTunnelLogging: "GET /config/tunnel-logging";
628
+ readonly SetTunnelLogging: "POST /config/tunnel-logging";
629
+ readonly GetLogPaths: "GET /logs/paths";
630
+ };
631
+ declare const ParamRoute: {
632
+ readonly GetTunnel: "GET /tunnels/:id";
633
+ readonly GetTunnelStats: "GET /tunnels/:id/stats";
634
+ readonly ResolveLogPath: "GET /logs/resolve";
635
+ };
636
+ interface PingResponse {
637
+ status: string;
638
+ pid: number;
639
+ uptime: number;
640
+ }
641
+ type ListTunnelsResponse = (TunnelResponseV2 & {
642
+ mode?: SessionMode;
643
+ })[] | ErrorResponse;
644
+ interface LogPathEntry {
645
+ tunnelId?: string;
646
+ name?: string;
647
+ origin: TunnelOrigin;
648
+ path: string;
649
+ mtime: number;
650
+ running: boolean;
651
+ }
652
+ interface LogPathsResponse {
653
+ daemon: string;
654
+ tunnels: LogPathEntry[];
655
+ }
656
+ type ResolveLogPathResponse = {
657
+ status: "running" | "historical";
658
+ path: string;
659
+ tunnelId?: string;
660
+ name?: string;
661
+ origin: TunnelOrigin;
662
+ running: boolean;
663
+ } | {
664
+ status: "config-only";
665
+ name: string;
666
+ configId: string;
667
+ } | {
668
+ status: "not-found";
669
+ };
670
+ interface ShutdownResponse {
671
+ status: string;
672
+ errors: string[];
673
+ }
674
+ type LogLevel = "debug" | "info" | "error";
675
+ declare const SessionMode: {
676
+ readonly Foreground: "foreground";
677
+ readonly Detached: "detached";
678
+ };
679
+ type SessionMode = typeof SessionMode[keyof typeof SessionMode];
680
+ type IPCRoutes = {
681
+ [Route.Ping]: {
682
+ req: void;
683
+ res: PingResponse;
684
+ };
685
+ [Route.ListTunnels]: {
686
+ req: void;
687
+ res: ListTunnelsResponse;
688
+ };
689
+ [Route.ListTunnelsV1]: {
690
+ req: void;
691
+ res: TunnelResponse[] | ErrorResponse;
692
+ };
693
+ [Route.StartTunnel]: {
694
+ req: {
695
+ name: string;
696
+ mode: SessionMode;
697
+ };
698
+ res: TunnelResponseV2 | ErrorResponse;
699
+ };
700
+ [Route.StartTunnelConfig]: {
701
+ req: {
702
+ config: TunnelConfigV1;
703
+ mode: SessionMode;
704
+ noWait?: boolean;
705
+ };
706
+ res: TunnelResponseV2 | ErrorResponse;
707
+ };
708
+ [Route.StartTunnelV1]: {
709
+ req: {
710
+ config: TunnelConfig;
711
+ mode: SessionMode;
712
+ noWait?: boolean;
713
+ };
714
+ res: TunnelResponse | ErrorResponse;
715
+ };
716
+ [Route.StopTunnel]: {
717
+ req: {
718
+ tunnelid: string;
719
+ };
720
+ res: TunnelResponse | ErrorResponse;
721
+ };
722
+ [Route.RestartTunnel]: {
723
+ req: {
724
+ tunnelid: string;
725
+ };
726
+ res: TunnelResponse | ErrorResponse;
727
+ };
728
+ [Route.UpdateConfig]: {
729
+ req: {
730
+ config: TunnelConfig;
731
+ noWait?: boolean;
732
+ };
733
+ res: TunnelResponse | ErrorResponse;
734
+ };
735
+ [Route.UpdateConfigV2]: {
736
+ req: {
737
+ config: TunnelConfigV1;
738
+ noWait?: boolean;
739
+ };
740
+ res: TunnelResponseV2 | ErrorResponse;
741
+ };
742
+ [Route.RemoveStopped]: {
743
+ req: {
744
+ tunnelid?: string;
745
+ configId?: string;
746
+ };
747
+ res: {
748
+ result: boolean | ErrorResponse;
749
+ };
750
+ };
751
+ [Route.Shutdown]: {
752
+ req: Record<string, never>;
753
+ res: ShutdownResponse;
754
+ };
755
+ [Route.GetLogLevel]: {
756
+ req: void;
757
+ res: {
758
+ level: LogLevel;
759
+ };
760
+ };
761
+ [Route.SetLogLevel]: {
762
+ req: {
763
+ level: LogLevel;
764
+ };
765
+ res: {
766
+ level: LogLevel;
767
+ appliedTo: string;
768
+ };
769
+ };
770
+ [Route.GetTunnelLogging]: {
771
+ req: void;
772
+ res: {
773
+ enabled: boolean;
774
+ };
775
+ };
776
+ [Route.SetTunnelLogging]: {
777
+ req: {
778
+ enabled: boolean;
779
+ };
780
+ res: {
781
+ enabled: boolean;
782
+ };
783
+ };
784
+ [Route.GetLogPaths]: {
785
+ req: void;
786
+ res: LogPathsResponse;
787
+ };
788
+ };
789
+ type RouteKey = keyof IPCRoutes;
790
+ type RouteRes<K extends RouteKey> = IPCRoutes[K]["res"];
791
+ /**
792
+ * Parameterized routes don't fit the static `METHOD PATH` key shape.
793
+ * Kept as a small parallel registry.
794
+ */
795
+ type ParameterizedRoutes = {
796
+ [ParamRoute.GetTunnel]: {
797
+ params: {
798
+ tunnelId: string;
799
+ };
800
+ res: TunnelResponse | ErrorResponse;
801
+ };
802
+ [ParamRoute.GetTunnelStats]: {
803
+ params: {
804
+ tunnelId: string;
805
+ };
806
+ res: TunnelUsageType[] | ErrorResponse;
807
+ };
808
+ [ParamRoute.ResolveLogPath]: {
809
+ params: {
810
+ q: string;
811
+ };
812
+ res: ResolveLogPathResponse;
813
+ };
814
+ };
815
+
580
816
  interface TunnelResponse {
581
817
  tunnelid: string;
582
818
  remoteurls: string[];
@@ -591,6 +827,7 @@ interface TunnelResponseV2 {
591
827
  status: Status;
592
828
  stats: TunnelUsageType;
593
829
  greetmsg?: string;
830
+ mode?: SessionMode;
594
831
  }
595
832
  interface TunnelHandler {
596
833
  handleStart(config: TunnelConfig, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
@@ -604,7 +841,7 @@ interface TunnelHandler {
604
841
  handleRestart(tunnelid: string, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
605
842
  handleRegisterStatsListener(tunnelid: string, listener: (tunnelId: string, stats: TunnelUsageType) => void): void;
606
843
  handleUnregisterStatsListener(tunnelid: string, listnerId: string): void;
607
- handleGetTunnelStats(tunnelid: string): TunnelUsageType[] | ErrorResponse;
844
+ handleGetTunnelStats(tunnelid: string): Promise<TunnelUsageType[] | ErrorResponse>;
608
845
  handleRegisterDisconnectListener(tunnelid: string, listener: DisconnectListener): void;
609
846
  handleRemoveStoppedTunnelByTunnelId(tunnelId: string): boolean | ErrorResponse;
610
847
  handleRemoveStoppedTunnelByConfigId(configId: string): boolean | ErrorResponse;
@@ -618,8 +855,8 @@ declare class TunnelOperations implements TunnelHandler {
618
855
  private buildTunnelResponse;
619
856
  private buildTunnelResponseV2;
620
857
  private error;
621
- handleStart(config: TunnelConfig, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
622
- handleStartV2(config: TunnelConfigV1, noWait?: boolean): Promise<TunnelResponseV2 | ErrorResponse>;
858
+ handleStart(config: TunnelConfig, noWait?: boolean, origin?: TunnelOrigin): Promise<TunnelResponse | ErrorResponse>;
859
+ handleStartV2(config: TunnelConfigV1, noWait?: boolean, origin?: TunnelOrigin): Promise<TunnelResponseV2 | ErrorResponse>;
623
860
  handleUpdateConfig(config: TunnelConfig, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
624
861
  handleUpdateConfigV2(config: TunnelConfigV1, noWait?: boolean): Promise<TunnelResponseV2 | ErrorResponse>;
625
862
  handleListV2(): Promise<TunnelResponseV2[] | ErrorResponse>;
@@ -629,12 +866,235 @@ declare class TunnelOperations implements TunnelHandler {
629
866
  handleRestart(tunnelid: string, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
630
867
  handleRegisterStatsListener(tunnelid: string, listener: (tunnelId: string, stats: TunnelUsageType) => void): void;
631
868
  handleUnregisterStatsListener(tunnelid: string, listnerId: string): void;
632
- handleGetTunnelStats(tunnelid: string): TunnelUsageType[] | ErrorResponse;
869
+ handleGetTunnelStats(tunnelid: string): Promise<TunnelUsageType[] | ErrorResponse>;
633
870
  handleRegisterDisconnectListener(tunnelid: string, listener: DisconnectListener): void;
634
871
  handleRemoveStoppedTunnelByConfigId(configId: string): boolean | ErrorResponse;
635
872
  handleRemoveStoppedTunnelByTunnelId(tunnelId: string): boolean | ErrorResponse;
636
873
  }
637
874
 
875
+ type ClientOrigin = "app" | "cli" | "remote";
876
+ declare class IPCClient {
877
+ private port;
878
+ private origin;
879
+ constructor(port: number, origin?: ClientOrigin);
880
+ ping(timeoutMs?: number): Promise<PingResponse>;
881
+ listTunnels(): Promise<RouteRes<typeof Route.ListTunnels>>;
882
+ getTunnel(tunnelId: string): Promise<ParameterizedRoutes[typeof ParamRoute.GetTunnel]["res"]>;
883
+ getTunnelStats(tunnelId: string): Promise<TunnelUsageType[] | ErrorResponse>;
884
+ startTunnel(name: string, mode: SessionMode): Promise<TunnelResponseV2 | ErrorResponse>;
885
+ startTunnelWithConfig(config: TunnelConfigV1, mode: SessionMode, noWait?: boolean): Promise<TunnelResponseV2 | ErrorResponse>;
886
+ stopTunnel(tunnelid: string): Promise<TunnelResponse | ErrorResponse>;
887
+ restartTunnel(tunnelid: string): Promise<TunnelResponse | ErrorResponse>;
888
+ startTunnelV1(config: TunnelConfig, mode: SessionMode, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
889
+ listTunnelsV1(): Promise<TunnelResponse[] | ErrorResponse>;
890
+ updateConfig(config: TunnelConfig, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
891
+ updateConfigV2(config: TunnelConfigV1, noWait?: boolean): Promise<TunnelResponseV2 | ErrorResponse>;
892
+ removeStoppedTunnel(opts: {
893
+ tunnelid?: string;
894
+ configId?: string;
895
+ }): Promise<{
896
+ result: boolean | ErrorResponse;
897
+ }>;
898
+ shutdown(): Promise<ShutdownResponse>;
899
+ getLogLevel(): Promise<{
900
+ level: LogLevel;
901
+ }>;
902
+ setLogLevel(level: LogLevel): Promise<{
903
+ level: LogLevel;
904
+ appliedTo: string;
905
+ }>;
906
+ getTunnelLogging(): Promise<{
907
+ enabled: boolean;
908
+ }>;
909
+ setTunnelLogging(enabled: boolean): Promise<{
910
+ enabled: boolean;
911
+ }>;
912
+ getLogPaths(): Promise<LogPathsResponse>;
913
+ resolveLogPath(q: string): Promise<ResolveLogPathResponse>;
914
+ /**
915
+ * Get the WebSocket URL for event streaming.
916
+ */
917
+ getWsUrl(): string;
918
+ getPort(): number;
919
+ private call;
920
+ private request;
921
+ }
922
+
923
+ type StatsCallback = (tunnelId: string, stats: TunnelUsageType) => void;
924
+ type DisconnectCallback = (tunnelId: string, error: string, messages: string[]) => void;
925
+ type ReconnectingCallback = (tunnelId: string, retryCnt: number) => void;
926
+ type ReconnectedCallback = (tunnelId: string, urls: string[]) => void;
927
+ type ReconnectionFailedCallback = (tunnelId: string, retryCnt: number) => void;
928
+ type ErrorCallback = (tunnelId: string, message: string, isFatal: boolean) => void;
929
+ type UrlReadyCallback = (tunnelId: string, urls: string[]) => void;
930
+ type WorkerErrorCallback = (tunnelId: string, message: string) => void;
931
+ type WillReconnectCallback = (tunnelId: string, error: string, messages: string[]) => void;
932
+ type StoppedCallback = (tunnelId: string) => void;
933
+ type SubscriptionMode = SessionMode;
934
+ type SubscriptionInfo = {
935
+ mode: SubscriptionMode;
936
+ };
937
+ declare class WsStream {
938
+ private getWsUrl;
939
+ private ws;
940
+ private wsReady;
941
+ private wsResolve;
942
+ private subscribedTunnels;
943
+ private callbacks;
944
+ private openListeners;
945
+ private closeListeners;
946
+ constructor(getWsUrl: () => string);
947
+ ensureOpen(): Promise<void>;
948
+ /** Send a normal close (1000). Use for user-initiated shutdown. */
949
+ closeNormally(): void;
950
+ /** Hard kill the socket. Use when daemon is known dead. */
951
+ terminate(): void;
952
+ isOpen(): boolean;
953
+ subscribe(tunnelId: string, mode?: SubscriptionMode): Promise<void>;
954
+ /**
955
+ * Remove the local subscription. Sends an unsubscribe frame if the socket
956
+ * is still open; if not, the daemon cleans up server-side when the session
957
+ * closes. Returns true if the caller had this subscription.
958
+ */
959
+ unsubscribe(tunnelId: string): boolean;
960
+ hasSubscriptions(): boolean;
961
+ subscriptionCount(): number;
962
+ /** Snapshot of current subscriptions, for the reconnect path to replay. */
963
+ snapshotSubscriptions(): Array<[string, SubscriptionInfo]>;
964
+ /**
965
+ * Re-open the WS (fresh socket) and re-subscribe a previously captured set.
966
+ * Clears existing state first so ensureOpen builds a new connection.
967
+ */
968
+ restoreSubscriptions(snapshot: Array<[string, SubscriptionInfo]>): Promise<void>;
969
+ onOpen(cb: () => void): void;
970
+ onClose(cb: (code: number) => void): void;
971
+ onStats(cb: StatsCallback): void;
972
+ onDisconnect(cb: DisconnectCallback): void;
973
+ onReconnecting(cb: ReconnectingCallback): void;
974
+ onReconnected(cb: ReconnectedCallback): void;
975
+ onReconnectionFailed(cb: ReconnectionFailedCallback): void;
976
+ onError(cb: ErrorCallback): void;
977
+ onUrlReady(cb: UrlReadyCallback): void;
978
+ onWorkerError(cb: WorkerErrorCallback): void;
979
+ onWillReconnect(cb: WillReconnectCallback): void;
980
+ onStopped(cb: StoppedCallback): void;
981
+ private handleMessage;
982
+ }
983
+
984
+ /**
985
+ * DaemonHealth: monitors liveness of the daemon process and decides when
986
+ * to declare it lost. Two signals drive it:
987
+ *
988
+ * 1. WS abnormal close → triggers a short reconnect loop (3 × 1s).
989
+ * During the loop we verify daemon.json still points to the same PID
990
+ * we connected to; a PID change means the daemon was respawned and
991
+ * the previous session is unrecoverable.
992
+ * 2. Heartbeat ping every 5s → after 2 consecutive failures the daemon
993
+ * is treated as hung; no reconnect (it won't help).
994
+ *
995
+ * Subscriptions are owned by WsStream. DaemonHealth asks it to snapshot
996
+ * and restore them across a successful reconnect.
997
+ */
998
+
999
+ type DaemonLostReason = "dead" | "respawned" | "hung" | "heartbeat";
1000
+ type DaemonLostCallback = (reason: DaemonLostReason, detail?: string) => void;
1001
+ type DaemonReconnectingCallback = (attempt: number, max: number) => void;
1002
+ type DaemonReconnectedCallback = () => void;
1003
+ declare class DaemonHealth {
1004
+ private getIpc;
1005
+ private stream;
1006
+ private originalPid;
1007
+ private lost;
1008
+ private reconnecting;
1009
+ private heartbeatTimer;
1010
+ private lostCallbacks;
1011
+ private reconnectingCallbacks;
1012
+ private reconnectedCallbacks;
1013
+ constructor(getIpc: () => IPCClient | null, stream: WsStream);
1014
+ bindPid(pid: number): void;
1015
+ isLost(): boolean;
1016
+ onLost(cb: DaemonLostCallback): void;
1017
+ onReconnecting(cb: DaemonReconnectingCallback): void;
1018
+ onReconnected(cb: DaemonReconnectedCallback): void;
1019
+ startHeartbeat(): void;
1020
+ stopHeartbeat(): void;
1021
+ private attemptReconnect;
1022
+ private triggerLost;
1023
+ }
1024
+
1025
+ /**
1026
+ * TunnelClient: public facade for tunnel operations via the daemon.
1027
+ * Used by both CLI and App. Mirrors TunnelOperations interface but routes
1028
+ * all operations through the daemon process via HTTP + WebSocket.
1029
+ *
1030
+ * Composes three helpers:
1031
+ * - IPCClient : HTTP request/response (start, stop, list, restart, log, …)
1032
+ * - WsStream : WebSocket lifecycle, per-tunnel subscriptions, event dispatch
1033
+ * - DaemonHealth : heartbeat, reconnect on transient WS drops, daemon-lost events
1034
+ */
1035
+
1036
+ interface TunnelClientOptions {
1037
+ origin?: ClientOrigin;
1038
+ }
1039
+ declare class TunnelClient {
1040
+ private ipc;
1041
+ private origin;
1042
+ private stream;
1043
+ health: DaemonHealth;
1044
+ constructor(options?: TunnelClientOptions);
1045
+ ensureDaemon(): Promise<void>;
1046
+ static forRemoteManagement(): Promise<TunnelClient>;
1047
+ /**
1048
+ * Close the WebSocket connection and cleanup.
1049
+ */
1050
+ close(): void;
1051
+ ping(): Promise<{
1052
+ status: string;
1053
+ pid: number;
1054
+ uptime: number;
1055
+ }>;
1056
+ handleStartV2(config: object, noWait?: boolean, mode?: SessionMode): Promise<TunnelResponseV2 | ErrorResponse>;
1057
+ handleStart(config: object, noWait?: boolean, mode?: SessionMode): Promise<TunnelResponse | ErrorResponse>;
1058
+ handleUpdateConfig(config: object, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
1059
+ handleUpdateConfigV2(config: object, noWait?: boolean): Promise<TunnelResponseV2 | ErrorResponse>;
1060
+ handleStop(tunnelId: string): Promise<TunnelResponse | ErrorResponse>;
1061
+ handleListV2(): Promise<TunnelResponseV2[] | ErrorResponse>;
1062
+ handleList(): Promise<TunnelResponse[] | ErrorResponse>;
1063
+ handleRemoveStoppedTunnelByTunnelId(tunnelId: string): boolean | ErrorResponse;
1064
+ handleRemoveStoppedTunnelByConfigId(configId: string): boolean | ErrorResponse;
1065
+ handleGet(tunnelId: string): Promise<TunnelResponse | ErrorResponse>;
1066
+ handleRestart(tunnelId: string): Promise<TunnelResponse | ErrorResponse>;
1067
+ shutdown(): Promise<void>;
1068
+ getLogLevel(): Promise<string>;
1069
+ setLogLevel(level: "debug" | "info" | "error"): Promise<void>;
1070
+ getTunnelLogging(): Promise<boolean>;
1071
+ setTunnelLogging(enabled: boolean): Promise<void>;
1072
+ getLogPaths(): Promise<LogPathsResponse>;
1073
+ resolveLogPath(q: string): Promise<ResolveLogPathResponse>;
1074
+ restart(tunnelId: string): Promise<void>;
1075
+ attach(tunnelId: string, mode?: SubscriptionMode): Promise<void>;
1076
+ detach(tunnelId: string): void;
1077
+ onStats(cb: StatsCallback): void;
1078
+ onDisconnect(cb: DisconnectCallback): void;
1079
+ onReconnecting(cb: ReconnectingCallback): void;
1080
+ onReconnected(cb: ReconnectedCallback): void;
1081
+ onReconnectionFailed(cb: ReconnectionFailedCallback): void;
1082
+ onError(cb: ErrorCallback): void;
1083
+ onUrlReady(cb: UrlReadyCallback): void;
1084
+ onWorkerError(cb: WorkerErrorCallback): void;
1085
+ onWillReconnect(cb: WillReconnectCallback): void;
1086
+ onStopped(cb: StoppedCallback): void;
1087
+ onDaemonLost(cb: DaemonLostCallback): void;
1088
+ onDaemonReconnecting(cb: DaemonReconnectingCallback): void;
1089
+ onDaemonReconnected(cb: DaemonReconnectedCallback): void;
1090
+ isDaemonLost(): boolean;
1091
+ handleRegisterStatsListener(tunnelId: string, listener: (tunnelId: string, stats: TunnelUsageType) => void): void;
1092
+ handleRegisterDisconnectListener(tunnelId: string, listener: (tunnelId: string, error: string, messages: string[]) => void): void;
1093
+ handleUnregisterStatsListener(_tunnelId: string, _listenerId: string): void;
1094
+ handleGetTunnelStats(tunnelId: string): Promise<TunnelUsageType[] | ErrorResponse>;
1095
+ private assertClient;
1096
+ }
1097
+
638
1098
  interface BaseLogConfig {
639
1099
  level?: string;
640
1100
  filePath?: string;
@@ -656,12 +1116,12 @@ declare class RemoteManagementUnauthorizedError extends Error {
656
1116
  * - On other failures: retry every 15 seconds
657
1117
  * - Keep running until closed or SIGINT
658
1118
  */
659
- declare function initiateRemoteManagement(remoteManagementConfig: RemoteManagementConfig): Promise<RemoteManagementState>;
1119
+ declare function initiateRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
660
1120
  declare function closeRemoteManagement(timeoutMs?: number): Promise<RemoteManagementState>;
661
1121
  /**
662
1122
  * Start remote management loop in background; returns after first connection attempt.
663
1123
  */
664
- declare function startRemoteManagement(remoteManagementConfig: RemoteManagementConfig): Promise<RemoteManagementState>;
1124
+ declare function startRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
665
1125
  declare function getRemoteManagementState(): RemoteManagementState;
666
1126
 
667
- export { type AdditionalForwarding, type DisconnectListener, type ErrorListener, type FinalConfig, type ITunnelManager, type ManagedTunnel, type ReconnectingListener, type ReconnectionCompletedListener, type ReconnectionFailedListener, RemoteManagementUnauthorizedError, type StartListener, type StatsListener, type Status, TunnelErrorCodeType, type TunnelList, TunnelManager, TunnelOperations, type TunnelResponse, TunnelStateType, type TunnelStatus, TunnelWarningCode, type TunnelWorkerErrorListner, type Warning, type WillReconnectListener, closeRemoteManagement, enablePackageLogging, getRemoteManagementState, initiateRemoteManagement, startRemoteManagement };
1127
+ export { type AdditionalForwarding, type DisconnectListener, type ErrorListener, type FinalConfig, type ITunnelManager, type ManagedTunnel, type ReconnectingListener, type ReconnectionCompletedListener, type ReconnectionFailedListener, RemoteManagementUnauthorizedError, type StartListener, type StatsListener, type Status, TunnelClient, TunnelErrorCodeType, type TunnelList, TunnelManager, TunnelOperations, type TunnelResponse, TunnelStateType, type TunnelStatus, TunnelWarningCode, type TunnelWorkerErrorListner, type Warning, type WillReconnectListener, closeRemoteManagement, enablePackageLogging, getRemoteManagementState, initiateRemoteManagement, startRemoteManagement };