dsh-mobile 0.7.0 → 0.7.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/CHANGELOG.md +11 -0
- package/CONTRIBUTORS.md +3 -0
- package/README.en.md +28 -16
- package/README.md +28 -16
- package/SECURITY.md +7 -0
- package/apps/mobile/README.md +1 -1
- package/apps/mobile/README.zh-CN.md +1 -1
- package/docs/CADDY_MANAGED.en.md +1 -1
- package/docs/CADDY_MANAGED.md +1 -1
- package/docs/CLOUDFLARE_TUNNEL.en.md +5 -5
- package/docs/CLOUDFLARE_TUNNEL.md +5 -5
- package/docs/CPOLAR_SUBDOMAIN.en.md +2 -2
- package/docs/CPOLAR_SUBDOMAIN.md +2 -2
- package/docs/IROH.en.md +24 -3
- package/docs/IROH.md +24 -3
- package/docs/README.md +1 -1
- package/docs/SELF_HOSTED_ORIGIN.en.md +2 -2
- package/docs/SELF_HOSTED_ORIGIN.md +3 -3
- package/lib/client.js +836 -234
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +2 -0
- package/lib/index.mjs +114 -6
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-layout.js +3 -0
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +1 -1
- package/packages/question-fixes/package.json +1 -1
package/lib/index.d.mts
CHANGED
|
@@ -2451,6 +2451,8 @@ export declare class IrohComponentManager {
|
|
|
2451
2451
|
private findCompanionExecutable;
|
|
2452
2452
|
private downloadArtifact;
|
|
2453
2453
|
private publishBytes;
|
|
2454
|
+
/** Remove the installed sidecar component from the plugin private directory. */
|
|
2455
|
+
purge(): Promise<IrohComponentStatus>;
|
|
2454
2456
|
private refresh;
|
|
2455
2457
|
}
|
|
2456
2458
|
//#endregion
|
package/lib/index.mjs
CHANGED
|
@@ -14589,6 +14589,22 @@ var IrohComponentManager = class {
|
|
|
14589
14589
|
if (failures.length > 0) throw new AggregateError(failures, "iroh_component_cleanup_failed");
|
|
14590
14590
|
}
|
|
14591
14591
|
}
|
|
14592
|
+
/** Remove the installed sidecar component from the plugin private directory. */
|
|
14593
|
+
async purge() {
|
|
14594
|
+
return this.enqueue(async () => {
|
|
14595
|
+
const failures = [];
|
|
14596
|
+
try {
|
|
14597
|
+
await removeIrohTree(this.stateDirectory, this.componentStorage);
|
|
14598
|
+
} catch (error) {
|
|
14599
|
+
if (error.code !== "ENOENT") failures.push(error);
|
|
14600
|
+
}
|
|
14601
|
+
this.cachedExecutable = void 0;
|
|
14602
|
+
this.origin = void 0;
|
|
14603
|
+
this.cached = this.baseStatus();
|
|
14604
|
+
if (failures.length > 0) throw new AggregateError(failures, "iroh_component_cleanup_failed");
|
|
14605
|
+
return this.cached;
|
|
14606
|
+
});
|
|
14607
|
+
}
|
|
14592
14608
|
async refresh() {
|
|
14593
14609
|
if (this.artifact === void 0) {
|
|
14594
14610
|
this.origin = void 0;
|
|
@@ -19297,9 +19313,37 @@ function androidRelease(version, tag) {
|
|
|
19297
19313
|
if (typeof version !== "string" || !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u.test(version) || parseSemver(version) === void 0 || tag !== `v${version}` && tag !== `android-v${version}`) return void 0;
|
|
19298
19314
|
return Object.freeze({
|
|
19299
19315
|
version,
|
|
19316
|
+
tag,
|
|
19300
19317
|
downloadUrl: `https://github.com/saya-ch/dsh-mobile/releases/download/${tag}/dsh-mobile-android-v${version}.apk`
|
|
19301
19318
|
});
|
|
19302
19319
|
}
|
|
19320
|
+
/** Read the published APK byte sizes from the GitHub release assets (best effort). */
|
|
19321
|
+
async function fetchAndroidAssetSizes(fetcher, version, tag, signal) {
|
|
19322
|
+
try {
|
|
19323
|
+
const response = await fetcher(`${GITHUB_RELEASE_API}${encodeURIComponent(tag)}`, {
|
|
19324
|
+
headers: {
|
|
19325
|
+
accept: "application/vnd.github+json",
|
|
19326
|
+
"user-agent": "dsh-mobile-release-check"
|
|
19327
|
+
},
|
|
19328
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS)])
|
|
19329
|
+
});
|
|
19330
|
+
if (!response.ok) return {};
|
|
19331
|
+
const payload = await response.json();
|
|
19332
|
+
if (payload === null || typeof payload !== "object" || !("assets" in payload) || !Array.isArray(payload.assets)) return {};
|
|
19333
|
+
const assets = payload.assets;
|
|
19334
|
+
const sizeOf = (name) => {
|
|
19335
|
+
for (const asset of assets) if (asset !== null && typeof asset === "object" && "name" in asset && asset.name === name && "size" in asset && typeof asset.size === "number" && Number.isSafeInteger(asset.size) && asset.size > 0) return asset.size;
|
|
19336
|
+
};
|
|
19337
|
+
const compatBytes = sizeOf(`dsh-mobile-android-v${version}.apk`);
|
|
19338
|
+
const irohBytes = sizeOf(`dsh-mobile-android-v${version}-iroh.apk`);
|
|
19339
|
+
return {
|
|
19340
|
+
...compatBytes === void 0 ? {} : { compatBytes },
|
|
19341
|
+
...irohBytes === void 0 ? {} : { irohBytes }
|
|
19342
|
+
};
|
|
19343
|
+
} catch {
|
|
19344
|
+
return {};
|
|
19345
|
+
}
|
|
19346
|
+
}
|
|
19303
19347
|
function androidReleaseDescriptor(payload) {
|
|
19304
19348
|
if (typeof payload !== "object" || payload === null) return void 0;
|
|
19305
19349
|
const fields = payload;
|
|
@@ -19518,6 +19562,9 @@ var PluginReleaseManager = class {
|
|
|
19518
19562
|
now;
|
|
19519
19563
|
cache;
|
|
19520
19564
|
activeUpdate;
|
|
19565
|
+
assetLookupOwner = new AbortController();
|
|
19566
|
+
assetLookup;
|
|
19567
|
+
assetSizes;
|
|
19521
19568
|
constructor(options) {
|
|
19522
19569
|
this.profileDirectory = options.profileDirectory;
|
|
19523
19570
|
this.installedVersion = options.installedVersion ?? DSH_MOBILE_VERSION;
|
|
@@ -19533,12 +19580,16 @@ var PluginReleaseManager = class {
|
|
|
19533
19580
|
const npmVersion = fetchNpmVersion(this.fetcher);
|
|
19534
19581
|
const [npmResult, androidResult, notesResult] = await Promise.allSettled([
|
|
19535
19582
|
npmVersion,
|
|
19536
|
-
fetchAndroidRelease(this.fetcher)
|
|
19583
|
+
fetchAndroidRelease(this.fetcher).then((android) => {
|
|
19584
|
+
if (android !== void 0) this.startAssetLookup(android);
|
|
19585
|
+
return android;
|
|
19586
|
+
}),
|
|
19537
19587
|
npmVersion.then((version) => version === void 0 ? void 0 : fetchReleaseNotes(this.fetcher, version))
|
|
19538
19588
|
]);
|
|
19539
19589
|
const latestVersion = npmResult.status === "fulfilled" ? npmResult.value : void 0;
|
|
19540
19590
|
const android = androidResult.status === "fulfilled" ? androidResult.value : void 0;
|
|
19541
19591
|
const releaseNotes = notesResult.status === "fulfilled" ? notesResult.value : void 0;
|
|
19592
|
+
const androidSizes = this.assetSizes?.downloadUrl === android?.downloadUrl ? this.assetSizes : void 0;
|
|
19542
19593
|
const comparison = latestVersion === void 0 ? void 0 : comparePluginVersions(latestVersion, this.installedVersion);
|
|
19543
19594
|
const status = Object.freeze({
|
|
19544
19595
|
installedVersion: this.installedVersion,
|
|
@@ -19547,6 +19598,8 @@ var PluginReleaseManager = class {
|
|
|
19547
19598
|
updateSupported,
|
|
19548
19599
|
...android === void 0 ? {} : { androidVersion: android.version },
|
|
19549
19600
|
androidDownloadUrl: android?.downloadUrl ?? GITHUB_RELEASES_URL,
|
|
19601
|
+
...androidSizes?.compatBytes === void 0 ? {} : { androidCompatBytes: androidSizes.compatBytes },
|
|
19602
|
+
...androidSizes?.irohBytes === void 0 ? {} : { androidIrohBytes: androidSizes.irohBytes },
|
|
19550
19603
|
...releaseNotes === void 0 ? {} : { releaseNotes }
|
|
19551
19604
|
});
|
|
19552
19605
|
this.cache = {
|
|
@@ -19555,6 +19608,31 @@ var PluginReleaseManager = class {
|
|
|
19555
19608
|
};
|
|
19556
19609
|
return status;
|
|
19557
19610
|
}
|
|
19611
|
+
/** Cancel and drain the optional APK-size lookup without delaying provider cleanup. */
|
|
19612
|
+
async dispose() {
|
|
19613
|
+
this.assetLookupOwner.abort();
|
|
19614
|
+
await this.assetLookup;
|
|
19615
|
+
}
|
|
19616
|
+
startAssetLookup(android) {
|
|
19617
|
+
if (this.assetLookupOwner.signal.aborted || this.assetLookup !== void 0 || this.assetSizes?.downloadUrl === android.downloadUrl) return;
|
|
19618
|
+
this.assetLookup = fetchAndroidAssetSizes(this.fetcher, android.version, android.tag, this.assetLookupOwner.signal).then((sizes) => {
|
|
19619
|
+
if (this.assetLookupOwner.signal.aborted) return;
|
|
19620
|
+
this.assetSizes = {
|
|
19621
|
+
downloadUrl: android.downloadUrl,
|
|
19622
|
+
...sizes
|
|
19623
|
+
};
|
|
19624
|
+
if (this.cache?.status.androidDownloadUrl === android.downloadUrl) this.cache = {
|
|
19625
|
+
...this.cache,
|
|
19626
|
+
status: Object.freeze({
|
|
19627
|
+
...this.cache.status,
|
|
19628
|
+
...sizes.compatBytes === void 0 ? {} : { androidCompatBytes: sizes.compatBytes },
|
|
19629
|
+
...sizes.irohBytes === void 0 ? {} : { androidIrohBytes: sizes.irohBytes }
|
|
19630
|
+
})
|
|
19631
|
+
};
|
|
19632
|
+
}).finally(() => {
|
|
19633
|
+
this.assetLookup = void 0;
|
|
19634
|
+
});
|
|
19635
|
+
}
|
|
19558
19636
|
/** Install the latest npm release into the active profile, then require a DSH restart. */
|
|
19559
19637
|
async update() {
|
|
19560
19638
|
if (this.activeUpdate !== void 0) return this.activeUpdate;
|
|
@@ -19908,6 +19986,7 @@ function installedDshVersion() {
|
|
|
19908
19986
|
function mapAdminError(error) {
|
|
19909
19987
|
if (error instanceof HttpError) return error;
|
|
19910
19988
|
if (error instanceof MobileExtensionError) return new HttpError(error.status, error.code);
|
|
19989
|
+
if (error instanceof Error && error.message === "remote_provider_changed") return new HttpError(409, "remote_provider_changed");
|
|
19911
19990
|
const code = error.code;
|
|
19912
19991
|
if (error instanceof Error && error.message.includes("spawn UNKNOWN")) return new HttpError(409, "frp_component_launch_failed");
|
|
19913
19992
|
if (code === "EADDRNOTAVAIL") return new HttpError(409, "network_address_changed");
|
|
@@ -20918,10 +20997,14 @@ async function apply(ctx, config) {
|
|
|
20918
20997
|
}
|
|
20919
20998
|
if (request.method === "POST" && target.decodedPathname === `/api/mobile-access/remote/cloudflared/tunnel`) {
|
|
20920
20999
|
const body = await readJsonObject(request, 8192);
|
|
20921
|
-
|
|
21000
|
+
if (body.connect !== void 0 && typeof body.connect !== "boolean") throw new HttpError(400, "bad_request");
|
|
21001
|
+
const configure = async () => {
|
|
20922
21002
|
await cloudflaredTunnel.configure(mergeSavedCloudflaredTunnelSettings(body, cloudflaredTunnel.settings()));
|
|
20923
|
-
if (remoteControllers.cloudflared.status().enabled) await remoteControllers.cloudflared.
|
|
20924
|
-
|
|
21003
|
+
if (body.connect === true && !remoteControllers.cloudflared.status().enabled) await remoteControllers.cloudflared.setEnabled(true);
|
|
21004
|
+
else if (remoteControllers.cloudflared.status().enabled) await remoteControllers.cloudflared.reconnect();
|
|
21005
|
+
};
|
|
21006
|
+
if (body.connect === true) await remoteProviders.mutateSelected("cloudflared", configure);
|
|
21007
|
+
else await remoteProviders.mutate(configure);
|
|
20925
21008
|
sendJson(response, 200, remotePayload(), false);
|
|
20926
21009
|
return;
|
|
20927
21010
|
}
|
|
@@ -20969,6 +21052,23 @@ async function apply(ctx, config) {
|
|
|
20969
21052
|
sendJson(response, 200, remotePayload(), false);
|
|
20970
21053
|
return;
|
|
20971
21054
|
}
|
|
21055
|
+
if (request.method === "POST" && target.decodedPathname === `/api/mobile-access/remote/iroh/component/purge`) {
|
|
21056
|
+
if ((await readJsonObject(request, 4096)).confirm !== true) throw new HttpError(400, "bad_request");
|
|
21057
|
+
logger.info("iroh component purge started");
|
|
21058
|
+
try {
|
|
21059
|
+
await remoteProviders.mutate(async () => {
|
|
21060
|
+
if (remoteControllers.iroh.status().enabled) throw new HttpError(409, "iroh_component_running");
|
|
21061
|
+
await remoteControllers.iroh.setEnabled(false);
|
|
21062
|
+
await irohComponent.purge();
|
|
21063
|
+
});
|
|
21064
|
+
logger.info("iroh component purge completed");
|
|
21065
|
+
} catch (error) {
|
|
21066
|
+
logger.error("iroh component purge failed: %s", error instanceof Error ? error.stack ?? error.message : String(error));
|
|
21067
|
+
throw error;
|
|
21068
|
+
}
|
|
21069
|
+
sendJson(response, 200, remotePayload(), false);
|
|
21070
|
+
return;
|
|
21071
|
+
}
|
|
20972
21072
|
if (request.method === "POST" && target.decodedPathname === `/api/mobile-access/remote/iroh/pairing/begin`) {
|
|
20973
21073
|
await readJsonObject(request, 4096);
|
|
20974
21074
|
logger.info("iroh pairing window requested");
|
|
@@ -21134,9 +21234,15 @@ async function apply(ctx, config) {
|
|
|
21134
21234
|
return;
|
|
21135
21235
|
}
|
|
21136
21236
|
if (request.method === "POST" && target.decodedPathname === `/api/mobile-access/remote/control`) {
|
|
21137
|
-
const
|
|
21237
|
+
const body = await readJsonObject(request, 4096);
|
|
21238
|
+
const running = body.running;
|
|
21138
21239
|
if (typeof running !== "boolean") throw new HttpError(400, "bad_request");
|
|
21139
|
-
|
|
21240
|
+
if (body.provider !== void 0 && body.provider !== "tailscale" && body.provider !== "cpolar" && body.provider !== "cloudflared" && body.provider !== "frp" && body.provider !== "origin" && body.provider !== "iroh") throw new HttpError(400, "bad_request");
|
|
21241
|
+
const control = async (controller) => {
|
|
21242
|
+
await controller.setEnabled(running);
|
|
21243
|
+
};
|
|
21244
|
+
if (body.provider !== void 0) await remoteProviders.mutateSelected(body.provider, control);
|
|
21245
|
+
else await remoteProviders.mutate(control);
|
|
21140
21246
|
sendJson(response, 200, remotePayload(), false);
|
|
21141
21247
|
return;
|
|
21142
21248
|
}
|
|
@@ -21256,6 +21362,7 @@ async function apply(ctx, config) {
|
|
|
21256
21362
|
await settleCleanupSteps([
|
|
21257
21363
|
stopAdmin,
|
|
21258
21364
|
drainRemoteWork,
|
|
21365
|
+
() => releaseManager.dispose(),
|
|
21259
21366
|
() => trustedNetworksWrite,
|
|
21260
21367
|
disposeMobileCommand,
|
|
21261
21368
|
disposeTaskEvents,
|
|
@@ -21276,6 +21383,7 @@ async function apply(ctx, config) {
|
|
|
21276
21383
|
await settleCleanupSteps([
|
|
21277
21384
|
stopAdmin,
|
|
21278
21385
|
drainRemoteWork,
|
|
21386
|
+
() => releaseManager.dispose(),
|
|
21279
21387
|
() => trustedNetworksWrite,
|
|
21280
21388
|
disposeMobileCommand,
|
|
21281
21389
|
disposeTaskEvents,
|