dsh-mobile 0.3.1 → 0.3.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/lib/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
+ import { ChildProcessWithoutNullStreams } from "node:child_process";
2
3
  import { Readable } from "node:stream";
3
4
  import { Context, Service } from "@deepseek-ai/cordis";
4
5
  import { WebRoute } from "@deepseek-ai/dsh-host-webserver";
@@ -373,6 +374,9 @@ declare const EXTENSION_LIMITS: Readonly<{
373
374
  script: number;
374
375
  css: number;
375
376
  asset: number;
377
+ assetFiles: 256;
378
+ assetBytes: number;
379
+ assetDepth: 8;
376
380
  }>;
377
381
  /** A controlled business failure returned by an extension action or route. */
378
382
  declare class MobileExtensionError extends Error {
@@ -402,7 +406,7 @@ interface MobileRouteRequest {
402
406
  readonly signal: AbortSignal;
403
407
  readonly deviceId: string;
404
408
  }
405
- /** Values an extension route may return. */
409
+ /** Values an extension route may return; status is a final HTTP code from 200 through 599. */
406
410
  interface MobileRouteResponse {
407
411
  readonly status?: number;
408
412
  readonly contentType?: string;
@@ -438,10 +442,16 @@ declare module '@deepseek-ai/cordis' {
438
442
  interface LocalExtensionManifest extends MobileExtensionManifest {}
439
443
  /** Public snapshot sent to the mobile browser. */
440
444
  interface MobileExtensionClientEntry extends MobileExtensionManifest {
445
+ readonly generation?: string;
441
446
  readonly scriptUrl?: string;
442
447
  readonly styleUrl?: string;
443
448
  readonly assetsUrl?: string;
444
449
  }
450
+ interface LocalAssetSnapshot {
451
+ readonly body: Buffer;
452
+ readonly digest: string;
453
+ readonly name: string;
454
+ }
445
455
  /** Small status summary used by the desktop mobile-access card. */
446
456
  interface MobileExtensionStatus {
447
457
  readonly loaded: number;
@@ -450,8 +460,9 @@ interface MobileExtensionStatus {
450
460
  interface ActiveLocalExtension {
451
461
  readonly manifest: LocalExtensionManifest;
452
462
  readonly directory: string;
453
- readonly scriptFile?: string;
454
- readonly styleFile?: string;
463
+ readonly scriptBody?: Buffer;
464
+ readonly styleBody?: Buffer;
465
+ readonly assets: ReadonlyMap<string, LocalAssetSnapshot>;
455
466
  readonly host: MobileExtensionDefinition;
456
467
  readonly controller: AbortController;
457
468
  readonly cleanups: readonly (() => void | Promise<void>)[];
@@ -465,42 +476,48 @@ declare function parseExtensionManifest(value: unknown): LocalExtensionManifest;
465
476
  declare class MobileAccessService extends Service {
466
477
  private readonly registered;
467
478
  private readonly local;
479
+ private readonly retired;
468
480
  private readonly failures;
481
+ private readonly contentListeners;
469
482
  private contentHash;
470
483
  private localRoot;
471
484
  private localContext;
472
485
  private localTimer;
473
486
  private localRefreshing;
487
+ private localRefreshAbort;
488
+ private localLifecycle;
474
489
  private localClosed;
475
490
  constructor(ctx: Context);
476
491
  /** Register a normal Cordis extension and return an idempotent disposer. */
477
492
  registerExtension(definition: MobileExtensionDefinition): () => void;
478
493
  /** Aggregate digest covering every registered and active local extension. */
479
494
  contentDigest(): string;
495
+ /** Subscribe to committed extension generation changes. */
496
+ onContentChanged(listener: () => void): () => void;
480
497
  private updateContentHash;
481
498
  /** Return the current client-facing manifest, deterministically sorted by id. */
482
499
  manifest(): readonly MobileExtensionClientEntry[];
483
500
  /** Return loaded and failed local extension counts without exposing host errors. */
484
501
  status(): MobileExtensionStatus;
485
502
  /** Locate one active extension. */
486
- extension(id: string): MobileExtensionDefinition | ActiveLocalExtension | undefined;
503
+ extension(id: string, generation?: string): MobileExtensionDefinition | ActiveLocalExtension | undefined;
487
504
  /** Return the active local generation signal for gateway cancellation wiring. */
488
- signal(id: string): AbortSignal | undefined;
505
+ signal(id: string, generation?: string): AbortSignal | undefined;
489
506
  /** Read a local client entry after validating that it remains inside its directory. */
490
- readClientFile(id: string, kind: 'script' | 'style', signal?: AbortSignal): Promise<{
507
+ readClientFile(id: string, kind: 'script' | 'style', signal?: AbortSignal, generation?: string): Promise<{
491
508
  readonly body: Buffer;
492
509
  readonly digest: string;
493
510
  }>;
494
- /** Read a local static asset after containment and size checks. */
495
- readAsset(id: string, assetPath: string, signal?: AbortSignal): Promise<{
511
+ /** Read a generation-pinned static asset from its validated snapshot. */
512
+ readAsset(id: string, assetPath: string, signal?: AbortSignal, generation?: string): Promise<{
496
513
  readonly body: Buffer;
497
514
  readonly digest: string;
498
515
  readonly name: string;
499
516
  }>;
500
517
  /** Invoke one action after parsing its input and binding the request lifetime. */
501
- invoke(id: string, actionName: string, input: unknown, context: MobileActionContext): Promise<unknown>;
518
+ invoke(id: string, actionName: string, input: unknown, context: MobileActionContext, generation?: string): Promise<unknown>;
502
519
  /** Match one route and invoke it with a generation-bound abort signal. */
503
- route(id: string, method: string, pathname: string, request: MobileRouteRequest): Promise<MobileRouteResponse>;
520
+ route(id: string, method: string, pathname: string, request: MobileRouteRequest, generation?: string): Promise<MobileRouteResponse>;
504
521
  /** Start the local directory watcher; an absent directory is intentionally inert. */
505
522
  startLocal(root: string, context: Context): Promise<void>;
506
523
  /** Stop the watcher and abort every local host generation. */
@@ -508,6 +525,7 @@ declare class MobileAccessService extends Service {
508
525
  /** Refresh all local extensions atomically; failures keep the previous snapshot. */
509
526
  refreshLocal(): Promise<void>;
510
527
  private stageAndCommit;
528
+ private retire;
511
529
  }
512
530
  /** Construct the service in a Cordis plugin without importing DSH internals. */
513
531
  declare function createMobileAccessService(ctx: Context): MobileAccessService;
@@ -534,6 +552,11 @@ declare class MobileAccessGateway {
534
552
  private readonly activeRequests;
535
553
  private readonly activeWebSockets;
536
554
  private readonly mobileBootBatches;
555
+ private readonly extensionEventListeners;
556
+ private extensionEventRevision;
557
+ private extensionChangeTimer;
558
+ private extensionChangeTask;
559
+ private legacyCustomDigest;
537
560
  private upstreamCookie;
538
561
  private upstreamCookieExpiresAt;
539
562
  private upstreamCookieTask;
@@ -543,6 +566,7 @@ declare class MobileAccessGateway {
543
566
  private started;
544
567
  private closeTask;
545
568
  private readonly removeSessionListener;
569
+ private readonly removeExtensionContentListener;
546
570
  private readonly renewLimiter;
547
571
  constructor(config: ResolvedGatewayConfig, store: DeviceStore, extensions?: MobileAccessService | undefined, upstreamAuthenticatedUrl?: string | undefined);
548
572
  /** Initialize durable state, validate TLS, and bind the externally reachable listener. */
@@ -580,6 +604,9 @@ declare class MobileAccessGateway {
580
604
  private allocateRequest;
581
605
  private proxyHttp;
582
606
  private abortSessionResources;
607
+ private broadcastExtensionChange;
608
+ private pollLegacyCustomChanges;
609
+ private openExtensionEventStream;
583
610
  private readUpgradeResponse;
584
611
  private handleUpgrade;
585
612
  /** Loopback-only DSH WebServer route for opening pairing and managing devices. */
@@ -605,6 +632,227 @@ declare const LOCAL_ADMIN_PREFIX = "/api/mobile-access";
605
632
  declare const AUTH_PREFIX = "/mobile-access";
606
633
  declare const WS_PATHS: Set<string>;
607
634
  //#endregion
635
+ //#region src/frp-component.d.ts
636
+ interface FrpArtifact {
637
+ readonly platform: NodeJS.Platform;
638
+ readonly arch: string;
639
+ readonly downloadUrl: string;
640
+ readonly downloadBytes: number;
641
+ readonly downloadSha256: string;
642
+ readonly archiveName: string;
643
+ readonly executableName: string;
644
+ }
645
+ /** Pinned official FRP release metadata for supported desktop targets. */
646
+ declare const FRP_COMPONENT_RELEASES: Readonly<Record<string, FrpArtifact>>;
647
+ /** Public, credential-free description of the managed FRP client. */
648
+ interface FrpComponentStatus {
649
+ readonly supported: boolean;
650
+ readonly installed: boolean;
651
+ readonly version: string;
652
+ readonly downloadBytes: number;
653
+ readonly installedBytes: number;
654
+ readonly sourceUrl: string;
655
+ readonly releasePage: string;
656
+ readonly storagePath: string;
657
+ readonly errorCode?: string;
658
+ }
659
+ interface FrpComponentManagerOptions {
660
+ readonly stateDirectory: string;
661
+ readonly platform?: NodeJS.Platform;
662
+ readonly arch?: string;
663
+ readonly fetchArtifact?: (artifact: FrpArtifact, signal: AbortSignal) => Promise<Uint8Array>;
664
+ readonly extractArtifact?: (archive: string, destination: string, executableName: string) => Promise<void>;
665
+ readonly inspectExecutable?: (executable: string) => Promise<string>;
666
+ }
667
+ /** Owns the optional official frpc binary inside the DSH Mobile state directory. */
668
+ declare class FrpComponentManager {
669
+ readonly executable: string;
670
+ readonly componentRoot: string;
671
+ readonly componentStorage: string;
672
+ readonly logRoot: string;
673
+ private readonly stagingRoot;
674
+ private readonly artifact;
675
+ private readonly fetchArtifact;
676
+ private readonly extractArtifact;
677
+ private readonly inspectExecutable;
678
+ private installed;
679
+ private installedBytes;
680
+ private errorCode;
681
+ private queue;
682
+ constructor(options: FrpComponentManagerOptions);
683
+ /** Inspect the managed executable without relying on global FRP installations. */
684
+ initialize(): Promise<void>;
685
+ /** Return component metadata without exposing configuration or credentials. */
686
+ status(): FrpComponentStatus;
687
+ /** Download, verify, and extract only frpc after explicit confirmation. */
688
+ install(): Promise<FrpComponentStatus>;
689
+ /** Remove all FRP executable, staging, and log files owned by DSH Mobile. */
690
+ purge(): Promise<FrpComponentStatus>;
691
+ private enqueue;
692
+ }
693
+ //#endregion
694
+ //#region src/frp-template.d.ts
695
+ /** Loopback-only HTTP vhost port used between Caddy and frps. */
696
+ declare const FRP_VHOST_HTTP_PORT = 7080;
697
+ /** Build the only supported frps and Caddy configuration from validated user inputs. */
698
+ declare function createRestrictedFrpServerTemplate(serverPort: number, token: string, publicOrigin: string): string;
699
+ //#endregion
700
+ //#region src/frp-config.d.ts
701
+ /** Credentials and endpoints required by the restricted FRP provider. */
702
+ interface FrpSettings {
703
+ readonly version: 1;
704
+ readonly serverAddress: string;
705
+ readonly serverPort: number;
706
+ readonly token: string;
707
+ readonly publicOrigin: string;
708
+ }
709
+ /** Safe FRP configuration fields returned to the desktop UI. */
710
+ interface FrpConfigurationStatus {
711
+ readonly configured: boolean;
712
+ readonly serverAddress?: string;
713
+ readonly serverPort?: number;
714
+ readonly publicOrigin?: string;
715
+ readonly vhostHttpPort: number;
716
+ readonly storagePath: string;
717
+ readonly errorCode?: string;
718
+ }
719
+ /** Validate the FRP server hostname or IP address. */
720
+ declare function validateFrpServerAddress(value: unknown): string;
721
+ /** Validate the FRP control port. */
722
+ declare function validateFrpServerPort(value: unknown): number;
723
+ /** Validate a high-entropy FRP token before durable storage. */
724
+ declare function validateFrpToken(value: unknown): string;
725
+ /** Validate the public HTTPS origin used by Caddy and Android pairing. */
726
+ declare function validateFrpPublicOrigin(value: unknown): string;
727
+ /** Parse FRP settings at the loopback request and filesystem boundaries. */
728
+ declare function parseFrpSettings(value: unknown): FrpSettings;
729
+ /** Build the single-purpose frpc configuration for the current loopback gateway. */
730
+ declare function createFrpcToml(settings: FrpSettings, localPort: number): string;
731
+ /** Build the matching restricted frps and Caddy templates for one VPS. */
732
+ declare function createFrpServerTemplate(settings: FrpSettings): string;
733
+ /** Owns private FRP settings and generation-specific frpc configuration. */
734
+ declare class FrpConfigStore {
735
+ readonly stateRoot: string;
736
+ readonly settingsFile: string;
737
+ readonly runtimeConfigFile: string;
738
+ private settingsValue;
739
+ private errorCode;
740
+ constructor(stateDirectory: string);
741
+ /** Load private settings while rejecting links, oversized files, and unknown fields. */
742
+ initialize(): Promise<void>;
743
+ /** Return configuration metadata without exposing the FRP token. */
744
+ status(): FrpConfigurationStatus;
745
+ /** Return private settings only to the provider lifecycle. */
746
+ settings(): FrpSettings | undefined;
747
+ /** Atomically replace private FRP settings. */
748
+ configure(value: unknown): Promise<FrpConfigurationStatus>;
749
+ /** Materialize the private generation-specific frpc configuration. */
750
+ writeRuntimeConfig(localPort: number): Promise<string>;
751
+ /** Remove only configuration files owned by the FRP provider. */
752
+ purge(): Promise<FrpConfigurationStatus>;
753
+ }
754
+ //#endregion
755
+ //#region src/remote.d.ts
756
+ /** Remote transports supported by the desktop plugin and Android client. */
757
+ type RemoteProvider = 'tailscale' | 'cpolar' | 'frp';
758
+ /** Common safe status returned by every remote provider controller. */
759
+ interface RemoteProviderStatus {
760
+ readonly enabled: boolean;
761
+ readonly state: string;
762
+ readonly origin?: string;
763
+ readonly loginUrl?: string;
764
+ readonly setupUrl?: string;
765
+ readonly errorCode?: string;
766
+ }
767
+ /** Lifecycle shared by selectable remote providers. */
768
+ interface RemoteProviderController {
769
+ initialize(): Promise<void>;
770
+ gateway(): MobileAccessGateway | undefined;
771
+ status(): RemoteProviderStatus;
772
+ setEnabled(enabled: boolean): Promise<RemoteProviderStatus>;
773
+ reconnect(): Promise<RemoteProviderStatus>;
774
+ reset(): Promise<RemoteProviderStatus>;
775
+ close(): Promise<void>;
776
+ }
777
+ /** Durable selection for the single active remote transport. */
778
+ interface RemoteProviderState {
779
+ readonly version: 1;
780
+ readonly provider: RemoteProvider;
781
+ }
782
+ /** Validate the provider selection loaded across the filesystem boundary. */
783
+ declare function parseRemoteProviderState(value: unknown): RemoteProviderState;
784
+ /** Atomic selection store whose absent-file state uses the configured default. */
785
+ declare class JsonRemoteProviderStore {
786
+ private readonly file;
787
+ private readonly defaultProvider;
788
+ constructor(file: string, defaultProvider: RemoteProvider);
789
+ load(): Promise<RemoteProviderState>;
790
+ save(state: RemoteProviderState): Promise<void>;
791
+ }
792
+ /** Resolve the first-run provider without letting environment values bypass validation. */
793
+ declare function configuredRemoteProvider(environment: NodeJS.ProcessEnv): RemoteProvider;
794
+ //#endregion
795
+ //#region src/frp.d.ts
796
+ /** Product-facing states for the restricted self-hosted FRP transport. */
797
+ type FrpState = 'off' | 'unavailable' | 'starting' | 'connecting' | 'ready' | 'error';
798
+ /** Safe FRP state returned only through the loopback DSH control route. */
799
+ interface FrpStatus {
800
+ readonly enabled: boolean;
801
+ readonly state: FrpState;
802
+ readonly origin?: string;
803
+ readonly errorCode?: string;
804
+ }
805
+ /** Inputs for one FRP client process and authenticated DSH gateway. */
806
+ interface FrpControllerOptions {
807
+ readonly store: MobileAccessControlStore;
808
+ readonly executable: string;
809
+ readonly config: FrpConfigStore;
810
+ readonly instanceId: string;
811
+ readonly createGateway: (origin: string) => Promise<MobileAccessGateway>;
812
+ readonly onStatus?: (status: FrpStatus) => void;
813
+ readonly verifyConfig?: (executable: string, configFile: string) => Promise<void>;
814
+ readonly launchClient?: (executable: string, configFile: string) => ChildProcessWithoutNullStreams;
815
+ readonly probeVhostExposure?: (serverAddress: string, port: number) => Promise<boolean>;
816
+ readonly probeDiscovery?: (origin: string, expectedInstanceId: string, signal: AbortSignal) => Promise<boolean>;
817
+ readonly startTimeoutMs?: number;
818
+ readonly retryIntervalMs?: number;
819
+ }
820
+ /** Owns frpc, its generation-specific configuration, and the remote gateway. */
821
+ declare class FrpController implements RemoteProviderController {
822
+ private readonly options;
823
+ private enabled;
824
+ private initialized;
825
+ private disposed;
826
+ private child;
827
+ private gatewayValue;
828
+ private generation;
829
+ private latest;
830
+ private queue;
831
+ private startupAbort;
832
+ constructor(options: FrpControllerOptions);
833
+ /** Restore the remembered FRP switch without changing LAN or other providers. */
834
+ initialize(): Promise<void>;
835
+ /** Return the active FRP-backed DSH gateway. */
836
+ gateway(): MobileAccessGateway | undefined;
837
+ /** Return state safe for the desktop control UI. */
838
+ status(): FrpStatus;
839
+ /** Enable or disable FRP without changing LAN or another provider. */
840
+ setEnabled(enabled: boolean): Promise<FrpStatus>;
841
+ /** Restart FRP while retaining its private server settings and devices. */
842
+ reconnect(): Promise<FrpStatus>;
843
+ /** Disable FRP without deleting its explicitly managed component or settings. */
844
+ reset(): Promise<FrpStatus>;
845
+ /** Stop all FRP resources without changing the remembered switch. */
846
+ close(): Promise<void>;
847
+ private enqueue;
848
+ private publish;
849
+ private start;
850
+ private waitForDiscovery;
851
+ private failGeneration;
852
+ private stop;
853
+ private stopProcessAndGateway;
854
+ }
855
+ //#endregion
608
856
  //#region src/plugin.d.ts
609
857
  /** Stable Cordis plugin name. */
610
858
  declare const name = "dsh-mobile";
@@ -613,5 +861,5 @@ declare const inject: string[];
613
861
  /** Mount the resident control route and its optional authenticated LAN gateway. */
614
862
  declare function apply(ctx: Context, config: PluginConfig): Promise<void>;
615
863
  //#endregion
616
- export { AUTH_PREFIX, AccessController, type AccessControllerOptions, AccessError, type AuthoritySpec, BoundedRateLimiter, CSRF_COOKIE, CSRF_HEADER, Config, DEVICE_COOKIE, type DeviceSnapshot, type DeviceStore, type DeviceSummary, type DisabledTlsConfig, EXTENSION_LIMITS, JsonDeviceStore, JsonMobileAccessControlStore, LOCAL_ADMIN_PREFIX, type LocalExtensionManifest, MemoryDeviceStore, type MobileAccessControlState, type MobileAccessControlStore, MobileAccessGateway, MobileAccessGatewayController, type MobileAccessService as MobileAccessRegistry, MobileAccessService, type MobileAccessRuntime, type MobileActionContext, type MobileExtensionClientEntry, type MobileExtensionDefinition, MobileExtensionError, type MobileExtensionManifest, type MobileExtensionStatus, type MobileHostAction, type MobileHostRoute, type MobileRouteRequest, type MobileRouteResponse, type PairingResult, type ParsedCidr, type PluginConfig, type ProvidedTlsConfig, type RenewalResult, RequestTrustPolicy, type ResolvedGatewayConfig, SESSION_COOKIE, SUPPORTED_DSH_VERSIONS, type SessionAuthorization, type StoredDevice, type TlsConfig, WS_PATHS, addressAllowed, apply, assertExtensionId, assertSupportedDshVersion, createMobileAccessService, inject, isLoopbackAddress, name, parseAuthority, parseCidr, parseControlFile, parseDeviceSnapshot, parseExtensionManifest, parseGatewayConfig, parseMobileAccessControlState, resolveAuthority, rewriteMobileIndex };
864
+ export { AUTH_PREFIX, AccessController, type AccessControllerOptions, AccessError, type AuthoritySpec, BoundedRateLimiter, CSRF_COOKIE, CSRF_HEADER, Config, FRP_VHOST_HTTP_PORT as DEFAULT_VHOST_HTTP_PORT, FRP_VHOST_HTTP_PORT, DEVICE_COOKIE, type DeviceSnapshot, type DeviceStore, type DeviceSummary, type DisabledTlsConfig, EXTENSION_LIMITS, FRP_COMPONENT_RELEASES, FrpComponentManager, type FrpComponentStatus, FrpConfigStore, type FrpConfigurationStatus, FrpController, type FrpControllerOptions, type FrpSettings, type FrpState, type FrpStatus, JsonDeviceStore, JsonMobileAccessControlStore, JsonRemoteProviderStore, LOCAL_ADMIN_PREFIX, type LocalExtensionManifest, MemoryDeviceStore, type MobileAccessControlState, type MobileAccessControlStore, MobileAccessGateway, MobileAccessGatewayController, type MobileAccessService as MobileAccessRegistry, MobileAccessService, type MobileAccessRuntime, type MobileActionContext, type MobileExtensionClientEntry, type MobileExtensionDefinition, MobileExtensionError, type MobileExtensionManifest, type MobileExtensionStatus, type MobileHostAction, type MobileHostRoute, type MobileRouteRequest, type MobileRouteResponse, type PairingResult, type ParsedCidr, type PluginConfig, type ProvidedTlsConfig, type RemoteProvider, type RemoteProviderController, type RemoteProviderState, type RemoteProviderStatus, type RenewalResult, RequestTrustPolicy, type ResolvedGatewayConfig, SESSION_COOKIE, SUPPORTED_DSH_VERSIONS, type SessionAuthorization, type StoredDevice, type TlsConfig, WS_PATHS, addressAllowed, apply, assertExtensionId, assertSupportedDshVersion, configuredRemoteProvider, createFrpServerTemplate, createFrpcToml, createMobileAccessService, createRestrictedFrpServerTemplate, inject, isLoopbackAddress, name, parseAuthority, parseCidr, parseControlFile, parseDeviceSnapshot, parseExtensionManifest, parseFrpSettings, parseGatewayConfig, parseMobileAccessControlState, parseRemoteProviderState, resolveAuthority, rewriteMobileIndex, validateFrpPublicOrigin, validateFrpServerAddress, validateFrpServerPort, validateFrpToken };
617
865
  //# sourceMappingURL=index.d.mts.map