dsh-mobile 0.8.2 → 0.8.4

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
@@ -1554,6 +1554,12 @@ interface FrpSettings {
1554
1554
  readonly vhostHttpPort?: number;
1555
1555
  /** Public entry port of the self-signed TCP proxy; defaults to 33080. */
1556
1556
  readonly publicPort?: number;
1557
+ /**
1558
+ * Optional frpc `user` field for shared frps services that namespace proxies
1559
+ * per user. When set, frps registers the proxy as `<user>.<proxyName>`;
1560
+ * the locally derived proxy name is unchanged. Absent keeps upstream behaviour.
1561
+ */
1562
+ readonly user?: string;
1557
1563
  }
1558
1564
  /** Safe FRP configuration fields returned to the desktop UI. */
1559
1565
  interface FrpConfigurationStatus {
@@ -1572,6 +1578,8 @@ interface FrpConfigurationStatus {
1572
1578
  readonly entryTls?: FrpEntryTls;
1573
1579
  /** Present only when a non-default public entry port is saved. */
1574
1580
  readonly publicPort?: number;
1581
+ /** Present only when the optional frpc `user` field is saved. */
1582
+ readonly user?: string;
1575
1583
  }
1576
1584
  /** Validate the FRP server hostname or IP address. */
1577
1585
  export declare function validateFrpServerAddress(value: unknown): string;
package/lib/index.mjs CHANGED
@@ -8244,7 +8244,7 @@ var MobileAccessGateway = class {
8244
8244
  }
8245
8245
  }
8246
8246
  operation.signal.throwIfAborted();
8247
- const headers = sanitizeResponseHeaders(index.headers, this.config.upstreamOrigin);
8247
+ const headers = sanitizeResponseHeaders(index.headers, new URL(request.url ?? "/", this.config.upstreamOrigin));
8248
8248
  delete headers["content-length"];
8249
8249
  delete headers["content-encoding"];
8250
8250
  delete headers.etag;
@@ -8749,7 +8749,7 @@ var MobileAccessGateway = class {
8749
8749
  }
8750
8750
  if (graphEvents) holder.request?.setTimeout(this.config.upstreamApiTimeoutMs);
8751
8751
  setSecurityHeaders(response, this.tlsEnabled, "proxied");
8752
- const headers = sanitizeResponseHeaders(proxied.headers, this.config.upstreamOrigin);
8752
+ const headers = sanitizeResponseHeaders(proxied.headers, new URL(request$2.url ?? "/", this.config.upstreamOrigin));
8753
8753
  if (graphEvents) {
8754
8754
  if (proxied.headers["content-encoding"] !== void 0 && proxied.headers["content-encoding"] !== "identity") throw new HttpError(502, "upstream_unavailable");
8755
8755
  delete headers["content-length"];
@@ -10954,7 +10954,8 @@ const FRP_SETTINGS_KEYS = /* @__PURE__ */ new Set([
10954
10954
  "mode",
10955
10955
  "entryTls",
10956
10956
  "vhostHttpPort",
10957
- "publicPort"
10957
+ "publicPort",
10958
+ "user"
10958
10959
  ]);
10959
10960
  function hostname$2(value) {
10960
10961
  if (value.length > 253 || !value.includes(".")) return false;
@@ -10977,6 +10978,11 @@ function validateFrpToken(value) {
10977
10978
  if (typeof value !== "string" || value.length < 16 || value.length > 512 || /[\s\u0000-\u001f\u007f]/u.test(value)) throw new Error("frp_token_invalid");
10978
10979
  return value;
10979
10980
  }
10981
+ /** Validate the optional frpc `user` field; it must stay one safe TOML token. */
10982
+ function validateFrpUser(value) {
10983
+ if (typeof value !== "string" || value.length < 1 || value.length > 128 || !/^[A-Za-z0-9._-]+$/u.test(value)) throw new Error("frp_user_invalid");
10984
+ return value;
10985
+ }
10980
10986
  /** Validate the public HTTPS origin used by Caddy and Android pairing. */
10981
10987
  function validateFrpPublicOrigin(value) {
10982
10988
  if (typeof value !== "string" || value.length > 512) throw new Error("frp_public_origin_invalid");
@@ -11067,6 +11073,7 @@ function parseFrpSettings(value) {
11067
11073
  const entryTls = record.entryTls === void 0 ? void 0 : validateFrpEntryTls(record.entryTls);
11068
11074
  const vhostHttpPort = record.vhostHttpPort === void 0 ? void 0 : validateFrpVhostHttpPort(record.vhostHttpPort);
11069
11075
  const publicPort = record.publicPort === void 0 ? void 0 : validateFrpPublicPort(record.publicPort);
11076
+ const user = record.user === void 0 || record.user === "" ? void 0 : validateFrpUser(record.user);
11070
11077
  const resolvedMode = mode ?? "deploy";
11071
11078
  const resolvedEntryTls = entryTls ?? "public-ip-cert";
11072
11079
  if (resolvedEntryTls === "self-signed" && resolvedMode !== "attach") throw new Error("frp_entry_tls_invalid");
@@ -11082,7 +11089,8 @@ function parseFrpSettings(value) {
11082
11089
  ...mode === void 0 ? {} : { mode },
11083
11090
  ...entryTls === void 0 ? {} : { entryTls },
11084
11091
  ...vhostHttpPort === void 0 ? {} : { vhostHttpPort },
11085
- ...publicPort === void 0 ? {} : { publicPort }
11092
+ ...publicPort === void 0 ? {} : { publicPort },
11093
+ ...user === void 0 ? {} : { user }
11086
11094
  });
11087
11095
  }
11088
11096
  /**
@@ -11101,6 +11109,7 @@ function mergeSavedFrpSettings(partial, saved) {
11101
11109
  const entryTls = pick("entryTls");
11102
11110
  const vhostHttpPort = pick("vhostHttpPort");
11103
11111
  const publicPort = pick("publicPort");
11112
+ const user = partial.user === void 0 ? saved?.user : partial.user;
11104
11113
  const merged = {
11105
11114
  serverAddress: partial.serverAddress === "" || partial.serverAddress === void 0 ? saved?.serverAddress : partial.serverAddress,
11106
11115
  serverPort: typeof partial.serverPort === "number" && Number.isSafeInteger(partial.serverPort) && partial.serverPort >= 1 ? partial.serverPort : saved?.serverPort,
@@ -11109,7 +11118,8 @@ function mergeSavedFrpSettings(partial, saved) {
11109
11118
  ...mode === "" || mode === void 0 ? {} : { mode },
11110
11119
  ...entryTls === "" || entryTls === void 0 ? {} : { entryTls },
11111
11120
  ...vhostHttpPort === "" || vhostHttpPort === void 0 ? {} : { vhostHttpPort },
11112
- ...publicPort === "" || publicPort === void 0 ? {} : { publicPort }
11121
+ ...publicPort === "" || publicPort === void 0 ? {} : { publicPort },
11122
+ ...user === "" || user === void 0 ? {} : { user }
11113
11123
  };
11114
11124
  if (merged.serverAddress === void 0 && merged.serverPort === void 0 && merged.token === void 0 && merged.publicOrigin === void 0) throw new Error("frp_config_missing");
11115
11125
  return parseFrpSettings(merged);
@@ -11179,6 +11189,7 @@ function createFrpcToml(settings, localPort, proxyName = FRP_DEFAULT_PROXY_NAME)
11179
11189
  `serverPort = ${String(settings.serverPort)}`,
11180
11190
  "auth.method = \"token\"",
11181
11191
  `auth.token = ${tomlString(settings.token)}`,
11192
+ ...settings.user === void 0 ? [] : [`user = ${tomlString(settings.user)}`],
11182
11193
  "transport.tls.enable = true",
11183
11194
  "",
11184
11195
  "[[proxies]]",
@@ -11291,7 +11302,8 @@ var FrpConfigStore = class {
11291
11302
  ...this.errorCode === void 0 ? {} : { errorCode: this.errorCode },
11292
11303
  ...mode === void 0 || mode === "deploy" ? {} : { mode },
11293
11304
  ...entryTls === void 0 || entryTls === "public-ip-cert" ? {} : { entryTls },
11294
- ...settings?.publicPort === void 0 ? {} : { publicPort: settings.publicPort }
11305
+ ...settings?.publicPort === void 0 ? {} : { publicPort: settings.publicPort },
11306
+ ...settings?.user === void 0 ? {} : { user: settings.user }
11295
11307
  });
11296
11308
  }
11297
11309
  /** Return private settings only to the provider lifecycle. */
@@ -13652,7 +13664,7 @@ var OriginController = class {
13652
13664
  const MAX_ARCHIVE_LIST_BYTES = 1048576;
13653
13665
  const MAX_ARCHIVE_ENTRIES = 256;
13654
13666
  const DOWNLOAD_TIMEOUT_MS = 3e5;
13655
- const CLOUDFLARED_VERSION = "2026.9.1";
13667
+ const CLOUDFLARED_VERSION = "2026.10.7";
13656
13668
  /** Pinned official cloudflared release metadata for supported desktop targets. */
13657
13669
  const CLOUDFLARED_COMPONENT_RELEASES = Object.freeze(Object.fromEntries([
13658
13670
  {
@@ -13660,44 +13672,44 @@ const CLOUDFLARED_COMPONENT_RELEASES = Object.freeze(Object.fromEntries([
13660
13672
  platform: "win32",
13661
13673
  arch: "x64",
13662
13674
  downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-windows-amd64.exe`,
13663
- downloadBytes: 54976432,
13664
- downloadSha256: "2837888cc0f5d58f15b6dc478376de90b4d3ba5241c7947455d1e0a0df429712",
13675
+ downloadBytes: 55390512,
13676
+ downloadSha256: "2493dbc168923bb3e59e5e7716f71deced92992eea9acd6e9d74f84ac7751343",
13665
13677
  executableName: "cloudflared.exe",
13666
- executableBytes: 54976432,
13667
- executableSha256: "2837888cc0f5d58f15b6dc478376de90b4d3ba5241c7947455d1e0a0df429712"
13678
+ executableBytes: 55390512,
13679
+ executableSha256: "2493dbc168923bb3e59e5e7716f71deced92992eea9acd6e9d74f84ac7751343"
13668
13680
  },
13669
13681
  {
13670
13682
  version: CLOUDFLARED_VERSION,
13671
13683
  platform: "linux",
13672
13684
  arch: "x64",
13673
13685
  downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64`,
13674
- downloadBytes: 39838488,
13675
- downloadSha256: "03f1f25d1cc93b9ad6c60569d44060bc4f17ed97075760ed8cfca4b12dcd68cc",
13686
+ downloadBytes: 40274445,
13687
+ downloadSha256: "1f99fb518b83dec2ba8d870a7cc991d82a77e6196295a3d2754ba17ab9f91872",
13676
13688
  executableName: "cloudflared",
13677
- executableBytes: 39838488,
13678
- executableSha256: "03f1f25d1cc93b9ad6c60569d44060bc4f17ed97075760ed8cfca4b12dcd68cc"
13689
+ executableBytes: 40274445,
13690
+ executableSha256: "1f99fb518b83dec2ba8d870a7cc991d82a77e6196295a3d2754ba17ab9f91872"
13679
13691
  },
13680
13692
  {
13681
13693
  version: CLOUDFLARED_VERSION,
13682
13694
  platform: "linux",
13683
13695
  arch: "arm64",
13684
13696
  downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64`,
13685
- downloadBytes: 37466252,
13686
- downloadSha256: "3d97437c71848bd8df68041e12436b484a661d95073ea1937f01a845ce88faa3",
13697
+ downloadBytes: 37667293,
13698
+ downloadSha256: "3df43aedc9ff291d1735b5cb576cb97e0b157488796db2e32b28aecabb445775",
13687
13699
  executableName: "cloudflared",
13688
- executableBytes: 37466252,
13689
- executableSha256: "3d97437c71848bd8df68041e12436b484a661d95073ea1937f01a845ce88faa3"
13700
+ executableBytes: 37667293,
13701
+ executableSha256: "3df43aedc9ff291d1735b5cb576cb97e0b157488796db2e32b28aecabb445775"
13690
13702
  },
13691
13703
  {
13692
13704
  version: CLOUDFLARED_VERSION,
13693
13705
  platform: "darwin",
13694
13706
  arch: "x64",
13695
13707
  downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-darwin-amd64.tgz`,
13696
- downloadBytes: 21118723,
13697
- downloadSha256: "ff0d3b51d5ff70eceef89d6b32145fee985018a2174596a5dbe405e2766e2ac4",
13708
+ downloadBytes: 21834416,
13709
+ downloadSha256: "02f3edfd87684dd5b705607688e4eb4b323f6e777b81e7b3a85af556aa356848",
13698
13710
  executableName: "cloudflared",
13699
- executableBytes: 41731488,
13700
- executableSha256: "1ea07ae775b03236bd6be18ca1848d6bdc4af2f4f3bce398823b5a36e5761b75",
13711
+ executableBytes: 42430512,
13712
+ executableSha256: "5c55e77da7012cebca19170357e196176c48c9647cc2ead404702fa125ddbd86",
13701
13713
  archive: {
13702
13714
  format: "tgz",
13703
13715
  member: "cloudflared"
@@ -13708,11 +13720,11 @@ const CLOUDFLARED_COMPONENT_RELEASES = Object.freeze(Object.fromEntries([
13708
13720
  platform: "darwin",
13709
13721
  arch: "arm64",
13710
13722
  downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-darwin-arm64.tgz`,
13711
- downloadBytes: 19217478,
13712
- downloadSha256: "c27ab8fd0aa489449e3d201eb02f957ef460a13b613662928b1b23394bf1bcfe",
13723
+ downloadBytes: 19847161,
13724
+ downloadSha256: "3586ccf397e98e5f437e60e7c307ca6ac463e110580503af8bb8e6f9ebf831bd",
13713
13725
  executableName: "cloudflared",
13714
- executableBytes: 38893072,
13715
- executableSha256: "9a0b19f67dc7a3011bc6b972c7ce06a5fcea8784ac6bd599ffa382ea4aeb5a6e",
13726
+ executableBytes: 39487168,
13727
+ executableSha256: "2136431d5eb467bf447d2ee514b713e192719b143aec1d9e09bf7dc290d5bb12",
13716
13728
  archive: {
13717
13729
  format: "tgz",
13718
13730
  member: "cloudflared"
@@ -15378,6 +15390,14 @@ function validatePairingDescriptor(value, options = {}) {
15378
15390
  function encodePairingDescriptor(value, options = {}) {
15379
15391
  return JSON.stringify(validatePairingDescriptor(value, options));
15380
15392
  }
15393
+ /** Draw the canonical pairing payload with its four-module quiet zone inside the SVG. */
15394
+ function renderIrohPairingQr(encoded) {
15395
+ return QRCode.toString(encoded, {
15396
+ type: "svg",
15397
+ margin: 4,
15398
+ errorCorrectionLevel: "M"
15399
+ });
15400
+ }
15381
15401
  /** Decode untrusted QR text, validating its expiry and public CA before use. */
15382
15402
  function decodePairingDescriptor(encoded, options = {}) {
15383
15403
  if (typeof encoded !== "string" || Buffer.byteLength(encoded, "utf8") > 2200) reject("pairing_descriptor_too_large");
@@ -24368,7 +24388,8 @@ async function apply(ctx, config) {
24368
24388
  }, cpolarComponent.status(), cloudflaredComponent.status(), cloudflaredTunnel.status(), frpComponent.status(), frpConfig.status(), originConfig.status(), caddyComponent.status(), irohComponent.status()),
24369
24389
  originMode: originController.mode(),
24370
24390
  caddyConfiguration: caddyConfig.status(),
24371
- caddyState: caddyController.status()
24391
+ caddyState: caddyController.status(),
24392
+ ...process.platform === "darwin" ? { macosHost: true } : {}
24372
24393
  });
24373
24394
  const lanPayload = () => ({
24374
24395
  ...loaded.kind === "unconfigured" ? {
@@ -24866,7 +24887,12 @@ async function apply(ctx, config) {
24866
24887
  if (request.method === "POST" && target.decodedPathname === `/api/mobile-access/remote/frp/configure`) {
24867
24888
  const body = await readJsonObject(request, 4096);
24868
24889
  await remoteProviders.mutate(async () => {
24869
- await frpConfig.configure(body);
24890
+ const saved = frpConfig.settings();
24891
+ const reuseToken = body.token === "" && saved !== void 0 && body.serverAddress === saved.serverAddress && body.serverPort === saved.serverPort;
24892
+ await frpConfig.configure(reuseToken ? {
24893
+ ...body,
24894
+ token: saved.token
24895
+ } : body);
24870
24896
  if (remoteControllers.frp.status().enabled) await remoteControllers.frp.reconnect();
24871
24897
  });
24872
24898
  sendJson(response, 200, remotePayload(), false);
@@ -24913,10 +24939,7 @@ async function apply(ctx, config) {
24913
24939
  });
24914
24940
  let qrSvg = "";
24915
24941
  try {
24916
- qrSvg = await QRCode.toString(pairing.encoded, {
24917
- type: "svg",
24918
- margin: 1
24919
- });
24942
+ qrSvg = await renderIrohPairingQr(pairing.encoded);
24920
24943
  } catch {}
24921
24944
  logger.info("iroh pairing window opened");
24922
24945
  sendJson(response, 201, {