pinggy 0.6.0 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -56,7 +56,7 @@ type FinalConfig = (TunnelConfigurationV1) & {
56
56
  conf?: string;
57
57
  saveconf?: string;
58
58
  };
59
- 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";
59
+ 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" | "DEVICE_NOT_ENROLLED";
60
60
  interface ErrorResponse {
61
61
  code: ErrorCodeType;
62
62
  message: string;
@@ -83,7 +83,8 @@ interface RemoteManagementState {
83
83
  * @singleton
84
84
  */
85
85
 
86
- type TunnelOrigin = "app" | "cli" | "remote";
86
+ /** Who asked the daemon for a tunnel. `device` is the device agent, acting for the dashboard's device page. */
87
+ type TunnelOrigin = "app" | "cli" | "remote" | "device";
87
88
  interface ManagedTunnel {
88
89
  tunnelid: string;
89
90
  configId: string;
@@ -96,6 +97,14 @@ interface ManagedTunnel {
96
97
  serve?: string;
97
98
  isStopped?: boolean;
98
99
  isStopping?: boolean;
100
+ /**
101
+ * Set on the entry a restart or config update replaces, until the new entry takes its place in the
102
+ * maps. The entry stays listed, so `GET /tunnels` never drops the tunnel. It reports `starting` when
103
+ * the new entry starts, and stays `exited` when a stopped tunnel only gets a new config.
104
+ */
105
+ rebuild?: {
106
+ startsAfter: boolean;
107
+ };
99
108
  createdAt?: string;
100
109
  startedAt?: string | null;
101
110
  stoppedAt?: string | null;
@@ -297,12 +306,30 @@ declare class TunnelManager implements ITunnelManager {
297
306
  * @throws Error if neither configId nor tunnelId is provided, or if tunnel is not found
298
307
  */
299
308
  getTunnelConfig(configId?: string, tunnelId?: string): Promise<TunnelConfigurationV1>;
309
+ /**
310
+ * The instance's config, asked of its worker. A stopped instance has let its worker go, so a stopped
311
+ * tunnel, including one a restart is replacing, answers with the config it was built from.
312
+ */
313
+ private currentConfig;
300
314
  /**
301
315
  * Stop a tunnel instance and wait for the stop to settle, capped by a
302
316
  * timeout. Failure is logged, not thrown: the caller replaces the
303
317
  * instance either way and a hung stop must not block that forever.
304
318
  */
305
319
  private stopInstanceGuarded;
320
+ /**
321
+ * Marks the entry a restart or config update replaces. It stays in both maps until
322
+ * `_createTunnelWithProcessedConfig` sets the new entry under the same ids. When the new entry
323
+ * starts, the entry reports `starting`, and an error from an earlier run no longer describes it.
324
+ */
325
+ private beginRebuild;
326
+ /** Stops the instance being replaced, and marks it stopped so no reader calls it again. */
327
+ private stopForRebuild;
328
+ /**
329
+ * Ends a failed rebuild. When the old entry is still in the maps, it stays listed as stopped with
330
+ * the reason. A new entry that failed to start already holds its own error.
331
+ */
332
+ private failRebuild;
306
333
  /**
307
334
  * Restarts a tunnel with its current configuration.
308
335
  * This function will stop the tunnel if it's running and start it again.
@@ -648,6 +675,9 @@ declare const Route: {
648
675
  readonly GetTunnelLogging: "GET /config/tunnel-logging";
649
676
  readonly SetTunnelLogging: "POST /config/tunnel-logging";
650
677
  readonly GetLogPaths: "GET /logs/paths";
678
+ readonly GetDeviceAgent: "GET /device";
679
+ readonly StartDeviceAgent: "POST /device/start";
680
+ readonly StopDeviceAgent: "POST /device/stop";
651
681
  };
652
682
  declare const ParamRoute: {
653
683
  readonly GetTunnel: "GET /tunnels/:id";
@@ -704,6 +734,35 @@ interface ShutdownResponse {
704
734
  errors: string[];
705
735
  }
706
736
  type LogLevel = "debug" | "info" | "error";
737
+ declare const DeviceAgentState: {
738
+ readonly Stopped: "stopped";
739
+ readonly Connecting: "connecting";
740
+ readonly Connected: "connected";
741
+ readonly Reconnecting: "reconnecting";
742
+ };
743
+ type DeviceAgentState = typeof DeviceAgentState[keyof typeof DeviceAgentState];
744
+ /** Why a device agent run ended for good. `code` names 1 of the agent's terminal outcomes. */
745
+ interface DeviceAgentError {
746
+ code: string;
747
+ message: string;
748
+ }
749
+ /**
750
+ * The device agent the daemon runs for `pinggy device connect -b`. It never carries the token: any
751
+ * local user can reach the IPC port.
752
+ */
753
+ interface DeviceAgentStatus {
754
+ state: DeviceAgentState;
755
+ deviceAgentId: string | null;
756
+ server: string | null;
757
+ /** The owner's email, from `welcome`. */
758
+ email: string | null;
759
+ /** The device's page in the dashboard, from `welcome`. */
760
+ deviceUrl: string | null;
761
+ startedAt: string | null;
762
+ connectedAt: string | null;
763
+ startedBy: TunnelOrigin | null;
764
+ lastError: DeviceAgentError | null;
765
+ }
707
766
  declare const SessionMode: {
708
767
  readonly Foreground: "foreground";
709
768
  readonly Detached: "detached";
@@ -818,6 +877,18 @@ type IPCRoutes = {
818
877
  req: void;
819
878
  res: LogPathsResponse;
820
879
  };
880
+ [Route.GetDeviceAgent]: {
881
+ req: void;
882
+ res: DeviceAgentStatus;
883
+ };
884
+ [Route.StartDeviceAgent]: {
885
+ req: Record<string, never>;
886
+ res: DeviceAgentStatus | ErrorResponse;
887
+ };
888
+ [Route.StopDeviceAgent]: {
889
+ req: Record<string, never>;
890
+ res: DeviceAgentStatus;
891
+ };
821
892
  };
822
893
  type RouteKey = keyof IPCRoutes;
823
894
  type RouteRes<K extends RouteKey> = IPCRoutes[K]["res"];
@@ -888,6 +959,11 @@ declare class TunnelOperations implements TunnelHandler {
888
959
  private buildTunnelResponse;
889
960
  private buildTunnelResponseV2;
890
961
  private error;
962
+ /**
963
+ * A restart or update of a tunnel another one is still rebuilding. A no-wait call checks this before
964
+ * it answers, because a throw from the call it does not await is only logged.
965
+ */
966
+ private rebuildInProgress;
891
967
  handleStart(config: TunnelConfig, noWait?: boolean, origin?: TunnelOrigin): Promise<TunnelResponse | ErrorResponse>;
892
968
  handleStartV2(config: TunnelConfigV1, noWait?: boolean, origin?: TunnelOrigin): Promise<TunnelResponseV2 | ErrorResponse>;
893
969
  handleUpdateConfig(config: TunnelConfig, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
@@ -905,7 +981,7 @@ declare class TunnelOperations implements TunnelHandler {
905
981
  handleRemoveStoppedTunnelByTunnelId(tunnelId: string): boolean | ErrorResponse;
906
982
  }
907
983
 
908
- type ClientOrigin = "app" | "cli" | "remote";
984
+ type ClientOrigin = "app" | "cli" | "remote" | "device";
909
985
  declare class IPCClient {
910
986
  private port;
911
987
  private origin;
@@ -944,6 +1020,9 @@ declare class IPCClient {
944
1020
  }>;
945
1021
  getLogPaths(): Promise<LogPathsResponse>;
946
1022
  resolveLogPath(q: string): Promise<ResolveLogPathResponse>;
1023
+ getDeviceAgentStatus(timeoutMs?: number): Promise<DeviceAgentStatus>;
1024
+ startDeviceAgent(): Promise<RouteRes<typeof Route.StartDeviceAgent>>;
1025
+ stopDeviceAgent(): Promise<DeviceAgentStatus>;
947
1026
  /**
948
1027
  * Get the WebSocket URL for event streaming.
949
1028
  */
@@ -1119,6 +1198,9 @@ declare class TunnelClient {
1119
1198
  getLogPaths(): Promise<LogPathsResponse>;
1120
1199
  resolveLogPath(q: string): Promise<ResolveLogPathResponse>;
1121
1200
  restart(tunnelId: string): Promise<void>;
1201
+ getDeviceAgentStatus(): Promise<DeviceAgentStatus>;
1202
+ startDeviceAgent(): Promise<DeviceAgentStatus | ErrorResponse>;
1203
+ stopDeviceAgent(): Promise<DeviceAgentStatus>;
1122
1204
  attach(tunnelId: string, mode?: SubscriptionMode): Promise<void>;
1123
1205
  detach(tunnelId: string): void;
1124
1206
  onStats(cb: StatsCallback): void;
@@ -1148,55 +1230,6 @@ declare class TunnelClient {
1148
1230
  private assertClient;
1149
1231
  }
1150
1232
 
1151
- interface DaemonInfo {
1152
- pid: number;
1153
- port: number;
1154
- startedAt: string;
1155
- host?: DaemonHost;
1156
- /** IPC protocol version the daemon speaks. Absent in daemon.json written by older builds. */
1157
- ipcVersion?: number;
1158
- }
1159
-
1160
- /**
1161
- * Read and validate daemon.json.
1162
- * Returns null if file doesn't exist, is malformed, or PID is stale.
1163
- * Automatically cleans up stale daemon.json.
1164
- */
1165
- declare function getDaemonInfo(): DaemonInfo | null;
1166
- /**
1167
- * Check if the daemon is currently running.
1168
- */
1169
- declare function isDaemonRunning(): boolean;
1170
-
1171
- interface BaseLogConfig {
1172
- level?: string;
1173
- filePath?: string;
1174
- stdout?: boolean;
1175
- source?: boolean;
1176
- silent?: boolean;
1177
- enableSdkLog?: boolean;
1178
- }
1179
- type BaseLogConfigType = BaseLogConfig;
1180
- declare function enablePackageLogging(opts?: BaseLogConfigType): winston.Logger;
1181
-
1182
- declare class RemoteManagementUnauthorizedError extends Error {
1183
- constructor();
1184
- }
1185
- /**
1186
- * Initiate remote management mode with a WebSocket connection.
1187
- * - Connect with Authorization: Bearer <token>
1188
- * - On HTTP 401: print Unauthorized and exit
1189
- * - On other failures: retry every 15 seconds
1190
- * - Keep running until closed or SIGINT
1191
- */
1192
- declare function initiateRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
1193
- declare function closeRemoteManagement(timeoutMs?: number): Promise<RemoteManagementState>;
1194
- /**
1195
- * Start remote management loop in background; returns after first connection attempt.
1196
- */
1197
- declare function startRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
1198
- declare function getRemoteManagementState(): RemoteManagementState;
1199
-
1200
1233
  interface SavedTunnelConfig {
1201
1234
  name: string;
1202
1235
  configId: string;
@@ -1316,6 +1349,55 @@ declare function updateTunnelConfig(nameOrId: string, tunnelConfig: TunnelConfig
1316
1349
  */
1317
1350
  declare function getAutoStartConfigs(): SavedTunnelConfig[];
1318
1351
 
1352
+ interface DaemonInfo {
1353
+ pid: number;
1354
+ port: number;
1355
+ startedAt: string;
1356
+ host?: DaemonHost;
1357
+ /** IPC protocol version the daemon speaks. Absent in daemon.json written by older builds. */
1358
+ ipcVersion?: number;
1359
+ }
1360
+
1361
+ /**
1362
+ * Read and validate daemon.json.
1363
+ * Returns null if file doesn't exist, is malformed, or PID is stale.
1364
+ * Automatically cleans up stale daemon.json.
1365
+ */
1366
+ declare function getDaemonInfo(): DaemonInfo | null;
1367
+ /**
1368
+ * Check if the daemon is currently running.
1369
+ */
1370
+ declare function isDaemonRunning(): boolean;
1371
+
1372
+ interface BaseLogConfig {
1373
+ level?: string;
1374
+ filePath?: string;
1375
+ stdout?: boolean;
1376
+ source?: boolean;
1377
+ silent?: boolean;
1378
+ enableSdkLog?: boolean;
1379
+ }
1380
+ type BaseLogConfigType = BaseLogConfig;
1381
+ declare function enablePackageLogging(opts?: BaseLogConfigType): winston.Logger;
1382
+
1383
+ declare class RemoteManagementUnauthorizedError extends Error {
1384
+ constructor();
1385
+ }
1386
+ /**
1387
+ * Initiate remote management mode with a WebSocket connection.
1388
+ * - Connect with Authorization: Bearer <token>
1389
+ * - On HTTP 401: print Unauthorized and exit
1390
+ * - On other failures: retry every 15 seconds
1391
+ * - Keep running until closed or SIGINT
1392
+ */
1393
+ declare function initiateRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
1394
+ declare function closeRemoteManagement(timeoutMs?: number): Promise<RemoteManagementState>;
1395
+ /**
1396
+ * Start remote management loop in background; returns after first connection attempt.
1397
+ */
1398
+ declare function startRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
1399
+ declare function getRemoteManagementState(): RemoteManagementState;
1400
+
1319
1401
  /**
1320
1402
  * Returns the base Pinggy config directory for the current platform.
1321
1403
  * - Linux/macOS: $XDG_CONFIG_HOME/pinggy or ~/.config/pinggy
@@ -1336,4 +1418,4 @@ declare function ensureTunnelConfigDir(): string;
1336
1418
  */
1337
1419
  declare function getDaemonInfoPath(): string;
1338
1420
 
1339
- export { type AdditionalForwarding, type DaemonInfo, type DisconnectListener, type ErrorListener, type FinalConfig, type ITunnelManager, type ManagedTunnel, type ReconnectingListener, type ReconnectionCompletedListener, type ReconnectionFailedListener, RemoteManagementUnauthorizedError, type SavedTunnelConfig, type StartListener, type StatsListener, type Status, type StorageLogger, TUNNEL_CONFIG_V1_KEYS, TunnelClient, type TunnelConfigV1, TunnelConfigV1Schema, TunnelErrorCodeType, type TunnelList, TunnelManager, TunnelOperations, type TunnelResponse, TunnelStateType, type TunnelStatus, TunnelWarningCode, type TunnelWorkerErrorListner, type Warning, type WillReconnectListener, bulkReplace, closeRemoteManagement, configureStorageLogger, deleteConfig, enablePackageLogging, ensureTunnelConfigDir, findConfig, findConfigByName, getAutoStartConfigs, getDaemonInfo, getDaemonInfoPath, getPinggyConfigDir, getRemoteManagementState, getTunnelConfigDir, initiateRemoteManagement, isDaemonRunning, listSavedConfigs, loadConfigByName, sanitizeName, saveConfig, startRemoteManagement, updateConfigAutoStart, updateTunnelConfig, upsertConfig, validateName, validateNameForStorage, validateNameStrict };
1421
+ export { type AdditionalForwarding, type DaemonInfo, type DeviceAgentError, DeviceAgentState, type DeviceAgentStatus, type DisconnectListener, type ErrorListener, type FinalConfig, type ITunnelManager, type ManagedTunnel, type ReconnectingListener, type ReconnectionCompletedListener, type ReconnectionFailedListener, RemoteManagementUnauthorizedError, type SavedTunnelConfig, type StartListener, type StatsListener, type Status, type StorageLogger, TUNNEL_CONFIG_V1_KEYS, TunnelClient, type TunnelConfigV1, TunnelConfigV1Schema, TunnelErrorCodeType, type TunnelList, TunnelManager, TunnelOperations, type TunnelResponse, TunnelStateType, type TunnelStatus, TunnelWarningCode, type TunnelWorkerErrorListner, type Warning, type WillReconnectListener, bulkReplace, closeRemoteManagement, configureStorageLogger, deleteConfig, enablePackageLogging, ensureTunnelConfigDir, findConfig, findConfigByName, getAutoStartConfigs, getDaemonInfo, getDaemonInfoPath, getPinggyConfigDir, getRemoteManagementState, getTunnelConfigDir, initiateRemoteManagement, isDaemonRunning, listSavedConfigs, loadConfigByName, sanitizeName, saveConfig, startRemoteManagement, updateConfigAutoStart, updateTunnelConfig, upsertConfig, validateName, validateNameForStorage, validateNameStrict };
package/dist/index.d.ts CHANGED
@@ -56,7 +56,7 @@ type FinalConfig = (TunnelConfigurationV1) & {
56
56
  conf?: string;
57
57
  saveconf?: string;
58
58
  };
59
- 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";
59
+ 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" | "DEVICE_NOT_ENROLLED";
60
60
  interface ErrorResponse {
61
61
  code: ErrorCodeType;
62
62
  message: string;
@@ -83,7 +83,8 @@ interface RemoteManagementState {
83
83
  * @singleton
84
84
  */
85
85
 
86
- type TunnelOrigin = "app" | "cli" | "remote";
86
+ /** Who asked the daemon for a tunnel. `device` is the device agent, acting for the dashboard's device page. */
87
+ type TunnelOrigin = "app" | "cli" | "remote" | "device";
87
88
  interface ManagedTunnel {
88
89
  tunnelid: string;
89
90
  configId: string;
@@ -96,6 +97,14 @@ interface ManagedTunnel {
96
97
  serve?: string;
97
98
  isStopped?: boolean;
98
99
  isStopping?: boolean;
100
+ /**
101
+ * Set on the entry a restart or config update replaces, until the new entry takes its place in the
102
+ * maps. The entry stays listed, so `GET /tunnels` never drops the tunnel. It reports `starting` when
103
+ * the new entry starts, and stays `exited` when a stopped tunnel only gets a new config.
104
+ */
105
+ rebuild?: {
106
+ startsAfter: boolean;
107
+ };
99
108
  createdAt?: string;
100
109
  startedAt?: string | null;
101
110
  stoppedAt?: string | null;
@@ -297,12 +306,30 @@ declare class TunnelManager implements ITunnelManager {
297
306
  * @throws Error if neither configId nor tunnelId is provided, or if tunnel is not found
298
307
  */
299
308
  getTunnelConfig(configId?: string, tunnelId?: string): Promise<TunnelConfigurationV1>;
309
+ /**
310
+ * The instance's config, asked of its worker. A stopped instance has let its worker go, so a stopped
311
+ * tunnel, including one a restart is replacing, answers with the config it was built from.
312
+ */
313
+ private currentConfig;
300
314
  /**
301
315
  * Stop a tunnel instance and wait for the stop to settle, capped by a
302
316
  * timeout. Failure is logged, not thrown: the caller replaces the
303
317
  * instance either way and a hung stop must not block that forever.
304
318
  */
305
319
  private stopInstanceGuarded;
320
+ /**
321
+ * Marks the entry a restart or config update replaces. It stays in both maps until
322
+ * `_createTunnelWithProcessedConfig` sets the new entry under the same ids. When the new entry
323
+ * starts, the entry reports `starting`, and an error from an earlier run no longer describes it.
324
+ */
325
+ private beginRebuild;
326
+ /** Stops the instance being replaced, and marks it stopped so no reader calls it again. */
327
+ private stopForRebuild;
328
+ /**
329
+ * Ends a failed rebuild. When the old entry is still in the maps, it stays listed as stopped with
330
+ * the reason. A new entry that failed to start already holds its own error.
331
+ */
332
+ private failRebuild;
306
333
  /**
307
334
  * Restarts a tunnel with its current configuration.
308
335
  * This function will stop the tunnel if it's running and start it again.
@@ -648,6 +675,9 @@ declare const Route: {
648
675
  readonly GetTunnelLogging: "GET /config/tunnel-logging";
649
676
  readonly SetTunnelLogging: "POST /config/tunnel-logging";
650
677
  readonly GetLogPaths: "GET /logs/paths";
678
+ readonly GetDeviceAgent: "GET /device";
679
+ readonly StartDeviceAgent: "POST /device/start";
680
+ readonly StopDeviceAgent: "POST /device/stop";
651
681
  };
652
682
  declare const ParamRoute: {
653
683
  readonly GetTunnel: "GET /tunnels/:id";
@@ -704,6 +734,35 @@ interface ShutdownResponse {
704
734
  errors: string[];
705
735
  }
706
736
  type LogLevel = "debug" | "info" | "error";
737
+ declare const DeviceAgentState: {
738
+ readonly Stopped: "stopped";
739
+ readonly Connecting: "connecting";
740
+ readonly Connected: "connected";
741
+ readonly Reconnecting: "reconnecting";
742
+ };
743
+ type DeviceAgentState = typeof DeviceAgentState[keyof typeof DeviceAgentState];
744
+ /** Why a device agent run ended for good. `code` names 1 of the agent's terminal outcomes. */
745
+ interface DeviceAgentError {
746
+ code: string;
747
+ message: string;
748
+ }
749
+ /**
750
+ * The device agent the daemon runs for `pinggy device connect -b`. It never carries the token: any
751
+ * local user can reach the IPC port.
752
+ */
753
+ interface DeviceAgentStatus {
754
+ state: DeviceAgentState;
755
+ deviceAgentId: string | null;
756
+ server: string | null;
757
+ /** The owner's email, from `welcome`. */
758
+ email: string | null;
759
+ /** The device's page in the dashboard, from `welcome`. */
760
+ deviceUrl: string | null;
761
+ startedAt: string | null;
762
+ connectedAt: string | null;
763
+ startedBy: TunnelOrigin | null;
764
+ lastError: DeviceAgentError | null;
765
+ }
707
766
  declare const SessionMode: {
708
767
  readonly Foreground: "foreground";
709
768
  readonly Detached: "detached";
@@ -818,6 +877,18 @@ type IPCRoutes = {
818
877
  req: void;
819
878
  res: LogPathsResponse;
820
879
  };
880
+ [Route.GetDeviceAgent]: {
881
+ req: void;
882
+ res: DeviceAgentStatus;
883
+ };
884
+ [Route.StartDeviceAgent]: {
885
+ req: Record<string, never>;
886
+ res: DeviceAgentStatus | ErrorResponse;
887
+ };
888
+ [Route.StopDeviceAgent]: {
889
+ req: Record<string, never>;
890
+ res: DeviceAgentStatus;
891
+ };
821
892
  };
822
893
  type RouteKey = keyof IPCRoutes;
823
894
  type RouteRes<K extends RouteKey> = IPCRoutes[K]["res"];
@@ -888,6 +959,11 @@ declare class TunnelOperations implements TunnelHandler {
888
959
  private buildTunnelResponse;
889
960
  private buildTunnelResponseV2;
890
961
  private error;
962
+ /**
963
+ * A restart or update of a tunnel another one is still rebuilding. A no-wait call checks this before
964
+ * it answers, because a throw from the call it does not await is only logged.
965
+ */
966
+ private rebuildInProgress;
891
967
  handleStart(config: TunnelConfig, noWait?: boolean, origin?: TunnelOrigin): Promise<TunnelResponse | ErrorResponse>;
892
968
  handleStartV2(config: TunnelConfigV1, noWait?: boolean, origin?: TunnelOrigin): Promise<TunnelResponseV2 | ErrorResponse>;
893
969
  handleUpdateConfig(config: TunnelConfig, noWait?: boolean): Promise<TunnelResponse | ErrorResponse>;
@@ -905,7 +981,7 @@ declare class TunnelOperations implements TunnelHandler {
905
981
  handleRemoveStoppedTunnelByTunnelId(tunnelId: string): boolean | ErrorResponse;
906
982
  }
907
983
 
908
- type ClientOrigin = "app" | "cli" | "remote";
984
+ type ClientOrigin = "app" | "cli" | "remote" | "device";
909
985
  declare class IPCClient {
910
986
  private port;
911
987
  private origin;
@@ -944,6 +1020,9 @@ declare class IPCClient {
944
1020
  }>;
945
1021
  getLogPaths(): Promise<LogPathsResponse>;
946
1022
  resolveLogPath(q: string): Promise<ResolveLogPathResponse>;
1023
+ getDeviceAgentStatus(timeoutMs?: number): Promise<DeviceAgentStatus>;
1024
+ startDeviceAgent(): Promise<RouteRes<typeof Route.StartDeviceAgent>>;
1025
+ stopDeviceAgent(): Promise<DeviceAgentStatus>;
947
1026
  /**
948
1027
  * Get the WebSocket URL for event streaming.
949
1028
  */
@@ -1119,6 +1198,9 @@ declare class TunnelClient {
1119
1198
  getLogPaths(): Promise<LogPathsResponse>;
1120
1199
  resolveLogPath(q: string): Promise<ResolveLogPathResponse>;
1121
1200
  restart(tunnelId: string): Promise<void>;
1201
+ getDeviceAgentStatus(): Promise<DeviceAgentStatus>;
1202
+ startDeviceAgent(): Promise<DeviceAgentStatus | ErrorResponse>;
1203
+ stopDeviceAgent(): Promise<DeviceAgentStatus>;
1122
1204
  attach(tunnelId: string, mode?: SubscriptionMode): Promise<void>;
1123
1205
  detach(tunnelId: string): void;
1124
1206
  onStats(cb: StatsCallback): void;
@@ -1148,55 +1230,6 @@ declare class TunnelClient {
1148
1230
  private assertClient;
1149
1231
  }
1150
1232
 
1151
- interface DaemonInfo {
1152
- pid: number;
1153
- port: number;
1154
- startedAt: string;
1155
- host?: DaemonHost;
1156
- /** IPC protocol version the daemon speaks. Absent in daemon.json written by older builds. */
1157
- ipcVersion?: number;
1158
- }
1159
-
1160
- /**
1161
- * Read and validate daemon.json.
1162
- * Returns null if file doesn't exist, is malformed, or PID is stale.
1163
- * Automatically cleans up stale daemon.json.
1164
- */
1165
- declare function getDaemonInfo(): DaemonInfo | null;
1166
- /**
1167
- * Check if the daemon is currently running.
1168
- */
1169
- declare function isDaemonRunning(): boolean;
1170
-
1171
- interface BaseLogConfig {
1172
- level?: string;
1173
- filePath?: string;
1174
- stdout?: boolean;
1175
- source?: boolean;
1176
- silent?: boolean;
1177
- enableSdkLog?: boolean;
1178
- }
1179
- type BaseLogConfigType = BaseLogConfig;
1180
- declare function enablePackageLogging(opts?: BaseLogConfigType): winston.Logger;
1181
-
1182
- declare class RemoteManagementUnauthorizedError extends Error {
1183
- constructor();
1184
- }
1185
- /**
1186
- * Initiate remote management mode with a WebSocket connection.
1187
- * - Connect with Authorization: Bearer <token>
1188
- * - On HTTP 401: print Unauthorized and exit
1189
- * - On other failures: retry every 15 seconds
1190
- * - Keep running until closed or SIGINT
1191
- */
1192
- declare function initiateRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
1193
- declare function closeRemoteManagement(timeoutMs?: number): Promise<RemoteManagementState>;
1194
- /**
1195
- * Start remote management loop in background; returns after first connection attempt.
1196
- */
1197
- declare function startRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
1198
- declare function getRemoteManagementState(): RemoteManagementState;
1199
-
1200
1233
  interface SavedTunnelConfig {
1201
1234
  name: string;
1202
1235
  configId: string;
@@ -1316,6 +1349,55 @@ declare function updateTunnelConfig(nameOrId: string, tunnelConfig: TunnelConfig
1316
1349
  */
1317
1350
  declare function getAutoStartConfigs(): SavedTunnelConfig[];
1318
1351
 
1352
+ interface DaemonInfo {
1353
+ pid: number;
1354
+ port: number;
1355
+ startedAt: string;
1356
+ host?: DaemonHost;
1357
+ /** IPC protocol version the daemon speaks. Absent in daemon.json written by older builds. */
1358
+ ipcVersion?: number;
1359
+ }
1360
+
1361
+ /**
1362
+ * Read and validate daemon.json.
1363
+ * Returns null if file doesn't exist, is malformed, or PID is stale.
1364
+ * Automatically cleans up stale daemon.json.
1365
+ */
1366
+ declare function getDaemonInfo(): DaemonInfo | null;
1367
+ /**
1368
+ * Check if the daemon is currently running.
1369
+ */
1370
+ declare function isDaemonRunning(): boolean;
1371
+
1372
+ interface BaseLogConfig {
1373
+ level?: string;
1374
+ filePath?: string;
1375
+ stdout?: boolean;
1376
+ source?: boolean;
1377
+ silent?: boolean;
1378
+ enableSdkLog?: boolean;
1379
+ }
1380
+ type BaseLogConfigType = BaseLogConfig;
1381
+ declare function enablePackageLogging(opts?: BaseLogConfigType): winston.Logger;
1382
+
1383
+ declare class RemoteManagementUnauthorizedError extends Error {
1384
+ constructor();
1385
+ }
1386
+ /**
1387
+ * Initiate remote management mode with a WebSocket connection.
1388
+ * - Connect with Authorization: Bearer <token>
1389
+ * - On HTTP 401: print Unauthorized and exit
1390
+ * - On other failures: retry every 15 seconds
1391
+ * - Keep running until closed or SIGINT
1392
+ */
1393
+ declare function initiateRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
1394
+ declare function closeRemoteManagement(timeoutMs?: number): Promise<RemoteManagementState>;
1395
+ /**
1396
+ * Start remote management loop in background; returns after first connection attempt.
1397
+ */
1398
+ declare function startRemoteManagement(remoteManagementConfig: RemoteManagementConfig, tunnelHandler?: TunnelHandler): Promise<RemoteManagementState>;
1399
+ declare function getRemoteManagementState(): RemoteManagementState;
1400
+
1319
1401
  /**
1320
1402
  * Returns the base Pinggy config directory for the current platform.
1321
1403
  * - Linux/macOS: $XDG_CONFIG_HOME/pinggy or ~/.config/pinggy
@@ -1336,4 +1418,4 @@ declare function ensureTunnelConfigDir(): string;
1336
1418
  */
1337
1419
  declare function getDaemonInfoPath(): string;
1338
1420
 
1339
- export { type AdditionalForwarding, type DaemonInfo, type DisconnectListener, type ErrorListener, type FinalConfig, type ITunnelManager, type ManagedTunnel, type ReconnectingListener, type ReconnectionCompletedListener, type ReconnectionFailedListener, RemoteManagementUnauthorizedError, type SavedTunnelConfig, type StartListener, type StatsListener, type Status, type StorageLogger, TUNNEL_CONFIG_V1_KEYS, TunnelClient, type TunnelConfigV1, TunnelConfigV1Schema, TunnelErrorCodeType, type TunnelList, TunnelManager, TunnelOperations, type TunnelResponse, TunnelStateType, type TunnelStatus, TunnelWarningCode, type TunnelWorkerErrorListner, type Warning, type WillReconnectListener, bulkReplace, closeRemoteManagement, configureStorageLogger, deleteConfig, enablePackageLogging, ensureTunnelConfigDir, findConfig, findConfigByName, getAutoStartConfigs, getDaemonInfo, getDaemonInfoPath, getPinggyConfigDir, getRemoteManagementState, getTunnelConfigDir, initiateRemoteManagement, isDaemonRunning, listSavedConfigs, loadConfigByName, sanitizeName, saveConfig, startRemoteManagement, updateConfigAutoStart, updateTunnelConfig, upsertConfig, validateName, validateNameForStorage, validateNameStrict };
1421
+ export { type AdditionalForwarding, type DaemonInfo, type DeviceAgentError, DeviceAgentState, type DeviceAgentStatus, type DisconnectListener, type ErrorListener, type FinalConfig, type ITunnelManager, type ManagedTunnel, type ReconnectingListener, type ReconnectionCompletedListener, type ReconnectionFailedListener, RemoteManagementUnauthorizedError, type SavedTunnelConfig, type StartListener, type StatsListener, type Status, type StorageLogger, TUNNEL_CONFIG_V1_KEYS, TunnelClient, type TunnelConfigV1, TunnelConfigV1Schema, TunnelErrorCodeType, type TunnelList, TunnelManager, TunnelOperations, type TunnelResponse, TunnelStateType, type TunnelStatus, TunnelWarningCode, type TunnelWorkerErrorListner, type Warning, type WillReconnectListener, bulkReplace, closeRemoteManagement, configureStorageLogger, deleteConfig, enablePackageLogging, ensureTunnelConfigDir, findConfig, findConfigByName, getAutoStartConfigs, getDaemonInfo, getDaemonInfoPath, getPinggyConfigDir, getRemoteManagementState, getTunnelConfigDir, initiateRemoteManagement, isDaemonRunning, listSavedConfigs, loadConfigByName, sanitizeName, saveConfig, startRemoteManagement, updateConfigAutoStart, updateTunnelConfig, upsertConfig, validateName, validateNameForStorage, validateNameStrict };
package/dist/index.js CHANGED
@@ -4,18 +4,17 @@ import {
4
4
  TUNNEL_CONFIG_V1_KEYS,
5
5
  TunnelClient,
6
6
  TunnelConfigV1Schema,
7
- TunnelErrorCodeType,
8
7
  TunnelOperations,
9
- TunnelStateType,
10
- TunnelWarningCode,
11
8
  closeRemoteManagement,
12
9
  getDaemonInfo,
13
10
  getRemoteManagementState,
14
11
  initiateRemoteManagement,
15
12
  isDaemonRunning,
16
13
  startRemoteManagement
17
- } from "./chunk-2HGC37SK.js";
18
- import "./chunk-I4HXBP5G.js";
14
+ } from "./chunk-GESMZQHI.js";
15
+ import {
16
+ DeviceAgentState
17
+ } from "./chunk-7EQKHVQX.js";
19
18
  import {
20
19
  bulkReplace,
21
20
  configureStorageLogger,
@@ -33,21 +32,24 @@ import {
33
32
  validateName,
34
33
  validateNameForStorage,
35
34
  validateNameStrict
36
- } from "./chunk-H5XJMFNT.js";
35
+ } from "./chunk-Y435RMHQ.js";
37
36
  import {
37
+ TunnelErrorCodeType,
38
38
  TunnelManager,
39
+ TunnelStateType,
40
+ TunnelWarningCode,
39
41
  printer_default
40
- } from "./chunk-J2U6P376.js";
42
+ } from "./chunk-QNUVQ6KD.js";
41
43
  import "./chunk-UB26QJ4T.js";
42
44
  import {
43
45
  enablePackageLogging
44
- } from "./chunk-76SOSXXR.js";
46
+ } from "./chunk-MV56U3E6.js";
45
47
  import {
46
48
  ensureTunnelConfigDir,
47
49
  getDaemonInfoPath,
48
50
  getPinggyConfigDir,
49
51
  getTunnelConfigDir
50
- } from "./chunk-GBYF2H4H.js";
52
+ } from "./chunk-AFMTIELL.js";
51
53
 
52
54
  // src/utils/detect_vc_redist_on_windows.ts
53
55
  import fs from "fs";
@@ -139,12 +141,13 @@ async function verifyAndLoad() {
139
141
  process.exit(1);
140
142
  }
141
143
  }
142
- await import("./main-WQ7NUQL4.js");
144
+ await import("./main-M6MACH7D.js");
143
145
  }
144
146
  verifyAndLoad().catch((err) => {
145
147
  printer_default.fatal(`Failed to start CLI:, ${err}`);
146
148
  });
147
149
  export {
150
+ DeviceAgentState,
148
151
  RemoteManagementUnauthorizedError,
149
152
  TUNNEL_CONFIG_V1_KEYS,
150
153
  TunnelClient,
@@ -0,0 +1,10 @@
1
+ import {
2
+ HTTP_NOT_FOUND,
3
+ IPCClient,
4
+ IPCHttpError
5
+ } from "./chunk-7EQKHVQX.js";
6
+ export {
7
+ HTTP_NOT_FOUND,
8
+ IPCClient,
9
+ IPCHttpError
10
+ };